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