1 #include <stdio.h>
2 #include <netdb.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <xtables.h>
6 #include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
7 #include <linux/netfilter_ipv4/ip_tables.h>
8 #include <linux/netfilter_ipv6/ip6_tables.h>
9 #include <linux/netfilter/xt_multiport.h>
10 
11 enum {
12 	O_SOURCE_PORTS = 0,
13 	O_DEST_PORTS,
14 	O_SD_PORTS,
15 	F_SOURCE_PORTS = 1 << O_SOURCE_PORTS,
16 	F_DEST_PORTS   = 1 << O_DEST_PORTS,
17 	F_SD_PORTS     = 1 << O_SD_PORTS,
18 	F_ANY          = F_SOURCE_PORTS | F_DEST_PORTS | F_SD_PORTS,
19 };
20 
21 /* Function which prints out usage message. */
multiport_help(void)22 static void multiport_help(void)
23 {
24 	printf(
25 "multiport match options:\n"
26 " --source-ports port[,port,port...]\n"
27 " --sports ...\n"
28 "				match source port(s)\n"
29 " --destination-ports port[,port,port...]\n"
30 " --dports ...\n"
31 "				match destination port(s)\n"
32 " --ports port[,port,port]\n"
33 "				match both source and destination port(s)\n"
34 " NOTE: this kernel does not support port ranges in multiport.\n");
35 }
36 
multiport_help_v1(void)37 static void multiport_help_v1(void)
38 {
39 	printf(
40 "multiport match options:\n"
41 "[!] --source-ports port[,port:port,port...]\n"
42 " --sports ...\n"
43 "				match source port(s)\n"
44 "[!] --destination-ports port[,port:port,port...]\n"
45 " --dports ...\n"
46 "				match destination port(s)\n"
47 "[!] --ports port[,port:port,port]\n"
48 "				match both source and destination port(s)\n");
49 }
50 
51 static const struct xt_option_entry multiport_opts[] = {
52 	{.name = "source-ports", .id = O_SOURCE_PORTS, .type = XTTYPE_STRING,
53 	 .excl = F_ANY, .flags = XTOPT_INVERT},
54 	{.name = "sports", .id = O_SOURCE_PORTS, .type = XTTYPE_STRING,
55 	 .excl = F_ANY, .flags = XTOPT_INVERT},
56 	{.name = "destination-ports", .id = O_DEST_PORTS,
57 	 .type = XTTYPE_STRING, .excl = F_ANY, .flags = XTOPT_INVERT},
58 	{.name = "dports", .id = O_DEST_PORTS, .type = XTTYPE_STRING,
59 	 .excl = F_ANY, .flags = XTOPT_INVERT},
60 	{.name = "ports", .id = O_SD_PORTS, .type = XTTYPE_STRING,
61 	 .excl = F_ANY, .flags = XTOPT_INVERT},
62 	XTOPT_TABLEEND,
63 };
64 
65 static const char *
proto_to_name(uint8_t proto)66 proto_to_name(uint8_t proto)
67 {
68 	switch (proto) {
69 	case IPPROTO_TCP:
70 		return "tcp";
71 	case IPPROTO_UDP:
72 		return "udp";
73 	case IPPROTO_UDPLITE:
74 		return "udplite";
75 	case IPPROTO_SCTP:
76 		return "sctp";
77 	case IPPROTO_DCCP:
78 		return "dccp";
79 	default:
80 		return NULL;
81 	}
82 }
83 
84 static unsigned int
parse_multi_ports(const char * portstring,uint16_t * ports,const char * proto)85 parse_multi_ports(const char *portstring, uint16_t *ports, const char *proto)
86 {
87 	char *buffer, *cp, *next;
88 	unsigned int i;
89 
90 	buffer = strdup(portstring);
91 	if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed");
92 
93 	for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next,i++)
94 	{
95 		next=strchr(cp, ',');
96 		if (next) *next++='\0';
97 		ports[i] = xtables_parse_port(cp, proto);
98 	}
99 	if (cp) xtables_error(PARAMETER_PROBLEM, "too many ports specified");
100 	free(buffer);
101 	return i;
102 }
103 
104 static void
parse_multi_ports_v1(const char * portstring,struct xt_multiport_v1 * multiinfo,const char * proto)105 parse_multi_ports_v1(const char *portstring,
106 		     struct xt_multiport_v1 *multiinfo,
107 		     const char *proto)
108 {
109 	char *buffer, *cp, *next, *range;
110 	unsigned int i;
111 	uint16_t m;
112 
113 	buffer = strdup(portstring);
114 	if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed");
115 
116 	for (i=0; i<XT_MULTI_PORTS; i++)
117 		multiinfo->pflags[i] = 0;
118 
119 	for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next, i++) {
120 		next=strchr(cp, ',');
121  		if (next) *next++='\0';
122 		range = strchr(cp, ':');
123 		if (range) {
124 			if (i == XT_MULTI_PORTS-1)
125 				xtables_error(PARAMETER_PROBLEM,
126 					   "too many ports specified");
127 			*range++ = '\0';
128 		}
129 		multiinfo->ports[i] = xtables_parse_port(cp, proto);
130 		if (range) {
131 			multiinfo->pflags[i] = 1;
132 			multiinfo->ports[++i] = xtables_parse_port(range, proto);
133 			if (multiinfo->ports[i-1] >= multiinfo->ports[i])
134 				xtables_error(PARAMETER_PROBLEM,
135 					   "invalid portrange specified");
136 			m <<= 1;
137 		}
138  	}
139 	multiinfo->count = i;
140 	if (cp) xtables_error(PARAMETER_PROBLEM, "too many ports specified");
141  	free(buffer);
142 }
143 
144 static const char *
check_proto(uint16_t pnum,uint8_t invflags)145 check_proto(uint16_t pnum, uint8_t invflags)
146 {
147 	const char *proto;
148 
149 	if (invflags & XT_INV_PROTO)
150 		xtables_error(PARAMETER_PROBLEM,
151 			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
152 
153 	if ((proto = proto_to_name(pnum)) != NULL)
154 		return proto;
155 	else if (!pnum)
156 		xtables_error(PARAMETER_PROBLEM,
157 			   "multiport needs `-p tcp', `-p udp', `-p udplite', "
158 			   "`-p sctp' or `-p dccp'");
159 	else
160 		xtables_error(PARAMETER_PROBLEM,
161 			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
162 }
163 
__multiport_parse(struct xt_option_call * cb,uint16_t pnum,uint8_t invflags)164 static void __multiport_parse(struct xt_option_call *cb, uint16_t pnum,
165 			      uint8_t invflags)
166 {
167 	const char *proto;
168 	struct xt_multiport *multiinfo = cb->data;
169 
170 	xtables_option_parse(cb);
171 	switch (cb->entry->id) {
172 	case O_SOURCE_PORTS:
173 		proto = check_proto(pnum, invflags);
174 		multiinfo->count = parse_multi_ports(cb->arg,
175 						     multiinfo->ports, proto);
176 		multiinfo->flags = XT_MULTIPORT_SOURCE;
177 		break;
178 	case O_DEST_PORTS:
179 		proto = check_proto(pnum, invflags);
180 		multiinfo->count = parse_multi_ports(cb->arg,
181 						     multiinfo->ports, proto);
182 		multiinfo->flags = XT_MULTIPORT_DESTINATION;
183 		break;
184 	case O_SD_PORTS:
185 		proto = check_proto(pnum, invflags);
186 		multiinfo->count = parse_multi_ports(cb->arg,
187 						     multiinfo->ports, proto);
188 		multiinfo->flags = XT_MULTIPORT_EITHER;
189 		break;
190 	}
191 	if (cb->invert)
192 		xtables_error(PARAMETER_PROBLEM,
193 			   "multiport.0 does not support invert");
194 }
195 
multiport_parse(struct xt_option_call * cb)196 static void multiport_parse(struct xt_option_call *cb)
197 {
198 	const struct ipt_entry *entry = cb->xt_entry;
199 	return __multiport_parse(cb,
200 	       entry->ip.proto, entry->ip.invflags);
201 }
202 
multiport_parse6(struct xt_option_call * cb)203 static void multiport_parse6(struct xt_option_call *cb)
204 {
205 	const struct ip6t_entry *entry = cb->xt_entry;
206 	return __multiport_parse(cb,
207 	       entry->ipv6.proto, entry->ipv6.invflags);
208 }
209 
__multiport_parse_v1(struct xt_option_call * cb,uint16_t pnum,uint8_t invflags)210 static void __multiport_parse_v1(struct xt_option_call *cb, uint16_t pnum,
211 				 uint8_t invflags)
212 {
213 	const char *proto;
214 	struct xt_multiport_v1 *multiinfo = cb->data;
215 
216 	xtables_option_parse(cb);
217 	switch (cb->entry->id) {
218 	case O_SOURCE_PORTS:
219 		proto = check_proto(pnum, invflags);
220 		parse_multi_ports_v1(cb->arg, multiinfo, proto);
221 		multiinfo->flags = XT_MULTIPORT_SOURCE;
222 		break;
223 	case O_DEST_PORTS:
224 		proto = check_proto(pnum, invflags);
225 		parse_multi_ports_v1(cb->arg, multiinfo, proto);
226 		multiinfo->flags = XT_MULTIPORT_DESTINATION;
227 		break;
228 	case O_SD_PORTS:
229 		proto = check_proto(pnum, invflags);
230 		parse_multi_ports_v1(cb->arg, multiinfo, proto);
231 		multiinfo->flags = XT_MULTIPORT_EITHER;
232 		break;
233 	}
234 	if (cb->invert)
235 		multiinfo->invert = 1;
236 }
237 
multiport_parse_v1(struct xt_option_call * cb)238 static void multiport_parse_v1(struct xt_option_call *cb)
239 {
240 	const struct ipt_entry *entry = cb->xt_entry;
241 	return __multiport_parse_v1(cb,
242 	       entry->ip.proto, entry->ip.invflags);
243 }
244 
multiport_parse6_v1(struct xt_option_call * cb)245 static void multiport_parse6_v1(struct xt_option_call *cb)
246 {
247 	const struct ip6t_entry *entry = cb->xt_entry;
248 	return __multiport_parse_v1(cb,
249 	       entry->ipv6.proto, entry->ipv6.invflags);
250 }
251 
multiport_check(struct xt_fcheck_call * cb)252 static void multiport_check(struct xt_fcheck_call *cb)
253 {
254 	if (cb->xflags == 0)
255 		xtables_error(PARAMETER_PROBLEM, "multiport expection an option");
256 }
257 
258 static const char *
port_to_service(int port,uint8_t proto)259 port_to_service(int port, uint8_t proto)
260 {
261 	const struct servent *service;
262 
263 	if ((service = getservbyport(htons(port), proto_to_name(proto))))
264 		return service->s_name;
265 
266 	return NULL;
267 }
268 
269 static void
print_port(uint16_t port,uint8_t protocol,int numeric)270 print_port(uint16_t port, uint8_t protocol, int numeric)
271 {
272 	const char *service;
273 
274 	if (numeric || (service = port_to_service(port, protocol)) == NULL)
275 		printf("%u", port);
276 	else
277 		printf("%s", service);
278 }
279 
280 static void
__multiport_print(const struct xt_entry_match * match,int numeric,uint16_t proto)281 __multiport_print(const struct xt_entry_match *match, int numeric,
282                   uint16_t proto)
283 {
284 	const struct xt_multiport *multiinfo
285 		= (const struct xt_multiport *)match->data;
286 	unsigned int i;
287 
288 	printf(" multiport ");
289 
290 	switch (multiinfo->flags) {
291 	case XT_MULTIPORT_SOURCE:
292 		printf("sports ");
293 		break;
294 
295 	case XT_MULTIPORT_DESTINATION:
296 		printf("dports ");
297 		break;
298 
299 	case XT_MULTIPORT_EITHER:
300 		printf("ports ");
301 		break;
302 
303 	default:
304 		printf("ERROR ");
305 		break;
306 	}
307 
308 	for (i=0; i < multiinfo->count; i++) {
309 		printf("%s", i ? "," : "");
310 		print_port(multiinfo->ports[i], proto, numeric);
311 	}
312 }
313 
multiport_print(const void * ip_void,const struct xt_entry_match * match,int numeric)314 static void multiport_print(const void *ip_void,
315                             const struct xt_entry_match *match, int numeric)
316 {
317 	const struct ipt_ip *ip = ip_void;
318 	__multiport_print(match, numeric, ip->proto);
319 }
320 
multiport_print6(const void * ip_void,const struct xt_entry_match * match,int numeric)321 static void multiport_print6(const void *ip_void,
322                              const struct xt_entry_match *match, int numeric)
323 {
324 	const struct ip6t_ip6 *ip = ip_void;
325 	__multiport_print(match, numeric, ip->proto);
326 }
327 
__multiport_print_v1(const struct xt_entry_match * match,int numeric,uint16_t proto)328 static void __multiport_print_v1(const struct xt_entry_match *match,
329                                  int numeric, uint16_t proto)
330 {
331 	const struct xt_multiport_v1 *multiinfo
332 		= (const struct xt_multiport_v1 *)match->data;
333 	unsigned int i;
334 
335 	printf(" multiport ");
336 
337 	switch (multiinfo->flags) {
338 	case XT_MULTIPORT_SOURCE:
339 		printf("sports ");
340 		break;
341 
342 	case XT_MULTIPORT_DESTINATION:
343 		printf("dports ");
344 		break;
345 
346 	case XT_MULTIPORT_EITHER:
347 		printf("ports ");
348 		break;
349 
350 	default:
351 		printf("ERROR ");
352 		break;
353 	}
354 
355 	if (multiinfo->invert)
356 		printf(" !");
357 
358 	for (i=0; i < multiinfo->count; i++) {
359 		printf("%s", i ? "," : "");
360 		print_port(multiinfo->ports[i], proto, numeric);
361 		if (multiinfo->pflags[i]) {
362 			printf(":");
363 			print_port(multiinfo->ports[++i], proto, numeric);
364 		}
365 	}
366 }
367 
multiport_print_v1(const void * ip_void,const struct xt_entry_match * match,int numeric)368 static void multiport_print_v1(const void *ip_void,
369                                const struct xt_entry_match *match, int numeric)
370 {
371 	const struct ipt_ip *ip = ip_void;
372 	__multiport_print_v1(match, numeric, ip->proto);
373 }
374 
multiport_print6_v1(const void * ip_void,const struct xt_entry_match * match,int numeric)375 static void multiport_print6_v1(const void *ip_void,
376                                 const struct xt_entry_match *match, int numeric)
377 {
378 	const struct ip6t_ip6 *ip = ip_void;
379 	__multiport_print_v1(match, numeric, ip->proto);
380 }
381 
__multiport_save(const struct xt_entry_match * match,uint16_t proto)382 static void __multiport_save(const struct xt_entry_match *match,
383                              uint16_t proto)
384 {
385 	const struct xt_multiport *multiinfo
386 		= (const struct xt_multiport *)match->data;
387 	unsigned int i;
388 
389 	switch (multiinfo->flags) {
390 	case XT_MULTIPORT_SOURCE:
391 		printf(" --sports ");
392 		break;
393 
394 	case XT_MULTIPORT_DESTINATION:
395 		printf(" --dports ");
396 		break;
397 
398 	case XT_MULTIPORT_EITHER:
399 		printf(" --ports ");
400 		break;
401 	}
402 
403 	for (i=0; i < multiinfo->count; i++) {
404 		printf("%s", i ? "," : "");
405 		print_port(multiinfo->ports[i], proto, 1);
406 	}
407 }
408 
multiport_save(const void * ip_void,const struct xt_entry_match * match)409 static void multiport_save(const void *ip_void,
410                            const struct xt_entry_match *match)
411 {
412 	const struct ipt_ip *ip = ip_void;
413 	__multiport_save(match, ip->proto);
414 }
415 
multiport_save6(const void * ip_void,const struct xt_entry_match * match)416 static void multiport_save6(const void *ip_void,
417                             const struct xt_entry_match *match)
418 {
419 	const struct ip6t_ip6 *ip = ip_void;
420 	__multiport_save(match, ip->proto);
421 }
422 
__multiport_save_v1(const struct xt_entry_match * match,uint16_t proto)423 static void __multiport_save_v1(const struct xt_entry_match *match,
424                                 uint16_t proto)
425 {
426 	const struct xt_multiport_v1 *multiinfo
427 		= (const struct xt_multiport_v1 *)match->data;
428 	unsigned int i;
429 
430 	if (multiinfo->invert)
431 		printf(" !");
432 
433 	switch (multiinfo->flags) {
434 	case XT_MULTIPORT_SOURCE:
435 		printf(" --sports ");
436 		break;
437 
438 	case XT_MULTIPORT_DESTINATION:
439 		printf(" --dports ");
440 		break;
441 
442 	case XT_MULTIPORT_EITHER:
443 		printf(" --ports ");
444 		break;
445 	}
446 
447 	for (i=0; i < multiinfo->count; i++) {
448 		printf("%s", i ? "," : "");
449 		print_port(multiinfo->ports[i], proto, 1);
450 		if (multiinfo->pflags[i]) {
451 			printf(":");
452 			print_port(multiinfo->ports[++i], proto, 1);
453 		}
454 	}
455 }
456 
multiport_save_v1(const void * ip_void,const struct xt_entry_match * match)457 static void multiport_save_v1(const void *ip_void,
458                               const struct xt_entry_match *match)
459 {
460 	const struct ipt_ip *ip = ip_void;
461 	__multiport_save_v1(match, ip->proto);
462 }
463 
multiport_save6_v1(const void * ip_void,const struct xt_entry_match * match)464 static void multiport_save6_v1(const void *ip_void,
465                                const struct xt_entry_match *match)
466 {
467 	const struct ip6t_ip6 *ip = ip_void;
468 	__multiport_save_v1(match, ip->proto);
469 }
470 
471 static struct xtables_match multiport_mt_reg[] = {
472 	{
473 		.family        = NFPROTO_IPV4,
474 		.name          = "multiport",
475 		.revision      = 0,
476 		.version       = XTABLES_VERSION,
477 		.size          = XT_ALIGN(sizeof(struct xt_multiport)),
478 		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport)),
479 		.help          = multiport_help,
480 		.x6_parse      = multiport_parse,
481 		.x6_fcheck     = multiport_check,
482 		.print         = multiport_print,
483 		.save          = multiport_save,
484 		.x6_options    = multiport_opts,
485 	},
486 	{
487 		.family        = NFPROTO_IPV6,
488 		.name          = "multiport",
489 		.revision      = 0,
490 		.version       = XTABLES_VERSION,
491 		.size          = XT_ALIGN(sizeof(struct xt_multiport)),
492 		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport)),
493 		.help          = multiport_help,
494 		.x6_parse      = multiport_parse6,
495 		.x6_fcheck     = multiport_check,
496 		.print         = multiport_print6,
497 		.save          = multiport_save6,
498 		.x6_options    = multiport_opts,
499 	},
500 	{
501 		.family        = NFPROTO_IPV4,
502 		.name          = "multiport",
503 		.version       = XTABLES_VERSION,
504 		.revision      = 1,
505 		.size          = XT_ALIGN(sizeof(struct xt_multiport_v1)),
506 		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)),
507 		.help          = multiport_help_v1,
508 		.x6_parse      = multiport_parse_v1,
509 		.x6_fcheck     = multiport_check,
510 		.print         = multiport_print_v1,
511 		.save          = multiport_save_v1,
512 		.x6_options    = multiport_opts,
513 	},
514 	{
515 		.family        = NFPROTO_IPV6,
516 		.name          = "multiport",
517 		.version       = XTABLES_VERSION,
518 		.revision      = 1,
519 		.size          = XT_ALIGN(sizeof(struct xt_multiport_v1)),
520 		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)),
521 		.help          = multiport_help_v1,
522 		.x6_parse      = multiport_parse6_v1,
523 		.x6_fcheck     = multiport_check,
524 		.print         = multiport_print6_v1,
525 		.save          = multiport_save6_v1,
526 		.x6_options    = multiport_opts,
527 	},
528 };
529 
530 void
_init(void)531 _init(void)
532 {
533 	xtables_register_matches(multiport_mt_reg, ARRAY_SIZE(multiport_mt_reg));
534 }
535