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