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