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