blob: 0465e77b5d906f553f359e394bc48c6ff7bb7b4e [file] [log] [blame]
Bart De Schuymer005837e2008-02-21 21:32:25 +00001/* ebt_ip6
2 *
3 * Authors:
4 * Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
5 * Manohar Castelino <manohar.castelino@intel.com>
6 *
7 * Summary:
8 * This is just a modification of the IPv4 code written by
9 * Bart De Schuymer <bdschuym@pandora.be>
10 * with the changes required to support IPv6
11 *
12 */
13
Bart De Schuymer6f7130a2011-01-18 20:08:34 +000014#include <errno.h>
15#include <inttypes.h>
16#include <limits.h>
Bart De Schuymer005837e2008-02-21 21:32:25 +000017#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <getopt.h>
21#include <netdb.h>
22#include "../include/ebtables_u.h"
23#include <linux/netfilter_bridge/ebt_ip6.h>
24
25
26
27#define IP_SOURCE '1'
28#define IP_DEST '2'
29#define IP_TCLASS '3'
30#define IP_PROTO '4'
31#define IP_SPORT '5'
32#define IP_DPORT '6'
Bart De Schuymer6f7130a2011-01-18 20:08:34 +000033#define IP_ICMP6 '7'
Bart De Schuymer005837e2008-02-21 21:32:25 +000034
Bart De Schuymer6f7130a2011-01-18 20:08:34 +000035static const struct option opts[] =
Bart De Schuymer005837e2008-02-21 21:32:25 +000036{
37 { "ip6-source" , required_argument, 0, IP_SOURCE },
38 { "ip6-src" , required_argument, 0, IP_SOURCE },
39 { "ip6-destination" , required_argument, 0, IP_DEST },
40 { "ip6-dst" , required_argument, 0, IP_DEST },
41 { "ip6-traffic-class" , required_argument, 0, IP_TCLASS },
42 { "ip6-tclass" , required_argument, 0, IP_TCLASS },
43 { "ip6-protocol" , required_argument, 0, IP_PROTO },
44 { "ip6-proto" , required_argument, 0, IP_PROTO },
45 { "ip6-source-port" , required_argument, 0, IP_SPORT },
46 { "ip6-sport" , required_argument, 0, IP_SPORT },
47 { "ip6-destination-port" , required_argument, 0, IP_DPORT },
48 { "ip6-dport" , required_argument, 0, IP_DPORT },
Bart De Schuymer6f7130a2011-01-18 20:08:34 +000049 { "ip6-icmp-type" , required_argument, 0, IP_ICMP6 },
Bart De Schuymer005837e2008-02-21 21:32:25 +000050 { 0 }
51};
52
Bart De Schuymer6f7130a2011-01-18 20:08:34 +000053
54struct icmpv6_names {
55 const char *name;
56 u_int8_t type;
57 u_int8_t code_min, code_max;
58};
59
60static const struct icmpv6_names icmpv6_codes[] = {
61 { "destination-unreachable", 1, 0, 0xFF },
62 { "no-route", 1, 0, 0 },
63 { "communication-prohibited", 1, 1, 1 },
64 { "address-unreachable", 1, 3, 3 },
65 { "port-unreachable", 1, 4, 4 },
66
67 { "packet-too-big", 2, 0, 0xFF },
68
69 { "time-exceeded", 3, 0, 0xFF },
70 /* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
71 { "ttl-zero-during-transit", 3, 0, 0 },
72 { "ttl-zero-during-reassembly", 3, 1, 1 },
73
74 { "parameter-problem", 4, 0, 0xFF },
75 { "bad-header", 4, 0, 0 },
76 { "unknown-header-type", 4, 1, 1 },
77 { "unknown-option", 4, 2, 2 },
78
79 { "echo-request", 128, 0, 0xFF },
80 /* Alias */ { "ping", 128, 0, 0xFF },
81
82 { "echo-reply", 129, 0, 0xFF },
83 /* Alias */ { "pong", 129, 0, 0xFF },
84
85 { "router-solicitation", 133, 0, 0xFF },
86
87 { "router-advertisement", 134, 0, 0xFF },
88
89 { "neighbour-solicitation", 135, 0, 0xFF },
90 /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
91
92 { "neighbour-advertisement", 136, 0, 0xFF },
93 /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
94
95 { "redirect", 137, 0, 0xFF },
96};
97
Bart De Schuymer005837e2008-02-21 21:32:25 +000098/* transform a protocol and service name into a port number */
99static uint16_t parse_port(const char *protocol, const char *name)
100{
101 struct servent *service;
102 char *end;
103 int port;
104
105 port = strtol(name, &end, 10);
106 if (*end != '\0') {
107 if (protocol &&
108 (service = getservbyname(name, protocol)) != NULL)
109 return ntohs(service->s_port);
110 }
111 else if (port >= 0 || port <= 0xFFFF) {
112 return port;
113 }
114 ebt_print_error("Problem with specified %s port '%s'",
115 protocol?protocol:"", name);
116 return 0;
117}
118
119static void
120parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
121{
122 char *buffer;
123 char *cp;
124
125 buffer = strdup(portstring);
126 if ((cp = strchr(buffer, ':')) == NULL)
127 ports[0] = ports[1] = parse_port(protocol, buffer);
128 else {
129 *cp = '\0';
130 cp++;
131 ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
132 if (ebt_errormsg[0] != '\0')
133 return;
134 ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
135 if (ebt_errormsg[0] != '\0')
136 return;
137
138 if (ports[0] > ports[1])
139 ebt_print_error("Invalid portrange (min > max)");
140 }
141 free(buffer);
142}
143
Bart De Schuymer6f7130a2011-01-18 20:08:34 +0000144static char*
145parse_num(const char *str, long min, long max, long *num)
146{
147 char *end;
148
149 errno = 0;
150 *num = strtol(str, &end, 10);
151 if (errno && (*num == LONG_MIN || *num == LONG_MAX)) {
152 ebt_print_error("Invalid number %s: %s", str, strerror(errno));
153 return NULL;
154 }
155 if (min <= max) {
156 if (*num > max || *num < min) {
157 ebt_print_error("Value %ld out of range (%ld, %ld)", *num, min, max);
158 return NULL;
159 }
160 }
161 if (*num == 0 && str == end)
162 return NULL;
163 return end;
164}
165
166static char *
167parse_range(const char *str, long min, long max, long num[])
168{
169 char *next;
170
171 next = parse_num(str, min, max, num);
172 if (next == NULL)
173 return NULL;
174 if (next && *next == ':')
175 next = parse_num(next+1, min, max, &num[1]);
176 else
177 num[1] = num[0];
178 return next;
179}
180
181static int
182parse_icmpv6(const char *icmpv6type, uint8_t type[], uint8_t code[])
183{
184 static const unsigned int limit = ARRAY_SIZE(icmpv6_codes);
185 unsigned int match = limit;
186 unsigned int i;
187 long number[2];
188
189 for (i = 0; i < limit; i++) {
190 if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type)))
191 continue;
192 if (match != limit)
193 ebt_print_error("Ambiguous ICMPv6 type `%s':"
194 " `%s' or `%s'?",
195 icmpv6type, icmpv6_codes[match].name,
196 icmpv6_codes[i].name);
197 match = i;
198 }
199
200 if (match < limit) {
201 type[0] = type[1] = icmpv6_codes[match].type;
202 code[0] = icmpv6_codes[match].code_min;
203 code[1] = icmpv6_codes[match].code_max;
204 } else {
205 char *next = parse_range(icmpv6type, 0, 255, number);
206 if (!next) {
207 ebt_print_error("Unknown ICMPv6 type `%s'",
208 icmpv6type);
209 return -1;
210 }
211 type[0] = (uint8_t) number[0];
212 type[1] = (uint8_t) number[1];
213 switch (*next) {
214 case 0:
215 code[0] = 0;
216 code[1] = 255;
217 return 0;
218 case '/':
219 next = parse_range(next+1, 0, 255, number);
220 code[0] = (uint8_t) number[0];
221 code[1] = (uint8_t) number[1];
222 if (next == NULL)
223 return -1;
224 if (next && *next == 0)
225 return 0;
226 /* fallthrough */
227 default:
228 ebt_print_error("unknown character %c", *next);
229 return -1;
230 }
231 }
232 return 0;
233}
234
Bart De Schuymer005837e2008-02-21 21:32:25 +0000235static void print_port_range(uint16_t *ports)
236{
237 if (ports[0] == ports[1])
238 printf("%d ", ports[0]);
239 else
240 printf("%d:%d ", ports[0], ports[1]);
241}
242
Bart De Schuymer6f7130a2011-01-18 20:08:34 +0000243static void print_icmp_code(uint8_t *code)
244{
245 if (code[0] == code[1])
246 printf("/%"PRIu8 " ", code[0]);
247 else
248 printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
249}
250
251static void print_icmp_type(uint8_t *type, uint8_t *code)
252{
253 unsigned int i;
254
255 if (type[0] != type[1]) {
256 printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
257 print_icmp_code(code);
258 return;
259 }
260
261 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) {
262 if (icmpv6_codes[i].type != type[0])
263 continue;
264
265 if (icmpv6_codes[i].code_min == code[0] &&
266 icmpv6_codes[i].code_max == code[1]) {
267 printf("%s ", icmpv6_codes[i].name);
268 return;
269 }
270 }
271 printf("%"PRIu8, type[0]);
272 print_icmp_code(code);
273}
274
275static void print_icmpv6types(void)
276{
277 unsigned int i;
278 printf("Valid ICMPv6 Types:");
279
280 for (i=0; i < ARRAY_SIZE(icmpv6_codes); i++) {
281 if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) {
282 if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min
283 && (icmpv6_codes[i].code_max
284 == icmpv6_codes[i-1].code_max))
285 printf(" (%s)", icmpv6_codes[i].name);
286 else
287 printf("\n %s", icmpv6_codes[i].name);
288 }
289 else
290 printf("\n%s", icmpv6_codes[i].name);
291 }
292 printf("\n");
293}
294
Bart De Schuymer005837e2008-02-21 21:32:25 +0000295static void print_help()
296{
297 printf(
298"ip6 options:\n"
299"--ip6-src [!] address[/mask]: ipv6 source specification\n"
300"--ip6-dst [!] address[/mask]: ipv6 destination specification\n"
301"--ip6-tclass [!] tclass : ipv6 traffic class specification\n"
302"--ip6-proto [!] protocol : ipv6 protocol specification\n"
303"--ip6-sport [!] port[:port] : tcp/udp source port or port range\n"
Bart De Schuymer6f7130a2011-01-18 20:08:34 +0000304"--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n"
305"--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
306print_icmpv6types();
Bart De Schuymer005837e2008-02-21 21:32:25 +0000307}
308
309static void init(struct ebt_entry_match *match)
310{
311 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
312
313 ipinfo->invflags = 0;
314 ipinfo->bitmask = 0;
315}
316
317#define OPT_SOURCE 0x01
318#define OPT_DEST 0x02
319#define OPT_TCLASS 0x04
320#define OPT_PROTO 0x08
321#define OPT_SPORT 0x10
322#define OPT_DPORT 0x20
323static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
324 unsigned int *flags, struct ebt_entry_match **match)
325{
326 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)(*match)->data;
327 char *end;
328 long int i;
329
330 switch (c) {
331 case IP_SOURCE:
332 ebt_check_option2(flags, OPT_SOURCE);
333 ipinfo->bitmask |= EBT_IP6_SOURCE;
334 if (ebt_check_inverse2(optarg)) {
335 ipinfo->invflags |= EBT_IP6_SOURCE;
336 }
337 ebt_parse_ip6_address(optarg, &ipinfo->saddr, &ipinfo->smsk);
338 break;
339
340 case IP_DEST:
341 ebt_check_option2(flags, OPT_DEST);
342 ipinfo->bitmask |= EBT_IP6_DEST;
343 if (ebt_check_inverse2(optarg)) {
344 ipinfo->invflags |= EBT_IP6_DEST;
345 }
346 ebt_parse_ip6_address(optarg, &ipinfo->daddr, &ipinfo->dmsk);
347 break;
348
349 case IP_SPORT:
350 case IP_DPORT:
351 if (c == IP_SPORT) {
352 ebt_check_option2(flags, OPT_SPORT);
353 ipinfo->bitmask |= EBT_IP6_SPORT;
354 if (ebt_check_inverse2(optarg))
355 ipinfo->invflags |= EBT_IP6_SPORT;
356 } else {
357 ebt_check_option2(flags, OPT_DPORT);
358 ipinfo->bitmask |= EBT_IP6_DPORT;
359 if (ebt_check_inverse2(optarg))
360 ipinfo->invflags |= EBT_IP6_DPORT;
361 }
362 if (c == IP_SPORT)
363 parse_port_range(NULL, optarg, ipinfo->sport);
364 else
365 parse_port_range(NULL, optarg, ipinfo->dport);
366 break;
367
Bart De Schuymer6f7130a2011-01-18 20:08:34 +0000368 case IP_ICMP6:
369 ebt_check_option2(flags, EBT_IP6_ICMP6);
370 ipinfo->bitmask |= EBT_IP6_ICMP6;
371 if (ebt_check_inverse2(optarg))
372 ipinfo->invflags |= EBT_IP6_ICMP6;
373 if (parse_icmpv6(optarg, ipinfo->icmpv6_type, ipinfo->icmpv6_code))
374 return 0;
375 break;
376
Bart De Schuymer005837e2008-02-21 21:32:25 +0000377 case IP_TCLASS:
378 ebt_check_option2(flags, OPT_TCLASS);
379 if (ebt_check_inverse2(optarg))
380 ipinfo->invflags |= EBT_IP6_TCLASS;
381 i = strtol(optarg, &end, 16);
382 if (i < 0 || i > 255 || *end != '\0')
383 ebt_print_error2("Problem with specified IPv6 traffic class");
384 ipinfo->tclass = i;
385 ipinfo->bitmask |= EBT_IP6_TCLASS;
386 break;
387
388 case IP_PROTO:
389 ebt_check_option2(flags, OPT_PROTO);
390 if (ebt_check_inverse2(optarg))
391 ipinfo->invflags |= EBT_IP6_PROTO;
392 i = strtoul(optarg, &end, 10);
393 if (*end != '\0') {
394 struct protoent *pe;
395
396 pe = getprotobyname(optarg);
397 if (pe == NULL)
398 ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]);
399 ipinfo->protocol = pe->p_proto;
400 } else {
401 ipinfo->protocol = (unsigned char) i;
402 }
403 ipinfo->bitmask |= EBT_IP6_PROTO;
404 break;
405 default:
406 return 0;
407 }
408 return 1;
409}
410
411static void final_check(const struct ebt_u_entry *entry,
412 const struct ebt_entry_match *match, const char *name,
413 unsigned int hookmask, unsigned int time)
414{
415 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
416
417 if (entry->ethproto != ETH_P_IPV6 || entry->invflags & EBT_IPROTO) {
418 ebt_print_error("For IPv6 filtering the protocol must be "
419 "specified as IPv6");
420 } else if (ipinfo->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT) &&
421 (!(ipinfo->bitmask & EBT_IP6_PROTO) ||
422 ipinfo->invflags & EBT_IP6_PROTO ||
423 (ipinfo->protocol!=IPPROTO_TCP &&
424 ipinfo->protocol!=IPPROTO_UDP &&
425 ipinfo->protocol!=IPPROTO_SCTP &&
426 ipinfo->protocol!=IPPROTO_DCCP)))
427 ebt_print_error("For port filtering the IP protocol must be "
428 "either 6 (tcp), 17 (udp), 33 (dccp) or "
429 "132 (sctp)");
Bart De Schuymer6f7130a2011-01-18 20:08:34 +0000430 if ((ipinfo->bitmask & EBT_IP6_ICMP6) &&
431 (!(ipinfo->bitmask & EBT_IP6_PROTO) ||
432 ipinfo->invflags & EBT_IP6_PROTO ||
433 ipinfo->protocol != IPPROTO_ICMPV6))
434 ebt_print_error("For ipv6-icmp filtering the IP protocol must be "
435 "58 (ipv6-icmp)");
Bart De Schuymer005837e2008-02-21 21:32:25 +0000436}
437
438static void print(const struct ebt_u_entry *entry,
439 const struct ebt_entry_match *match)
440{
441 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
442
443 if (ipinfo->bitmask & EBT_IP6_SOURCE) {
444 printf("--ip6-src ");
445 if (ipinfo->invflags & EBT_IP6_SOURCE)
446 printf("! ");
447 printf("%s", ebt_ip6_to_numeric(&ipinfo->saddr));
448 printf("/%s ", ebt_ip6_to_numeric(&ipinfo->smsk));
449 }
450 if (ipinfo->bitmask & EBT_IP6_DEST) {
451 printf("--ip6-dst ");
452 if (ipinfo->invflags & EBT_IP6_DEST)
453 printf("! ");
454 printf("%s", ebt_ip6_to_numeric(&ipinfo->daddr));
455 printf("/%s ", ebt_ip6_to_numeric(&ipinfo->dmsk));
456 }
457 if (ipinfo->bitmask & EBT_IP6_TCLASS) {
458 printf("--ip6-tclass ");
459 if (ipinfo->invflags & EBT_IP6_TCLASS)
460 printf("! ");
461 printf("0x%02X ", ipinfo->tclass);
462 }
463 if (ipinfo->bitmask & EBT_IP6_PROTO) {
464 struct protoent *pe;
465
466 printf("--ip6-proto ");
467 if (ipinfo->invflags & EBT_IP6_PROTO)
468 printf("! ");
469 pe = getprotobynumber(ipinfo->protocol);
470 if (pe == NULL) {
471 printf("%d ", ipinfo->protocol);
472 } else {
473 printf("%s ", pe->p_name);
474 }
475 }
476 if (ipinfo->bitmask & EBT_IP6_SPORT) {
477 printf("--ip6-sport ");
478 if (ipinfo->invflags & EBT_IP6_SPORT)
479 printf("! ");
480 print_port_range(ipinfo->sport);
481 }
482 if (ipinfo->bitmask & EBT_IP6_DPORT) {
483 printf("--ip6-dport ");
484 if (ipinfo->invflags & EBT_IP6_DPORT)
485 printf("! ");
486 print_port_range(ipinfo->dport);
487 }
Bart De Schuymer6f7130a2011-01-18 20:08:34 +0000488 if (ipinfo->bitmask & EBT_IP6_ICMP6) {
489 printf("--ip6-icmp-type ");
490 if (ipinfo->invflags & EBT_IP6_ICMP6)
491 printf("! ");
492 print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
493 }
Bart De Schuymer005837e2008-02-21 21:32:25 +0000494}
495
496static int compare(const struct ebt_entry_match *m1,
497 const struct ebt_entry_match *m2)
498{
499 struct ebt_ip6_info *ipinfo1 = (struct ebt_ip6_info *)m1->data;
500 struct ebt_ip6_info *ipinfo2 = (struct ebt_ip6_info *)m2->data;
501
502 if (ipinfo1->bitmask != ipinfo2->bitmask)
503 return 0;
504 if (ipinfo1->invflags != ipinfo2->invflags)
505 return 0;
506 if (ipinfo1->bitmask & EBT_IP6_SOURCE) {
507 if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->saddr, &ipinfo2->saddr))
508 return 0;
509 if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->smsk, &ipinfo2->smsk))
510 return 0;
511 }
512 if (ipinfo1->bitmask & EBT_IP6_DEST) {
513 if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->daddr, &ipinfo2->daddr))
514 return 0;
515 if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->dmsk, &ipinfo2->dmsk))
516 return 0;
517 }
518 if (ipinfo1->bitmask & EBT_IP6_TCLASS) {
519 if (ipinfo1->tclass != ipinfo2->tclass)
520 return 0;
521 }
522 if (ipinfo1->bitmask & EBT_IP6_PROTO) {
523 if (ipinfo1->protocol != ipinfo2->protocol)
524 return 0;
525 }
526 if (ipinfo1->bitmask & EBT_IP6_SPORT) {
527 if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
528 ipinfo1->sport[1] != ipinfo2->sport[1])
529 return 0;
530 }
531 if (ipinfo1->bitmask & EBT_IP6_DPORT) {
532 if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
533 ipinfo1->dport[1] != ipinfo2->dport[1])
534 return 0;
535 }
Bart De Schuymer6f7130a2011-01-18 20:08:34 +0000536 if (ipinfo1->bitmask & EBT_IP6_ICMP6) {
537 if (ipinfo1->icmpv6_type[0] != ipinfo2->icmpv6_type[0] ||
538 ipinfo1->icmpv6_type[1] != ipinfo2->icmpv6_type[1] ||
539 ipinfo1->icmpv6_code[0] != ipinfo2->icmpv6_code[0] ||
540 ipinfo1->icmpv6_code[1] != ipinfo2->icmpv6_code[1])
541 return 0;
542 }
Bart De Schuymer005837e2008-02-21 21:32:25 +0000543 return 1;
544}
545
546static struct ebt_u_match ip6_match =
547{
548 .name = EBT_IP6_MATCH,
549 .size = sizeof(struct ebt_ip6_info),
550 .help = print_help,
551 .init = init,
552 .parse = parse,
553 .final_check = final_check,
554 .print = print,
555 .compare = compare,
556 .extra_ops = opts,
557};
558
559void _init(void)
560{
561 ebt_register_match(&ip6_match);
562}