libxt_multiport.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
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} 344 345static void multiport_print(const void *ip_void, 346 const struct xt_entry_match *match, int numeric) 347{ 348 const struct ipt_ip *ip = ip_void; 349 __multiport_print(match, numeric, ip->proto); 350} 351 352static void multiport_print6(const void *ip_void, 353 const struct xt_entry_match *match, int numeric) 354{ 355 const struct ip6t_ip6 *ip = ip_void; 356 __multiport_print(match, numeric, ip->proto); 357} 358 359static void __multiport_print_v1(const struct xt_entry_match *match, 360 int numeric, uint16_t proto) 361{ 362 const struct xt_multiport_v1 *multiinfo 363 = (const struct xt_multiport_v1 *)match->data; 364 unsigned int i; 365 366 printf(" multiport "); 367 368 switch (multiinfo->flags) { 369 case XT_MULTIPORT_SOURCE: 370 printf("sports "); 371 break; 372 373 case XT_MULTIPORT_DESTINATION: 374 printf("dports "); 375 break; 376 377 case XT_MULTIPORT_EITHER: 378 printf("ports "); 379 break; 380 381 default: 382 printf("ERROR "); 383 break; 384 } 385 386 if (multiinfo->invert) 387 printf(" !"); 388 389 for (i=0; i < multiinfo->count; i++) { 390 printf("%s", i ? "," : ""); 391 print_port(multiinfo->ports[i], proto, numeric); 392 if (multiinfo->pflags[i]) { 393 printf(":"); 394 print_port(multiinfo->ports[++i], proto, numeric); 395 } 396 } 397} 398 399static void multiport_print_v1(const void *ip_void, 400 const struct xt_entry_match *match, int numeric) 401{ 402 const struct ipt_ip *ip = ip_void; 403 __multiport_print_v1(match, numeric, ip->proto); 404} 405 406static void multiport_print6_v1(const void *ip_void, 407 const struct xt_entry_match *match, int numeric) 408{ 409 const struct ip6t_ip6 *ip = ip_void; 410 __multiport_print_v1(match, numeric, ip->proto); 411} 412 413/* Saves the union ipt_matchinfo in parsable form to stdout. */ 414static void __multiport_save(const struct xt_entry_match *match, 415 uint16_t proto) 416{ 417 const struct xt_multiport *multiinfo 418 = (const struct xt_multiport *)match->data; 419 unsigned int i; 420 421 switch (multiinfo->flags) { 422 case XT_MULTIPORT_SOURCE: 423 printf(" --sports "); 424 break; 425 426 case XT_MULTIPORT_DESTINATION: 427 printf(" --dports "); 428 break; 429 430 case XT_MULTIPORT_EITHER: 431 printf(" --ports "); 432 break; 433 } 434 435 for (i=0; i < multiinfo->count; i++) { 436 printf("%s", i ? "," : ""); 437 print_port(multiinfo->ports[i], proto, 1); 438 } 439} 440 441static void multiport_save(const void *ip_void, 442 const struct xt_entry_match *match) 443{ 444 const struct ipt_ip *ip = ip_void; 445 __multiport_save(match, ip->proto); 446} 447 448static void multiport_save6(const void *ip_void, 449 const struct xt_entry_match *match) 450{ 451 const struct ip6t_ip6 *ip = ip_void; 452 __multiport_save(match, ip->proto); 453} 454 455static void __multiport_save_v1(const struct xt_entry_match *match, 456 uint16_t proto) 457{ 458 const struct xt_multiport_v1 *multiinfo 459 = (const struct xt_multiport_v1 *)match->data; 460 unsigned int i; 461 462 if (multiinfo->invert) 463 printf(" !"); 464 465 switch (multiinfo->flags) { 466 case XT_MULTIPORT_SOURCE: 467 printf(" --sports "); 468 break; 469 470 case XT_MULTIPORT_DESTINATION: 471 printf(" --dports "); 472 break; 473 474 case XT_MULTIPORT_EITHER: 475 printf(" --ports "); 476 break; 477 } 478 479 for (i=0; i < multiinfo->count; i++) { 480 printf("%s", i ? "," : ""); 481 print_port(multiinfo->ports[i], proto, 1); 482 if (multiinfo->pflags[i]) { 483 printf(":"); 484 print_port(multiinfo->ports[++i], proto, 1); 485 } 486 } 487} 488 489static void multiport_save_v1(const void *ip_void, 490 const struct xt_entry_match *match) 491{ 492 const struct ipt_ip *ip = ip_void; 493 __multiport_save_v1(match, ip->proto); 494} 495 496static void multiport_save6_v1(const void *ip_void, 497 const struct xt_entry_match *match) 498{ 499 const struct ip6t_ip6 *ip = ip_void; 500 __multiport_save_v1(match, ip->proto); 501} 502 503static struct xtables_match multiport_mt_reg[] = { 504 { 505 .family = NFPROTO_IPV4, 506 .name = "multiport", 507 .revision = 0, 508 .version = XTABLES_VERSION, 509 .size = XT_ALIGN(sizeof(struct xt_multiport)), 510 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 511 .help = multiport_help, 512 .parse = multiport_parse, 513 .final_check = multiport_check, 514 .print = multiport_print, 515 .save = multiport_save, 516 .extra_opts = multiport_opts, 517 }, 518 { 519 .family = NFPROTO_IPV6, 520 .name = "multiport", 521 .revision = 0, 522 .version = XTABLES_VERSION, 523 .size = XT_ALIGN(sizeof(struct xt_multiport)), 524 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 525 .help = multiport_help, 526 .parse = multiport_parse6, 527 .final_check = multiport_check, 528 .print = multiport_print6, 529 .save = multiport_save6, 530 .extra_opts = multiport_opts, 531 }, 532 { 533 .family = NFPROTO_IPV4, 534 .name = "multiport", 535 .version = XTABLES_VERSION, 536 .revision = 1, 537 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 538 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 539 .help = multiport_help_v1, 540 .parse = multiport_parse_v1, 541 .final_check = multiport_check, 542 .print = multiport_print_v1, 543 .save = multiport_save_v1, 544 .extra_opts = multiport_opts, 545 }, 546 { 547 .family = NFPROTO_IPV6, 548 .name = "multiport", 549 .version = XTABLES_VERSION, 550 .revision = 1, 551 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 552 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 553 .help = multiport_help_v1, 554 .parse = multiport_parse6_v1, 555 .final_check = multiport_check, 556 .print = multiport_print6_v1, 557 .save = multiport_save6_v1, 558 .extra_opts = multiport_opts, 559 }, 560}; 561 562void 563_init(void) 564{ 565 xtables_register_matches(multiport_mt_reg, ARRAY_SIZE(multiport_mt_reg)); 566} 567