iptables-1.2.9-2.3.1.src.rpm
[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
16 #include <iptables.h>
17 #include <linux/netfilter_ipv4/ip_tables.h>
18 #include <linux/netfilter_ipv4/ipt_sctp.h>
19
20 /* Initialize the match. */
21 static void
22 init(struct ipt_entry_match *m, unsigned int *nfcache)
23 {
24         struct ipt_sctp_info *einfo = (struct ipt_sctp_info *)m->data;
25
26         einfo->spts[1] = einfo->dpts[1] = 0xFFFF;
27 }
28
29 static void help(void) 
30 {
31         printf(
32 "SCTP match v%s options\n"
33 " --sctp-chunks [!] mask comp   match when SCTP chunks & mask == comp\n"
34 " --source-port [!] port[:port]\n"
35 " --sport ...\n"
36 "                               match source port(s)"
37 " --destination-port [!] port[:port]\n"
38 " --dport ...\n\n",
39         IPTABLES_VERSION);
40 }
41
42 static struct option opts[] = {
43         { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' },
44         { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
45         { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
46         { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
47         { .name = "sctp-chunks", .has_arg = 1, .flag = 0, .val = '3' },
48         { .name = 0 }
49 };
50
51 static int
52 service_to_port(const char *name)
53 {
54         struct servent *service;
55
56         if ((service = getservbyname(name, "sctp")) != NULL)
57                 return ntohs((unsigned short) service->s_port);
58
59         return -1;
60 }
61
62 static u_int16_t
63 parse_sctp_port(const char *port)
64 {
65         unsigned int portnum;
66
67         if (string_to_number(port, 0, 65535, &portnum) != -1 ||
68             (portnum = service_to_port(port)) != -1)
69                 return (u_int16_t)portnum;
70
71         exit_error(PARAMETER_PROBLEM,
72                    "invalid TCP port/service `%s' specified", port);
73 }
74
75
76 static void
77 parse_sctp_ports(const char *portstring, u_int16_t *ports)
78 {
79         char *buffer;
80         char *cp;
81
82         buffer = strdup(portstring);
83         if ((cp = strchr(buffer, ':')) == NULL)
84                 ports[0] = ports[1] = parse_sctp_port(buffer);
85         else {
86                 *cp = '\0';
87                 cp++;
88
89                 ports[0] = buffer[0] ? parse_sctp_port(buffer) : 0;
90                 ports[1] = cp[0] ? parse_sctp_port(cp) : 0xFFFF;
91
92                 if (ports[0] > ports[1])
93                         exit_error(PARAMETER_PROBLEM,
94                                    "invalid portrange (min > max)");
95         }
96         free(buffer);
97 }
98
99 struct sctp_chunk_names {
100         const char *name;
101         unsigned int flag;
102 };
103
104 /* FIXME: */
105 #define ALL_CHUNKS      0xabcdef
106 static struct sctp_chunk_names sctp_chunk_names[]
107 = { { .name = "DATA",           .flag = (1 << 0) },
108     { .name = "INIT",           .flag = (1 << 1) },
109     { .name = "INIT_ACK",       .flag = (1 << 2) },
110     { .name = "SACK",           .flag = (1 << 3) },
111     { .name = "HEARTBEAT",      .flag = (1 << 4) },
112     { .name = "HEARTBEAT_ACK",  .flag = (1 << 5) },
113     { .name = "ABORT",          .flag = (1 << 6) },
114     { .name = "SHUTDOWN",       .flag = (1 << 7) },
115     { .name = "SHUTDOWN_ACK",   .flag = (1 << 8) },
116     { .name = "ERROR",          .flag = (1 << 9) },
117     { .name = "COOKIE_ECHO",    .flag = (1 << 10) },
118     { .name = "COOKIE_ACK",     .flag = (1 << 11) },
119     { .name = "ECN_ECNE",       .flag = (1 << 12) },
120     { .name = "ECN_CWR",        .flag = (1 << 13) },
121     { .name = "SHUTDOWN_COMPLETE", .flag = (1 << 14) },
122     { .name = "ASCONF",         .flag = (1 << 31) },
123     { .name = "ASCONF_ACK",     .flag = (1 << 30) },
124     { .name = "ALL",            .flag = ALL_CHUNKS },
125     { .name = "NONE",           .flag = 0 },
126 };
127
128
129 static unsigned int
130 parse_sctp_chunk(const char *flags)
131 {
132         unsigned int ret = 0;
133         char *ptr;
134         char *buffer;
135
136         buffer = strdup(flags);
137
138         for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
139                 unsigned int i;
140                 int found = 0;
141                 for (i = 0;
142                      i < sizeof(sctp_chunk_names)/sizeof(struct sctp_chunk_names);
143                      i++) {
144                         if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
145                                 ret |= sctp_chunk_names[i].flag;
146                                 found = 1;
147                                 break;
148                         }
149                 }
150                 if (!found)
151                         exit_error(PARAMETER_PROBLEM,
152                                    "Unknown sctp chunk `%s'", ptr);
153         }
154
155         free(buffer);
156         return ret;
157 }
158
159 static void
160 parse_sctp_chunks(struct ipt_sctp_info *einfo,
161                 const char *mask,
162                 const char *cmp,
163                 int invert)
164 {
165         einfo->chunks = parse_sctp_chunk(mask);
166         einfo->chunk_mask = parse_sctp_chunk(cmp);
167
168         if (invert)
169                 einfo->invflags |= IPT_SCTP_INV_CHUNKS;
170 }
171
172 #define SCTP_SRC_PORTS  0x01
173 #define SCTP_DST_PORTS  0x02
174 #define SCTP_CHUNKS     0x03
175
176 static int
177 parse(int c, char **argv, int invert, unsigned int *flags,
178       const struct ipt_entry *entry,
179       unsigned int *nfcache,
180       struct ipt_entry_match **match)
181 {
182         struct ipt_sctp_info *einfo
183                 = (struct ipt_sctp_info *)(*match)->data;
184
185         switch (c) {
186         case '1':
187                 if (*flags & SCTP_SRC_PORTS)
188                         exit_error(PARAMETER_PROBLEM,
189                                    "Only one `--source-port' allowed");
190                 check_inverse(optarg, &invert, &optind, 0);
191                 parse_sctp_ports(argv[optind-1], einfo->spts);
192                 if (invert)
193                         einfo->invflags |= IPT_SCTP_INV_SRCPT;
194                 *flags |= SCTP_SRC_PORTS;
195                 *nfcache |= NFC_IP_SRC_PT;
196                 break;
197
198         case '2':
199                 if (*flags & SCTP_DST_PORTS)
200                         exit_error(PARAMETER_PROBLEM,
201                                    "Only one `--destination-port' allowed");
202                 check_inverse(optarg, &invert, &optind, 0);
203                 parse_sctp_ports(argv[optind-1], einfo->dpts);
204                 if (invert)
205                         einfo->invflags |= IPT_SCTP_INV_DSTPT;
206                 *flags |= SCTP_DST_PORTS;
207                 *nfcache |= NFC_IP_DST_PT;
208                 break;
209
210         case '3':
211                 if (*flags & SCTP_CHUNKS)
212                         exit_error(PARAMETER_PROBLEM,
213                                    "Only one `--sctp-chunks' allowed");
214                 check_inverse(optarg, &invert, &optind, 0);
215
216                 if (!argv[optind] 
217                     || argv[optind][0] == '-' || argv[optind][0] == '!')
218                         exit_error(PARAMETER_PROBLEM,
219                                    "--sctp-chunks requires two args");
220
221                 parse_sctp_chunks(einfo, argv[optind-1], argv[optind], invert);
222                 optind++;
223                 *flags |= SCTP_CHUNKS;
224                 break;
225         default:
226                 return 0;
227         }
228
229         return 1;
230 }
231
232 static void
233 final_check(unsigned int flags)
234 {
235 }
236
237 static char *
238 port_to_service(int port)
239 {
240         struct servent *service;
241
242         if ((service = getservbyport(htons(port), "sctp")))
243                 return service->s_name;
244
245         return NULL;
246 }
247
248 static void
249 print_port(u_int16_t port, int numeric)
250 {
251         char *service;
252
253         if (numeric || (service = port_to_service(port)) == NULL)
254                 printf("%u", port);
255         else
256                 printf("%s", service);
257 }
258
259 static void
260 print_ports(const char *name, u_int16_t min, u_int16_t max,
261             int invert, int numeric)
262 {
263         const char *inv = invert ? "!" : "";
264
265         if (min != 0 || max != 0xFFFF || invert) {
266                 printf("%s", name);
267                 if (min == max) {
268                         printf(":%s", inv);
269                         print_port(min, numeric);
270                 } else {
271                         printf("s:%s", inv);
272                         print_port(min, numeric);
273                         printf(":");
274                         print_port(max, numeric);
275                 }
276                 printf(" ");
277         }
278 }
279
280 static void
281 print_chunk(u_int32_t chunks)
282 {
283         unsigned int have_flag = 0;
284
285         while (chunks) {
286                 unsigned int i;
287
288                 for (i = 0; (chunks & sctp_chunk_names[i].flag) == 0; i++);
289
290                 if (have_flag)
291                         printf(",");
292                 printf("%s", sctp_chunk_names[i].name);
293                 have_flag = 1;
294
295                 chunks &= ~sctp_chunk_names[i].flag;
296         }
297
298         if (!have_flag)
299                 printf("NONE");
300 }
301
302 static void
303 print_chunks(u_int32_t mask, u_int32_t cmp, int invert, int numeric)
304 {
305         if (mask || invert) {
306                 printf("flags:%s", invert ? "!" : "");
307                 if (numeric)
308                         printf("0x%04X/0x%04X ", mask, cmp);
309                 else {
310                         print_chunk(mask);
311                         printf("/");
312                         print_chunk(cmp);
313                         printf(" ");
314                 }
315         }
316 }
317
318 /* Prints out the matchinfo. */
319 static void
320 print(const struct ipt_ip *ip,
321       const struct ipt_entry_match *match,
322       int numeric)
323 {
324         const struct ipt_sctp_info *einfo =
325                 (const struct ipt_sctp_info *)match->data;
326
327         printf("sctp ");
328
329         print_ports("spt", einfo->spts[0], einfo->spts[1],
330                     einfo->invflags & IPT_SCTP_INV_SRCPT,
331                     numeric);
332         print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
333                     einfo->invflags & IPT_SCTP_INV_DSTPT,
334                     numeric);
335
336         print_chunks(einfo->chunks, einfo->chunk_mask,
337                      einfo->invflags & ~IPT_SCTP_INV_MASK,
338                      numeric);
339 }
340
341 /* Saves the union ipt_matchinfo in parsable form to stdout. */
342 static void
343 save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
344 {
345         const struct ipt_sctp_info *einfo =
346                 (const struct ipt_sctp_info *)match->data;
347
348         if (einfo->spts[0] != 0
349             || einfo->spts[1] != 0xFFFF) {
350                 if (einfo->invflags & IPT_SCTP_INV_SRCPT)
351                         printf("! ");
352                 if (einfo->spts[0] != einfo->spts[1])
353                         printf("--sport %u:%u ", 
354                                einfo->spts[0], einfo->spts[1]);
355                 else
356                         printf("--sport %u ", einfo->spts[0]);
357         }
358
359         if (einfo->dpts[0] != 0
360             || einfo->dpts[1] != 0xFFFF) {
361                 if (einfo->invflags & IPT_SCTP_INV_DSTPT)
362                         printf("! ");
363                 if (einfo->dpts[0] != einfo->dpts[1])
364                         printf("--dport %u:%u ",
365                                einfo->dpts[0], einfo->dpts[1]);
366                 else
367                         printf("--dport %u ", einfo->dpts[0]);
368         }
369
370         if (einfo->chunks
371             || (einfo->invflags & IPT_SCTP_INV_CHUNKS)) {
372                 if (einfo->invflags & IPT_SCTP_INV_CHUNKS)
373                         printf("! ");
374                 printf("--sctp-chunks ");
375                 if (einfo->chunks != ALL_CHUNKS) {
376                         print_chunk(einfo->chunks);
377                 }
378                 printf(" ");
379                 print_chunk(einfo->chunk_mask);
380                 printf(" ");
381         }
382 }
383
384 static
385 struct iptables_match sctp
386 = { .name          = "sctp",
387     .version       = IPTABLES_VERSION,
388     .size          = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
389     .userspacesize = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
390     .help          = &help,
391     .init          = &init,
392     .parse         = &parse,
393     .final_check   = &final_check,
394     .print         = &print,
395     .save          = &save,
396     .extra_opts    = opts
397 };
398
399 void _init(void)
400 {
401         register_match(&sctp);
402 }