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