libxt_sctp.c revision 830132ac9c0d270bf9dcfe85c2464e3fe8c73fb9
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, .val = '1' }, 73 { .name = "sport", .has_arg = 1, .val = '1' }, 74 { .name = "destination-port", .has_arg = 1, .val = '2' }, 75 { .name = "dport", .has_arg = 1, .val = '2' }, 76 { .name = "chunk-types", .has_arg = 1, .val = '3' }, 77 { } 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 char * 321port_to_service(int port) 322{ 323 struct servent *service; 324 325 if ((service = getservbyport(htons(port), "sctp"))) 326 return service->s_name; 327 328 return NULL; 329} 330 331static void 332print_port(u_int16_t port, int numeric) 333{ 334 char *service; 335 336 if (numeric || (service = port_to_service(port)) == NULL) 337 printf("%u", port); 338 else 339 printf("%s", service); 340} 341 342static void 343print_ports(const char *name, u_int16_t min, u_int16_t max, 344 int invert, int numeric) 345{ 346 const char *inv = invert ? "!" : ""; 347 348 if (min != 0 || max != 0xFFFF || invert) { 349 printf("%s", name); 350 if (min == max) { 351 printf(":%s", inv); 352 print_port(min, numeric); 353 } else { 354 printf("s:%s", inv); 355 print_port(min, numeric); 356 printf(":"); 357 print_port(max, numeric); 358 } 359 printf(" "); 360 } 361} 362 363static void 364print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask) 365{ 366 int i; 367 368 DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 369 chunk_flags_mask); 370 371 if (chunk_flags_mask) { 372 printf(":"); 373 } 374 375 for (i = 7; i >= 0; i--) { 376 if (chunk_flags_mask & (1 << i)) { 377 if (chunk_flags & (1 << i)) { 378 printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]); 379 } else { 380 printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i])); 381 } 382 } 383 } 384} 385 386static void 387print_chunk(u_int32_t chunknum, int numeric) 388{ 389 if (numeric) { 390 printf("0x%04X", chunknum); 391 } 392 else { 393 int i; 394 395 for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) { 396 if (sctp_chunk_names[i].chunk_type == chunknum) 397 printf("%s", sctp_chunk_names[chunknum].name); 398 } 399 } 400} 401 402static void 403print_chunks(u_int32_t chunk_match_type, 404 const u_int32_t *chunkmap, 405 const struct xt_sctp_flag_info *flag_info, 406 int flag_count, 407 int numeric) 408{ 409 int i, j; 410 int flag; 411 412 switch (chunk_match_type) { 413 case SCTP_CHUNK_MATCH_ANY: printf("any "); break; 414 case SCTP_CHUNK_MATCH_ALL: printf("all "); break; 415 case SCTP_CHUNK_MATCH_ONLY: printf("only "); break; 416 default: printf("Never reach herer\n"); break; 417 } 418 419 if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) { 420 printf("NONE "); 421 goto out; 422 } 423 424 if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) { 425 printf("ALL "); 426 goto out; 427 } 428 429 flag = 0; 430 for (i = 0; i < 256; i++) { 431 if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) { 432 if (flag) 433 printf(","); 434 flag = 1; 435 print_chunk(i, numeric); 436 for (j = 0; j < flag_count; j++) { 437 if (flag_info[j].chunktype == i) { 438 print_chunk_flags(i, flag_info[j].flag, 439 flag_info[j].flag_mask); 440 } 441 } 442 } 443 } 444 445 if (flag) 446 printf(" "); 447out: 448 return; 449} 450 451/* Prints out the matchinfo. */ 452static void 453print(const void *ip, 454 const struct xt_entry_match *match, 455 int numeric) 456{ 457 const struct xt_sctp_info *einfo = 458 (const struct xt_sctp_info *)match->data; 459 460 printf("sctp "); 461 462 if (einfo->flags & XT_SCTP_SRC_PORTS) { 463 print_ports("spt", einfo->spts[0], einfo->spts[1], 464 einfo->invflags & XT_SCTP_SRC_PORTS, 465 numeric); 466 } 467 468 if (einfo->flags & XT_SCTP_DEST_PORTS) { 469 print_ports("dpt", einfo->dpts[0], einfo->dpts[1], 470 einfo->invflags & XT_SCTP_DEST_PORTS, 471 numeric); 472 } 473 474 if (einfo->flags & XT_SCTP_CHUNK_TYPES) { 475 /* FIXME: print_chunks() is used in save() where the printing of '!' 476 s taken care of, so we need to do that here as well */ 477 if (einfo->invflags & XT_SCTP_CHUNK_TYPES) { 478 printf("! "); 479 } 480 print_chunks(einfo->chunk_match_type, einfo->chunkmap, 481 einfo->flag_info, einfo->flag_count, numeric); 482 } 483} 484 485/* Saves the union ipt_matchinfo in parsable form to stdout. */ 486static void 487save(const void *ip, 488 const struct xt_entry_match *match) 489{ 490 const struct xt_sctp_info *einfo = 491 (const struct xt_sctp_info *)match->data; 492 493 if (einfo->flags & XT_SCTP_SRC_PORTS) { 494 if (einfo->invflags & XT_SCTP_SRC_PORTS) 495 printf("! "); 496 if (einfo->spts[0] != einfo->spts[1]) 497 printf("--sport %u:%u ", 498 einfo->spts[0], einfo->spts[1]); 499 else 500 printf("--sport %u ", einfo->spts[0]); 501 } 502 503 if (einfo->flags & XT_SCTP_DEST_PORTS) { 504 if (einfo->invflags & XT_SCTP_DEST_PORTS) 505 printf("! "); 506 if (einfo->dpts[0] != einfo->dpts[1]) 507 printf("--dport %u:%u ", 508 einfo->dpts[0], einfo->dpts[1]); 509 else 510 printf("--dport %u ", einfo->dpts[0]); 511 } 512 513 if (einfo->flags & XT_SCTP_CHUNK_TYPES) { 514 if (einfo->invflags & XT_SCTP_CHUNK_TYPES) 515 printf("! "); 516 printf("--chunk-types "); 517 518 print_chunks(einfo->chunk_match_type, einfo->chunkmap, 519 einfo->flag_info, einfo->flag_count, 0); 520 } 521} 522 523static struct xtables_match sctp = { 524 .name = "sctp", 525 .family = AF_INET, 526 .version = IPTABLES_VERSION, 527 .size = XT_ALIGN(sizeof(struct xt_sctp_info)), 528 .userspacesize = XT_ALIGN(sizeof(struct xt_sctp_info)), 529 .help = &help, 530 .init = &init, 531 .parse = &parse, 532 .print = &print, 533 .save = &save, 534 .extra_opts = opts 535}; 536 537static struct xtables_match sctp6 = { 538 .name = "sctp", 539 .family = AF_INET6, 540 .version = IPTABLES_VERSION, 541 .size = XT_ALIGN(sizeof(struct xt_sctp_info)), 542 .userspacesize = XT_ALIGN(sizeof(struct xt_sctp_info)), 543 .help = &help, 544 .init = &init, 545 .parse = &parse, 546 .print = &print, 547 .save = &save, 548 .extra_opts = opts 549}; 550 551void _init(void) 552{ 553 xtables_register_match(&sctp); 554 xtables_register_match(&sctp6); 555} 556 557