• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Shared library add-on to iptables to add multiple TCP port support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <iptables.h>
8 #include <netinet/in.h>
9 /* To ensure that iptables compiles with an old kernel */
10 #include "../include/linux/netfilter_ipv4/ipt_multiport.h"
11 
12 /* Function which prints out usage message. */
13 static void
help(void)14 help(void)
15 {
16 	printf(
17 "multiport v%s options:\n"
18 " --source-ports port[,port,port...]\n"
19 " --sports ...\n"
20 "				match source port(s)\n"
21 " --destination-ports port[,port,port...]\n"
22 " --dports ...\n"
23 "				match destination port(s)\n"
24 " --ports port[,port,port]\n"
25 "				match both source and destination port(s)\n"
26 " NOTE: this kernel does not support port ranges in multiport.\n",
27 IPTABLES_VERSION);
28 }
29 
30 static void
help_v1(void)31 help_v1(void)
32 {
33 	printf(
34 "multiport v%s options:\n"
35 " --source-ports [!] port[,port:port,port...]\n"
36 " --sports ...\n"
37 "				match source port(s)\n"
38 " --destination-ports [!] port[,port:port,port...]\n"
39 " --dports ...\n"
40 "				match destination port(s)\n"
41 " --ports [!] port[,port:port,port]\n"
42 "				match both source and destination port(s)\n",
43 IPTABLES_VERSION);
44 }
45 
46 static struct option opts[] = {
47 	{ "source-ports", 1, 0, '1' },
48 	{ "sports", 1, 0, '1' }, /* synonym */
49 	{ "destination-ports", 1, 0, '2' },
50 	{ "dports", 1, 0, '2' }, /* synonym */
51 	{ "ports", 1, 0, '3' },
52 	{0}
53 };
54 
55 static char *
proto_to_name(u_int8_t proto)56 proto_to_name(u_int8_t proto)
57 {
58 	switch (proto) {
59 	case IPPROTO_TCP:
60 		return "tcp";
61 	case IPPROTO_UDP:
62 		return "udp";
63 	case IPPROTO_UDPLITE:
64 		return "udplite";
65 	case IPPROTO_SCTP:
66 		return "sctp";
67 	case IPPROTO_DCCP:
68 		return "dccp";
69 	default:
70 		return NULL;
71 	}
72 }
73 
74 static unsigned int
parse_multi_ports(const char * portstring,u_int16_t * ports,const char * proto)75 parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
76 {
77 	char *buffer, *cp, *next;
78 	unsigned int i;
79 
80 	buffer = strdup(portstring);
81 	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
82 
83 	for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
84 	{
85 		next=strchr(cp, ',');
86 		if (next) *next++='\0';
87 		ports[i] = parse_port(cp, proto);
88 	}
89 	if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
90 	free(buffer);
91 	return i;
92 }
93 
94 static void
parse_multi_ports_v1(const char * portstring,struct ipt_multiport_v1 * multiinfo,const char * proto)95 parse_multi_ports_v1(const char *portstring,
96 		     struct ipt_multiport_v1 *multiinfo,
97 		     const char *proto)
98 {
99 	char *buffer, *cp, *next, *range;
100 	unsigned int i;
101 	u_int16_t m;
102 
103 	buffer = strdup(portstring);
104 	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
105 
106 	for (i=0; i<IPT_MULTI_PORTS; i++)
107 		multiinfo->pflags[i] = 0;
108 
109 	for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) {
110 		next=strchr(cp, ',');
111  		if (next) *next++='\0';
112 		range = strchr(cp, ':');
113 		if (range) {
114 			if (i == IPT_MULTI_PORTS-1)
115 				exit_error(PARAMETER_PROBLEM,
116 					   "too many ports specified");
117 			*range++ = '\0';
118 		}
119 		multiinfo->ports[i] = parse_port(cp, proto);
120 		if (range) {
121 			multiinfo->pflags[i] = 1;
122 			multiinfo->ports[++i] = parse_port(range, proto);
123 			if (multiinfo->ports[i-1] >= multiinfo->ports[i])
124 				exit_error(PARAMETER_PROBLEM,
125 					   "invalid portrange specified");
126 			m <<= 1;
127 		}
128  	}
129 	multiinfo->count = i;
130  	if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
131  	free(buffer);
132 }
133 
134 /* Initialize the match. */
135 static void
init(struct ipt_entry_match * m,unsigned int * nfcache)136 init(struct ipt_entry_match *m, unsigned int *nfcache)
137 {
138 }
139 
140 static const char *
check_proto(const struct ipt_entry * entry)141 check_proto(const struct ipt_entry *entry)
142 {
143 	char *proto;
144 
145 	if (entry->ip.invflags & IPT_INV_PROTO)
146 		exit_error(PARAMETER_PROBLEM,
147 			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
148 
149 	if ((proto = proto_to_name(entry->ip.proto)) != NULL)
150 		return proto;
151 	else if (!entry->ip.proto)
152 		exit_error(PARAMETER_PROBLEM,
153 			   "multiport needs `-p tcp', `-p udp', `-p udplite', "
154 			   "`-p sctp' or `-p dccp'");
155 	else
156 		exit_error(PARAMETER_PROBLEM,
157 			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
158 }
159 
160 /* Function which parses command options; returns true if it
161    ate an option */
162 static int
parse(int c,char ** argv,int invert,unsigned int * flags,const struct ipt_entry * entry,unsigned int * nfcache,struct ipt_entry_match ** match)163 parse(int c, char **argv, int invert, unsigned int *flags,
164       const struct ipt_entry *entry,
165       unsigned int *nfcache,
166       struct ipt_entry_match **match)
167 {
168 	const char *proto;
169 	struct ipt_multiport *multiinfo
170 		= (struct ipt_multiport *)(*match)->data;
171 
172 	switch (c) {
173 	case '1':
174 		check_inverse(argv[optind-1], &invert, &optind, 0);
175 		proto = check_proto(entry);
176 		multiinfo->count = parse_multi_ports(argv[optind-1],
177 						     multiinfo->ports, proto);
178 		multiinfo->flags = IPT_MULTIPORT_SOURCE;
179 		break;
180 
181 	case '2':
182 		check_inverse(argv[optind-1], &invert, &optind, 0);
183 		proto = check_proto(entry);
184 		multiinfo->count = parse_multi_ports(argv[optind-1],
185 						     multiinfo->ports, proto);
186 		multiinfo->flags = IPT_MULTIPORT_DESTINATION;
187 		break;
188 
189 	case '3':
190 		check_inverse(argv[optind-1], &invert, &optind, 0);
191 		proto = check_proto(entry);
192 		multiinfo->count = parse_multi_ports(argv[optind-1],
193 						     multiinfo->ports, proto);
194 		multiinfo->flags = IPT_MULTIPORT_EITHER;
195 		break;
196 
197 	default:
198 		return 0;
199 	}
200 
201 	if (invert)
202 		exit_error(PARAMETER_PROBLEM,
203 			   "multiport does not support invert");
204 
205 	if (*flags)
206 		exit_error(PARAMETER_PROBLEM,
207 			   "multiport can only have one option");
208 	*flags = 1;
209 	return 1;
210 }
211 
212 static int
parse_v1(int c,char ** argv,int invert,unsigned int * flags,const struct ipt_entry * entry,unsigned int * nfcache,struct ipt_entry_match ** match)213 parse_v1(int c, char **argv, int invert, unsigned int *flags,
214 	 const struct ipt_entry *entry,
215 	 unsigned int *nfcache,
216 	 struct ipt_entry_match **match)
217 {
218 	const char *proto;
219 	struct ipt_multiport_v1 *multiinfo
220 		= (struct ipt_multiport_v1 *)(*match)->data;
221 
222 	switch (c) {
223 	case '1':
224 		check_inverse(argv[optind-1], &invert, &optind, 0);
225 		proto = check_proto(entry);
226 		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
227 		multiinfo->flags = IPT_MULTIPORT_SOURCE;
228 		break;
229 
230 	case '2':
231 		check_inverse(argv[optind-1], &invert, &optind, 0);
232 		proto = check_proto(entry);
233 		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
234 		multiinfo->flags = IPT_MULTIPORT_DESTINATION;
235 		break;
236 
237 	case '3':
238 		check_inverse(argv[optind-1], &invert, &optind, 0);
239 		proto = check_proto(entry);
240 		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
241 		multiinfo->flags = IPT_MULTIPORT_EITHER;
242 		break;
243 
244 	default:
245 		return 0;
246 	}
247 
248 	if (invert)
249 		multiinfo->invert = 1;
250 
251 	if (*flags)
252 		exit_error(PARAMETER_PROBLEM,
253 			   "multiport can only have one option");
254 	*flags = 1;
255 	return 1;
256 }
257 
258 /* Final check; must specify something. */
259 static void
final_check(unsigned int flags)260 final_check(unsigned int flags)
261 {
262 	if (!flags)
263 		exit_error(PARAMETER_PROBLEM, "multiport expection an option");
264 }
265 
266 static char *
port_to_service(int port,u_int8_t proto)267 port_to_service(int port, u_int8_t proto)
268 {
269 	struct servent *service;
270 
271 	if ((service = getservbyport(htons(port), proto_to_name(proto))))
272 		return service->s_name;
273 
274 	return NULL;
275 }
276 
277 static void
print_port(u_int16_t port,u_int8_t protocol,int numeric)278 print_port(u_int16_t port, u_int8_t protocol, int numeric)
279 {
280 	char *service;
281 
282 	if (numeric || (service = port_to_service(port, protocol)) == NULL)
283 		printf("%u", port);
284 	else
285 		printf("%s", service);
286 }
287 
288 /* Prints out the matchinfo. */
289 static void
print(const struct ipt_ip * ip,const struct ipt_entry_match * match,int numeric)290 print(const struct ipt_ip *ip,
291       const struct ipt_entry_match *match,
292       int numeric)
293 {
294 	const struct ipt_multiport *multiinfo
295 		= (const struct ipt_multiport *)match->data;
296 	unsigned int i;
297 
298 	printf("multiport ");
299 
300 	switch (multiinfo->flags) {
301 	case IPT_MULTIPORT_SOURCE:
302 		printf("sports ");
303 		break;
304 
305 	case IPT_MULTIPORT_DESTINATION:
306 		printf("dports ");
307 		break;
308 
309 	case IPT_MULTIPORT_EITHER:
310 		printf("ports ");
311 		break;
312 
313 	default:
314 		printf("ERROR ");
315 		break;
316 	}
317 
318 	for (i=0; i < multiinfo->count; i++) {
319 		printf("%s", i ? "," : "");
320 		print_port(multiinfo->ports[i], ip->proto, numeric);
321 	}
322 	printf(" ");
323 }
324 
325 static void
print_v1(const struct ipt_ip * ip,const struct ipt_entry_match * match,int numeric)326 print_v1(const struct ipt_ip *ip,
327 	 const struct ipt_entry_match *match,
328 	 int numeric)
329 {
330 	const struct ipt_multiport_v1 *multiinfo
331 		= (const struct ipt_multiport_v1 *)match->data;
332 	unsigned int i;
333 
334 	printf("multiport ");
335 
336 	switch (multiinfo->flags) {
337 	case IPT_MULTIPORT_SOURCE:
338 		printf("sports ");
339 		break;
340 
341 	case IPT_MULTIPORT_DESTINATION:
342 		printf("dports ");
343 		break;
344 
345 	case IPT_MULTIPORT_EITHER:
346 		printf("ports ");
347 		break;
348 
349 	default:
350 		printf("ERROR ");
351 		break;
352 	}
353 
354 	if (multiinfo->invert)
355 		printf("! ");
356 
357 	for (i=0; i < multiinfo->count; i++) {
358 		printf("%s", i ? "," : "");
359 		print_port(multiinfo->ports[i], ip->proto, numeric);
360 		if (multiinfo->pflags[i]) {
361 			printf(":");
362 			print_port(multiinfo->ports[++i], ip->proto, numeric);
363 		}
364 	}
365 	printf(" ");
366 }
367 
368 /* Saves the union ipt_matchinfo in parsable form to stdout. */
save(const struct ipt_ip * ip,const struct ipt_entry_match * match)369 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
370 {
371 	const struct ipt_multiport *multiinfo
372 		= (const struct ipt_multiport *)match->data;
373 	unsigned int i;
374 
375 	switch (multiinfo->flags) {
376 	case IPT_MULTIPORT_SOURCE:
377 		printf("--sports ");
378 		break;
379 
380 	case IPT_MULTIPORT_DESTINATION:
381 		printf("--dports ");
382 		break;
383 
384 	case IPT_MULTIPORT_EITHER:
385 		printf("--ports ");
386 		break;
387 	}
388 
389 	for (i=0; i < multiinfo->count; i++) {
390 		printf("%s", i ? "," : "");
391 		print_port(multiinfo->ports[i], ip->proto, 1);
392 	}
393 	printf(" ");
394 }
395 
save_v1(const struct ipt_ip * ip,const struct ipt_entry_match * match)396 static void save_v1(const struct ipt_ip *ip,
397 		    const struct ipt_entry_match *match)
398 {
399 	const struct ipt_multiport_v1 *multiinfo
400 		= (const struct ipt_multiport_v1 *)match->data;
401 	unsigned int i;
402 
403 	switch (multiinfo->flags) {
404 	case IPT_MULTIPORT_SOURCE:
405 		printf("--sports ");
406 		break;
407 
408 	case IPT_MULTIPORT_DESTINATION:
409 		printf("--dports ");
410 		break;
411 
412 	case IPT_MULTIPORT_EITHER:
413 		printf("--ports ");
414 		break;
415 	}
416 
417 	if (multiinfo->invert)
418 		printf("! ");
419 
420 	for (i=0; i < multiinfo->count; i++) {
421 		printf("%s", i ? "," : "");
422 		print_port(multiinfo->ports[i], ip->proto, 1);
423 		if (multiinfo->pflags[i]) {
424 			printf(":");
425 			print_port(multiinfo->ports[++i], ip->proto, 1);
426 		}
427 	}
428 	printf(" ");
429 }
430 
431 static struct iptables_match multiport = {
432 	.next		= NULL,
433 	.name		= "multiport",
434 	.revision	= 0,
435 	.version	= IPTABLES_VERSION,
436 	.size		= IPT_ALIGN(sizeof(struct ipt_multiport)),
437 	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_multiport)),
438 	.help		= &help,
439 	.init		= &init,
440 	.parse		= &parse,
441 	.final_check	= &final_check,
442 	.print		= &print,
443 	.save		= &save,
444 	.extra_opts	= opts
445 };
446 
447 static struct iptables_match multiport_v1 = {
448 	.next		= NULL,
449 	.name		= "multiport",
450 	.version	= IPTABLES_VERSION,
451 	.revision	= 1,
452 	.size		= IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
453 	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
454 	.help		= &help_v1,
455 	.init		= &init,
456 	.parse		= &parse_v1,
457 	.final_check	= &final_check,
458 	.print		= &print_v1,
459 	.save		= &save_v1,
460 	.extra_opts	= opts
461 };
462 
463 void
ipt_multiport_init(void)464 ipt_multiport_init(void)
465 {
466 	register_match(&multiport);
467 	register_match(&multiport_v1);
468 }
469