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