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