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