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