libxt_multiport.c revision 0463ee1f28946cc49815737daa0ced0c68f39f0b
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 8#include <xtables.h> 9#include <libiptc/libiptc.h> 10#include <libiptc/libip6tc.h> 11/* To ensure that iptables compiles with an old kernel */ 12#include "../include/linux/netfilter/xt_multiport.h" 13 14/* Function which prints out usage message. */ 15static void 16help(void) 17{ 18 printf( 19"multiport v%s options:\n" 20" --source-ports port[,port,port...]\n" 21" --sports ...\n" 22" match source port(s)\n" 23" --destination-ports port[,port,port...]\n" 24" --dports ...\n" 25" match destination port(s)\n" 26" --ports port[,port,port]\n" 27" match both source and destination port(s)\n" 28" NOTE: this kernel does not support port ranges in multiport.\n", 29IPTABLES_VERSION); 30} 31 32static void 33help_v1(void) 34{ 35 printf( 36"multiport v%s options:\n" 37" --source-ports [!] port[,port:port,port...]\n" 38" --sports ...\n" 39" match source port(s)\n" 40" --destination-ports [!] port[,port:port,port...]\n" 41" --dports ...\n" 42" match destination port(s)\n" 43" --ports [!] port[,port:port,port]\n" 44" match both source and destination port(s)\n", 45IPTABLES_VERSION); 46} 47 48static const struct option opts[] = { 49 { "source-ports", 1, NULL, '1' }, 50 { "sports", 1, NULL, '1' }, /* synonym */ 51 { "destination-ports", 1, NULL, '2' }, 52 { "dports", 1, NULL, '2' }, /* synonym */ 53 { "ports", 1, NULL, '3' }, 54 { } 55}; 56 57static char * 58proto_to_name(u_int8_t proto) 59{ 60 switch (proto) { 61 case IPPROTO_TCP: 62 return "tcp"; 63 case IPPROTO_UDP: 64 return "udp"; 65 case IPPROTO_UDPLITE: 66 return "udplite"; 67 case IPPROTO_SCTP: 68 return "sctp"; 69 case IPPROTO_DCCP: 70 return "dccp"; 71 default: 72 return NULL; 73 } 74} 75 76static unsigned int 77parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto) 78{ 79 char *buffer, *cp, *next; 80 unsigned int i; 81 82 buffer = strdup(portstring); 83 if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed"); 84 85 for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next,i++) 86 { 87 next=strchr(cp, ','); 88 if (next) *next++='\0'; 89 ports[i] = parse_port(cp, proto); 90 } 91 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified"); 92 free(buffer); 93 return i; 94} 95 96static void 97parse_multi_ports_v1(const char *portstring, 98 struct xt_multiport_v1 *multiinfo, 99 const char *proto) 100{ 101 char *buffer, *cp, *next, *range; 102 unsigned int i; 103 u_int16_t m; 104 105 buffer = strdup(portstring); 106 if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed"); 107 108 for (i=0; i<XT_MULTI_PORTS; i++) 109 multiinfo->pflags[i] = 0; 110 111 for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next, i++) { 112 next=strchr(cp, ','); 113 if (next) *next++='\0'; 114 range = strchr(cp, ':'); 115 if (range) { 116 if (i == XT_MULTI_PORTS-1) 117 exit_error(PARAMETER_PROBLEM, 118 "too many ports specified"); 119 *range++ = '\0'; 120 } 121 multiinfo->ports[i] = parse_port(cp, proto); 122 if (range) { 123 multiinfo->pflags[i] = 1; 124 multiinfo->ports[++i] = parse_port(range, proto); 125 if (multiinfo->ports[i-1] >= multiinfo->ports[i]) 126 exit_error(PARAMETER_PROBLEM, 127 "invalid portrange specified"); 128 m <<= 1; 129 } 130 } 131 multiinfo->count = i; 132 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified"); 133 free(buffer); 134} 135 136static const char * 137check_proto(u_int16_t pnum, u_int8_t invflags) 138{ 139 char *proto; 140 141 if (invflags & XT_INV_PROTO) 142 exit_error(PARAMETER_PROBLEM, 143 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 144 145 if ((proto = proto_to_name(pnum)) != NULL) 146 return proto; 147 else if (!pnum) 148 exit_error(PARAMETER_PROBLEM, 149 "multiport needs `-p tcp', `-p udp', `-p udplite', " 150 "`-p sctp' or `-p dccp'"); 151 else 152 exit_error(PARAMETER_PROBLEM, 153 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 154} 155 156/* Function which parses command options; returns true if it 157 ate an option */ 158static int 159__parse(int c, char **argv, int invert, unsigned int *flags, 160 struct xt_entry_match **match, 161 u_int16_t pnum, u_int8_t invflags) 162{ 163 const char *proto; 164 struct xt_multiport *multiinfo 165 = (struct xt_multiport *)(*match)->data; 166 167 switch (c) { 168 case '1': 169 check_inverse(argv[optind-1], &invert, &optind, 0); 170 proto = check_proto(pnum, invflags); 171 multiinfo->count = parse_multi_ports(argv[optind-1], 172 multiinfo->ports, proto); 173 multiinfo->flags = XT_MULTIPORT_SOURCE; 174 break; 175 176 case '2': 177 check_inverse(argv[optind-1], &invert, &optind, 0); 178 proto = check_proto(pnum, invflags); 179 multiinfo->count = parse_multi_ports(argv[optind-1], 180 multiinfo->ports, proto); 181 multiinfo->flags = XT_MULTIPORT_DESTINATION; 182 break; 183 184 case '3': 185 check_inverse(argv[optind-1], &invert, &optind, 0); 186 proto = check_proto(pnum, invflags); 187 multiinfo->count = parse_multi_ports(argv[optind-1], 188 multiinfo->ports, proto); 189 multiinfo->flags = XT_MULTIPORT_EITHER; 190 break; 191 192 default: 193 return 0; 194 } 195 196 if (invert) 197 exit_error(PARAMETER_PROBLEM, 198 "multiport does not support invert"); 199 200 if (*flags) 201 exit_error(PARAMETER_PROBLEM, 202 "multiport can only have one option"); 203 *flags = 1; 204 return 1; 205} 206 207static int 208parse(int c, char **argv, int invert, unsigned int *flags, 209 const void *e, 210 struct xt_entry_match **match) 211{ 212 const struct ipt_entry *entry = e; 213 return __parse(c, argv, invert, flags, match, entry->ip.proto, 214 entry->ip.invflags); 215} 216 217static int 218parse6(int c, char **argv, int invert, unsigned int *flags, 219 const void *e, 220 struct xt_entry_match **match) 221{ 222 const struct ip6t_entry *entry = (const struct ip6t_entry *)e; 223 return __parse(c, argv, invert, flags, match, entry->ipv6.proto, 224 entry->ipv6.invflags); 225} 226 227static int 228__parse_v1(int c, char **argv, int invert, unsigned int *flags, 229 struct xt_entry_match **match, 230 u_int16_t pnum, u_int8_t invflags) 231{ 232 const char *proto; 233 struct xt_multiport_v1 *multiinfo 234 = (struct xt_multiport_v1 *)(*match)->data; 235 236 switch (c) { 237 case '1': 238 check_inverse(argv[optind-1], &invert, &optind, 0); 239 proto = check_proto(pnum, invflags); 240 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 241 multiinfo->flags = XT_MULTIPORT_SOURCE; 242 break; 243 244 case '2': 245 check_inverse(argv[optind-1], &invert, &optind, 0); 246 proto = check_proto(pnum, invflags); 247 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 248 multiinfo->flags = XT_MULTIPORT_DESTINATION; 249 break; 250 251 case '3': 252 check_inverse(argv[optind-1], &invert, &optind, 0); 253 proto = check_proto(pnum, invflags); 254 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 255 multiinfo->flags = XT_MULTIPORT_EITHER; 256 break; 257 258 default: 259 return 0; 260 } 261 262 if (invert) 263 multiinfo->invert = 1; 264 265 if (*flags) 266 exit_error(PARAMETER_PROBLEM, 267 "multiport can only have one option"); 268 *flags = 1; 269 return 1; 270} 271 272static int 273parse_v1(int c, char **argv, int invert, unsigned int *flags, 274 const void *e, 275 struct xt_entry_match **match) 276{ 277 const struct ipt_entry *entry = e; 278 return __parse_v1(c, argv, invert, flags, match, entry->ip.proto, 279 entry->ip.invflags); 280} 281 282static int 283parse6_v1(int c, char **argv, int invert, unsigned int *flags, 284 const void *e, 285 struct xt_entry_match **match) 286{ 287 const struct ip6t_entry *entry = (const struct ip6t_entry *)e; 288 return __parse_v1(c, argv, invert, flags, match, entry->ipv6.proto, 289 entry->ipv6.invflags); 290} 291 292/* Final check; must specify something. */ 293static void 294final_check(unsigned int flags) 295{ 296 if (!flags) 297 exit_error(PARAMETER_PROBLEM, "multiport expection an option"); 298} 299 300static char * 301port_to_service(int port, u_int8_t proto) 302{ 303 struct servent *service; 304 305 if ((service = getservbyport(htons(port), proto_to_name(proto)))) 306 return service->s_name; 307 308 return NULL; 309} 310 311static void 312print_port(u_int16_t port, u_int8_t protocol, int numeric) 313{ 314 char *service; 315 316 if (numeric || (service = port_to_service(port, protocol)) == NULL) 317 printf("%u", port); 318 else 319 printf("%s", service); 320} 321 322/* Prints out the matchinfo. */ 323static void 324__print(const struct xt_entry_match *match, int numeric, u_int16_t proto) 325{ 326 const struct xt_multiport *multiinfo 327 = (const struct xt_multiport *)match->data; 328 unsigned int i; 329 330 printf("multiport "); 331 332 switch (multiinfo->flags) { 333 case XT_MULTIPORT_SOURCE: 334 printf("sports "); 335 break; 336 337 case XT_MULTIPORT_DESTINATION: 338 printf("dports "); 339 break; 340 341 case XT_MULTIPORT_EITHER: 342 printf("ports "); 343 break; 344 345 default: 346 printf("ERROR "); 347 break; 348 } 349 350 for (i=0; i < multiinfo->count; i++) { 351 printf("%s", i ? "," : ""); 352 print_port(multiinfo->ports[i], proto, numeric); 353 } 354 printf(" "); 355} 356 357static void 358print(const void *ip_void, const struct xt_entry_match *match, int numeric) 359{ 360 const struct ipt_ip *ip = ip_void; 361 __print(match, numeric, ip->proto); 362} 363 364static void 365print6(const void *ip_void, const struct xt_entry_match *match, int numeric) 366{ 367 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 368 __print(match, numeric, ip->proto); 369} 370 371static void 372__print_v1(const struct xt_entry_match *match, int numeric, u_int16_t proto) 373{ 374 const struct xt_multiport_v1 *multiinfo 375 = (const struct xt_multiport_v1 *)match->data; 376 unsigned int i; 377 378 printf("multiport "); 379 380 switch (multiinfo->flags) { 381 case XT_MULTIPORT_SOURCE: 382 printf("sports "); 383 break; 384 385 case XT_MULTIPORT_DESTINATION: 386 printf("dports "); 387 break; 388 389 case XT_MULTIPORT_EITHER: 390 printf("ports "); 391 break; 392 393 default: 394 printf("ERROR "); 395 break; 396 } 397 398 if (multiinfo->invert) 399 printf("! "); 400 401 for (i=0; i < multiinfo->count; i++) { 402 printf("%s", i ? "," : ""); 403 print_port(multiinfo->ports[i], proto, numeric); 404 if (multiinfo->pflags[i]) { 405 printf(":"); 406 print_port(multiinfo->ports[++i], proto, numeric); 407 } 408 } 409 printf(" "); 410} 411 412static void 413print_v1(const void *ip_void, const struct xt_entry_match *match, int numeric) 414{ 415 const struct ipt_ip *ip = ip_void; 416 __print_v1(match, numeric, ip->proto); 417} 418 419static void 420print6_v1(const void *ip_void, const struct xt_entry_match *match, int numeric) 421{ 422 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 423 __print_v1(match, numeric, ip->proto); 424} 425 426/* Saves the union ipt_matchinfo in parsable form to stdout. */ 427static void __save(const struct xt_entry_match *match, u_int16_t proto) 428{ 429 const struct xt_multiport *multiinfo 430 = (const struct xt_multiport *)match->data; 431 unsigned int i; 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 } 451 printf(" "); 452} 453 454static void save(const void *ip_void, const struct xt_entry_match *match) 455{ 456 const struct ipt_ip *ip = ip_void; 457 __save(match, ip->proto); 458} 459 460static void save6(const void *ip_void, const struct xt_entry_match *match) 461{ 462 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 463 __save(match, ip->proto); 464} 465 466static void __save_v1(const struct xt_entry_match *match, u_int16_t proto) 467{ 468 const struct xt_multiport_v1 *multiinfo 469 = (const struct xt_multiport_v1 *)match->data; 470 unsigned int i; 471 472 switch (multiinfo->flags) { 473 case XT_MULTIPORT_SOURCE: 474 printf("--sports "); 475 break; 476 477 case XT_MULTIPORT_DESTINATION: 478 printf("--dports "); 479 break; 480 481 case XT_MULTIPORT_EITHER: 482 printf("--ports "); 483 break; 484 } 485 486 if (multiinfo->invert) 487 printf("! "); 488 489 for (i=0; i < multiinfo->count; i++) { 490 printf("%s", i ? "," : ""); 491 print_port(multiinfo->ports[i], proto, 1); 492 if (multiinfo->pflags[i]) { 493 printf(":"); 494 print_port(multiinfo->ports[++i], proto, 1); 495 } 496 } 497 printf(" "); 498} 499 500static void save_v1(const void *ip_void, const struct xt_entry_match *match) 501{ 502 const struct ipt_ip *ip = ip_void; 503 __save_v1(match, ip->proto); 504} 505 506static void save6_v1(const void *ip_void, const struct xt_entry_match *match) 507{ 508 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 509 __save_v1(match, ip->proto); 510} 511 512static struct xtables_match multiport = { 513 .family = AF_INET, 514 .name = "multiport", 515 .revision = 0, 516 .version = IPTABLES_VERSION, 517 .size = XT_ALIGN(sizeof(struct xt_multiport)), 518 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 519 .help = &help, 520 .parse = &parse, 521 .final_check = &final_check, 522 .print = &print, 523 .save = &save, 524 .extra_opts = opts 525}; 526 527static struct xtables_match multiport6 = { 528 .family = AF_INET6, 529 .name = "multiport", 530 .revision = 0, 531 .version = IPTABLES_VERSION, 532 .size = XT_ALIGN(sizeof(struct xt_multiport)), 533 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 534 .help = &help, 535 .parse = &parse6, 536 .final_check = &final_check, 537 .print = &print6, 538 .save = &save6, 539 .extra_opts = opts 540}; 541 542static struct xtables_match multiport_v1 = { 543 .family = AF_INET, 544 .name = "multiport", 545 .version = IPTABLES_VERSION, 546 .revision = 1, 547 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 548 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 549 .help = &help_v1, 550 .parse = &parse_v1, 551 .final_check = &final_check, 552 .print = &print_v1, 553 .save = &save_v1, 554 .extra_opts = opts 555}; 556 557static struct xtables_match multiport6_v1 = { 558 .family = AF_INET6, 559 .name = "multiport", 560 .version = IPTABLES_VERSION, 561 .revision = 1, 562 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 563 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 564 .help = &help_v1, 565 .parse = &parse6_v1, 566 .final_check = &final_check, 567 .print = &print6_v1, 568 .save = &save6_v1, 569 .extra_opts = opts 570}; 571 572void 573_init(void) 574{ 575 xtables_register_match(&multiport); 576 xtables_register_match(&multiport6); 577 xtables_register_match(&multiport_v1); 578 xtables_register_match(&multiport6_v1); 579} 580