iptables-1.3.2-20050720
[iptables.git] / extensions / libipt_multiport.c
1 /* Shared library add-on to iptables to add multiple TCP port support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <iptables.h>
8 /* To ensure that iptables compiles with an old kernel */
9 #include "../include/linux/netfilter_ipv4/ipt_multiport.h"
10
11 /* Function which prints out usage message. */
12 static void
13 help(void)
14 {
15         printf(
16 "multiport v%s options:\n"
17 " --source-ports port[,port,port...]\n"
18 " --sports ...\n"
19 "                               match source port(s)\n"
20 " --destination-ports port[,port,port...]\n"
21 " --dports ...\n"
22 "                               match destination port(s)\n"
23 " --ports port[,port,port]\n"
24 "                               match both source and destination port(s)\n"
25 " NOTE: this kernel does not support port ranges in multiport.\n",
26 IPTABLES_VERSION);
27 }
28
29 static void
30 help_v1(void)
31 {
32         printf(
33 "multiport v%s options:\n"
34 " --source-ports [!] port[,port:port,port...]\n"
35 " --sports ...\n"
36 "                               match source port(s)\n"
37 " --destination-ports [!] port[,port:port,port...]\n"
38 " --dports ...\n"
39 "                               match destination port(s)\n"
40 " --ports [!] port[,port:port,port]\n"
41 "                               match both source and destination port(s)\n",
42 IPTABLES_VERSION);
43 }
44
45 static struct option opts[] = {
46         { "source-ports", 1, 0, '1' },
47         { "sports", 1, 0, '1' }, /* synonym */
48         { "destination-ports", 1, 0, '2' },
49         { "dports", 1, 0, '2' }, /* synonym */
50         { "ports", 1, 0, '3' },
51         {0}
52 };
53
54 static int
55 service_to_port(const char *name, const char *proto)
56 {
57         struct servent *service;
58
59         if ((service = getservbyname(name, proto)) != NULL)
60                 return ntohs((unsigned short) service->s_port);
61
62                 return -1;
63 }
64
65 static u_int16_t
66 parse_port(const char *port, const char *proto)
67 {
68         unsigned int portnum;
69
70         if (string_to_number(port, 0, 65535, &portnum) != -1 ||
71             (portnum = service_to_port(port, proto)) != -1)
72                 return (u_int16_t)portnum;
73
74         exit_error(PARAMETER_PROBLEM,
75                    "invalid port/service `%s' specified", port);
76 }
77
78 static unsigned int
79 parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
80 {
81         char *buffer, *cp, *next;
82         unsigned int i;
83
84         buffer = strdup(portstring);
85         if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
86
87         for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
88         {
89                 next=strchr(cp, ',');
90                 if (next) *next++='\0';
91                 ports[i] = parse_port(cp, proto);
92         }
93         if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
94         free(buffer);
95         return i;
96 }
97
98 static void
99 parse_multi_ports_v1(const char *portstring, 
100                      struct ipt_multiport_v1 *multiinfo,
101                      const char *proto)
102 {
103         char *buffer, *cp, *next, *range;
104         unsigned int i;
105         u_int16_t m;
106
107         buffer = strdup(portstring);
108         if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
109
110         for (i=0; i<IPT_MULTI_PORTS; i++)
111                 multiinfo->pflags[i] = 0;
112  
113         for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) {
114                 next=strchr(cp, ',');
115                 if (next) *next++='\0';
116                 range = strchr(cp, ':');
117                 if (range) {
118                         if (i == IPT_MULTI_PORTS-1)
119                                 exit_error(PARAMETER_PROBLEM,
120                                            "too many ports specified");
121                         *range++ = '\0';
122                 }
123                 multiinfo->ports[i] = parse_port(cp, proto);
124                 if (range) {
125                         multiinfo->pflags[i] = 1;
126                         multiinfo->ports[++i] = parse_port(range, proto);
127                         if (multiinfo->ports[i-1] >= multiinfo->ports[i])
128                                 exit_error(PARAMETER_PROBLEM,
129                                            "invalid portrange specified");
130                         m <<= 1;
131                 }
132         }
133         multiinfo->count = i;
134         if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
135         free(buffer);
136 }
137
138 /* Initialize the match. */
139 static void
140 init(struct ipt_entry_match *m, unsigned int *nfcache)
141 {
142 }
143
144 static const char *
145 check_proto(const struct ipt_entry *entry)
146 {
147         if (entry->ip.invflags & IPT_INV_PROTO)
148                 exit_error(PARAMETER_PROBLEM,
149                            "multiport only works with TCP or UDP");
150
151         if (entry->ip.proto == IPPROTO_TCP)
152                 return "tcp";
153         else if (entry->ip.proto == IPPROTO_UDP)
154                 return "udp";
155         else if (!entry->ip.proto)
156                 exit_error(PARAMETER_PROBLEM,
157                            "multiport needs `-p tcp' or `-p udp'");
158         else
159                 exit_error(PARAMETER_PROBLEM,
160                            "multiport only works with TCP or UDP");
161 }
162
163 /* Function which parses command options; returns true if it
164    ate an option */
165 static int
166 parse(int c, char **argv, int invert, unsigned int *flags,
167       const struct ipt_entry *entry,
168       unsigned int *nfcache,
169       struct ipt_entry_match **match)
170 {
171         const char *proto;
172         struct ipt_multiport *multiinfo
173                 = (struct ipt_multiport *)(*match)->data;
174
175         switch (c) {
176         case '1':
177                 check_inverse(argv[optind-1], &invert, &optind, 0);
178                 proto = check_proto(entry);
179                 multiinfo->count = parse_multi_ports(argv[optind-1],
180                                                      multiinfo->ports, proto);
181                 multiinfo->flags = IPT_MULTIPORT_SOURCE;
182                 break;
183
184         case '2':
185                 check_inverse(argv[optind-1], &invert, &optind, 0);
186                 proto = check_proto(entry);
187                 multiinfo->count = parse_multi_ports(argv[optind-1],
188                                                      multiinfo->ports, proto);
189                 multiinfo->flags = IPT_MULTIPORT_DESTINATION;
190                 break;
191
192         case '3':
193                 check_inverse(argv[optind-1], &invert, &optind, 0);
194                 proto = check_proto(entry);
195                 multiinfo->count = parse_multi_ports(argv[optind-1],
196                                                      multiinfo->ports, proto);
197                 multiinfo->flags = IPT_MULTIPORT_EITHER;
198                 break;
199
200         default:
201                 return 0;
202         }
203
204         if (invert)
205                 exit_error(PARAMETER_PROBLEM,
206                            "multiport does not support invert");
207
208         if (*flags)
209                 exit_error(PARAMETER_PROBLEM,
210                            "multiport can only have one option");
211         *flags = 1;
212         return 1;
213 }
214
215 static int
216 parse_v1(int c, char **argv, int invert, unsigned int *flags,
217          const struct ipt_entry *entry,
218          unsigned int *nfcache,
219          struct ipt_entry_match **match)
220 {
221         const char *proto;
222         struct ipt_multiport_v1 *multiinfo
223                 = (struct ipt_multiport_v1 *)(*match)->data;
224
225         switch (c) {
226         case '1':
227                 check_inverse(argv[optind-1], &invert, &optind, 0);
228                 proto = check_proto(entry);
229                 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
230                 multiinfo->flags = IPT_MULTIPORT_SOURCE;
231                 break;
232
233         case '2':
234                 check_inverse(argv[optind-1], &invert, &optind, 0);
235                 proto = check_proto(entry);
236                 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
237                 multiinfo->flags = IPT_MULTIPORT_DESTINATION;
238                 break;
239
240         case '3':
241                 check_inverse(argv[optind-1], &invert, &optind, 0);
242                 proto = check_proto(entry);
243                 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
244                 multiinfo->flags = IPT_MULTIPORT_EITHER;
245                 break;
246
247         default:
248                 return 0;
249         }
250
251         if (invert)
252                 multiinfo->invert = 1;
253
254         if (*flags)
255                 exit_error(PARAMETER_PROBLEM,
256                            "multiport can only have one option");
257         *flags = 1;
258         return 1;
259 }
260
261 /* Final check; must specify something. */
262 static void
263 final_check(unsigned int flags)
264 {
265         if (!flags)
266                 exit_error(PARAMETER_PROBLEM, "multiport expection an option");
267 }
268
269 static char *
270 port_to_service(int port, u_int8_t proto)
271 {
272         struct servent *service;
273
274         if ((service = getservbyport(htons(port),
275                                      proto == IPPROTO_TCP ? "tcp" : "udp")))
276                 return service->s_name;
277
278         return NULL;
279 }
280
281 static void
282 print_port(u_int16_t port, u_int8_t protocol, int numeric)
283 {
284         char *service;
285
286         if (numeric || (service = port_to_service(port, protocol)) == NULL)
287                 printf("%u", port);
288         else
289                 printf("%s", service);
290 }
291
292 /* Prints out the matchinfo. */
293 static void
294 print(const struct ipt_ip *ip,
295       const struct ipt_entry_match *match,
296       int numeric)
297 {
298         const struct ipt_multiport *multiinfo
299                 = (const struct ipt_multiport *)match->data;
300         unsigned int i;
301
302         printf("multiport ");
303
304         switch (multiinfo->flags) {
305         case IPT_MULTIPORT_SOURCE:
306                 printf("sports ");
307                 break;
308
309         case IPT_MULTIPORT_DESTINATION:
310                 printf("dports ");
311                 break;
312
313         case IPT_MULTIPORT_EITHER:
314                 printf("ports ");
315                 break;
316
317         default:
318                 printf("ERROR ");
319                 break;
320         }
321
322         for (i=0; i < multiinfo->count; i++) {
323                 printf("%s", i ? "," : "");
324                 print_port(multiinfo->ports[i], ip->proto, numeric);
325         }
326         printf(" ");
327 }
328
329 static void
330 print_v1(const struct ipt_ip *ip,
331          const struct ipt_entry_match *match,
332          int numeric)
333 {
334         const struct ipt_multiport_v1 *multiinfo
335                 = (const struct ipt_multiport_v1 *)match->data;
336         unsigned int i;
337
338         printf("multiport ");
339
340         switch (multiinfo->flags) {
341         case IPT_MULTIPORT_SOURCE:
342                 printf("sports ");
343                 break;
344
345         case IPT_MULTIPORT_DESTINATION:
346                 printf("dports ");
347                 break;
348
349         case IPT_MULTIPORT_EITHER:
350                 printf("ports ");
351                 break;
352
353         default:
354                 printf("ERROR ");
355                 break;
356         }
357
358         if (multiinfo->invert)
359                 printf("! ");
360
361         for (i=0; i < multiinfo->count; i++) {
362                 printf("%s", i ? "," : "");
363                 print_port(multiinfo->ports[i], ip->proto, numeric);
364                 if (multiinfo->pflags[i]) {
365                         printf(":");
366                         print_port(multiinfo->ports[++i], ip->proto, numeric);
367                 }
368         }
369         printf(" ");
370 }
371
372 /* Saves the union ipt_matchinfo in parsable form to stdout. */
373 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
374 {
375         const struct ipt_multiport *multiinfo
376                 = (const struct ipt_multiport *)match->data;
377         unsigned int i;
378
379         switch (multiinfo->flags) {
380         case IPT_MULTIPORT_SOURCE:
381                 printf("--sports ");
382                 break;
383
384         case IPT_MULTIPORT_DESTINATION:
385                 printf("--dports ");
386                 break;
387
388         case IPT_MULTIPORT_EITHER:
389                 printf("--ports ");
390                 break;
391         }
392
393         for (i=0; i < multiinfo->count; i++) {
394                 printf("%s", i ? "," : "");
395                 print_port(multiinfo->ports[i], ip->proto, 1);
396         }
397         printf(" ");
398 }
399
400 static void save_v1(const struct ipt_ip *ip, 
401                     const struct ipt_entry_match *match)
402 {
403         const struct ipt_multiport_v1 *multiinfo
404                 = (const struct ipt_multiport_v1 *)match->data;
405         unsigned int i;
406
407         switch (multiinfo->flags) {
408         case IPT_MULTIPORT_SOURCE:
409                 printf("--sports ");
410                 break;
411
412         case IPT_MULTIPORT_DESTINATION:
413                 printf("--dports ");
414                 break;
415
416         case IPT_MULTIPORT_EITHER:
417                 printf("--ports ");
418                 break;
419         }
420
421         if (multiinfo->invert)
422                 printf("! ");
423
424         for (i=0; i < multiinfo->count; i++) {
425                 printf("%s", i ? "," : "");
426                 print_port(multiinfo->ports[i], ip->proto, 1);
427                 if (multiinfo->pflags[i]) {
428                         printf(":");
429                         print_port(multiinfo->ports[++i], ip->proto, 1);
430                 }
431         }
432         printf(" ");
433 }
434
435 static struct iptables_match multiport = { 
436         .next           = NULL,
437         .name           = "multiport",
438         .revision       = 0,
439         .version        = IPTABLES_VERSION,
440         .size           = IPT_ALIGN(sizeof(struct ipt_multiport)),
441         .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport)),
442         .help           = &help,
443         .init           = &init,
444         .parse          = &parse,
445         .final_check    = &final_check,
446         .print          = &print,
447         .save           = &save,
448         .extra_opts     = opts
449 };
450
451 static struct iptables_match multiport_v1 = { 
452         .next           = NULL,
453         .name           = "multiport",
454         .version        = IPTABLES_VERSION,
455         .revision       = 1,
456         .size           = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
457         .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
458         .help           = &help_v1,
459         .init           = &init,
460         .parse          = &parse_v1,
461         .final_check    = &final_check,
462         .print          = &print_v1,
463         .save           = &save_v1,
464         .extra_opts     = opts
465 };
466
467 void
468 _init(void)
469 {
470         register_match(&multiport);
471         register_match(&multiport_v1);
472 }