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