libxt_multiport.c revision c7ae1dfb28382b3bbafeda1a7a87ffa77f2799ea
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/* To ensure that iptables compiles with an old kernel */ 11#include "../include/linux/netfilter/xt_multiport.h" 12 13/* Function which prints out usage message. */ 14static void 15help(void) 16{ 17 printf( 18"multiport v%s options:\n" 19" --source-ports port[,port,port...]\n" 20" --sports ...\n" 21" match source port(s)\n" 22" --destination-ports port[,port,port...]\n" 23" --dports ...\n" 24" match destination port(s)\n" 25" --ports port[,port,port]\n" 26" match both source and destination port(s)\n" 27" NOTE: this kernel does not support port ranges in multiport.\n", 28IPTABLES_VERSION); 29} 30 31static void 32help_v1(void) 33{ 34 printf( 35"multiport v%s 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", 44IPTABLES_VERSION); 45} 46 47static struct option opts[] = { 48 { "source-ports", 1, 0, '1' }, 49 { "sports", 1, 0, '1' }, /* synonym */ 50 { "destination-ports", 1, 0, '2' }, 51 { "dports", 1, 0, '2' }, /* synonym */ 52 { "ports", 1, 0, '3' }, 53 {0} 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) exit_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] = parse_port(cp, proto); 89 } 90 if (cp) exit_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) exit_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 exit_error(PARAMETER_PROBLEM, 117 "too many ports specified"); 118 *range++ = '\0'; 119 } 120 multiinfo->ports[i] = parse_port(cp, proto); 121 if (range) { 122 multiinfo->pflags[i] = 1; 123 multiinfo->ports[++i] = parse_port(range, proto); 124 if (multiinfo->ports[i-1] >= multiinfo->ports[i]) 125 exit_error(PARAMETER_PROBLEM, 126 "invalid portrange specified"); 127 m <<= 1; 128 } 129 } 130 multiinfo->count = i; 131 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified"); 132 free(buffer); 133} 134 135/* Initialize the match. */ 136static void 137init(struct xt_entry_match *m, unsigned int *nfcache) 138{ 139} 140 141static const char * 142check_proto(u_int16_t pnum, u_int8_t invflags) 143{ 144 char *proto; 145 146 if (invflags & XT_INV_PROTO) 147 exit_error(PARAMETER_PROBLEM, 148 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 149 150 if ((proto = proto_to_name(pnum)) != NULL) 151 return proto; 152 else if (!pnum) 153 exit_error(PARAMETER_PROBLEM, 154 "multiport needs `-p tcp', `-p udp', `-p udplite', " 155 "`-p sctp' or `-p dccp'"); 156 else 157 exit_error(PARAMETER_PROBLEM, 158 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 159} 160 161/* Function which parses command options; returns true if it 162 ate an option */ 163static int 164__parse(int c, char **argv, int invert, unsigned int *flags, 165 struct xt_entry_match **match, 166 u_int16_t pnum, u_int8_t invflags) 167{ 168 const char *proto; 169 struct xt_multiport *multiinfo 170 = (struct xt_multiport *)(*match)->data; 171 172 switch (c) { 173 case '1': 174 check_inverse(argv[optind-1], &invert, &optind, 0); 175 proto = check_proto(pnum, invflags); 176 multiinfo->count = parse_multi_ports(argv[optind-1], 177 multiinfo->ports, proto); 178 multiinfo->flags = XT_MULTIPORT_SOURCE; 179 break; 180 181 case '2': 182 check_inverse(argv[optind-1], &invert, &optind, 0); 183 proto = check_proto(pnum, invflags); 184 multiinfo->count = parse_multi_ports(argv[optind-1], 185 multiinfo->ports, proto); 186 multiinfo->flags = XT_MULTIPORT_DESTINATION; 187 break; 188 189 case '3': 190 check_inverse(argv[optind-1], &invert, &optind, 0); 191 proto = check_proto(pnum, invflags); 192 multiinfo->count = parse_multi_ports(argv[optind-1], 193 multiinfo->ports, proto); 194 multiinfo->flags = XT_MULTIPORT_EITHER; 195 break; 196 197 default: 198 return 0; 199 } 200 201 if (invert) 202 exit_error(PARAMETER_PROBLEM, 203 "multiport does not support invert"); 204 205 if (*flags) 206 exit_error(PARAMETER_PROBLEM, 207 "multiport can only have one option"); 208 *flags = 1; 209 return 1; 210} 211 212static int 213parse(int c, char **argv, int invert, unsigned int *flags, 214 const void *e, 215 unsigned int *nfcache, 216 struct xt_entry_match **match) 217{ 218 const struct ipt_entry *entry = e; 219 return __parse(c, argv, invert, flags, match, entry->ip.proto, 220 entry->ip.invflags); 221} 222 223static int 224__parse_v1(int c, char **argv, int invert, unsigned int *flags, 225 struct xt_entry_match **match, 226 u_int16_t pnum, 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 check_inverse(argv[optind-1], &invert, &optind, 0); 235 proto = check_proto(pnum, invflags); 236 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 237 multiinfo->flags = XT_MULTIPORT_SOURCE; 238 break; 239 240 case '2': 241 check_inverse(argv[optind-1], &invert, &optind, 0); 242 proto = check_proto(pnum, invflags); 243 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 244 multiinfo->flags = XT_MULTIPORT_DESTINATION; 245 break; 246 247 case '3': 248 check_inverse(argv[optind-1], &invert, &optind, 0); 249 proto = check_proto(pnum, invflags); 250 parse_multi_ports_v1(argv[optind-1], 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 exit_error(PARAMETER_PROBLEM, 263 "multiport can only have one option"); 264 *flags = 1; 265 return 1; 266} 267 268static int 269parse_v1(int c, char **argv, int invert, unsigned int *flags, 270 const void *e, 271 unsigned int *nfcache, 272 struct xt_entry_match **match) 273{ 274 const struct ipt_entry *entry = e; 275 return __parse_v1(c, argv, invert, flags, match, entry->ip.proto, 276 entry->ip.invflags); 277} 278 279/* Final check; must specify something. */ 280static void 281final_check(unsigned int flags) 282{ 283 if (!flags) 284 exit_error(PARAMETER_PROBLEM, "multiport expection an option"); 285} 286 287static char * 288port_to_service(int port, u_int8_t proto) 289{ 290 struct servent *service; 291 292 if ((service = getservbyport(htons(port), proto_to_name(proto)))) 293 return service->s_name; 294 295 return NULL; 296} 297 298static void 299print_port(u_int16_t port, u_int8_t protocol, int numeric) 300{ 301 char *service; 302 303 if (numeric || (service = port_to_service(port, protocol)) == NULL) 304 printf("%u", port); 305 else 306 printf("%s", service); 307} 308 309/* Prints out the matchinfo. */ 310static void 311__print(const struct xt_entry_match *match, int numeric, u_int16_t proto) 312{ 313 const struct xt_multiport *multiinfo 314 = (const struct xt_multiport *)match->data; 315 unsigned int i; 316 317 printf("multiport "); 318 319 switch (multiinfo->flags) { 320 case XT_MULTIPORT_SOURCE: 321 printf("sports "); 322 break; 323 324 case XT_MULTIPORT_DESTINATION: 325 printf("dports "); 326 break; 327 328 case XT_MULTIPORT_EITHER: 329 printf("ports "); 330 break; 331 332 default: 333 printf("ERROR "); 334 break; 335 } 336 337 for (i=0; i < multiinfo->count; i++) { 338 printf("%s", i ? "," : ""); 339 print_port(multiinfo->ports[i], proto, numeric); 340 } 341 printf(" "); 342} 343 344static void 345print(const void *ip_void, const struct xt_entry_match *match, int numeric) 346{ 347 const struct ipt_ip *ip = ip_void; 348 __print(match, numeric, ip->proto); 349} 350 351static void 352__print_v1(const struct xt_entry_match *match, int numeric, u_int16_t proto) 353{ 354 const struct xt_multiport_v1 *multiinfo 355 = (const struct xt_multiport_v1 *)match->data; 356 unsigned int i; 357 358 printf("multiport "); 359 360 switch (multiinfo->flags) { 361 case XT_MULTIPORT_SOURCE: 362 printf("sports "); 363 break; 364 365 case XT_MULTIPORT_DESTINATION: 366 printf("dports "); 367 break; 368 369 case XT_MULTIPORT_EITHER: 370 printf("ports "); 371 break; 372 373 default: 374 printf("ERROR "); 375 break; 376 } 377 378 if (multiinfo->invert) 379 printf("! "); 380 381 for (i=0; i < multiinfo->count; i++) { 382 printf("%s", i ? "," : ""); 383 print_port(multiinfo->ports[i], proto, numeric); 384 if (multiinfo->pflags[i]) { 385 printf(":"); 386 print_port(multiinfo->ports[++i], proto, numeric); 387 } 388 } 389 printf(" "); 390} 391 392static void 393print_v1(const void *ip_void, const struct xt_entry_match *match, int numeric) 394{ 395 const struct ipt_ip *ip = ip_void; 396 __print_v1(match, numeric, ip->proto); 397} 398 399/* Saves the union ipt_matchinfo in parsable form to stdout. */ 400static void __save(const struct xt_entry_match *match, u_int16_t proto) 401{ 402 const struct xt_multiport *multiinfo 403 = (const struct xt_multiport *)match->data; 404 unsigned int i; 405 406 switch (multiinfo->flags) { 407 case XT_MULTIPORT_SOURCE: 408 printf("--sports "); 409 break; 410 411 case XT_MULTIPORT_DESTINATION: 412 printf("--dports "); 413 break; 414 415 case XT_MULTIPORT_EITHER: 416 printf("--ports "); 417 break; 418 } 419 420 for (i=0; i < multiinfo->count; i++) { 421 printf("%s", i ? "," : ""); 422 print_port(multiinfo->ports[i], proto, 1); 423 } 424 printf(" "); 425} 426 427static void save(const void *ip_void, const struct xt_entry_match *match) 428{ 429 const struct ipt_ip *ip = ip_void; 430 __save(match, ip->proto); 431} 432 433static void __save_v1(const struct xt_entry_match *match, u_int16_t proto) 434{ 435 const struct xt_multiport_v1 *multiinfo 436 = (const struct xt_multiport_v1 *)match->data; 437 unsigned int i; 438 439 switch (multiinfo->flags) { 440 case XT_MULTIPORT_SOURCE: 441 printf("--sports "); 442 break; 443 444 case XT_MULTIPORT_DESTINATION: 445 printf("--dports "); 446 break; 447 448 case XT_MULTIPORT_EITHER: 449 printf("--ports "); 450 break; 451 } 452 453 if (multiinfo->invert) 454 printf("! "); 455 456 for (i=0; i < multiinfo->count; i++) { 457 printf("%s", i ? "," : ""); 458 print_port(multiinfo->ports[i], proto, 1); 459 if (multiinfo->pflags[i]) { 460 printf(":"); 461 print_port(multiinfo->ports[++i], proto, 1); 462 } 463 } 464 printf(" "); 465} 466 467static void save_v1(const void *ip_void, const struct xt_entry_match *match) 468{ 469 const struct ipt_ip *ip = ip_void; 470 __save_v1(match, ip->proto); 471} 472 473static struct xtables_match multiport = { 474 .next = NULL, 475 .family = AF_INET, 476 .name = "multiport", 477 .revision = 0, 478 .version = IPTABLES_VERSION, 479 .size = XT_ALIGN(sizeof(struct xt_multiport)), 480 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), 481 .help = &help, 482 .init = &init, 483 .parse = &parse, 484 .final_check = &final_check, 485 .print = &print, 486 .save = &save, 487 .extra_opts = opts 488}; 489 490static struct xtables_match multiport_v1 = { 491 .next = NULL, 492 .family = AF_INET, 493 .name = "multiport", 494 .version = IPTABLES_VERSION, 495 .revision = 1, 496 .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), 497 .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), 498 .help = &help_v1, 499 .init = &init, 500 .parse = &parse_v1, 501 .final_check = &final_check, 502 .print = &print_v1, 503 .save = &save_v1, 504 .extra_opts = opts 505}; 506 507void 508_init(void) 509{ 510 xtables_register_match(&multiport); 511 xtables_register_match(&multiport_v1); 512} 513