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