1/* Shared library add-on to iptables for SCTP matching 2 * 3 * (C) 2003 by Harald Welte <laforge@gnumonks.org> 4 * 5 * This program is distributed under the terms of GNU GPL v2, 1991 6 * 7 * libipt_ecn.c borrowed heavily from libipt_dscp.c 8 * 9 */ 10#include <stdio.h> 11#include <string.h> 12#include <stdlib.h> 13#include <getopt.h> 14#include <netdb.h> 15#include <ctype.h> 16 17#include <iptables.h> 18#include <linux/netfilter_ipv4/ip_tables.h> 19 20#ifndef ARRAY_SIZE 21#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 22#endif 23 24#include <linux/netfilter_ipv4/ipt_sctp.h> 25 26/* Some ZS!#@:$%*#$! has replaced the ELEMCOUNT macro in ipt_sctp.h with 27 * ARRAY_SIZE without noticing that this file is used from userserspace, 28 * and userspace doesn't have ARRAY_SIZE */ 29 30#ifndef ELEMCOUNT 31#define ELEMCOUNT ARRAY_SIZE 32#endif 33 34#if 0 35#define DEBUGP(format, first...) printf(format, ##first) 36#define static 37#else 38#define DEBUGP(format, fist...) 39#endif 40 41static void 42print_chunk(u_int32_t chunknum, int numeric); 43 44/* Initialize the match. */ 45static void 46init(struct ipt_entry_match *m, 47 unsigned int *nfcache) 48{ 49 int i; 50 struct ipt_sctp_info *einfo = (struct ipt_sctp_info *)m->data; 51 52 memset(einfo, 0, sizeof(struct ipt_sctp_info)); 53 54 for (i = 0; i < IPT_NUM_SCTP_FLAGS; i++) { 55 einfo->flag_info[i].chunktype = -1; 56 } 57} 58 59static void help(void) 60{ 61 printf( 62"SCTP match v%s options\n" 63" --source-port [!] port[:port] match source port(s)\n" 64" --sport ...\n" 65" --destination-port [!] port[:port] match destination port(s)\n" 66" --dport ...\n" 67" --chunk-types [!] (all|any|none) (chunktype[:flags])+ match if all, any or none of\n" 68" chunktypes are present\n" 69"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK ALL NONE\n", 70 IPTABLES_VERSION); 71} 72 73static struct option opts[] = { 74 { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' }, 75 { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' }, 76 { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' }, 77 { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' }, 78 { .name = "chunk-types", .has_arg = 1, .flag = 0, .val = '3' }, 79 { .name = 0 } 80}; 81 82static void 83parse_sctp_ports(const char *portstring, 84 u_int16_t *ports) 85{ 86 char *buffer; 87 char *cp; 88 89 buffer = strdup(portstring); 90 DEBUGP("%s\n", portstring); 91 if ((cp = strchr(buffer, ':')) == NULL) { 92 ports[0] = ports[1] = parse_port(buffer, "sctp"); 93 } 94 else { 95 *cp = '\0'; 96 cp++; 97 98 ports[0] = buffer[0] ? parse_port(buffer, "sctp") : 0; 99 ports[1] = cp[0] ? parse_port(cp, "sctp") : 0xFFFF; 100 101 if (ports[0] > ports[1]) 102 exit_error(PARAMETER_PROBLEM, 103 "invalid portrange (min > max)"); 104 } 105 free(buffer); 106} 107 108struct sctp_chunk_names { 109 const char *name; 110 unsigned int chunk_type; 111 const char *valid_flags; 112}; 113 114/*'ALL' and 'NONE' will be treated specially. */ 115static struct sctp_chunk_names sctp_chunk_names[] 116= { { .name = "DATA", .chunk_type = 0, .valid_flags = "-----UBE"}, 117 { .name = "INIT", .chunk_type = 1, .valid_flags = "--------"}, 118 { .name = "INIT_ACK", .chunk_type = 2, .valid_flags = "--------"}, 119 { .name = "SACK", .chunk_type = 3, .valid_flags = "--------"}, 120 { .name = "HEARTBEAT", .chunk_type = 4, .valid_flags = "--------"}, 121 { .name = "HEARTBEAT_ACK", .chunk_type = 5, .valid_flags = "--------"}, 122 { .name = "ABORT", .chunk_type = 6, .valid_flags = "-------T"}, 123 { .name = "SHUTDOWN", .chunk_type = 7, .valid_flags = "--------"}, 124 { .name = "SHUTDOWN_ACK", .chunk_type = 8, .valid_flags = "--------"}, 125 { .name = "ERROR", .chunk_type = 9, .valid_flags = "--------"}, 126 { .name = "COOKIE_ECHO", .chunk_type = 10, .valid_flags = "--------"}, 127 { .name = "COOKIE_ACK", .chunk_type = 11, .valid_flags = "--------"}, 128 { .name = "ECN_ECNE", .chunk_type = 12, .valid_flags = "--------"}, 129 { .name = "ECN_CWR", .chunk_type = 13, .valid_flags = "--------"}, 130 { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14, .valid_flags = "-------T"}, 131 { .name = "ASCONF", .chunk_type = 31, .valid_flags = "--------"}, 132 { .name = "ASCONF_ACK", .chunk_type = 30, .valid_flags = "--------"}, 133}; 134 135static void 136save_chunk_flag_info(struct ipt_sctp_flag_info *flag_info, 137 int *flag_count, 138 int chunktype, 139 int bit, 140 int set) 141{ 142 int i; 143 144 for (i = 0; i < *flag_count; i++) { 145 if (flag_info[i].chunktype == chunktype) { 146 DEBUGP("Previous match found\n"); 147 flag_info[i].chunktype = chunktype; 148 flag_info[i].flag_mask |= (1 << bit); 149 if (set) { 150 flag_info[i].flag |= (1 << bit); 151 } 152 153 return; 154 } 155 } 156 157 if (*flag_count == IPT_NUM_SCTP_FLAGS) { 158 exit_error (PARAMETER_PROBLEM, 159 "Number of chunk types with flags exceeds currently allowed limit." 160 "Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and" 161 "recompiling both the kernel space and user space modules\n"); 162 } 163 164 flag_info[*flag_count].chunktype = chunktype; 165 flag_info[*flag_count].flag_mask |= (1 << bit); 166 if (set) { 167 flag_info[*flag_count].flag |= (1 << bit); 168 } 169 (*flag_count)++; 170} 171 172static void 173parse_sctp_chunk(struct ipt_sctp_info *einfo, 174 const char *chunks) 175{ 176 char *ptr; 177 char *buffer; 178 unsigned int i, j; 179 int found = 0; 180 char *chunk_flags; 181 182 buffer = strdup(chunks); 183 DEBUGP("Buffer: %s\n", buffer); 184 185 SCTP_CHUNKMAP_RESET(einfo->chunkmap); 186 187 if (!strcasecmp(buffer, "ALL")) { 188 SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap); 189 goto out; 190 } 191 192 if (!strcasecmp(buffer, "NONE")) { 193 SCTP_CHUNKMAP_RESET(einfo->chunkmap); 194 goto out; 195 } 196 197 for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) { 198 found = 0; 199 DEBUGP("Next Chunk type %s\n", ptr); 200 201 if ((chunk_flags = strchr(ptr, ':')) != NULL) { 202 *chunk_flags++ = 0; 203 } 204 205 for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) { 206 if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) { 207 DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type); 208 SCTP_CHUNKMAP_SET(einfo->chunkmap, 209 sctp_chunk_names[i].chunk_type); 210 found = 1; 211 break; 212 } 213 } 214 if (!found) 215 exit_error(PARAMETER_PROBLEM, 216 "Unknown sctp chunk `%s'", ptr); 217 218 if (chunk_flags) { 219 DEBUGP("Chunk flags %s\n", chunk_flags); 220 for (j = 0; j < strlen(chunk_flags); j++) { 221 char *p; 222 int bit; 223 224 if ((p = strchr(sctp_chunk_names[i].valid_flags, 225 toupper(chunk_flags[j]))) != NULL) { 226 bit = p - sctp_chunk_names[i].valid_flags; 227 bit = 7 - bit; 228 229 save_chunk_flag_info(einfo->flag_info, 230 &(einfo->flag_count), i, bit, 231 isupper(chunk_flags[j])); 232 } else { 233 exit_error(PARAMETER_PROBLEM, 234 "Invalid flags for chunk type %d\n", i); 235 } 236 } 237 } 238 } 239out: 240 free(buffer); 241} 242 243static void 244parse_sctp_chunks(struct ipt_sctp_info *einfo, 245 const char *match_type, 246 const char *chunks) 247{ 248 DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks); 249 if (!strcasecmp(match_type, "ANY")) { 250 einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY; 251 } else if (!strcasecmp(match_type, "ALL")) { 252 einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL; 253 } else if (!strcasecmp(match_type, "ONLY")) { 254 einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY; 255 } else { 256 exit_error (PARAMETER_PROBLEM, 257 "Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\""); 258 } 259 260 SCTP_CHUNKMAP_RESET(einfo->chunkmap); 261 parse_sctp_chunk(einfo, chunks); 262} 263 264static int 265parse(int c, char **argv, int invert, unsigned int *flags, 266 const struct ipt_entry *entry, 267 unsigned int *nfcache, 268 struct ipt_entry_match **match) 269{ 270 struct ipt_sctp_info *einfo 271 = (struct ipt_sctp_info *)(*match)->data; 272 273 switch (c) { 274 case '1': 275 if (*flags & IPT_SCTP_SRC_PORTS) 276 exit_error(PARAMETER_PROBLEM, 277 "Only one `--source-port' allowed"); 278 einfo->flags |= IPT_SCTP_SRC_PORTS; 279 check_inverse(optarg, &invert, &optind, 0); 280 parse_sctp_ports(argv[optind-1], einfo->spts); 281 if (invert) 282 einfo->invflags |= IPT_SCTP_SRC_PORTS; 283 *flags |= IPT_SCTP_SRC_PORTS; 284 break; 285 286 case '2': 287 if (*flags & IPT_SCTP_DEST_PORTS) 288 exit_error(PARAMETER_PROBLEM, 289 "Only one `--destination-port' allowed"); 290 einfo->flags |= IPT_SCTP_DEST_PORTS; 291 check_inverse(optarg, &invert, &optind, 0); 292 parse_sctp_ports(argv[optind-1], einfo->dpts); 293 if (invert) 294 einfo->invflags |= IPT_SCTP_DEST_PORTS; 295 *flags |= IPT_SCTP_DEST_PORTS; 296 break; 297 298 case '3': 299 if (*flags & IPT_SCTP_CHUNK_TYPES) 300 exit_error(PARAMETER_PROBLEM, 301 "Only one `--chunk-types' allowed"); 302 check_inverse(optarg, &invert, &optind, 0); 303 304 if (!argv[optind] 305 || argv[optind][0] == '-' || argv[optind][0] == '!') 306 exit_error(PARAMETER_PROBLEM, 307 "--chunk-types requires two args"); 308 309 einfo->flags |= IPT_SCTP_CHUNK_TYPES; 310 parse_sctp_chunks(einfo, argv[optind-1], argv[optind]); 311 if (invert) 312 einfo->invflags |= IPT_SCTP_CHUNK_TYPES; 313 optind++; 314 *flags |= IPT_SCTP_CHUNK_TYPES; 315 break; 316 317 default: 318 return 0; 319 } 320 return 1; 321} 322 323static void 324final_check(unsigned int flags) 325{ 326} 327 328static char * 329port_to_service(int port) 330{ 331 struct servent *service; 332 333 if ((service = getservbyport(htons(port), "sctp"))) 334 return service->s_name; 335 336 return NULL; 337} 338 339static void 340print_port(u_int16_t port, int numeric) 341{ 342 char *service; 343 344 if (numeric || (service = port_to_service(port)) == NULL) 345 printf("%u", port); 346 else 347 printf("%s", service); 348} 349 350static void 351print_ports(const char *name, u_int16_t min, u_int16_t max, 352 int invert, int numeric) 353{ 354 const char *inv = invert ? "!" : ""; 355 356 if (min != 0 || max != 0xFFFF || invert) { 357 printf("%s", name); 358 if (min == max) { 359 printf(":%s", inv); 360 print_port(min, numeric); 361 } else { 362 printf("s:%s", inv); 363 print_port(min, numeric); 364 printf(":"); 365 print_port(max, numeric); 366 } 367 printf(" "); 368 } 369} 370 371static void 372print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask) 373{ 374 int i; 375 376 DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 377 chunk_flags_mask); 378 379 if (chunk_flags_mask) { 380 printf(":"); 381 } 382 383 for (i = 7; i >= 0; i--) { 384 if (chunk_flags_mask & (1 << i)) { 385 if (chunk_flags & (1 << i)) { 386 printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]); 387 } else { 388 printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i])); 389 } 390 } 391 } 392} 393 394static void 395print_chunk(u_int32_t chunknum, int numeric) 396{ 397 if (numeric) { 398 printf("0x%04X", chunknum); 399 } 400 else { 401 int i; 402 403 for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) { 404 if (sctp_chunk_names[i].chunk_type == chunknum) 405 printf("%s", sctp_chunk_names[chunknum].name); 406 } 407 } 408} 409 410static void 411print_chunks(u_int32_t chunk_match_type, 412 const u_int32_t *chunkmap, 413 const struct ipt_sctp_flag_info *flag_info, 414 int flag_count, 415 int numeric) 416{ 417 int i, j; 418 int flag; 419 420 switch (chunk_match_type) { 421 case SCTP_CHUNK_MATCH_ANY: printf("any "); break; 422 case SCTP_CHUNK_MATCH_ALL: printf("all "); break; 423 case SCTP_CHUNK_MATCH_ONLY: printf("only "); break; 424 default: printf("Never reach herer\n"); break; 425 } 426 427 if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) { 428 printf("NONE "); 429 goto out; 430 } 431 432 if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) { 433 printf("ALL "); 434 goto out; 435 } 436 437 flag = 0; 438 for (i = 0; i < 256; i++) { 439 if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) { 440 if (flag) 441 printf(","); 442 flag = 1; 443 print_chunk(i, numeric); 444 for (j = 0; j < flag_count; j++) { 445 if (flag_info[j].chunktype == i) { 446 print_chunk_flags(i, flag_info[j].flag, 447 flag_info[j].flag_mask); 448 } 449 } 450 } 451 } 452 453 if (flag) 454 printf(" "); 455out: 456 return; 457} 458 459/* Prints out the matchinfo. */ 460static void 461print(const struct ipt_ip *ip, 462 const struct ipt_entry_match *match, 463 int numeric) 464{ 465 const struct ipt_sctp_info *einfo = 466 (const struct ipt_sctp_info *)match->data; 467 468 printf("sctp "); 469 470 if (einfo->flags & IPT_SCTP_SRC_PORTS) { 471 print_ports("spt", einfo->spts[0], einfo->spts[1], 472 einfo->invflags & IPT_SCTP_SRC_PORTS, 473 numeric); 474 } 475 476 if (einfo->flags & IPT_SCTP_DEST_PORTS) { 477 print_ports("dpt", einfo->dpts[0], einfo->dpts[1], 478 einfo->invflags & IPT_SCTP_DEST_PORTS, 479 numeric); 480 } 481 482 if (einfo->flags & IPT_SCTP_CHUNK_TYPES) { 483 /* FIXME: print_chunks() is used in save() where the printing of '!' 484 s taken care of, so we need to do that here as well */ 485 if (einfo->invflags & IPT_SCTP_CHUNK_TYPES) { 486 printf("! "); 487 } 488 print_chunks(einfo->chunk_match_type, einfo->chunkmap, 489 einfo->flag_info, einfo->flag_count, numeric); 490 } 491} 492 493/* Saves the union ipt_matchinfo in parsable form to stdout. */ 494static void 495save(const struct ipt_ip *ip, 496 const struct ipt_entry_match *match) 497{ 498 const struct ipt_sctp_info *einfo = 499 (const struct ipt_sctp_info *)match->data; 500 501 if (einfo->flags & IPT_SCTP_SRC_PORTS) { 502 if (einfo->invflags & IPT_SCTP_SRC_PORTS) 503 printf("! "); 504 if (einfo->spts[0] != einfo->spts[1]) 505 printf("--sport %u:%u ", 506 einfo->spts[0], einfo->spts[1]); 507 else 508 printf("--sport %u ", einfo->spts[0]); 509 } 510 511 if (einfo->flags & IPT_SCTP_DEST_PORTS) { 512 if (einfo->invflags & IPT_SCTP_DEST_PORTS) 513 printf("! "); 514 if (einfo->dpts[0] != einfo->dpts[1]) 515 printf("--dport %u:%u ", 516 einfo->dpts[0], einfo->dpts[1]); 517 else 518 printf("--dport %u ", einfo->dpts[0]); 519 } 520 521 if (einfo->flags & IPT_SCTP_CHUNK_TYPES) { 522 if (einfo->invflags & IPT_SCTP_CHUNK_TYPES) 523 printf("! "); 524 printf("--chunk-types "); 525 526 print_chunks(einfo->chunk_match_type, einfo->chunkmap, 527 einfo->flag_info, einfo->flag_count, 0); 528 } 529} 530 531static 532struct iptables_match sctp 533= { .name = "sctp", 534 .version = IPTABLES_VERSION, 535 .size = IPT_ALIGN(sizeof(struct ipt_sctp_info)), 536 .userspacesize = IPT_ALIGN(sizeof(struct ipt_sctp_info)), 537 .help = &help, 538 .init = &init, 539 .parse = &parse, 540 .final_check = &final_check, 541 .print = &print, 542 .save = &save, 543 .extra_opts = opts 544}; 545 546void _init(void) 547{ 548 register_match(&sctp); 549} 550 551