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