Fork for IVI and add .changes file
[profile/ivi/iptables.git] / extensions / libxt_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 <netinet/in.h>
18 #include <xtables.h>
19
20 #include <linux/netfilter/xt_sctp.h>
21
22 #if 0
23 #define DEBUGP(format, first...) printf(format, ##first)
24 #define static
25 #else
26 #define DEBUGP(format, fist...) 
27 #endif
28
29 static void
30 print_chunk(u_int32_t chunknum, int numeric);
31
32 static void sctp_init(struct xt_entry_match *m)
33 {
34         int i;
35         struct xt_sctp_info *einfo = (struct xt_sctp_info *)m->data;
36
37         memset(einfo, 0, sizeof(struct xt_sctp_info));
38
39         for (i = 0; i < XT_NUM_SCTP_FLAGS; i++) {
40                 einfo->flag_info[i].chunktype = -1;
41         }
42 }
43
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 ASCONF ASCONF_ACK FORWARD_TSN ALL NONE\n");
55 }
56
57 static const struct option sctp_opts[] = {
58         { .name = "source-port", .has_arg = 1, .val = '1' },
59         { .name = "sport", .has_arg = 1, .val = '1' },
60         { .name = "destination-port", .has_arg = 1, .val = '2' },
61         { .name = "dport", .has_arg = 1, .val = '2' },
62         { .name = "chunk-types", .has_arg = 1, .val = '3' },
63         { .name = NULL }
64 };
65
66 static void
67 parse_sctp_ports(const char *portstring, 
68                  u_int16_t *ports)
69 {
70         char *buffer;
71         char *cp;
72
73         buffer = 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 };
97
98 /*'ALL' and 'NONE' will be treated specially. */
99 static const struct sctp_chunk_names sctp_chunk_names[]
100 = { { .name = "DATA",           .chunk_type = 0,   .valid_flags = "----IUBE"},
101     { .name = "INIT",           .chunk_type = 1,   .valid_flags = "--------"},
102     { .name = "INIT_ACK",       .chunk_type = 2,   .valid_flags = "--------"},
103     { .name = "SACK",           .chunk_type = 3,   .valid_flags = "--------"},
104     { .name = "HEARTBEAT",      .chunk_type = 4,   .valid_flags = "--------"},
105     { .name = "HEARTBEAT_ACK",  .chunk_type = 5,   .valid_flags = "--------"},
106     { .name = "ABORT",          .chunk_type = 6,   .valid_flags = "-------T"},
107     { .name = "SHUTDOWN",       .chunk_type = 7,   .valid_flags = "--------"},
108     { .name = "SHUTDOWN_ACK",   .chunk_type = 8,   .valid_flags = "--------"},
109     { .name = "ERROR",          .chunk_type = 9,   .valid_flags = "--------"},
110     { .name = "COOKIE_ECHO",    .chunk_type = 10,  .valid_flags = "--------"},
111     { .name = "COOKIE_ACK",     .chunk_type = 11,  .valid_flags = "--------"},
112     { .name = "ECN_ECNE",       .chunk_type = 12,  .valid_flags = "--------"},
113     { .name = "ECN_CWR",        .chunk_type = 13,  .valid_flags = "--------"},
114     { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14,  .valid_flags = "-------T"},
115     { .name = "ASCONF",         .chunk_type = 193,  .valid_flags = "--------"},
116     { .name = "ASCONF_ACK",     .chunk_type = 128,  .valid_flags = "--------"},
117     { .name = "FORWARD_TSN",    .chunk_type = 192,  .valid_flags = "--------"},
118 };
119
120 static void
121 save_chunk_flag_info(struct xt_sctp_flag_info *flag_info,
122                      int *flag_count,
123                      int chunktype, 
124                      int bit, 
125                      int set)
126 {
127         int i;
128
129         for (i = 0; i < *flag_count; i++) {
130                 if (flag_info[i].chunktype == chunktype) {
131                         DEBUGP("Previous match found\n");
132                         flag_info[i].chunktype = chunktype;
133                         flag_info[i].flag_mask |= (1 << bit);
134                         if (set) {
135                                 flag_info[i].flag |= (1 << bit);
136                         }
137
138                         return;
139                 }
140         }
141         
142         if (*flag_count == XT_NUM_SCTP_FLAGS) {
143                 xtables_error (PARAMETER_PROBLEM,
144                         "Number of chunk types with flags exceeds currently allowed limit."
145                         "Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
146                         "recompiling both the kernel space and user space modules\n");
147         }
148
149         flag_info[*flag_count].chunktype = chunktype;
150         flag_info[*flag_count].flag_mask |= (1 << bit);
151         if (set) {
152                 flag_info[*flag_count].flag |= (1 << bit);
153         }
154         (*flag_count)++;
155 }
156
157 static void
158 parse_sctp_chunk(struct xt_sctp_info *einfo, 
159                  const char *chunks)
160 {
161         char *ptr;
162         char *buffer;
163         unsigned int i, j;
164         int found = 0;
165         char *chunk_flags;
166
167         buffer = strdup(chunks);
168         DEBUGP("Buffer: %s\n", buffer);
169
170         SCTP_CHUNKMAP_RESET(einfo->chunkmap);
171
172         if (!strcasecmp(buffer, "ALL")) {
173                 SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
174                 goto out;
175         }
176         
177         if (!strcasecmp(buffer, "NONE")) {
178                 SCTP_CHUNKMAP_RESET(einfo->chunkmap);
179                 goto out;
180         }
181
182         for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
183                 found = 0;
184                 DEBUGP("Next Chunk type %s\n", ptr);
185                 
186                 if ((chunk_flags = strchr(ptr, ':')) != NULL) {
187                         *chunk_flags++ = 0;
188                 }
189                 
190                 for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i)
191                         if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
192                                 DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
193                                 SCTP_CHUNKMAP_SET(einfo->chunkmap, 
194                                         sctp_chunk_names[i].chunk_type);
195                                 found = 1;
196                                 break;
197                         }
198                 if (!found)
199                         xtables_error(PARAMETER_PROBLEM,
200                                    "Unknown sctp chunk `%s'", ptr);
201
202                 if (chunk_flags) {
203                         DEBUGP("Chunk flags %s\n", chunk_flags);
204                         for (j = 0; j < strlen(chunk_flags); j++) {
205                                 char *p;
206                                 int bit;
207
208                                 if ((p = strchr(sctp_chunk_names[i].valid_flags, 
209                                                 toupper(chunk_flags[j]))) != NULL) {
210                                         bit = p - sctp_chunk_names[i].valid_flags;
211                                         bit = 7 - bit;
212
213                                         save_chunk_flag_info(einfo->flag_info, 
214                                                 &(einfo->flag_count), i, bit, 
215                                                 isupper(chunk_flags[j]));
216                                 } else {
217                                         xtables_error(PARAMETER_PROBLEM,
218                                                 "Invalid flags for chunk type %d\n", i);
219                                 }
220                         }
221                 }
222         }
223 out:
224         free(buffer);
225 }
226
227 static void
228 parse_sctp_chunks(struct xt_sctp_info *einfo,
229                   const char *match_type,
230                   const char *chunks)
231 {
232         DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
233         if (!strcasecmp(match_type, "ANY")) {
234                 einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
235         } else  if (!strcasecmp(match_type, "ALL")) {
236                 einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
237         } else  if (!strcasecmp(match_type, "ONLY")) {
238                 einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
239         } else {
240                 xtables_error (PARAMETER_PROBLEM,
241                         "Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
242         }
243
244         SCTP_CHUNKMAP_RESET(einfo->chunkmap);
245         parse_sctp_chunk(einfo, chunks);
246 }
247
248 static int
249 sctp_parse(int c, char **argv, int invert, unsigned int *flags,
250            const void *entry, struct xt_entry_match **match)
251 {
252         struct xt_sctp_info *einfo
253                 = (struct xt_sctp_info *)(*match)->data;
254
255         switch (c) {
256         case '1':
257                 if (*flags & XT_SCTP_SRC_PORTS)
258                         xtables_error(PARAMETER_PROBLEM,
259                                    "Only one `--source-port' allowed");
260                 einfo->flags |= XT_SCTP_SRC_PORTS;
261                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
262                 parse_sctp_ports(optarg, einfo->spts);
263                 if (invert)
264                         einfo->invflags |= XT_SCTP_SRC_PORTS;
265                 *flags |= XT_SCTP_SRC_PORTS;
266                 break;
267
268         case '2':
269                 if (*flags & XT_SCTP_DEST_PORTS)
270                         xtables_error(PARAMETER_PROBLEM,
271                                    "Only one `--destination-port' allowed");
272                 einfo->flags |= XT_SCTP_DEST_PORTS;
273                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
274                 parse_sctp_ports(optarg, einfo->dpts);
275                 if (invert)
276                         einfo->invflags |= XT_SCTP_DEST_PORTS;
277                 *flags |= XT_SCTP_DEST_PORTS;
278                 break;
279
280         case '3':
281                 if (*flags & XT_SCTP_CHUNK_TYPES)
282                         xtables_error(PARAMETER_PROBLEM,
283                                    "Only one `--chunk-types' allowed");
284                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
285
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         default:
300                 return 0;
301         }
302         return 1;
303 }
304
305 static char *
306 port_to_service(int port)
307 {
308         struct servent *service;
309
310         if ((service = getservbyport(htons(port), "sctp")))
311                 return service->s_name;
312
313         return NULL;
314 }
315
316 static void
317 print_port(u_int16_t port, int numeric)
318 {
319         char *service;
320
321         if (numeric || (service = port_to_service(port)) == NULL)
322                 printf("%u", port);
323         else
324                 printf("%s", service);
325 }
326
327 static void
328 print_ports(const char *name, u_int16_t min, u_int16_t max,
329             int invert, int numeric)
330 {
331         const char *inv = invert ? "!" : "";
332
333         if (min != 0 || max != 0xFFFF || invert) {
334                 printf("%s", name);
335                 if (min == max) {
336                         printf(":%s", inv);
337                         print_port(min, numeric);
338                 } else {
339                         printf("s:%s", inv);
340                         print_port(min, numeric);
341                         printf(":");
342                         print_port(max, numeric);
343                 }
344                 printf(" ");
345         }
346 }
347
348 static void
349 print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask)
350 {
351         int i;
352
353         DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 
354                         chunk_flags_mask);
355
356         if (chunk_flags_mask) {
357                 printf(":");
358         }
359
360         for (i = 7; i >= 0; i--) {
361                 if (chunk_flags_mask & (1 << i)) {
362                         if (chunk_flags & (1 << i)) {
363                                 printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
364                         } else {
365                                 printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
366                         }
367                 }
368         }
369 }
370
371 static void
372 print_chunk(u_int32_t chunknum, int numeric)
373 {
374         if (numeric) {
375                 printf("0x%04X", chunknum);
376         }
377         else {
378                 int i;
379
380                 for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i)
381                         if (sctp_chunk_names[i].chunk_type == chunknum)
382                                 printf("%s", sctp_chunk_names[chunknum].name);
383         }
384 }
385
386 static void
387 print_chunks(const struct xt_sctp_info *einfo, int numeric)
388 {
389         u_int32_t chunk_match_type = einfo->chunk_match_type;
390         const struct xt_sctp_flag_info *flag_info = einfo->flag_info;
391         int flag_count = einfo->flag_count;
392         int i, j;
393         int flag;
394
395         switch (chunk_match_type) {
396                 case SCTP_CHUNK_MATCH_ANY:      printf("any "); break;
397                 case SCTP_CHUNK_MATCH_ALL:      printf("all "); break;
398                 case SCTP_CHUNK_MATCH_ONLY:     printf("only "); break;
399                 default:        printf("Never reach herer\n"); break;
400         }
401
402         if (SCTP_CHUNKMAP_IS_CLEAR(einfo->chunkmap)) {
403                 printf("NONE ");
404                 goto out;
405         }
406         
407         if (SCTP_CHUNKMAP_IS_ALL_SET(einfo->chunkmap)) {
408                 printf("ALL ");
409                 goto out;
410         }
411         
412         flag = 0;
413         for (i = 0; i < 256; i++) {
414                 if (SCTP_CHUNKMAP_IS_SET(einfo->chunkmap, i)) {
415                         if (flag)
416                                 printf(",");
417                         flag = 1;
418                         print_chunk(i, numeric);
419                         for (j = 0; j < flag_count; j++) {
420                                 if (flag_info[j].chunktype == i) {
421                                         print_chunk_flags(i, flag_info[j].flag,
422                                                 flag_info[j].flag_mask);
423                                 }
424                         }
425                 }
426         }
427
428         if (flag)
429                 printf(" ");
430 out:
431         return;
432 }
433
434 static void
435 sctp_print(const void *ip, const struct xt_entry_match *match, int numeric)
436 {
437         const struct xt_sctp_info *einfo =
438                 (const struct xt_sctp_info *)match->data;
439
440         printf("sctp ");
441
442         if (einfo->flags & XT_SCTP_SRC_PORTS) {
443                 print_ports("spt", einfo->spts[0], einfo->spts[1],
444                         einfo->invflags & XT_SCTP_SRC_PORTS,
445                         numeric);
446         }
447
448         if (einfo->flags & XT_SCTP_DEST_PORTS) {
449                 print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
450                         einfo->invflags & XT_SCTP_DEST_PORTS,
451                         numeric);
452         }
453
454         if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
455                 /* FIXME: print_chunks() is used in save() where the printing of '!'
456                 s taken care of, so we need to do that here as well */
457                 if (einfo->invflags & XT_SCTP_CHUNK_TYPES) {
458                         printf("! ");
459                 }
460                 print_chunks(einfo, numeric);
461         }
462 }
463
464 static void sctp_save(const void *ip, const struct xt_entry_match *match)
465 {
466         const struct xt_sctp_info *einfo =
467                 (const struct xt_sctp_info *)match->data;
468
469         if (einfo->flags & XT_SCTP_SRC_PORTS) {
470                 if (einfo->invflags & XT_SCTP_SRC_PORTS)
471                         printf("! ");
472                 if (einfo->spts[0] != einfo->spts[1])
473                         printf("--sport %u:%u ", 
474                                einfo->spts[0], einfo->spts[1]);
475                 else
476                         printf("--sport %u ", einfo->spts[0]);
477         }
478
479         if (einfo->flags & XT_SCTP_DEST_PORTS) {
480                 if (einfo->invflags & XT_SCTP_DEST_PORTS)
481                         printf("! ");
482                 if (einfo->dpts[0] != einfo->dpts[1])
483                         printf("--dport %u:%u ",
484                                einfo->dpts[0], einfo->dpts[1]);
485                 else
486                         printf("--dport %u ", einfo->dpts[0]);
487         }
488
489         if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
490                 if (einfo->invflags & XT_SCTP_CHUNK_TYPES)
491                         printf("! ");
492                 printf("--chunk-types ");
493
494                 print_chunks(einfo, 0);
495         }
496 }
497
498 static struct xtables_match sctp_match = {
499         .name           = "sctp",
500         .family         = NFPROTO_UNSPEC,
501         .version        = XTABLES_VERSION,
502         .size           = XT_ALIGN(sizeof(struct xt_sctp_info)),
503         .userspacesize  = XT_ALIGN(sizeof(struct xt_sctp_info)),
504         .help           = sctp_help,
505         .init           = sctp_init,
506         .parse          = sctp_parse,
507         .print          = sctp_print,
508         .save           = sctp_save,
509         .extra_opts     = sctp_opts,
510 };
511
512 void _init(void)
513 {
514         xtables_register_match(&sctp_match);
515 }