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