blob: 58b6a0eb1ad923678c92aaa4d7da210c3f2f7522 [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* 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>
Pablo Neira5df95472005-01-03 09:37:07 +00008/* To ensure that iptables compiles with an old kernel */
9#include "../include/linux/netfilter_ipv4/ipt_multiport.h"
Marc Bouchere6869a82000-03-20 06:03:29 +000010
11/* Function which prints out usage message. */
12static void
13help(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"
Pablo Neira5df95472005-01-03 09:37:07 +000024" match both source and destination port(s)\n"
25" NOTE: this kernel does not support port ranges in multiport.\n",
26IPTABLES_VERSION);
27}
28
29static void
30help_v1(void)
31{
32 printf(
33"multiport v%s options:\n"
Phil Oesterb2eedcd2005-02-02 19:20:15 +000034" --source-ports [!] port[,port:port,port...]\n"
Pablo Neira5df95472005-01-03 09:37:07 +000035" --sports ...\n"
36" match source port(s)\n"
Phil Oesterb2eedcd2005-02-02 19:20:15 +000037" --destination-ports [!] port[,port:port,port...]\n"
Pablo Neira5df95472005-01-03 09:37:07 +000038" --dports ...\n"
39" match destination port(s)\n"
Phil Oesterb2eedcd2005-02-02 19:20:15 +000040" --ports [!] port[,port:port,port]\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000041" match both source and destination port(s)\n",
Harald Welte80fe35d2002-05-29 13:08:15 +000042IPTABLES_VERSION);
Marc Bouchere6869a82000-03-20 06:03:29 +000043}
44
45static 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
Patrick McHardyJesper Brouerc1eae412006-07-25 01:50:48 +000054static char *
55proto_to_name(u_int8_t proto)
56{
57 switch (proto) {
58 case IPPROTO_TCP:
59 return "tcp";
60 case IPPROTO_UDP:
61 return "udp";
Patrick McHardy95616062007-01-11 09:08:22 +000062 case IPPROTO_UDPLITE:
63 return "udplite";
Patrick McHardyJesper Brouerc1eae412006-07-25 01:50:48 +000064 case IPPROTO_SCTP:
65 return "sctp";
66 case IPPROTO_DCCP:
67 return "dccp";
68 default:
69 return NULL;
70 }
71}
72
Marc Bouchere6869a82000-03-20 06:03:29 +000073static unsigned int
74parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
75{
76 char *buffer, *cp, *next;
77 unsigned int i;
78
79 buffer = strdup(portstring);
80 if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
81
82 for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
83 {
84 next=strchr(cp, ',');
85 if (next) *next++='\0';
86 ports[i] = parse_port(cp, proto);
87 }
88 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
89 free(buffer);
90 return i;
91}
92
Pablo Neira5df95472005-01-03 09:37:07 +000093static void
94parse_multi_ports_v1(const char *portstring,
95 struct ipt_multiport_v1 *multiinfo,
96 const char *proto)
97{
98 char *buffer, *cp, *next, *range;
99 unsigned int i;
100 u_int16_t m;
101
102 buffer = strdup(portstring);
103 if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
104
105 for (i=0; i<IPT_MULTI_PORTS; i++)
106 multiinfo->pflags[i] = 0;
107
108 for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) {
109 next=strchr(cp, ',');
110 if (next) *next++='\0';
111 range = strchr(cp, ':');
112 if (range) {
113 if (i == IPT_MULTI_PORTS-1)
114 exit_error(PARAMETER_PROBLEM,
115 "too many ports specified");
116 *range++ = '\0';
117 }
118 multiinfo->ports[i] = parse_port(cp, proto);
119 if (range) {
120 multiinfo->pflags[i] = 1;
121 multiinfo->ports[++i] = parse_port(range, proto);
122 if (multiinfo->ports[i-1] >= multiinfo->ports[i])
123 exit_error(PARAMETER_PROBLEM,
124 "invalid portrange specified");
125 m <<= 1;
126 }
127 }
128 multiinfo->count = i;
129 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
130 free(buffer);
131}
132
Marc Bouchere6869a82000-03-20 06:03:29 +0000133/* Initialize the match. */
134static void
135init(struct ipt_entry_match *m, unsigned int *nfcache)
136{
137}
138
139static const char *
140check_proto(const struct ipt_entry *entry)
141{
Patrick McHardy2452baf2006-04-28 08:10:08 +0000142 char *proto;
143
Rusty Russell225f4622005-01-03 09:51:58 +0000144 if (entry->ip.invflags & IPT_INV_PROTO)
145 exit_error(PARAMETER_PROBLEM,
Patrick McHardy95616062007-01-11 09:08:22 +0000146 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
Rusty Russell225f4622005-01-03 09:51:58 +0000147
Patrick McHardyJesper Brouerc1eae412006-07-25 01:50:48 +0000148 if ((proto = proto_to_name(entry->ip.proto)) != NULL)
Patrick McHardy2452baf2006-04-28 08:10:08 +0000149 return proto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000150 else if (!entry->ip.proto)
151 exit_error(PARAMETER_PROBLEM,
Patrick McHardy95616062007-01-11 09:08:22 +0000152 "multiport needs `-p tcp', `-p udp', `-p udplite', "
153 "`-p sctp' or `-p dccp'");
Marc Bouchere6869a82000-03-20 06:03:29 +0000154 else
155 exit_error(PARAMETER_PROBLEM,
Patrick McHardy95616062007-01-11 09:08:22 +0000156 "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
Marc Bouchere6869a82000-03-20 06:03:29 +0000157}
158
159/* Function which parses command options; returns true if it
160 ate an option */
161static int
162parse(int c, char **argv, int invert, unsigned int *flags,
163 const struct ipt_entry *entry,
164 unsigned int *nfcache,
165 struct ipt_entry_match **match)
166{
167 const char *proto;
168 struct ipt_multiport *multiinfo
169 = (struct ipt_multiport *)(*match)->data;
170
171 switch (c) {
172 case '1':
Patrick McHardyd0a2e8a2004-09-18 17:43:36 +0000173 check_inverse(argv[optind-1], &invert, &optind, 0);
Marc Bouchere6869a82000-03-20 06:03:29 +0000174 proto = check_proto(entry);
175 multiinfo->count = parse_multi_ports(argv[optind-1],
176 multiinfo->ports, proto);
177 multiinfo->flags = IPT_MULTIPORT_SOURCE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000178 break;
179
180 case '2':
Patrick McHardyd0a2e8a2004-09-18 17:43:36 +0000181 check_inverse(argv[optind-1], &invert, &optind, 0);
Marc Bouchere6869a82000-03-20 06:03:29 +0000182 proto = check_proto(entry);
183 multiinfo->count = parse_multi_ports(argv[optind-1],
184 multiinfo->ports, proto);
185 multiinfo->flags = IPT_MULTIPORT_DESTINATION;
Marc Bouchere6869a82000-03-20 06:03:29 +0000186 break;
187
188 case '3':
Patrick McHardyd0a2e8a2004-09-18 17:43:36 +0000189 check_inverse(argv[optind-1], &invert, &optind, 0);
Marc Bouchere6869a82000-03-20 06:03:29 +0000190 proto = check_proto(entry);
191 multiinfo->count = parse_multi_ports(argv[optind-1],
192 multiinfo->ports, proto);
193 multiinfo->flags = IPT_MULTIPORT_EITHER;
Marc Bouchere6869a82000-03-20 06:03:29 +0000194 break;
195
196 default:
197 return 0;
198 }
199
Patrick McHardyd0a2e8a2004-09-18 17:43:36 +0000200 if (invert)
201 exit_error(PARAMETER_PROBLEM,
202 "multiport does not support invert");
203
Marc Bouchere6869a82000-03-20 06:03:29 +0000204 if (*flags)
205 exit_error(PARAMETER_PROBLEM,
206 "multiport can only have one option");
207 *flags = 1;
208 return 1;
209}
210
Pablo Neira5df95472005-01-03 09:37:07 +0000211static int
212parse_v1(int c, char **argv, int invert, unsigned int *flags,
213 const struct ipt_entry *entry,
214 unsigned int *nfcache,
215 struct ipt_entry_match **match)
216{
217 const char *proto;
218 struct ipt_multiport_v1 *multiinfo
219 = (struct ipt_multiport_v1 *)(*match)->data;
220
221 switch (c) {
222 case '1':
223 check_inverse(argv[optind-1], &invert, &optind, 0);
224 proto = check_proto(entry);
225 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
226 multiinfo->flags = IPT_MULTIPORT_SOURCE;
Pablo Neira5df95472005-01-03 09:37:07 +0000227 break;
228
229 case '2':
230 check_inverse(argv[optind-1], &invert, &optind, 0);
231 proto = check_proto(entry);
232 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
233 multiinfo->flags = IPT_MULTIPORT_DESTINATION;
Pablo Neira5df95472005-01-03 09:37:07 +0000234 break;
235
236 case '3':
237 check_inverse(argv[optind-1], &invert, &optind, 0);
238 proto = check_proto(entry);
239 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
240 multiinfo->flags = IPT_MULTIPORT_EITHER;
Pablo Neira5df95472005-01-03 09:37:07 +0000241 break;
242
243 default:
244 return 0;
245 }
246
247 if (invert)
Phil Oesterb2eedcd2005-02-02 19:20:15 +0000248 multiinfo->invert = 1;
Pablo Neira5df95472005-01-03 09:37:07 +0000249
250 if (*flags)
251 exit_error(PARAMETER_PROBLEM,
252 "multiport can only have one option");
253 *flags = 1;
254 return 1;
255}
256
Marc Bouchere6869a82000-03-20 06:03:29 +0000257/* Final check; must specify something. */
258static void
259final_check(unsigned int flags)
260{
261 if (!flags)
262 exit_error(PARAMETER_PROBLEM, "multiport expection an option");
263}
264
265static char *
266port_to_service(int port, u_int8_t proto)
267{
268 struct servent *service;
269
Patrick McHardyJesper Brouerc1eae412006-07-25 01:50:48 +0000270 if ((service = getservbyport(htons(port), proto_to_name(proto))))
Marc Bouchere6869a82000-03-20 06:03:29 +0000271 return service->s_name;
272
273 return NULL;
274}
275
276static void
277print_port(u_int16_t port, u_int8_t protocol, int numeric)
278{
279 char *service;
280
281 if (numeric || (service = port_to_service(port, protocol)) == NULL)
282 printf("%u", port);
283 else
284 printf("%s", service);
285}
286
287/* Prints out the matchinfo. */
288static void
289print(const struct ipt_ip *ip,
290 const struct ipt_entry_match *match,
291 int numeric)
292{
293 const struct ipt_multiport *multiinfo
294 = (const struct ipt_multiport *)match->data;
295 unsigned int i;
296
297 printf("multiport ");
298
299 switch (multiinfo->flags) {
300 case IPT_MULTIPORT_SOURCE:
301 printf("sports ");
302 break;
303
304 case IPT_MULTIPORT_DESTINATION:
305 printf("dports ");
306 break;
307
308 case IPT_MULTIPORT_EITHER:
309 printf("ports ");
310 break;
311
312 default:
313 printf("ERROR ");
314 break;
315 }
316
317 for (i=0; i < multiinfo->count; i++) {
318 printf("%s", i ? "," : "");
319 print_port(multiinfo->ports[i], ip->proto, numeric);
320 }
321 printf(" ");
322}
323
Pablo Neira5df95472005-01-03 09:37:07 +0000324static void
325print_v1(const struct ipt_ip *ip,
326 const struct ipt_entry_match *match,
327 int numeric)
328{
329 const struct ipt_multiport_v1 *multiinfo
330 = (const struct ipt_multiport_v1 *)match->data;
331 unsigned int i;
332
333 printf("multiport ");
334
335 switch (multiinfo->flags) {
336 case IPT_MULTIPORT_SOURCE:
337 printf("sports ");
338 break;
339
340 case IPT_MULTIPORT_DESTINATION:
341 printf("dports ");
342 break;
343
344 case IPT_MULTIPORT_EITHER:
345 printf("ports ");
346 break;
347
348 default:
349 printf("ERROR ");
350 break;
351 }
352
Phil Oesterb2eedcd2005-02-02 19:20:15 +0000353 if (multiinfo->invert)
354 printf("! ");
355
Pablo Neira5df95472005-01-03 09:37:07 +0000356 for (i=0; i < multiinfo->count; i++) {
357 printf("%s", i ? "," : "");
358 print_port(multiinfo->ports[i], ip->proto, numeric);
359 if (multiinfo->pflags[i]) {
360 printf(":");
361 print_port(multiinfo->ports[++i], ip->proto, numeric);
362 }
363 }
364 printf(" ");
365}
366
Marc Bouchere6869a82000-03-20 06:03:29 +0000367/* Saves the union ipt_matchinfo in parsable form to stdout. */
368static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
369{
370 const struct ipt_multiport *multiinfo
371 = (const struct ipt_multiport *)match->data;
372 unsigned int i;
373
374 switch (multiinfo->flags) {
375 case IPT_MULTIPORT_SOURCE:
376 printf("--sports ");
377 break;
378
379 case IPT_MULTIPORT_DESTINATION:
380 printf("--dports ");
381 break;
382
383 case IPT_MULTIPORT_EITHER:
384 printf("--ports ");
385 break;
386 }
387
388 for (i=0; i < multiinfo->count; i++) {
389 printf("%s", i ? "," : "");
Thomas Woerner01cbaa62003-07-14 20:01:29 +0000390 print_port(multiinfo->ports[i], ip->proto, 1);
Marc Bouchere6869a82000-03-20 06:03:29 +0000391 }
392 printf(" ");
393}
394
Pablo Neira5df95472005-01-03 09:37:07 +0000395static void save_v1(const struct ipt_ip *ip,
396 const struct ipt_entry_match *match)
397{
398 const struct ipt_multiport_v1 *multiinfo
399 = (const struct ipt_multiport_v1 *)match->data;
400 unsigned int i;
401
402 switch (multiinfo->flags) {
403 case IPT_MULTIPORT_SOURCE:
404 printf("--sports ");
405 break;
406
407 case IPT_MULTIPORT_DESTINATION:
408 printf("--dports ");
409 break;
410
411 case IPT_MULTIPORT_EITHER:
412 printf("--ports ");
413 break;
414 }
415
Phil Oesterb2eedcd2005-02-02 19:20:15 +0000416 if (multiinfo->invert)
417 printf("! ");
418
Pablo Neira5df95472005-01-03 09:37:07 +0000419 for (i=0; i < multiinfo->count; i++) {
420 printf("%s", i ? "," : "");
421 print_port(multiinfo->ports[i], ip->proto, 1);
422 if (multiinfo->pflags[i]) {
423 printf(":");
424 print_port(multiinfo->ports[++i], ip->proto, 1);
425 }
426 }
427 printf(" ");
428}
429
Pablo Neira8caee8b2004-12-28 13:11:59 +0000430static struct iptables_match multiport = {
431 .next = NULL,
432 .name = "multiport",
Pablo Neira5df95472005-01-03 09:37:07 +0000433 .revision = 0,
Pablo Neira8caee8b2004-12-28 13:11:59 +0000434 .version = IPTABLES_VERSION,
435 .size = IPT_ALIGN(sizeof(struct ipt_multiport)),
436 .userspacesize = IPT_ALIGN(sizeof(struct ipt_multiport)),
437 .help = &help,
438 .init = &init,
439 .parse = &parse,
440 .final_check = &final_check,
441 .print = &print,
442 .save = &save,
443 .extra_opts = opts
Marc Bouchere6869a82000-03-20 06:03:29 +0000444};
445
Pablo Neira5df95472005-01-03 09:37:07 +0000446static struct iptables_match multiport_v1 = {
447 .next = NULL,
448 .name = "multiport",
449 .version = IPTABLES_VERSION,
450 .revision = 1,
451 .size = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
452 .userspacesize = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
453 .help = &help_v1,
454 .init = &init,
455 .parse = &parse_v1,
456 .final_check = &final_check,
457 .print = &print_v1,
458 .save = &save_v1,
459 .extra_opts = opts
460};
461
Marc Bouchere6869a82000-03-20 06:03:29 +0000462void
463_init(void)
464{
465 register_match(&multiport);
Pablo Neira5df95472005-01-03 09:37:07 +0000466 register_match(&multiport_v1);
Marc Bouchere6869a82000-03-20 06:03:29 +0000467}