libxt_multiport.c revision 32b8e61e4e5bd405d9ad07bf9468498dfbb19f9e
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(u_int8_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, u_int16_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 u_int16_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(u_int16_t pnum, u_int8_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, u_int16_t pnum, 160 u_int8_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 default: 192 return 0; 193 } 194 195 if (invert) 196 xtables_error(PARAMETER_PROBLEM, 197 "multiport does not support invert"); 198 199 if (*flags) 200 xtables_error(PARAMETER_PROBLEM, 201 "multiport can only have one option"); 202 *flags = 1; 203 return 1; 204} 205 206static int 207multiport_parse(int c, char **argv, int invert, unsigned int *flags, 208 const void *e, struct xt_entry_match **match) 209{ 210 const struct ipt_entry *entry = e; 211 return __multiport_parse(c, argv, invert, flags, match, 212 entry->ip.proto, entry->ip.invflags); 213} 214 215static int 216multiport_parse6(int c, char **argv, int invert, unsigned int *flags, 217 const void *e, struct xt_entry_match **match) 218{ 219 const struct ip6t_entry *entry = e; 220 return __multiport_parse(c, argv, invert, flags, match, 221 entry->ipv6.proto, entry->ipv6.invflags); 222} 223 224static int 225__multiport_parse_v1(int c, char **argv, int invert, unsigned int *flags, 226 struct xt_entry_match **match, u_int16_t pnum, 227 u_int8_t invflags) 228{ 229 const char *proto; 230 struct xt_multiport_v1 *multiinfo 231 = (struct xt_multiport_v1 *)(*match)->data; 232 233 switch (c) { 234 case '1': 235 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 236 proto = check_proto(pnum, invflags); 237 parse_multi_ports_v1(optarg, multiinfo, proto); 238 multiinfo->flags = XT_MULTIPORT_SOURCE; 239 break; 240 241 case '2': 242 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 243 proto = check_proto(pnum, invflags); 244 parse_multi_ports_v1(optarg, multiinfo, proto); 245 multiinfo->flags = XT_MULTIPORT_DESTINATION; 246 break; 247 248 case '3': 249 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 250 proto = check_proto(pnum, invflags); 251 parse_multi_ports_v1(optarg, multiinfo, proto); 252 multiinfo->flags = XT_MULTIPORT_EITHER; 253 break; 254 255 default: 256 return 0; 257 } 258 259 if (invert) 260 multiinfo->invert = 1; 261 262 if (*flags) 263 xtables_error(PARAMETER_PROBLEM, 264 "multiport can only have one option"); 265 *flags = 1; 266 return 1; 267} 268 269static int 270multiport_parse_v1(int c, char **argv, int invert, unsigned int *flags, 271 const void *e, struct xt_entry_match **match) 272{ 273 const struct ipt_entry *entry = e; 274 return __multiport_parse_v1(c, argv, invert, flags, match, 275 entry->ip.proto, entry->ip.invflags); 276} 277 278static int 279multiport_parse6_v1(int c, char **argv, int invert, unsigned int *flags, 280 const void *e, struct xt_entry_match **match) 281{ 282 const struct ip6t_entry *entry = e; 283 return __multiport_parse_v1(c, argv, invert, flags, match, 284 entry->ipv6.proto, entry->ipv6.invflags); 285} 286 287/* Final check; must specify something. */ 288static void multiport_check(unsigned int flags) 289{ 290 if (!flags) 291 xtables_error(PARAMETER_PROBLEM, "multiport expection an option"); 292} 293 294static char * 295port_to_service(int port, u_int8_t proto) 296{ 297 struct servent *service; 298 299 if ((service = getservbyport(htons(port), proto_to_name(proto)))) 300 return service->s_name; 301 302 return NULL; 303} 304 305static void 306print_port(u_int16_t port, u_int8_t protocol, int numeric) 307{ 308 char *service; 309 310 if (numeric || (service = port_to_service(port, protocol)) == NULL) 311 printf("%u", port); 312 else 313 printf("%s", service); 314} 315 316/* Prints out the matchinfo. */ 317static void 318__multiport_print(const struct xt_entry_match *match, int numeric, 319 u_int16_t proto) 320{ 321 const struct xt_multiport *multiinfo 322 = (const struct xt_multiport *)match->data; 323 unsigned int i; 324 325 printf("multiport "); 326 327 switch (multiinfo->flags) { 328 case XT_MULTIPORT_SOURCE: 329 printf("sports "); 330 break; 331 332 case XT_MULTIPORT_DESTINATION: 333 printf("dports "); 334 break; 335 336 case XT_MULTIPORT_EITHER: 337 printf("ports "); 338 break; 339 340 default: 341 printf("ERROR "); 342 break; 343 } 344 345 for (i=0; i < multiinfo->count; i++) { 346 printf("%s", i ? "," : ""); 347 print_port(multiinfo->ports[i], proto, numeric); 348 } 349 printf(" "); 350} 351 352static void multiport_print(const void *ip_void, 353 const struct xt_entry_match *match, int numeric) 354{ 355 const struct ipt_ip *ip = ip_void; 356 __multiport_print(match, numeric, ip->proto); 357} 358 359static void multiport_print6(const void *ip_void, 360 const struct xt_entry_match *match, int numeric) 361{ 362 const struct ip6t_ip6 *ip = ip_void; 363 __multiport_print(match, numeric, ip->proto); 364} 365 366static void __multiport_print_v1(const struct xt_entry_match *match, 367 int numeric, u_int16_t proto) 368{ 369 const struct xt_multiport_v1 *multiinfo 370 = (const struct xt_multiport_v1 *)match->data; 371 unsigned int i; 372 373 printf("multiport "); 374 375 switch (multiinfo->flags) { 376 case XT_MULTIPORT_SOURCE: 377 printf("sports "); 378 break; 379 380 case XT_MULTIPORT_DESTINATION: 381 printf("dports "); 382 break; 383 384 case XT_MULTIPORT_EITHER: 385 printf("ports "); 386 break; 387 388 default: 389 printf("ERROR "); 390 break; 391 } 392 393 if (multiinfo->invert) 394 printf("! "); 395 396 for (i=0; i < multiinfo->count; i++) { 397 printf("%s", i ? "," : ""); 398 print_port(multiinfo->ports[i], proto, numeric); 399 if (multiinfo->pflags[i]) { 400 printf(":"); 401 print_port(multiinfo->ports[++i], proto, numeric); 402 } 403 } 404 printf(" "); 405} 406 407static void multiport_print_v1(const void *ip_void, 408 const struct xt_entry_match *match, int numeric) 409{ 410 const struct ipt_ip *ip = ip_void; 411 __multiport_print_v1(match, numeric, ip->proto); 412} 413 414static void multiport_print6_v1(const void *ip_void, 415 const struct xt_entry_match *match, int numeric) 416{ 417 const struct ip6t_ip6 *ip = ip_void; 418 __multiport_print_v1(match, numeric, ip->proto); 419} 420 421/* Saves the union ipt_matchinfo in parsable form to stdout. */ 422static void __multiport_save(const struct xt_entry_match *match, 423 u_int16_t proto) 424{ 425 const struct xt_multiport *multiinfo 426 = (const struct xt_multiport *)match->data; 427 unsigned int i; 428 429 switch (multiinfo->flags) { 430 case XT_MULTIPORT_SOURCE: 431 printf("--sports "); 432 break; 433 434 case XT_MULTIPORT_DESTINATION: 435 printf("--dports "); 436 break; 437 438 case XT_MULTIPORT_EITHER: 439 printf("--ports "); 440 break; 441 } 442 443 for (i=0; i < multiinfo->count; i++) { 444 printf("%s", i ? "," : ""); 445 print_port(multiinfo->ports[i], proto, 1); 446 } 447 printf(" "); 448} 449 450static void multiport_save(const void *ip_void, 451 const struct xt_entry_match *match) 452{ 453 const struct ipt_ip *ip = ip_void; 454 __multiport_save(match, ip->proto); 455} 456 457static void multiport_save6(const void *ip_void, 458 const struct xt_entry_match *match) 459{ 460 const struct ip6t_ip6 *ip = ip_void; 461 __multiport_save(match, ip->proto); 462} 463 464static void __multiport_save_v1(const struct xt_entry_match *match, 465 u_int16_t proto) 466{ 467 const struct xt_multiport_v1 *multiinfo 468 = (const struct xt_multiport_v1 *)match->data; 469 unsigned int i; 470 471 if (multiinfo->invert) 472 printf("! "); 473 474 switch (multiinfo->flags) { 475 case XT_MULTIPORT_SOURCE: 476 printf("--sports "); 477 break; 478 479 case XT_MULTIPORT_DESTINATION: 480 printf("--dports "); 481 break; 482 483 case XT_MULTIPORT_EITHER: 484 printf("--ports "); 485 break; 486 } 487 488 for (i=0; i < multiinfo->count; i++) { 489 printf("%s", i ? "," : ""); 490 print_port(multiinfo->ports[i], proto, 1); 491 if (multiinfo->pflags[i]) { 492 printf(":"); 493 print_port(multiinfo->ports[++i], proto, 1); 494 } 495 } 496 printf(" "); 497} 498 499static void multiport_save_v1(const void *ip_void, 500 const struct xt_entry_match *match) 501{ 502 const struct ipt_ip *ip = ip_void; 503 __multiport_save_v1(match, ip->proto); 504} 505 506static void multiport_save6_v1(const void *ip_void, 507 const struct xt_entry_match *match) 508{ 509 const struct ip6t_ip6 *ip = ip_void; 510 __multiport_save_v1(match, ip->proto); 511} 512 513static struct xtables_match multiport_mt_reg[] = { 514 { 515 .family = NFPROTO_IPV4, 516 .name = "multiport", 517 .revision = 0, 518 .version = XTABLES_VERSION, 519 .size = XT_ALIGN(sizeof(struct xt_multiport)), 520 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 521 .help = multiport_help, 522 .parse = multiport_parse, 523 .final_check = multiport_check, 524 .print = multiport_print, 525 .save = multiport_save, 526 .extra_opts = multiport_opts, 527 }, 528 { 529 .family = NFPROTO_IPV6, 530 .name = "multiport", 531 .revision = 0, 532 .version = XTABLES_VERSION, 533 .size = XT_ALIGN(sizeof(struct xt_multiport)), 534 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 535 .help = multiport_help, 536 .parse = multiport_parse6, 537 .final_check = multiport_check, 538 .print = multiport_print6, 539 .save = multiport_save6, 540 .extra_opts = multiport_opts, 541 }, 542 { 543 .family = NFPROTO_IPV4, 544 .name = "multiport", 545 .version = XTABLES_VERSION, 546 .revision = 1, 547 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 548 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 549 .help = multiport_help_v1, 550 .parse = multiport_parse_v1, 551 .final_check = multiport_check, 552 .print = multiport_print_v1, 553 .save = multiport_save_v1, 554 .extra_opts = multiport_opts, 555 }, 556 { 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}; 571 572void 573_init(void) 574{ 575 xtables_register_matches(multiport_mt_reg, ARRAY_SIZE(multiport_mt_reg)); 576} 577