This commit was manufactured by cvs2svn to create branch
[iptables.git] / extensions / libipt_dccp.c
1 /* Shared library add-on to iptables for DCCP matching
2  *
3  * (C) 2005 by Harald Welte <laforge@netfilter.org>
4  *
5  * This program is distributed under the terms of GNU GPL v2, 1991
6  *
7  */
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <getopt.h>
12 #include <netdb.h>
13 #include <ctype.h>
14
15 #include <iptables.h>
16 #include <linux/dccp.h>
17 #include <linux/netfilter_ipv4/ip_tables.h>
18 #include <linux/netfilter_ipv4/ipt_dccp.h>
19
20 #if 0
21 #define DEBUGP(format, first...) printf(format, ##first)
22 #define static
23 #else
24 #define DEBUGP(format, fist...) 
25 #endif
26
27 /* Initialize the match. */
28 static void
29 init(struct ipt_entry_match *m, 
30      unsigned int *nfcache)
31 {
32         struct ipt_dccp_info *einfo = (struct ipt_dccp_info *)m->data;
33
34         memset(einfo, 0, sizeof(struct ipt_dccp_info));
35 }
36
37 static void help(void)
38 {
39         printf(
40 "DCCP match v%s options\n"
41 " --source-port [!] port[:port]                          match source port(s)\n"
42 " --sport ...\n"
43 " --destination-port [!] port[:port]                     match destination port(s)\n"
44 " --dport ...\n"
45 ,
46         IPTABLES_VERSION);
47 }
48
49 static struct option opts[] = {
50         { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' },
51         { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
52         { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
53         { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
54         { .name = "dccp-types", .has_arg = 1, .flag = 0, .val = '3' },
55         { .name = "dccp-option", .has_arg = 1, .flag = 0, .val = '4' },
56         { .name = 0 }
57 };
58
59 static int
60 service_to_port(const char *name)
61 {
62         struct servent *service;
63
64         if ((service = getservbyname(name, "dccp")) != NULL)
65                 return ntohs((unsigned short) service->s_port);
66
67         return -1;
68 }
69
70 static u_int16_t
71 parse_dccp_port(const char *port)
72 {
73         unsigned int portnum;
74
75         DEBUGP("%s\n", port);
76         if (string_to_number(port, 0, 65535, &portnum) != -1 ||
77             (portnum = service_to_port(port)) != -1)
78                 return (u_int16_t)portnum;
79
80         exit_error(PARAMETER_PROBLEM,
81                    "invalid DCCP port/service `%s' specified", port);
82 }
83
84 static void
85 parse_dccp_ports(const char *portstring, 
86                  u_int16_t *ports)
87 {
88         char *buffer;
89         char *cp;
90
91         buffer = strdup(portstring);
92         DEBUGP("%s\n", portstring);
93         if ((cp = strchr(buffer, ':')) == NULL) {
94                 ports[0] = ports[1] = parse_dccp_port(buffer);
95         }
96         else {
97                 *cp = '\0';
98                 cp++;
99
100                 ports[0] = buffer[0] ? parse_dccp_port(buffer) : 0;
101                 ports[1] = cp[0] ? parse_dccp_port(cp) : 0xFFFF;
102
103                 if (ports[0] > ports[1])
104                         exit_error(PARAMETER_PROBLEM,
105                                    "invalid portrange (min > max)");
106         }
107         free(buffer);
108 }
109
110 static char *dccp_pkt_types[] = {
111         [DCCP_PKT_REQUEST]      = "REQUEST",
112         [DCCP_PKT_RESPONSE]     = "RESPONSE",
113         [DCCP_PKT_DATA]         = "DATA",
114         [DCCP_PKT_ACK]          = "ACK",
115         [DCCP_PKT_DATAACK]      = "DATAACK",
116         [DCCP_PKT_CLOSEREQ]     = "CLOSEREQ",
117         [DCCP_PKT_CLOSE]        = "CLOSE",
118         [DCCP_PKT_RESET]        = "RESET",
119         [DCCP_PKT_SYNC]         = "SYNC",
120         [DCCP_PKT_SYNCACK]      = "SYNCACK",
121         [DCCP_PKT_INVALID]      = "INVALID",
122 };
123
124 static u_int16_t
125 parse_dccp_types(const char *typestring)
126 {
127         u_int16_t typemask = 0;
128         char *ptr, *buffer;
129
130         buffer = strdup(typestring);
131
132         for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
133                 unsigned int i;
134                 for (i = 0; i < sizeof(dccp_pkt_types)/sizeof(char *); i++) {
135                         if (!strcasecmp(dccp_pkt_types[i], ptr)) {
136                                 typemask |= (1 << i);
137                                 break;
138                         }
139                 }
140                 if (i == sizeof(dccp_pkt_types)/sizeof(char *))
141                         exit_error(PARAMETER_PROBLEM,
142                                    "Unknown DCCP type `%s'", ptr);
143         }
144
145         free(buffer);
146         return typemask;
147 }
148
149 static u_int8_t parse_dccp_option(char *optstring)
150 {
151         unsigned int ret;
152
153         if (string_to_number(optstring, 1, 255, &ret) == -1)
154                 exit_error(PARAMETER_PROBLEM, "Bad DCCP option `%s'",
155                            optstring);
156
157         return (u_int8_t)ret;
158 }
159
160 static int
161 parse(int c, char **argv, int invert, unsigned int *flags,
162       const struct ipt_entry *entry,
163       unsigned int *nfcache,
164       struct ipt_entry_match **match)
165 {
166         struct ipt_dccp_info *einfo
167                 = (struct ipt_dccp_info *)(*match)->data;
168
169         switch (c) {
170         case '1':
171                 if (*flags & IPT_DCCP_SRC_PORTS)
172                         exit_error(PARAMETER_PROBLEM,
173                                    "Only one `--source-port' allowed");
174                 einfo->flags |= IPT_DCCP_SRC_PORTS;
175                 check_inverse(optarg, &invert, &optind, 0);
176                 parse_dccp_ports(argv[optind-1], einfo->spts);
177                 if (invert)
178                         einfo->invflags |= IPT_DCCP_SRC_PORTS;
179                 *flags |= IPT_DCCP_SRC_PORTS;
180                 break;
181
182         case '2':
183                 if (*flags & IPT_DCCP_DEST_PORTS)
184                         exit_error(PARAMETER_PROBLEM,
185                                    "Only one `--destination-port' allowed");
186                 einfo->flags |= IPT_DCCP_DEST_PORTS;
187                 check_inverse(optarg, &invert, &optind, 0);
188                 parse_dccp_ports(argv[optind-1], einfo->dpts);
189                 if (invert)
190                         einfo->invflags |= IPT_DCCP_DEST_PORTS;
191                 *flags |= IPT_DCCP_DEST_PORTS;
192                 break;
193
194         case '3':
195                 if (*flags & IPT_DCCP_TYPE)
196                         exit_error(PARAMETER_PROBLEM,
197                                    "Only one `--dccp-types' allowed");
198                 einfo->flags |= IPT_DCCP_TYPE;
199                 check_inverse(optarg, &invert, &optind, 0);
200                 einfo->typemask = parse_dccp_types(argv[optind-1]);
201                 if (invert)
202                         einfo->invflags |= IPT_DCCP_TYPE;
203                 *flags |= IPT_DCCP_TYPE;
204                 break;
205
206         case '4':
207                 if (*flags & IPT_DCCP_OPTION)
208                         exit_error(PARAMETER_PROBLEM,
209                                    "Only one `--dccp-option' allowed");
210                 einfo->flags |= IPT_DCCP_OPTION;
211                 check_inverse(optarg, &invert, &optind, 0);
212                 einfo->option = parse_dccp_option(argv[optind-1]);
213                 if (invert)
214                         einfo->invflags |= IPT_DCCP_OPTION;
215                 *flags |= IPT_DCCP_OPTION;
216                 break;
217         default:
218                 return 0;
219         }
220         return 1;
221 }
222
223 static void
224 final_check(unsigned int flags)
225 {
226 }
227
228 static char *
229 port_to_service(int port)
230 {
231         struct servent *service;
232
233         if ((service = getservbyport(htons(port), "dccp")))
234                 return service->s_name;
235
236         return NULL;
237 }
238
239 static void
240 print_port(u_int16_t port, int numeric)
241 {
242         char *service;
243
244         if (numeric || (service = port_to_service(port)) == NULL)
245                 printf("%u", port);
246         else
247                 printf("%s", service);
248 }
249
250 static void
251 print_ports(const char *name, u_int16_t min, u_int16_t max,
252             int invert, int numeric)
253 {
254         const char *inv = invert ? "!" : "";
255
256         if (min != 0 || max != 0xFFFF || invert) {
257                 printf("%s", name);
258                 if (min == max) {
259                         printf(":%s", inv);
260                         print_port(min, numeric);
261                 } else {
262                         printf("s:%s", inv);
263                         print_port(min, numeric);
264                         printf(":");
265                         print_port(max, numeric);
266                 }
267                 printf(" ");
268         }
269 }
270
271 static void
272 print_types(u_int16_t types, int inverted, int numeric)
273 {
274         int have_type = 0;
275
276         if (inverted)
277                 printf("! ");
278
279         while (types) {
280                 unsigned int i;
281
282                 for (i = 0; !(types & (1 << i)); i++);
283
284                 if (have_type)
285                         printf(",");
286                 else
287                         have_type = 1;
288
289                 if (numeric)
290                         printf("%u", i);
291                 else
292                         printf("%s", dccp_pkt_types[i]);
293
294                 types &= ~(1 << i);
295         }
296 }
297
298 static void
299 print_option(u_int8_t option, int invert, int numeric)
300 {
301         if (option || invert)
302                 printf("option=%s%u ", invert ? "!" : "", option);
303 }
304
305 /* Prints out the matchinfo. */
306 static void
307 print(const struct ipt_ip *ip,
308       const struct ipt_entry_match *match,
309       int numeric)
310 {
311         const struct ipt_dccp_info *einfo =
312                 (const struct ipt_dccp_info *)match->data;
313
314         printf("dccp ");
315
316         if (einfo->flags & IPT_DCCP_SRC_PORTS) {
317                 print_ports("spt", einfo->spts[0], einfo->spts[1],
318                         einfo->invflags & IPT_DCCP_SRC_PORTS,
319                         numeric);
320         }
321
322         if (einfo->flags & IPT_DCCP_DEST_PORTS) {
323                 print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
324                         einfo->invflags & IPT_DCCP_DEST_PORTS,
325                         numeric);
326         }
327
328         if (einfo->flags & IPT_DCCP_TYPE) {
329                 print_types(einfo->typemask,
330                            einfo->invflags & IPT_DCCP_TYPE,
331                            numeric);
332         }
333
334         if (einfo->flags & IPT_DCCP_OPTION) {
335                 print_option(einfo->option,
336                              einfo->invflags & IPT_DCCP_OPTION, numeric);
337         }
338 }
339
340 /* Saves the union ipt_matchinfo in parsable form to stdout. */
341 static void
342 save(const struct ipt_ip *ip, 
343      const struct ipt_entry_match *match)
344 {
345         const struct ipt_dccp_info *einfo =
346                 (const struct ipt_dccp_info *)match->data;
347
348         if (einfo->flags & IPT_DCCP_SRC_PORTS) {
349                 if (einfo->invflags & IPT_DCCP_SRC_PORTS)
350                         printf("! ");
351                 if (einfo->spts[0] != einfo->spts[1])
352                         printf("--sport %u:%u ", 
353                                einfo->spts[0], einfo->spts[1]);
354                 else
355                         printf("--sport %u ", einfo->spts[0]);
356         }
357
358         if (einfo->flags & IPT_DCCP_DEST_PORTS) {
359                 if (einfo->invflags & IPT_DCCP_DEST_PORTS)
360                         printf("! ");
361                 if (einfo->dpts[0] != einfo->dpts[1])
362                         printf("--dport %u:%u ",
363                                einfo->dpts[0], einfo->dpts[1]);
364                 else
365                         printf("--dport %u ", einfo->dpts[0]);
366         }
367
368         if (einfo->flags & IPT_DCCP_TYPE) {
369                 printf("--dccp-type ");
370                 print_types(einfo->typemask, einfo->invflags & IPT_DCCP_TYPE,0);
371         }
372
373         if (einfo->flags & IPT_DCCP_OPTION) {
374                 printf("--dccp-option %s%u ", 
375                         einfo->typemask & IPT_DCCP_OPTION ? "! " : "",
376                         einfo->option);
377         }
378 }
379
380 static
381 struct iptables_match dccp
382 = { .name          = "dccp",
383     .version       = IPTABLES_VERSION,
384     .size          = IPT_ALIGN(sizeof(struct ipt_dccp_info)),
385     .userspacesize = IPT_ALIGN(sizeof(struct ipt_dccp_info)),
386     .help          = &help,
387     .init          = &init,
388     .parse         = &parse,
389     .final_check   = &final_check,
390     .print         = &print,
391     .save          = &save,
392     .extra_opts    = opts
393 };
394
395 void _init(void)
396 {
397         register_match(&dccp);
398 }
399