libxt_multiport.c revision 500f483fff529dcd88ec96b9d5054be6cd6363a0
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 136/* Initialize the match. */ 137static void 138init(struct xt_entry_match *m) 139{ 140} 141 142static const char * 143check_proto(u_int16_t pnum, u_int8_t invflags) 144{ 145 char *proto; 146 147 if (invflags & XT_INV_PROTO) 148 exit_error(PARAMETER_PROBLEM, 149 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 150 151 if ((proto = proto_to_name(pnum)) != NULL) 152 return proto; 153 else if (!pnum) 154 exit_error(PARAMETER_PROBLEM, 155 "multiport needs `-p tcp', `-p udp', `-p udplite', " 156 "`-p sctp' or `-p dccp'"); 157 else 158 exit_error(PARAMETER_PROBLEM, 159 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 160} 161 162/* Function which parses command options; returns true if it 163 ate an option */ 164static int 165__parse(int c, char **argv, int invert, unsigned int *flags, 166 struct xt_entry_match **match, 167 u_int16_t pnum, u_int8_t invflags) 168{ 169 const char *proto; 170 struct xt_multiport *multiinfo 171 = (struct xt_multiport *)(*match)->data; 172 173 switch (c) { 174 case '1': 175 check_inverse(argv[optind-1], &invert, &optind, 0); 176 proto = check_proto(pnum, invflags); 177 multiinfo->count = parse_multi_ports(argv[optind-1], 178 multiinfo->ports, proto); 179 multiinfo->flags = XT_MULTIPORT_SOURCE; 180 break; 181 182 case '2': 183 check_inverse(argv[optind-1], &invert, &optind, 0); 184 proto = check_proto(pnum, invflags); 185 multiinfo->count = parse_multi_ports(argv[optind-1], 186 multiinfo->ports, proto); 187 multiinfo->flags = XT_MULTIPORT_DESTINATION; 188 break; 189 190 case '3': 191 check_inverse(argv[optind-1], &invert, &optind, 0); 192 proto = check_proto(pnum, invflags); 193 multiinfo->count = parse_multi_ports(argv[optind-1], 194 multiinfo->ports, proto); 195 multiinfo->flags = XT_MULTIPORT_EITHER; 196 break; 197 198 default: 199 return 0; 200 } 201 202 if (invert) 203 exit_error(PARAMETER_PROBLEM, 204 "multiport does not support invert"); 205 206 if (*flags) 207 exit_error(PARAMETER_PROBLEM, 208 "multiport can only have one option"); 209 *flags = 1; 210 return 1; 211} 212 213static int 214parse(int c, char **argv, int invert, unsigned int *flags, 215 const void *e, 216 struct xt_entry_match **match) 217{ 218 const struct ipt_entry *entry = e; 219 return __parse(c, argv, invert, flags, match, entry->ip.proto, 220 entry->ip.invflags); 221} 222 223static int 224parse6(int c, char **argv, int invert, unsigned int *flags, 225 const void *e, 226 struct xt_entry_match **match) 227{ 228 const struct ip6t_entry *entry = (const struct ip6t_entry *)e; 229 return __parse(c, argv, invert, flags, match, entry->ipv6.proto, 230 entry->ipv6.invflags); 231} 232 233static int 234__parse_v1(int c, char **argv, int invert, unsigned int *flags, 235 struct xt_entry_match **match, 236 u_int16_t pnum, u_int8_t invflags) 237{ 238 const char *proto; 239 struct xt_multiport_v1 *multiinfo 240 = (struct xt_multiport_v1 *)(*match)->data; 241 242 switch (c) { 243 case '1': 244 check_inverse(argv[optind-1], &invert, &optind, 0); 245 proto = check_proto(pnum, invflags); 246 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 247 multiinfo->flags = XT_MULTIPORT_SOURCE; 248 break; 249 250 case '2': 251 check_inverse(argv[optind-1], &invert, &optind, 0); 252 proto = check_proto(pnum, invflags); 253 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 254 multiinfo->flags = XT_MULTIPORT_DESTINATION; 255 break; 256 257 case '3': 258 check_inverse(argv[optind-1], &invert, &optind, 0); 259 proto = check_proto(pnum, invflags); 260 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 261 multiinfo->flags = XT_MULTIPORT_EITHER; 262 break; 263 264 default: 265 return 0; 266 } 267 268 if (invert) 269 multiinfo->invert = 1; 270 271 if (*flags) 272 exit_error(PARAMETER_PROBLEM, 273 "multiport can only have one option"); 274 *flags = 1; 275 return 1; 276} 277 278static int 279parse_v1(int c, char **argv, int invert, unsigned int *flags, 280 const void *e, 281 struct xt_entry_match **match) 282{ 283 const struct ipt_entry *entry = e; 284 return __parse_v1(c, argv, invert, flags, match, entry->ip.proto, 285 entry->ip.invflags); 286} 287 288static int 289parse6_v1(int c, char **argv, int invert, unsigned int *flags, 290 const void *e, 291 struct xt_entry_match **match) 292{ 293 const struct ip6t_entry *entry = (const struct ip6t_entry *)e; 294 return __parse_v1(c, argv, invert, flags, match, entry->ipv6.proto, 295 entry->ipv6.invflags); 296} 297 298/* Final check; must specify something. */ 299static void 300final_check(unsigned int flags) 301{ 302 if (!flags) 303 exit_error(PARAMETER_PROBLEM, "multiport expection an option"); 304} 305 306static char * 307port_to_service(int port, u_int8_t proto) 308{ 309 struct servent *service; 310 311 if ((service = getservbyport(htons(port), proto_to_name(proto)))) 312 return service->s_name; 313 314 return NULL; 315} 316 317static void 318print_port(u_int16_t port, u_int8_t protocol, int numeric) 319{ 320 char *service; 321 322 if (numeric || (service = port_to_service(port, protocol)) == NULL) 323 printf("%u", port); 324 else 325 printf("%s", service); 326} 327 328/* Prints out the matchinfo. */ 329static void 330__print(const struct xt_entry_match *match, int numeric, u_int16_t proto) 331{ 332 const struct xt_multiport *multiinfo 333 = (const struct xt_multiport *)match->data; 334 unsigned int i; 335 336 printf("multiport "); 337 338 switch (multiinfo->flags) { 339 case XT_MULTIPORT_SOURCE: 340 printf("sports "); 341 break; 342 343 case XT_MULTIPORT_DESTINATION: 344 printf("dports "); 345 break; 346 347 case XT_MULTIPORT_EITHER: 348 printf("ports "); 349 break; 350 351 default: 352 printf("ERROR "); 353 break; 354 } 355 356 for (i=0; i < multiinfo->count; i++) { 357 printf("%s", i ? "," : ""); 358 print_port(multiinfo->ports[i], proto, numeric); 359 } 360 printf(" "); 361} 362 363static void 364print(const void *ip_void, const struct xt_entry_match *match, int numeric) 365{ 366 const struct ipt_ip *ip = ip_void; 367 __print(match, numeric, ip->proto); 368} 369 370static void 371print6(const void *ip_void, const struct xt_entry_match *match, int numeric) 372{ 373 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 374 __print(match, numeric, ip->proto); 375} 376 377static void 378__print_v1(const struct xt_entry_match *match, int numeric, u_int16_t proto) 379{ 380 const struct xt_multiport_v1 *multiinfo 381 = (const struct xt_multiport_v1 *)match->data; 382 unsigned int i; 383 384 printf("multiport "); 385 386 switch (multiinfo->flags) { 387 case XT_MULTIPORT_SOURCE: 388 printf("sports "); 389 break; 390 391 case XT_MULTIPORT_DESTINATION: 392 printf("dports "); 393 break; 394 395 case XT_MULTIPORT_EITHER: 396 printf("ports "); 397 break; 398 399 default: 400 printf("ERROR "); 401 break; 402 } 403 404 if (multiinfo->invert) 405 printf("! "); 406 407 for (i=0; i < multiinfo->count; i++) { 408 printf("%s", i ? "," : ""); 409 print_port(multiinfo->ports[i], proto, numeric); 410 if (multiinfo->pflags[i]) { 411 printf(":"); 412 print_port(multiinfo->ports[++i], proto, numeric); 413 } 414 } 415 printf(" "); 416} 417 418static void 419print_v1(const void *ip_void, const struct xt_entry_match *match, int numeric) 420{ 421 const struct ipt_ip *ip = ip_void; 422 __print_v1(match, numeric, ip->proto); 423} 424 425static void 426print6_v1(const void *ip_void, const struct xt_entry_match *match, int numeric) 427{ 428 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 429 __print_v1(match, numeric, ip->proto); 430} 431 432/* Saves the union ipt_matchinfo in parsable form to stdout. */ 433static void __save(const struct xt_entry_match *match, u_int16_t proto) 434{ 435 const struct xt_multiport *multiinfo 436 = (const struct xt_multiport *)match->data; 437 unsigned int i; 438 439 switch (multiinfo->flags) { 440 case XT_MULTIPORT_SOURCE: 441 printf("--sports "); 442 break; 443 444 case XT_MULTIPORT_DESTINATION: 445 printf("--dports "); 446 break; 447 448 case XT_MULTIPORT_EITHER: 449 printf("--ports "); 450 break; 451 } 452 453 for (i=0; i < multiinfo->count; i++) { 454 printf("%s", i ? "," : ""); 455 print_port(multiinfo->ports[i], proto, 1); 456 } 457 printf(" "); 458} 459 460static void save(const void *ip_void, const struct xt_entry_match *match) 461{ 462 const struct ipt_ip *ip = ip_void; 463 __save(match, ip->proto); 464} 465 466static void save6(const void *ip_void, const struct xt_entry_match *match) 467{ 468 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 469 __save(match, ip->proto); 470} 471 472static void __save_v1(const struct xt_entry_match *match, u_int16_t proto) 473{ 474 const struct xt_multiport_v1 *multiinfo 475 = (const struct xt_multiport_v1 *)match->data; 476 unsigned int i; 477 478 switch (multiinfo->flags) { 479 case XT_MULTIPORT_SOURCE: 480 printf("--sports "); 481 break; 482 483 case XT_MULTIPORT_DESTINATION: 484 printf("--dports "); 485 break; 486 487 case XT_MULTIPORT_EITHER: 488 printf("--ports "); 489 break; 490 } 491 492 if (multiinfo->invert) 493 printf("! "); 494 495 for (i=0; i < multiinfo->count; i++) { 496 printf("%s", i ? "," : ""); 497 print_port(multiinfo->ports[i], proto, 1); 498 if (multiinfo->pflags[i]) { 499 printf(":"); 500 print_port(multiinfo->ports[++i], proto, 1); 501 } 502 } 503 printf(" "); 504} 505 506static void save_v1(const void *ip_void, const struct xt_entry_match *match) 507{ 508 const struct ipt_ip *ip = ip_void; 509 __save_v1(match, ip->proto); 510} 511 512static void save6_v1(const void *ip_void, const struct xt_entry_match *match) 513{ 514 const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void; 515 __save_v1(match, ip->proto); 516} 517 518static struct xtables_match multiport = { 519 .family = AF_INET, 520 .name = "multiport", 521 .revision = 0, 522 .version = IPTABLES_VERSION, 523 .size = XT_ALIGN(sizeof(struct xt_multiport)), 524 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 525 .help = &help, 526 .init = &init, 527 .parse = &parse, 528 .final_check = &final_check, 529 .print = &print, 530 .save = &save, 531 .extra_opts = opts 532}; 533 534static struct xtables_match multiport6 = { 535 .family = AF_INET6, 536 .name = "multiport", 537 .revision = 0, 538 .version = IPTABLES_VERSION, 539 .size = XT_ALIGN(sizeof(struct xt_multiport)), 540 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 541 .help = &help, 542 .init = &init, 543 .parse = &parse6, 544 .final_check = &final_check, 545 .print = &print6, 546 .save = &save6, 547 .extra_opts = opts 548}; 549 550static struct xtables_match multiport_v1 = { 551 .family = AF_INET, 552 .name = "multiport", 553 .version = IPTABLES_VERSION, 554 .revision = 1, 555 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 556 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 557 .help = &help_v1, 558 .init = &init, 559 .parse = &parse_v1, 560 .final_check = &final_check, 561 .print = &print_v1, 562 .save = &save_v1, 563 .extra_opts = opts 564}; 565 566static struct xtables_match multiport6_v1 = { 567 .family = AF_INET6, 568 .name = "multiport", 569 .version = IPTABLES_VERSION, 570 .revision = 1, 571 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 572 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 573 .help = &help_v1, 574 .init = &init, 575 .parse = &parse6_v1, 576 .final_check = &final_check, 577 .print = &print6_v1, 578 .save = &save6_v1, 579 .extra_opts = opts 580}; 581 582void 583_init(void) 584{ 585 xtables_register_match(&multiport); 586 xtables_register_match(&multiport6); 587 xtables_register_match(&multiport_v1); 588 xtables_register_match(&multiport6_v1); 589} 590