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