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