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#include <iptables.h> 8/* To ensure that iptables compiles with an old kernel */ 9#include "../include/linux/netfilter_ipv4/ipt_multiport.h" 10 11/* Function which prints out usage message. */ 12static void 13help(void) 14{ 15 printf( 16"multiport v%s options:\n" 17" --source-ports port[,port,port...]\n" 18" --sports ...\n" 19" match source port(s)\n" 20" --destination-ports port[,port,port...]\n" 21" --dports ...\n" 22" match destination port(s)\n" 23" --ports port[,port,port]\n" 24" match both source and destination port(s)\n" 25" NOTE: this kernel does not support port ranges in multiport.\n", 26IPTABLES_VERSION); 27} 28 29static void 30help_v1(void) 31{ 32 printf( 33"multiport v%s options:\n" 34" --source-ports [!] port[,port:port,port...]\n" 35" --sports ...\n" 36" match source port(s)\n" 37" --destination-ports [!] port[,port:port,port...]\n" 38" --dports ...\n" 39" match destination port(s)\n" 40" --ports [!] port[,port:port,port]\n" 41" match both source and destination port(s)\n", 42IPTABLES_VERSION); 43} 44 45static struct option opts[] = { 46 { "source-ports", 1, 0, '1' }, 47 { "sports", 1, 0, '1' }, /* synonym */ 48 { "destination-ports", 1, 0, '2' }, 49 { "dports", 1, 0, '2' }, /* synonym */ 50 { "ports", 1, 0, '3' }, 51 {0} 52}; 53 54static char * 55proto_to_name(u_int8_t proto) 56{ 57 switch (proto) { 58 case IPPROTO_TCP: 59 return "tcp"; 60 case IPPROTO_UDP: 61 return "udp"; 62 case IPPROTO_UDPLITE: 63 return "udplite"; 64 case IPPROTO_SCTP: 65 return "sctp"; 66 case IPPROTO_DCCP: 67 return "dccp"; 68 default: 69 return NULL; 70 } 71} 72 73static unsigned int 74parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto) 75{ 76 char *buffer, *cp, *next; 77 unsigned int i; 78 79 buffer = strdup(portstring); 80 if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed"); 81 82 for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++) 83 { 84 next=strchr(cp, ','); 85 if (next) *next++='\0'; 86 ports[i] = parse_port(cp, proto); 87 } 88 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified"); 89 free(buffer); 90 return i; 91} 92 93static void 94parse_multi_ports_v1(const char *portstring, 95 struct ipt_multiport_v1 *multiinfo, 96 const char *proto) 97{ 98 char *buffer, *cp, *next, *range; 99 unsigned int i; 100 u_int16_t m; 101 102 buffer = strdup(portstring); 103 if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed"); 104 105 for (i=0; i<IPT_MULTI_PORTS; i++) 106 multiinfo->pflags[i] = 0; 107 108 for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) { 109 next=strchr(cp, ','); 110 if (next) *next++='\0'; 111 range = strchr(cp, ':'); 112 if (range) { 113 if (i == IPT_MULTI_PORTS-1) 114 exit_error(PARAMETER_PROBLEM, 115 "too many ports specified"); 116 *range++ = '\0'; 117 } 118 multiinfo->ports[i] = parse_port(cp, proto); 119 if (range) { 120 multiinfo->pflags[i] = 1; 121 multiinfo->ports[++i] = parse_port(range, proto); 122 if (multiinfo->ports[i-1] >= multiinfo->ports[i]) 123 exit_error(PARAMETER_PROBLEM, 124 "invalid portrange specified"); 125 m <<= 1; 126 } 127 } 128 multiinfo->count = i; 129 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified"); 130 free(buffer); 131} 132 133/* Initialize the match. */ 134static void 135init(struct ipt_entry_match *m, unsigned int *nfcache) 136{ 137} 138 139static const char * 140check_proto(const struct ipt_entry *entry) 141{ 142 char *proto; 143 144 if (entry->ip.invflags & IPT_INV_PROTO) 145 exit_error(PARAMETER_PROBLEM, 146 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 147 148 if ((proto = proto_to_name(entry->ip.proto)) != NULL) 149 return proto; 150 else if (!entry->ip.proto) 151 exit_error(PARAMETER_PROBLEM, 152 "multiport needs `-p tcp', `-p udp', `-p udplite', " 153 "`-p sctp' or `-p dccp'"); 154 else 155 exit_error(PARAMETER_PROBLEM, 156 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); 157} 158 159/* Function which parses command options; returns true if it 160 ate an option */ 161static int 162parse(int c, char **argv, int invert, unsigned int *flags, 163 const struct ipt_entry *entry, 164 unsigned int *nfcache, 165 struct ipt_entry_match **match) 166{ 167 const char *proto; 168 struct ipt_multiport *multiinfo 169 = (struct ipt_multiport *)(*match)->data; 170 171 switch (c) { 172 case '1': 173 check_inverse(argv[optind-1], &invert, &optind, 0); 174 proto = check_proto(entry); 175 multiinfo->count = parse_multi_ports(argv[optind-1], 176 multiinfo->ports, proto); 177 multiinfo->flags = IPT_MULTIPORT_SOURCE; 178 break; 179 180 case '2': 181 check_inverse(argv[optind-1], &invert, &optind, 0); 182 proto = check_proto(entry); 183 multiinfo->count = parse_multi_ports(argv[optind-1], 184 multiinfo->ports, proto); 185 multiinfo->flags = IPT_MULTIPORT_DESTINATION; 186 break; 187 188 case '3': 189 check_inverse(argv[optind-1], &invert, &optind, 0); 190 proto = check_proto(entry); 191 multiinfo->count = parse_multi_ports(argv[optind-1], 192 multiinfo->ports, proto); 193 multiinfo->flags = IPT_MULTIPORT_EITHER; 194 break; 195 196 default: 197 return 0; 198 } 199 200 if (invert) 201 exit_error(PARAMETER_PROBLEM, 202 "multiport does not support invert"); 203 204 if (*flags) 205 exit_error(PARAMETER_PROBLEM, 206 "multiport can only have one option"); 207 *flags = 1; 208 return 1; 209} 210 211static int 212parse_v1(int c, char **argv, int invert, unsigned int *flags, 213 const struct ipt_entry *entry, 214 unsigned int *nfcache, 215 struct ipt_entry_match **match) 216{ 217 const char *proto; 218 struct ipt_multiport_v1 *multiinfo 219 = (struct ipt_multiport_v1 *)(*match)->data; 220 221 switch (c) { 222 case '1': 223 check_inverse(argv[optind-1], &invert, &optind, 0); 224 proto = check_proto(entry); 225 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 226 multiinfo->flags = IPT_MULTIPORT_SOURCE; 227 break; 228 229 case '2': 230 check_inverse(argv[optind-1], &invert, &optind, 0); 231 proto = check_proto(entry); 232 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 233 multiinfo->flags = IPT_MULTIPORT_DESTINATION; 234 break; 235 236 case '3': 237 check_inverse(argv[optind-1], &invert, &optind, 0); 238 proto = check_proto(entry); 239 parse_multi_ports_v1(argv[optind-1], multiinfo, proto); 240 multiinfo->flags = IPT_MULTIPORT_EITHER; 241 break; 242 243 default: 244 return 0; 245 } 246 247 if (invert) 248 multiinfo->invert = 1; 249 250 if (*flags) 251 exit_error(PARAMETER_PROBLEM, 252 "multiport can only have one option"); 253 *flags = 1; 254 return 1; 255} 256 257/* Final check; must specify something. */ 258static void 259final_check(unsigned int flags) 260{ 261 if (!flags) 262 exit_error(PARAMETER_PROBLEM, "multiport expection an option"); 263} 264 265static char * 266port_to_service(int port, u_int8_t proto) 267{ 268 struct servent *service; 269 270 if ((service = getservbyport(htons(port), proto_to_name(proto)))) 271 return service->s_name; 272 273 return NULL; 274} 275 276static void 277print_port(u_int16_t port, u_int8_t protocol, int numeric) 278{ 279 char *service; 280 281 if (numeric || (service = port_to_service(port, protocol)) == NULL) 282 printf("%u", port); 283 else 284 printf("%s", service); 285} 286 287/* Prints out the matchinfo. */ 288static void 289print(const struct ipt_ip *ip, 290 const struct ipt_entry_match *match, 291 int numeric) 292{ 293 const struct ipt_multiport *multiinfo 294 = (const struct ipt_multiport *)match->data; 295 unsigned int i; 296 297 printf("multiport "); 298 299 switch (multiinfo->flags) { 300 case IPT_MULTIPORT_SOURCE: 301 printf("sports "); 302 break; 303 304 case IPT_MULTIPORT_DESTINATION: 305 printf("dports "); 306 break; 307 308 case IPT_MULTIPORT_EITHER: 309 printf("ports "); 310 break; 311 312 default: 313 printf("ERROR "); 314 break; 315 } 316 317 for (i=0; i < multiinfo->count; i++) { 318 printf("%s", i ? "," : ""); 319 print_port(multiinfo->ports[i], ip->proto, numeric); 320 } 321 printf(" "); 322} 323 324static void 325print_v1(const struct ipt_ip *ip, 326 const struct ipt_entry_match *match, 327 int numeric) 328{ 329 const struct ipt_multiport_v1 *multiinfo 330 = (const struct ipt_multiport_v1 *)match->data; 331 unsigned int i; 332 333 printf("multiport "); 334 335 switch (multiinfo->flags) { 336 case IPT_MULTIPORT_SOURCE: 337 printf("sports "); 338 break; 339 340 case IPT_MULTIPORT_DESTINATION: 341 printf("dports "); 342 break; 343 344 case IPT_MULTIPORT_EITHER: 345 printf("ports "); 346 break; 347 348 default: 349 printf("ERROR "); 350 break; 351 } 352 353 if (multiinfo->invert) 354 printf("! "); 355 356 for (i=0; i < multiinfo->count; i++) { 357 printf("%s", i ? "," : ""); 358 print_port(multiinfo->ports[i], ip->proto, numeric); 359 if (multiinfo->pflags[i]) { 360 printf(":"); 361 print_port(multiinfo->ports[++i], ip->proto, numeric); 362 } 363 } 364 printf(" "); 365} 366 367/* Saves the union ipt_matchinfo in parsable form to stdout. */ 368static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) 369{ 370 const struct ipt_multiport *multiinfo 371 = (const struct ipt_multiport *)match->data; 372 unsigned int i; 373 374 switch (multiinfo->flags) { 375 case IPT_MULTIPORT_SOURCE: 376 printf("--sports "); 377 break; 378 379 case IPT_MULTIPORT_DESTINATION: 380 printf("--dports "); 381 break; 382 383 case IPT_MULTIPORT_EITHER: 384 printf("--ports "); 385 break; 386 } 387 388 for (i=0; i < multiinfo->count; i++) { 389 printf("%s", i ? "," : ""); 390 print_port(multiinfo->ports[i], ip->proto, 1); 391 } 392 printf(" "); 393} 394 395static void save_v1(const struct ipt_ip *ip, 396 const struct ipt_entry_match *match) 397{ 398 const struct ipt_multiport_v1 *multiinfo 399 = (const struct ipt_multiport_v1 *)match->data; 400 unsigned int i; 401 402 switch (multiinfo->flags) { 403 case IPT_MULTIPORT_SOURCE: 404 printf("--sports "); 405 break; 406 407 case IPT_MULTIPORT_DESTINATION: 408 printf("--dports "); 409 break; 410 411 case IPT_MULTIPORT_EITHER: 412 printf("--ports "); 413 break; 414 } 415 416 if (multiinfo->invert) 417 printf("! "); 418 419 for (i=0; i < multiinfo->count; i++) { 420 printf("%s", i ? "," : ""); 421 print_port(multiinfo->ports[i], ip->proto, 1); 422 if (multiinfo->pflags[i]) { 423 printf(":"); 424 print_port(multiinfo->ports[++i], ip->proto, 1); 425 } 426 } 427 printf(" "); 428} 429 430static struct iptables_match multiport = { 431 .next = NULL, 432 .name = "multiport", 433 .revision = 0, 434 .version = IPTABLES_VERSION, 435 .size = IPT_ALIGN(sizeof(struct ipt_multiport)), 436 .userspacesize = IPT_ALIGN(sizeof(struct ipt_multiport)), 437 .help = &help, 438 .init = &init, 439 .parse = &parse, 440 .final_check = &final_check, 441 .print = &print, 442 .save = &save, 443 .extra_opts = opts 444}; 445 446static struct iptables_match multiport_v1 = { 447 .next = NULL, 448 .name = "multiport", 449 .version = IPTABLES_VERSION, 450 .revision = 1, 451 .size = IPT_ALIGN(sizeof(struct ipt_multiport_v1)), 452 .userspacesize = IPT_ALIGN(sizeof(struct ipt_multiport_v1)), 453 .help = &help_v1, 454 .init = &init, 455 .parse = &parse_v1, 456 .final_check = &final_check, 457 .print = &print_v1, 458 .save = &save_v1, 459 .extra_opts = opts 460}; 461 462void 463_init(void) 464{ 465 register_match(&multiport); 466 register_match(&multiport_v1); 467} 468