Home | History | Annotate | Download | only in extensions
      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
     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
     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 *
     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
     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
     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
    136 init(struct ipt_entry_match *m, unsigned int *nfcache)
    137 {
    138 }
    139 
    140 static const char *
    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
    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
    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
    260 final_check(unsigned int flags)
    261 {
    262 	if (!flags)
    263 		exit_error(PARAMETER_PROBLEM, "multiport expection an option");
    264 }
    265 
    266 static char *
    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
    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
    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
    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. */
    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 
    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
    464 ipt_multiport_init(void)
    465 {
    466 	register_match(&multiport);
    467 	register_match(&multiport_v1);
    468 }
    469