1 /* ebt_ip6
2 *
3 * Authors:
4 * Kuo-Lang Tseng <[email protected]>
5 * Manohar Castelino <[email protected]>
6 *
7 * Summary:
8 * This is just a modification of the IPv4 code written by
9 * Bart De Schuymer <[email protected]>
10 * with the changes required to support IPv6
11 *
12 */
13
14 #include <errno.h>
15 #include <arpa/inet.h>
16 #include <inttypes.h>
17 #include <limits.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <getopt.h>
22 #include <netdb.h>
23 #include <xtables.h>
24 #include <linux/netfilter_bridge/ebt_ip6.h>
25
26 #include "libxt_icmp.h"
27
28 #define IP_SOURCE '1'
29 #define IP_DEST '2'
30 #define IP_TCLASS '3'
31 #define IP_PROTO '4'
32 #define IP_SPORT '5'
33 #define IP_DPORT '6'
34 #define IP_ICMP6 '7'
35
36 static const struct option brip6_opts[] = {
37 { .name = "ip6-source", .has_arg = true, .val = IP_SOURCE },
38 { .name = "ip6-src", .has_arg = true, .val = IP_SOURCE },
39 { .name = "ip6-destination", .has_arg = true, .val = IP_DEST },
40 { .name = "ip6-dst", .has_arg = true, .val = IP_DEST },
41 { .name = "ip6-tclass", .has_arg = true, .val = IP_TCLASS },
42 { .name = "ip6-protocol", .has_arg = true, .val = IP_PROTO },
43 { .name = "ip6-proto", .has_arg = true, .val = IP_PROTO },
44 { .name = "ip6-source-port", .has_arg = true, .val = IP_SPORT },
45 { .name = "ip6-sport", .has_arg = true, .val = IP_SPORT },
46 { .name = "ip6-destination-port",.has_arg = true,.val = IP_DPORT },
47 { .name = "ip6-dport", .has_arg = true, .val = IP_DPORT },
48 { .name = "ip6-icmp-type", .has_arg = true, .val = IP_ICMP6 },
49 XT_GETOPT_TABLEEND,
50 };
51
52 static void
parse_port_range(const char * protocol,const char * portstring,uint16_t * ports)53 parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
54 {
55 char *buffer;
56 char *cp;
57
58 buffer = xtables_strdup(portstring);
59 if ((cp = strchr(buffer, ':')) == NULL)
60 ports[0] = ports[1] = xtables_parse_port(buffer, NULL);
61 else {
62 *cp = '\0';
63 cp++;
64
65 ports[0] = buffer[0] ? xtables_parse_port(buffer, NULL) : 0;
66 ports[1] = cp[0] ? xtables_parse_port(cp, NULL) : 0xFFFF;
67
68 if (ports[0] > ports[1])
69 xtables_error(PARAMETER_PROBLEM,
70 "invalid portrange (min > max)");
71 }
72 free(buffer);
73 }
74
print_port_range(uint16_t * ports)75 static void print_port_range(uint16_t *ports)
76 {
77 if (ports[0] == ports[1])
78 printf("%d ", ports[0]);
79 else
80 printf("%d:%d ", ports[0], ports[1]);
81 }
82
print_icmp_code(uint8_t * code)83 static void print_icmp_code(uint8_t *code)
84 {
85 if (code[0] == code[1])
86 printf("/%"PRIu8 " ", code[0]);
87 else
88 printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
89 }
90
print_icmp_type(uint8_t * type,uint8_t * code)91 static void print_icmp_type(uint8_t *type, uint8_t *code)
92 {
93 unsigned int i;
94
95 if (type[0] != type[1]) {
96 printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
97 print_icmp_code(code);
98 return;
99 }
100
101 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) {
102 if (icmpv6_codes[i].type != type[0])
103 continue;
104
105 if (icmpv6_codes[i].code_min == code[0] &&
106 icmpv6_codes[i].code_max == code[1]) {
107 printf("%s ", icmpv6_codes[i].name);
108 return;
109 }
110 }
111 printf("%"PRIu8, type[0]);
112 print_icmp_code(code);
113 }
114
brip6_print_help(void)115 static void brip6_print_help(void)
116 {
117 printf(
118 "ip6 options:\n"
119 "--ip6-src [!] address[/mask]: ipv6 source specification\n"
120 "--ip6-dst [!] address[/mask]: ipv6 destination specification\n"
121 "--ip6-tclass [!] tclass : ipv6 traffic class specification\n"
122 "--ip6-proto [!] protocol : ipv6 protocol specification\n"
123 "--ip6-sport [!] port[:port] : tcp/udp source port or port range\n"
124 "--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n"
125 "--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
126 printf("Valid ICMPv6 Types:");
127 xt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
128 }
129
brip6_init(struct xt_entry_match * match)130 static void brip6_init(struct xt_entry_match *match)
131 {
132 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
133
134 ipinfo->invflags = 0;
135 ipinfo->bitmask = 0;
136 memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr));
137 memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr));
138 memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr));
139 memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr));
140 }
141
142 /* wrap xtables_ip6parse_any(), ignoring any but the first returned address */
ebt_parse_ip6_address(char * address,struct in6_addr * addr,struct in6_addr * msk)143 static void ebt_parse_ip6_address(char *address,
144 struct in6_addr *addr, struct in6_addr *msk)
145 {
146 struct in6_addr *addrp;
147 unsigned int naddrs;
148
149 xtables_ip6parse_any(address, &addrp, msk, &naddrs);
150 if (naddrs != 1)
151 xtables_error(PARAMETER_PROBLEM,
152 "Invalid IPv6 Address '%s' specified", address);
153 memcpy(addr, addrp, sizeof(*addr));
154 free(addrp);
155 }
156
157 #define OPT_SOURCE 0x01
158 #define OPT_DEST 0x02
159 #define OPT_TCLASS 0x04
160 #define OPT_PROTO 0x08
161 #define OPT_SPORT 0x10
162 #define OPT_DPORT 0x20
163 static int
brip6_parse(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)164 brip6_parse(int c, char **argv, int invert, unsigned int *flags,
165 const void *entry, struct xt_entry_match **match)
166 {
167 struct ebt_ip6_info *info = (struct ebt_ip6_info *)(*match)->data;
168 unsigned int i;
169 char *end;
170
171 switch (c) {
172 case IP_SOURCE:
173 if (invert)
174 info->invflags |= EBT_IP6_SOURCE;
175 ebt_parse_ip6_address(optarg, &info->saddr, &info->smsk);
176 info->bitmask |= EBT_IP6_SOURCE;
177 break;
178 case IP_DEST:
179 if (invert)
180 info->invflags |= EBT_IP6_DEST;
181 ebt_parse_ip6_address(optarg, &info->daddr, &info->dmsk);
182 info->bitmask |= EBT_IP6_DEST;
183 break;
184 case IP_SPORT:
185 if (invert)
186 info->invflags |= EBT_IP6_SPORT;
187 parse_port_range(NULL, optarg, info->sport);
188 info->bitmask |= EBT_IP6_SPORT;
189 break;
190 case IP_DPORT:
191 if (invert)
192 info->invflags |= EBT_IP6_DPORT;
193 parse_port_range(NULL, optarg, info->dport);
194 info->bitmask |= EBT_IP6_DPORT;
195 break;
196 case IP_ICMP6:
197 if (invert)
198 info->invflags |= EBT_IP6_ICMP6;
199 ebt_parse_icmpv6(optarg, info->icmpv6_type, info->icmpv6_code);
200 info->bitmask |= EBT_IP6_ICMP6;
201 break;
202 case IP_TCLASS:
203 if (invert)
204 info->invflags |= EBT_IP6_TCLASS;
205 if (!xtables_strtoui(optarg, &end, &i, 0, 255))
206 xtables_error(PARAMETER_PROBLEM, "Problem with specified IPv6 traffic class '%s'", optarg);
207 info->tclass = i;
208 info->bitmask |= EBT_IP6_TCLASS;
209 break;
210 case IP_PROTO:
211 if (invert)
212 info->invflags |= EBT_IP6_PROTO;
213 info->protocol = xtables_parse_protocol(optarg);
214 info->bitmask |= EBT_IP6_PROTO;
215 break;
216 default:
217 return 0;
218 }
219
220 *flags |= info->bitmask;
221 return 1;
222 }
223
brip6_final_check(unsigned int flags)224 static void brip6_final_check(unsigned int flags)
225 {
226 if (!flags)
227 xtables_error(PARAMETER_PROBLEM,
228 "You must specify proper arguments");
229 }
230
brip6_print(const void * ip,const struct xt_entry_match * match,int numeric)231 static void brip6_print(const void *ip, const struct xt_entry_match *match,
232 int numeric)
233 {
234 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
235
236 if (ipinfo->bitmask & EBT_IP6_SOURCE) {
237 printf("--ip6-src ");
238 if (ipinfo->invflags & EBT_IP6_SOURCE)
239 printf("! ");
240 printf("%s", xtables_ip6addr_to_numeric(&ipinfo->saddr));
241 printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->smsk));
242 }
243 if (ipinfo->bitmask & EBT_IP6_DEST) {
244 printf("--ip6-dst ");
245 if (ipinfo->invflags & EBT_IP6_DEST)
246 printf("! ");
247 printf("%s", xtables_ip6addr_to_numeric(&ipinfo->daddr));
248 printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->dmsk));
249 }
250 if (ipinfo->bitmask & EBT_IP6_TCLASS) {
251 printf("--ip6-tclass ");
252 if (ipinfo->invflags & EBT_IP6_TCLASS)
253 printf("! ");
254 printf("0x%02X ", ipinfo->tclass);
255 }
256 if (ipinfo->bitmask & EBT_IP6_PROTO) {
257 struct protoent *pe;
258
259 printf("--ip6-proto ");
260 if (ipinfo->invflags & EBT_IP6_PROTO)
261 printf("! ");
262 pe = getprotobynumber(ipinfo->protocol);
263 if (pe == NULL) {
264 printf("%d ", ipinfo->protocol);
265 } else {
266 printf("%s ", pe->p_name);
267 }
268 }
269 if (ipinfo->bitmask & EBT_IP6_SPORT) {
270 printf("--ip6-sport ");
271 if (ipinfo->invflags & EBT_IP6_SPORT)
272 printf("! ");
273 print_port_range(ipinfo->sport);
274 }
275 if (ipinfo->bitmask & EBT_IP6_DPORT) {
276 printf("--ip6-dport ");
277 if (ipinfo->invflags & EBT_IP6_DPORT)
278 printf("! ");
279 print_port_range(ipinfo->dport);
280 }
281 if (ipinfo->bitmask & EBT_IP6_ICMP6) {
282 printf("--ip6-icmp-type ");
283 if (ipinfo->invflags & EBT_IP6_ICMP6)
284 printf("! ");
285 print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
286 }
287 }
288
brip_xlate_th(struct xt_xlate * xl,const struct ebt_ip6_info * info,int bit,const char * pname)289 static void brip_xlate_th(struct xt_xlate *xl,
290 const struct ebt_ip6_info *info, int bit,
291 const char *pname)
292 {
293 const uint16_t *ports;
294
295 if ((info->bitmask & bit) == 0)
296 return;
297
298 switch (bit) {
299 case EBT_IP6_SPORT:
300 if (pname)
301 xt_xlate_add(xl, "%s sport ", pname);
302 else
303 xt_xlate_add(xl, "@th,0,16 ");
304
305 ports = info->sport;
306 break;
307 case EBT_IP6_DPORT:
308 if (pname)
309 xt_xlate_add(xl, "%s dport ", pname);
310 else
311 xt_xlate_add(xl, "@th,16,16 ");
312
313 ports = info->dport;
314 break;
315 default:
316 return;
317 }
318
319 if (info->invflags & bit)
320 xt_xlate_add(xl, "!= ");
321
322 if (ports[0] == ports[1])
323 xt_xlate_add(xl, "%d ", ports[0]);
324 else
325 xt_xlate_add(xl, "%d-%d ", ports[0], ports[1]);
326 }
327
brip_xlate_nh(struct xt_xlate * xl,const struct ebt_ip6_info * info,int bit)328 static void brip_xlate_nh(struct xt_xlate *xl,
329 const struct ebt_ip6_info *info, int bit)
330 {
331 struct in6_addr *addrp, *maskp;
332
333 if ((info->bitmask & bit) == 0)
334 return;
335
336 switch (bit) {
337 case EBT_IP6_SOURCE:
338 xt_xlate_add(xl, "ip6 saddr ");
339 addrp = (struct in6_addr *)&info->saddr;
340 maskp = (struct in6_addr *)&info->smsk;
341 break;
342 case EBT_IP6_DEST:
343 xt_xlate_add(xl, "ip6 daddr ");
344 addrp = (struct in6_addr *)&info->daddr;
345 maskp = (struct in6_addr *)&info->dmsk;
346 break;
347 default:
348 return;
349 }
350
351 if (info->invflags & bit)
352 xt_xlate_add(xl, "!= ");
353
354 xt_xlate_add(xl, "%s%s ", xtables_ip6addr_to_numeric(addrp),
355 xtables_ip6mask_to_numeric(maskp));
356 }
357
brip6_xlate_proto_to_name(uint8_t proto)358 static const char *brip6_xlate_proto_to_name(uint8_t proto)
359 {
360 switch (proto) {
361 case IPPROTO_TCP:
362 return "tcp";
363 case IPPROTO_UDP:
364 return "udp";
365 case IPPROTO_UDPLITE:
366 return "udplite";
367 case IPPROTO_SCTP:
368 return "sctp";
369 case IPPROTO_DCCP:
370 return "dccp";
371 default:
372 return NULL;
373 }
374 }
375
brip6_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)376 static int brip6_xlate(struct xt_xlate *xl,
377 const struct xt_xlate_mt_params *params)
378 {
379 const struct ebt_ip6_info *info = (const void *)params->match->data;
380 const char *pname = NULL;
381
382 if ((info->bitmask & (EBT_IP6_SOURCE|EBT_IP6_DEST|EBT_IP6_ICMP6|EBT_IP6_TCLASS)) == 0)
383 xt_xlate_add(xl, "ether type ip6 ");
384
385 brip_xlate_nh(xl, info, EBT_IP6_SOURCE);
386 brip_xlate_nh(xl, info, EBT_IP6_DEST);
387
388 if (info->bitmask & EBT_IP6_TCLASS) {
389 xt_xlate_add(xl, "ip6 dscp ");
390 if (info->invflags & EBT_IP6_TCLASS)
391 xt_xlate_add(xl, "!= ");
392 xt_xlate_add(xl, "0x%02x ", info->tclass & 0x3f); /* remove ECN bits */
393 }
394
395 if (info->bitmask & EBT_IP6_PROTO) {
396 struct protoent *pe;
397
398 if (info->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT|EBT_IP6_ICMP6) &&
399 (info->invflags & EBT_IP6_PROTO) == 0) {
400 /* port number given and not inverted, no need to
401 * add explicit 'meta l4proto'.
402 */
403 pname = brip6_xlate_proto_to_name(info->protocol);
404 } else {
405 xt_xlate_add(xl, "meta l4proto ");
406 if (info->invflags & EBT_IP6_PROTO)
407 xt_xlate_add(xl, "!= ");
408 pe = getprotobynumber(info->protocol);
409 if (pe == NULL)
410 xt_xlate_add(xl, "%d ", info->protocol);
411 else
412 xt_xlate_add(xl, "%s ", pe->p_name);
413 }
414 }
415
416 brip_xlate_th(xl, info, EBT_IP6_SPORT, pname);
417 brip_xlate_th(xl, info, EBT_IP6_DPORT, pname);
418
419 if (info->bitmask & EBT_IP6_ICMP6) {
420 xt_xlate_add(xl, "icmpv6 type ");
421 if (info->invflags & EBT_IP6_ICMP6)
422 xt_xlate_add(xl, "!= ");
423
424 if (info->icmpv6_type[0] == info->icmpv6_type[1])
425 xt_xlate_add(xl, "%d ", info->icmpv6_type[0]);
426 else
427 xt_xlate_add(xl, "%d-%d ", info->icmpv6_type[0],
428 info->icmpv6_type[1]);
429
430 if (info->icmpv6_code[0] == 0 &&
431 info->icmpv6_code[1] == 0xff)
432 return 1;
433
434 xt_xlate_add(xl, "icmpv6 code ");
435 if (info->invflags & EBT_IP6_ICMP6)
436 xt_xlate_add(xl, "!= ");
437
438 if (info->icmpv6_code[0] == info->icmpv6_code[1])
439 xt_xlate_add(xl, "%d ", info->icmpv6_code[0]);
440 else
441 xt_xlate_add(xl, "%d-%d ", info->icmpv6_code[0],
442 info->icmpv6_code[1]);
443 }
444
445 return 1;
446 }
447
448 static struct xtables_match brip6_match = {
449 .name = "ip6",
450 .revision = 0,
451 .version = XTABLES_VERSION,
452 .family = NFPROTO_BRIDGE,
453 .size = XT_ALIGN(sizeof(struct ebt_ip6_info)),
454 .userspacesize = XT_ALIGN(sizeof(struct ebt_ip6_info)),
455 .init = brip6_init,
456 .help = brip6_print_help,
457 .parse = brip6_parse,
458 .final_check = brip6_final_check,
459 .print = brip6_print,
460 .xlate = brip6_xlate,
461 .extra_opts = brip6_opts,
462 };
463
_init(void)464 void _init(void)
465 {
466 xtables_register_match(&brip6_match);
467 }
468