blob: c816b99b8ca6cfe287b093fac6277d16b39d4c34 [file] [log] [blame]
Patrick McHardy38100132006-11-13 19:38:44 +00001/* 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
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +000017#include <xtables.h>
Patrick McHardy38100132006-11-13 19:38:44 +000018
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
Peter Rileyea146a92007-09-02 13:09:07 +000045init(struct xt_entry_match *m)
Patrick McHardy38100132006-11-13 19:38:44 +000046{
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
Jan Engelhardt661f1122007-07-30 14:46:51 +000071static const struct option opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000072 { .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 { }
Patrick McHardy38100132006-11-13 19:38:44 +000078};
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."
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +0000158 "Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
Patrick McHardy38100132006-11-13 19:38:44 +0000159 "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,
Yasuyuki KOZAKAIa620c612007-07-24 06:03:45 +0000264 const void *entry,
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +0000265 struct xt_entry_match **match)
Patrick McHardy38100132006-11-13 19:38:44 +0000266{
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
Yasuyuki KOZAKAIa620c612007-07-24 06:03:45 +0000458print(const void *ip,
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +0000459 const struct xt_entry_match *match,
Patrick McHardy38100132006-11-13 19:38:44 +0000460 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
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +0000490/* Saves the union ipt_matchinfo in parsable form to stdout. */
Patrick McHardy38100132006-11-13 19:38:44 +0000491static void
Yasuyuki KOZAKAIa620c612007-07-24 06:03:45 +0000492save(const void *ip,
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +0000493 const struct xt_entry_match *match)
Patrick McHardy38100132006-11-13 19:38:44 +0000494{
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
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +0000528static 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
Patrick McHardy38100132006-11-13 19:38:44 +0000556};
557
558void _init(void)
559{
Yasuyuki KOZAKAI19f29502007-07-24 07:02:26 +0000560 xtables_register_match(&sctp);
561 xtables_register_match(&sctp6);
Patrick McHardy38100132006-11-13 19:38:44 +0000562}
563