• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
3  * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
11  */
12 
13 #include <string.h>
14 #include <stdio.h>
15 
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <arpa/inet.h>
19 #include <netinet/ip6.h>
20 #include <netdb.h>
21 
22 #include <xtables.h>
23 
24 #include <linux/netfilter/nf_tables.h>
25 #include "nft.h"
26 #include "nft-shared.h"
27 
nft_ipv6_add(struct nftnl_rule * r,void * data)28 static int nft_ipv6_add(struct nftnl_rule *r, void *data)
29 {
30 	struct iptables_command_state *cs = data;
31 	struct xtables_rule_match *matchp;
32 	uint32_t op;
33 	int ret;
34 
35 	if (cs->fw6.ipv6.iniface[0] != '\0') {
36 		op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_IN);
37 		add_iniface(r, cs->fw6.ipv6.iniface, op);
38 	}
39 
40 	if (cs->fw6.ipv6.outiface[0] != '\0') {
41 		op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_OUT);
42 		add_outiface(r, cs->fw6.ipv6.outiface, op);
43 	}
44 
45 	if (cs->fw6.ipv6.proto != 0) {
46 		op = nft_invflags2cmp(cs->fw6.ipv6.invflags, XT_INV_PROTO);
47 		add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1,
48 			  cs->fw6.ipv6.proto, op);
49 	}
50 
51 	if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) {
52 		op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_SRCIP);
53 		add_addr(r, offsetof(struct ip6_hdr, ip6_src),
54 			 &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
55 			 sizeof(struct in6_addr), op);
56 	}
57 	if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)) {
58 		op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_DSTIP);
59 		add_addr(r, offsetof(struct ip6_hdr, ip6_dst),
60 			 &cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
61 			 sizeof(struct in6_addr), op);
62 	}
63 	add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags);
64 
65 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
66 		/* Use nft built-in comments support instead of comment match */
67 		if (strcmp(matchp->match->name, "comment") == 0) {
68 			ret = add_comment(r, (char *)matchp->match->m->data);
69 			if (ret < 0)
70 				return ret;
71 		} else {
72 			ret = add_match(r, matchp->match->m);
73 			if (ret < 0)
74 				return ret;
75 		}
76 	}
77 
78 	/* Counters need to me added before the target, otherwise they are
79 	 * increased for each rule because of the way nf_tables works.
80 	 */
81 	if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
82 		return -1;
83 
84 	return add_action(r, cs, !!(cs->fw6.ipv6.flags & IP6T_F_GOTO));
85 }
86 
nft_ipv6_is_same(const void * data_a,const void * data_b)87 static bool nft_ipv6_is_same(const void *data_a,
88 			     const void *data_b)
89 {
90 	const struct iptables_command_state *a = data_a;
91 	const struct iptables_command_state *b = data_b;
92 
93 	if (memcmp(a->fw6.ipv6.src.s6_addr, b->fw6.ipv6.src.s6_addr,
94 		   sizeof(struct in6_addr)) != 0
95 	    || memcmp(a->fw6.ipv6.dst.s6_addr, b->fw6.ipv6.dst.s6_addr,
96 		    sizeof(struct in6_addr)) != 0
97 	    || a->fw6.ipv6.proto != b->fw6.ipv6.proto
98 	    || a->fw6.ipv6.flags != b->fw6.ipv6.flags
99 	    || a->fw6.ipv6.invflags != b->fw6.ipv6.invflags) {
100 		DEBUGP("different src/dst/proto/flags/invflags\n");
101 		return false;
102 	}
103 
104 	return is_same_interfaces(a->fw6.ipv6.iniface, a->fw6.ipv6.outiface,
105 				  a->fw6.ipv6.iniface_mask,
106 				  a->fw6.ipv6.outiface_mask,
107 				  b->fw6.ipv6.iniface, b->fw6.ipv6.outiface,
108 				  b->fw6.ipv6.iniface_mask,
109 				  b->fw6.ipv6.outiface_mask);
110 }
111 
nft_ipv6_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)112 static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
113 				void *data)
114 {
115 	struct iptables_command_state *cs = data;
116 
117 	parse_meta(e, ctx->meta.key, cs->fw6.ipv6.iniface,
118 		   cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface,
119 		   cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags);
120 }
121 
parse_mask_ipv6(struct nft_xt_ctx * ctx,struct in6_addr * mask)122 static void parse_mask_ipv6(struct nft_xt_ctx *ctx, struct in6_addr *mask)
123 {
124 	memcpy(mask, ctx->bitwise.mask, sizeof(struct in6_addr));
125 }
126 
nft_ipv6_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)127 static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
128 				   struct nftnl_expr *e, void *data)
129 {
130 	struct iptables_command_state *cs = data;
131 	struct in6_addr addr;
132 	uint8_t proto;
133 	bool inv;
134 
135 	switch (ctx->payload.offset) {
136 	case offsetof(struct ip6_hdr, ip6_src):
137 		get_cmp_data(e, &addr, sizeof(addr), &inv);
138 		memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr));
139 		if (ctx->flags & NFT_XT_CTX_BITWISE) {
140 			parse_mask_ipv6(ctx, &cs->fw6.ipv6.smsk);
141 			ctx->flags &= ~NFT_XT_CTX_BITWISE;
142 		} else {
143 			memset(&cs->fw.ip.smsk, 0xff, sizeof(struct in6_addr));
144 		}
145 
146 		if (inv)
147 			cs->fw6.ipv6.invflags |= IP6T_INV_SRCIP;
148 		break;
149 	case offsetof(struct ip6_hdr, ip6_dst):
150 		get_cmp_data(e, &addr, sizeof(addr), &inv);
151 		memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr));
152 		if (ctx->flags & NFT_XT_CTX_BITWISE) {
153 			parse_mask_ipv6(ctx, &cs->fw6.ipv6.dmsk);
154 			ctx->flags &= ~NFT_XT_CTX_BITWISE;
155 		} else {
156 			memset(&cs->fw.ip.dmsk, 0xff, sizeof(struct in6_addr));
157 		}
158 
159 		if (inv)
160 			cs->fw6.ipv6.invflags |= IP6T_INV_DSTIP;
161 		break;
162 	case offsetof(struct ip6_hdr, ip6_nxt):
163 		get_cmp_data(e, &proto, sizeof(proto), &inv);
164 		cs->fw6.ipv6.flags |= IP6T_F_PROTO;
165 		cs->fw6.ipv6.proto = proto;
166 		if (inv)
167 			cs->fw6.ipv6.invflags |= IP6T_INV_PROTO;
168 	default:
169 		DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
170 		break;
171 	}
172 }
173 
nft_ipv6_parse_immediate(const char * jumpto,bool nft_goto,void * data)174 static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto,
175 				     void *data)
176 {
177 	struct iptables_command_state *cs = data;
178 
179 	cs->jumpto = jumpto;
180 
181 	if (nft_goto)
182 		cs->fw6.ipv6.flags |= IP6T_F_GOTO;
183 }
184 
nft_ipv6_print_header(unsigned int format,const char * chain,const char * pol,const struct xt_counters * counters,bool basechain,uint32_t refs)185 static void nft_ipv6_print_header(unsigned int format, const char *chain,
186 				  const char *pol,
187 				  const struct xt_counters *counters,
188 				  bool basechain, uint32_t refs)
189 {
190 	print_header(format, chain, pol, counters, basechain, refs);
191 }
192 
print_ipv6_addr(const struct iptables_command_state * cs,unsigned int format)193 static void print_ipv6_addr(const struct iptables_command_state *cs,
194 			    unsigned int format)
195 {
196 	char buf[BUFSIZ];
197 
198 	fputc(cs->fw6.ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
199 	if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)
200 	    && !(format & FMT_NUMERIC))
201 		printf(FMT("%-19s ","%s "), "anywhere");
202 	else {
203 		if (format & FMT_NUMERIC)
204 			strcpy(buf,
205 			       xtables_ip6addr_to_numeric(&cs->fw6.ipv6.src));
206 		else
207 			strcpy(buf,
208 			       xtables_ip6addr_to_anyname(&cs->fw6.ipv6.src));
209 		strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.smsk));
210 		printf(FMT("%-19s ","%s "), buf);
211 	}
212 
213 
214 	fputc(cs->fw6.ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
215 	if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)
216 	    && !(format & FMT_NUMERIC))
217 		printf(FMT("%-19s ","-> %s"), "anywhere");
218 	else {
219 		if (format & FMT_NUMERIC)
220 			strcpy(buf,
221 			       xtables_ip6addr_to_numeric(&cs->fw6.ipv6.dst));
222 		else
223 			strcpy(buf,
224 			       xtables_ip6addr_to_anyname(&cs->fw6.ipv6.dst));
225 		strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.dmsk));
226 		printf(FMT("%-19s ","-> %s"), buf);
227 	}
228 }
229 
nft_ipv6_print_firewall(struct nftnl_rule * r,unsigned int num,unsigned int format)230 static void nft_ipv6_print_firewall(struct nftnl_rule *r, unsigned int num,
231 				    unsigned int format)
232 {
233 	struct iptables_command_state cs = {};
234 
235 	nft_rule_to_iptables_command_state(r, &cs);
236 
237 	print_firewall_details(&cs, cs.jumpto, cs.fw6.ipv6.flags,
238 			       cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto,
239 			       num, format);
240 	print_ifaces(cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface,
241 		     cs.fw6.ipv6.invflags, format);
242 	print_ipv6_addr(&cs, format);
243 
244 	if (format & FMT_NOTABLE)
245 		fputs("  ", stdout);
246 
247 	if (cs.fw6.ipv6.flags & IP6T_F_GOTO)
248 		printf("[goto] ");
249 
250 	print_matches_and_target(&cs, format);
251 
252 	if (!(format & FMT_NONEWLINE))
253 		fputc('\n', stdout);
254 }
255 
save_ipv6_addr(char letter,const struct in6_addr * addr,int invert)256 static void save_ipv6_addr(char letter, const struct in6_addr *addr,
257 			   int invert)
258 {
259 	char addr_str[INET6_ADDRSTRLEN];
260 
261 	if (!invert && IN6_IS_ADDR_UNSPECIFIED(addr))
262 		return;
263 
264 	inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
265 	printf("%s-%c %s ", invert ? "! " : "", letter, addr_str);
266 }
267 
nft_ipv6_save_firewall(const void * data,unsigned int format)268 static void nft_ipv6_save_firewall(const void *data, unsigned int format)
269 {
270 	const struct iptables_command_state *cs = data;
271 
272 	save_firewall_details(cs, cs->fw6.ipv6.invflags, cs->fw6.ipv6.proto,
273 			      cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask,
274 			      cs->fw6.ipv6.outiface,
275 			      cs->fw6.ipv6.outiface_mask);
276 
277 	save_ipv6_addr('s', &cs->fw6.ipv6.src,
278 		       cs->fw6.ipv6.invflags & IP6T_INV_SRCIP);
279 	save_ipv6_addr('d', &cs->fw6.ipv6.dst,
280 		       cs->fw6.ipv6.invflags & IP6T_INV_DSTIP);
281 
282 	save_matches_and_target(cs->matches, cs->target,
283 				cs->jumpto, cs->fw6.ipv6.flags, &cs->fw6);
284 
285 	if (cs->target == NULL && strlen(cs->jumpto) > 0) {
286 		printf("-%c %s", cs->fw6.ipv6.flags & IP6T_F_GOTO ? 'g' : 'j',
287 		       cs->jumpto);
288 	}
289 	printf("\n");
290 }
291 
292 /* These are invalid numbers as upper layer protocol */
is_exthdr(uint16_t proto)293 static int is_exthdr(uint16_t proto)
294 {
295 	return (proto == IPPROTO_ROUTING ||
296 		proto == IPPROTO_FRAGMENT ||
297 		proto == IPPROTO_AH ||
298 		proto == IPPROTO_DSTOPTS);
299 }
300 
nft_ipv6_proto_parse(struct iptables_command_state * cs,struct xtables_args * args)301 static void nft_ipv6_proto_parse(struct iptables_command_state *cs,
302 				 struct xtables_args *args)
303 {
304 	cs->fw6.ipv6.proto = args->proto;
305 	cs->fw6.ipv6.invflags = args->invflags;
306 
307 	if (is_exthdr(cs->fw6.ipv6.proto)
308 	    && (cs->fw6.ipv6.invflags & XT_INV_PROTO) == 0)
309 		fprintf(stderr,
310 			"Warning: never matched protocol: %s. "
311 			"use extension match instead.\n",
312 			cs->protocol);
313 }
314 
nft_ipv6_post_parse(int command,struct iptables_command_state * cs,struct xtables_args * args)315 static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs,
316 				struct xtables_args *args)
317 {
318 	if (args->proto != 0)
319 		args->flags |= IP6T_F_PROTO;
320 
321 	cs->fw6.ipv6.flags = args->flags;
322 	/* We already set invflags in proto_parse, but we need to refresh it
323 	 * to include new parsed options.
324 	 */
325 	cs->fw6.ipv6.invflags = args->invflags;
326 
327 	strncpy(cs->fw6.ipv6.iniface, args->iniface, IFNAMSIZ);
328 	memcpy(cs->fw6.ipv6.iniface_mask,
329 	       args->iniface_mask, IFNAMSIZ*sizeof(unsigned char));
330 
331 	strncpy(cs->fw6.ipv6.outiface, args->outiface, IFNAMSIZ);
332 	memcpy(cs->fw6.ipv6.outiface_mask,
333 	       args->outiface_mask, IFNAMSIZ*sizeof(unsigned char));
334 
335 	if (args->goto_set)
336 		cs->fw6.ipv6.flags |= IP6T_F_GOTO;
337 
338 	cs->fw6.counters.pcnt = args->pcnt_cnt;
339 	cs->fw6.counters.bcnt = args->bcnt_cnt;
340 
341 	if (command & (CMD_REPLACE | CMD_INSERT |
342 			CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
343 		if (!(cs->options & OPT_DESTINATION))
344 			args->dhostnetworkmask = "::0/0";
345 		if (!(cs->options & OPT_SOURCE))
346 			args->shostnetworkmask = "::0/0";
347 	}
348 
349 	if (args->shostnetworkmask)
350 		xtables_ip6parse_multiple(args->shostnetworkmask,
351 					  &args->s.addr.v6,
352 					  &args->s.mask.v6,
353 					  &args->s.naddrs);
354 	if (args->dhostnetworkmask)
355 		xtables_ip6parse_multiple(args->dhostnetworkmask,
356 					  &args->d.addr.v6,
357 					  &args->d.mask.v6,
358 					  &args->d.naddrs);
359 
360 	if ((args->s.naddrs > 1 || args->d.naddrs > 1) &&
361 	    (cs->fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP)))
362 		xtables_error(PARAMETER_PROBLEM,
363 			      "! not allowed with multiple"
364 			      " source or destination IP addresses");
365 }
366 
nft_ipv6_parse_target(struct xtables_target * t,void * data)367 static void nft_ipv6_parse_target(struct xtables_target *t, void *data)
368 {
369 	struct iptables_command_state *cs = data;
370 
371 	cs->target = t;
372 }
373 
nft_ipv6_rule_find(struct nft_family_ops * ops,struct nftnl_rule * r,void * data)374 static bool nft_ipv6_rule_find(struct nft_family_ops *ops,
375 			       struct nftnl_rule *r, void *data)
376 {
377 	struct iptables_command_state *cs = data;
378 
379 	return nft_ipv46_rule_find(ops, r, cs);
380 }
381 
nft_ipv6_save_counters(const void * data)382 static void nft_ipv6_save_counters(const void *data)
383 {
384 	const struct iptables_command_state *cs = data;
385 
386 	save_counters(cs->counters.pcnt, cs->counters.bcnt);
387 }
388 
xlate_ipv6_addr(const char * selector,const struct in6_addr * addr,const struct in6_addr * mask,int invert,struct xt_xlate * xl)389 static void xlate_ipv6_addr(const char *selector, const struct in6_addr *addr,
390 			    const struct in6_addr *mask,
391 			    int invert, struct xt_xlate *xl)
392 {
393 	char addr_str[INET6_ADDRSTRLEN];
394 
395 	if (!invert && IN6_IS_ADDR_UNSPECIFIED(addr))
396 		return;
397 
398 	inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
399 	xt_xlate_add(xl, "%s %s%s%s ", selector, invert ? "!= " : "", addr_str,
400 			xtables_ip6mask_to_numeric(mask));
401 }
402 
nft_ipv6_xlate(const void * data,struct xt_xlate * xl)403 static int nft_ipv6_xlate(const void *data, struct xt_xlate *xl)
404 {
405 	const struct iptables_command_state *cs = data;
406 	const char *comment;
407 	int ret;
408 
409 	xlate_ifname(xl, "iifname", cs->fw6.ipv6.iniface,
410 		     cs->fw6.ipv6.invflags & IP6T_INV_VIA_IN);
411 	xlate_ifname(xl, "oifname", cs->fw6.ipv6.outiface,
412 		     cs->fw6.ipv6.invflags & IP6T_INV_VIA_OUT);
413 
414 	if (cs->fw6.ipv6.proto != 0) {
415 		const struct protoent *pent =
416 			getprotobynumber(cs->fw6.ipv6.proto);
417 		char protonum[strlen("255") + 1];
418 
419 		if (!xlate_find_match(cs, pent->p_name)) {
420 			snprintf(protonum, sizeof(protonum), "%u",
421 				 cs->fw6.ipv6.proto);
422 			protonum[sizeof(protonum) - 1] = '\0';
423 			xt_xlate_add(xl, "meta l4proto %s%s ",
424 				   cs->fw6.ipv6.invflags & IP6T_INV_PROTO ?
425 					"!= " : "",
426 				   pent ? pent->p_name : protonum);
427 		}
428 	}
429 
430 	xlate_ipv6_addr("ip6 saddr", &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
431 			cs->fw6.ipv6.invflags & IP6T_INV_SRCIP, xl);
432 	xlate_ipv6_addr("ip6 daddr", &cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
433 			cs->fw6.ipv6.invflags & IP6T_INV_DSTIP, xl);
434 
435 	ret = xlate_matches(cs, xl);
436 	if (!ret)
437 		return ret;
438 
439 	/* Always add counters per rule, as in iptables */
440 	xt_xlate_add(xl, "counter ");
441 	ret = xlate_action(cs, !!(cs->fw6.ipv6.flags & IP6T_F_GOTO), xl);
442 
443 	comment = xt_xlate_get_comment(xl);
444 	if (comment)
445 		xt_xlate_add(xl, " comment %s", comment);
446 
447 	return ret;
448 }
449 
450 struct nft_family_ops nft_family_ops_ipv6 = {
451 	.add			= nft_ipv6_add,
452 	.is_same		= nft_ipv6_is_same,
453 	.parse_meta		= nft_ipv6_parse_meta,
454 	.parse_payload		= nft_ipv6_parse_payload,
455 	.parse_immediate	= nft_ipv6_parse_immediate,
456 	.print_header		= nft_ipv6_print_header,
457 	.print_firewall		= nft_ipv6_print_firewall,
458 	.save_firewall		= nft_ipv6_save_firewall,
459 	.save_counters		= nft_ipv6_save_counters,
460 	.proto_parse		= nft_ipv6_proto_parse,
461 	.post_parse		= nft_ipv6_post_parse,
462 	.parse_target		= nft_ipv6_parse_target,
463 	.rule_find		= nft_ipv6_rule_find,
464 	.xlate			= nft_ipv6_xlate,
465 };
466