• 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/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <netinet/ip.h>
20 #include <netdb.h>
21 
22 #include <xtables.h>
23 
24 #include <linux/netfilter/nf_tables.h>
25 
26 #include "nft.h"
27 #include "nft-shared.h"
28 
nft_ipv4_add(struct nft_handle * h,struct nftnl_rule * r,void * data)29 static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
30 {
31 	struct iptables_command_state *cs = data;
32 	struct xtables_rule_match *matchp;
33 	uint32_t op;
34 	int ret;
35 
36 	if (cs->fw.ip.iniface[0] != '\0') {
37 		op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN);
38 		add_iniface(r, cs->fw.ip.iniface, op);
39 	}
40 
41 	if (cs->fw.ip.outiface[0] != '\0') {
42 		op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT);
43 		add_outiface(r, cs->fw.ip.outiface, op);
44 	}
45 
46 	if (cs->fw.ip.proto != 0) {
47 		op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO);
48 		add_l4proto(r, cs->fw.ip.proto, op);
49 	}
50 
51 	if (cs->fw.ip.src.s_addr || cs->fw.ip.smsk.s_addr || cs->fw.ip.invflags & IPT_INV_SRCIP) {
52 		op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP);
53 		add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
54 			 offsetof(struct iphdr, saddr),
55 			 &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr,
56 			 sizeof(struct in_addr), op);
57 	}
58 	if (cs->fw.ip.dst.s_addr || cs->fw.ip.dmsk.s_addr || cs->fw.ip.invflags & IPT_INV_DSTIP) {
59 		op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP);
60 		add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
61 			 offsetof(struct iphdr, daddr),
62 			 &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr,
63 			 sizeof(struct in_addr), op);
64 	}
65 	if (cs->fw.ip.flags & IPT_F_FRAG) {
66 		add_payload(r, offsetof(struct iphdr, frag_off), 2,
67 			    NFT_PAYLOAD_NETWORK_HEADER);
68 		/* get the 13 bits that contain the fragment offset */
69 		add_bitwise_u16(r, htons(0x1fff), 0);
70 
71 		/* if offset is non-zero, this is a fragment */
72 		op = NFT_CMP_NEQ;
73 		if (cs->fw.ip.invflags & IPT_INV_FRAG)
74 			op = NFT_CMP_EQ;
75 
76 		add_cmp_u16(r, 0, op);
77 	}
78 
79 	add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags & XT_INV_PROTO);
80 
81 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
82 		ret = add_match(h, r, matchp->match->m);
83 		if (ret < 0)
84 			return ret;
85 	}
86 
87 	/* Counters need to me added before the target, otherwise they are
88 	 * increased for each rule because of the way nf_tables works.
89 	 */
90 	if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
91 		return -1;
92 
93 	return add_action(r, cs, !!(cs->fw.ip.flags & IPT_F_GOTO));
94 }
95 
nft_ipv4_is_same(const void * data_a,const void * data_b)96 static bool nft_ipv4_is_same(const void *data_a,
97 			     const void *data_b)
98 {
99 	const struct iptables_command_state *a = data_a;
100 	const struct iptables_command_state *b = data_b;
101 
102 	if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr
103 	    || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr
104 	    || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr
105 	    || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr
106 	    || a->fw.ip.proto != b->fw.ip.proto
107 	    || a->fw.ip.flags != b->fw.ip.flags
108 	    || a->fw.ip.invflags != b->fw.ip.invflags) {
109 		DEBUGP("different src/dst/proto/flags/invflags\n");
110 		return false;
111 	}
112 
113 	return is_same_interfaces(a->fw.ip.iniface, a->fw.ip.outiface,
114 				  a->fw.ip.iniface_mask, a->fw.ip.outiface_mask,
115 				  b->fw.ip.iniface, b->fw.ip.outiface,
116 				  b->fw.ip.iniface_mask, b->fw.ip.outiface_mask);
117 }
118 
get_frag(struct nft_xt_ctx * ctx,struct nftnl_expr * e,bool * inv)119 static void get_frag(struct nft_xt_ctx *ctx, struct nftnl_expr *e, bool *inv)
120 {
121 	uint8_t op;
122 
123 	/* we assume correct mask and xor */
124 	if (!(ctx->flags & NFT_XT_CTX_BITWISE))
125 		return;
126 
127 	/* we assume correct data */
128 	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
129 	if (op == NFT_CMP_EQ)
130 		*inv = true;
131 	else
132 		*inv = false;
133 
134 	ctx->flags &= ~NFT_XT_CTX_BITWISE;
135 }
136 
mask_to_str(uint32_t mask)137 static const char *mask_to_str(uint32_t mask)
138 {
139 	static char mask_str[sizeof("255.255.255.255")];
140 	uint32_t bits, hmask = ntohl(mask);
141 	struct in_addr mask_addr = {
142 		.s_addr = mask,
143 	};
144 	int i;
145 
146 	if (mask == 0xFFFFFFFFU) {
147 		sprintf(mask_str, "32");
148 		return mask_str;
149 	}
150 
151 	i    = 32;
152 	bits = 0xFFFFFFFEU;
153 	while (--i >= 0 && hmask != bits)
154 		bits <<= 1;
155 	if (i >= 0)
156 		sprintf(mask_str, "%u", i);
157 	else
158 		sprintf(mask_str, "%s", inet_ntoa(mask_addr));
159 
160 	return mask_str;
161 }
162 
nft_ipv4_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)163 static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
164 				void *data)
165 {
166 	struct iptables_command_state *cs = data;
167 
168 	switch (ctx->meta.key) {
169 	case NFT_META_L4PROTO:
170 		cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
171 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
172 			cs->fw.ip.invflags |= XT_INV_PROTO;
173 		return;
174 	default:
175 		break;
176 	}
177 
178 	parse_meta(e, ctx->meta.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
179 		   cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
180 		   &cs->fw.ip.invflags);
181 }
182 
parse_mask_ipv4(struct nft_xt_ctx * ctx,struct in_addr * mask)183 static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
184 {
185 	mask->s_addr = ctx->bitwise.mask[0];
186 }
187 
nft_ipv4_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)188 static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
189 				   struct nftnl_expr *e, void *data)
190 {
191 	struct iptables_command_state *cs = data;
192 	struct in_addr addr;
193 	uint8_t proto;
194 	bool inv;
195 
196 	switch(ctx->payload.offset) {
197 	case offsetof(struct iphdr, saddr):
198 		get_cmp_data(e, &addr, sizeof(addr), &inv);
199 		cs->fw.ip.src.s_addr = addr.s_addr;
200 		if (ctx->flags & NFT_XT_CTX_BITWISE) {
201 			parse_mask_ipv4(ctx, &cs->fw.ip.smsk);
202 			ctx->flags &= ~NFT_XT_CTX_BITWISE;
203 		} else {
204 			memset(&cs->fw.ip.smsk, 0xff,
205 			       min(ctx->payload.len, sizeof(struct in_addr)));
206 		}
207 
208 		if (inv)
209 			cs->fw.ip.invflags |= IPT_INV_SRCIP;
210 		break;
211 	case offsetof(struct iphdr, daddr):
212 		get_cmp_data(e, &addr, sizeof(addr), &inv);
213 		cs->fw.ip.dst.s_addr = addr.s_addr;
214 		if (ctx->flags & NFT_XT_CTX_BITWISE) {
215 			parse_mask_ipv4(ctx, &cs->fw.ip.dmsk);
216 			ctx->flags &= ~NFT_XT_CTX_BITWISE;
217 		} else {
218 			memset(&cs->fw.ip.dmsk, 0xff,
219 			       min(ctx->payload.len, sizeof(struct in_addr)));
220 		}
221 
222 		if (inv)
223 			cs->fw.ip.invflags |= IPT_INV_DSTIP;
224 		break;
225 	case offsetof(struct iphdr, protocol):
226 		get_cmp_data(e, &proto, sizeof(proto), &inv);
227 		cs->fw.ip.proto = proto;
228 		if (inv)
229 			cs->fw.ip.invflags |= IPT_INV_PROTO;
230 		break;
231 	case offsetof(struct iphdr, frag_off):
232 		cs->fw.ip.flags |= IPT_F_FRAG;
233 		inv = false;
234 		get_frag(ctx, e, &inv);
235 		if (inv)
236 			cs->fw.ip.invflags |= IPT_INV_FRAG;
237 		break;
238 	default:
239 		DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
240 		break;
241 	}
242 }
243 
nft_ipv4_parse_immediate(const char * jumpto,bool nft_goto,void * data)244 static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto,
245 				     void *data)
246 {
247 	struct iptables_command_state *cs = data;
248 
249 	cs->jumpto = jumpto;
250 
251 	if (nft_goto)
252 		cs->fw.ip.flags |= IPT_F_GOTO;
253 }
254 
print_fragment(unsigned int flags,unsigned int invflags,unsigned int format)255 static void print_fragment(unsigned int flags, unsigned int invflags,
256 			   unsigned int format)
257 {
258 	if (!(format & FMT_OPTIONS))
259 		return;
260 
261 	if (format & FMT_NOTABLE)
262 		fputs("opt ", stdout);
263 	fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout);
264 	fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
265 	fputc(' ', stdout);
266 }
267 
nft_ipv4_print_rule(struct nft_handle * h,struct nftnl_rule * r,unsigned int num,unsigned int format)268 static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r,
269 				unsigned int num, unsigned int format)
270 {
271 	struct iptables_command_state cs = {};
272 
273 	nft_rule_to_iptables_command_state(h, r, &cs);
274 
275 	print_rule_details(&cs, cs.jumpto, cs.fw.ip.flags,
276 			   cs.fw.ip.invflags, cs.fw.ip.proto, num, format);
277 	print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format);
278 	print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags,
279 		     format);
280 	print_ipv4_addresses(&cs.fw, format);
281 
282 	if (format & FMT_NOTABLE)
283 		fputs("  ", stdout);
284 
285 #ifdef IPT_F_GOTO
286 	if (cs.fw.ip.flags & IPT_F_GOTO)
287 		printf("[goto] ");
288 #endif
289 
290 	print_matches_and_target(&cs, format);
291 
292 	if (!(format & FMT_NONEWLINE))
293 		fputc('\n', stdout);
294 
295 	nft_clear_iptables_command_state(&cs);
296 }
297 
save_ipv4_addr(char letter,const struct in_addr * addr,uint32_t mask,int invert)298 static void save_ipv4_addr(char letter, const struct in_addr *addr,
299 			   uint32_t mask, int invert)
300 {
301 	if (!mask && !invert && !addr->s_addr)
302 		return;
303 
304 	printf("%s-%c %s/%s ", invert ? "! " : "", letter, inet_ntoa(*addr),
305 	       mask_to_str(mask));
306 }
307 
nft_ipv4_save_rule(const void * data,unsigned int format)308 static void nft_ipv4_save_rule(const void *data, unsigned int format)
309 {
310 	const struct iptables_command_state *cs = data;
311 
312 	save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr,
313 		       cs->fw.ip.invflags & IPT_INV_SRCIP);
314 	save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr,
315 		       cs->fw.ip.invflags & IPT_INV_DSTIP);
316 
317 	save_rule_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto,
318 			  cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
319 			  cs->fw.ip.outiface, cs->fw.ip.outiface_mask);
320 
321 	if (cs->fw.ip.flags & IPT_F_FRAG) {
322 		if (cs->fw.ip.invflags & IPT_INV_FRAG)
323 			printf("! ");
324 		printf("-f ");
325 	}
326 
327 	save_matches_and_target(cs, cs->fw.ip.flags & IPT_F_GOTO,
328 				&cs->fw, format);
329 }
330 
nft_ipv4_proto_parse(struct iptables_command_state * cs,struct xtables_args * args)331 static void nft_ipv4_proto_parse(struct iptables_command_state *cs,
332 				 struct xtables_args *args)
333 {
334 	cs->fw.ip.proto = args->proto;
335 	cs->fw.ip.invflags = args->invflags;
336 }
337 
nft_ipv4_post_parse(int command,struct iptables_command_state * cs,struct xtables_args * args)338 static void nft_ipv4_post_parse(int command,
339 				struct iptables_command_state *cs,
340 				struct xtables_args *args)
341 {
342 	cs->fw.ip.flags = args->flags;
343 	/* We already set invflags in proto_parse, but we need to refresh it
344 	 * to include new parsed options.
345 	 */
346 	cs->fw.ip.invflags = args->invflags;
347 
348 	strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ);
349 	memcpy(cs->fw.ip.iniface_mask,
350 	       args->iniface_mask, IFNAMSIZ*sizeof(unsigned char));
351 
352 	strncpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ);
353 	memcpy(cs->fw.ip.outiface_mask,
354 	       args->outiface_mask, IFNAMSIZ*sizeof(unsigned char));
355 
356 	if (args->goto_set)
357 		cs->fw.ip.flags |= IPT_F_GOTO;
358 
359 	cs->counters.pcnt = args->pcnt_cnt;
360 	cs->counters.bcnt = args->bcnt_cnt;
361 
362 	if (command & (CMD_REPLACE | CMD_INSERT |
363 			CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
364 		if (!(cs->options & OPT_DESTINATION))
365 			args->dhostnetworkmask = "0.0.0.0/0";
366 		if (!(cs->options & OPT_SOURCE))
367 			args->shostnetworkmask = "0.0.0.0/0";
368 	}
369 
370 	if (args->shostnetworkmask)
371 		xtables_ipparse_multiple(args->shostnetworkmask,
372 					 &args->s.addr.v4, &args->s.mask.v4,
373 					 &args->s.naddrs);
374 	if (args->dhostnetworkmask)
375 		xtables_ipparse_multiple(args->dhostnetworkmask,
376 					 &args->d.addr.v4, &args->d.mask.v4,
377 					 &args->d.naddrs);
378 
379 	if ((args->s.naddrs > 1 || args->d.naddrs > 1) &&
380 	    (cs->fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
381 		xtables_error(PARAMETER_PROBLEM,
382 			      "! not allowed with multiple"
383 			      " source or destination IP addresses");
384 }
385 
nft_ipv4_xlate(const void * data,struct xt_xlate * xl)386 static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl)
387 {
388 	const struct iptables_command_state *cs = data;
389 	const char *comment;
390 	int ret;
391 
392 	xlate_ifname(xl, "iifname", cs->fw.ip.iniface,
393 		     cs->fw.ip.invflags & IPT_INV_VIA_IN);
394 	xlate_ifname(xl, "oifname", cs->fw.ip.outiface,
395 		     cs->fw.ip.invflags & IPT_INV_VIA_OUT);
396 
397 	if (cs->fw.ip.flags & IPT_F_FRAG) {
398 		xt_xlate_add(xl, "ip frag-off & 0x1fff %s%x ",
399 			   cs->fw.ip.invflags & IPT_INV_FRAG? "" : "!= ", 0);
400 	}
401 
402 	if (cs->fw.ip.proto != 0) {
403 		const struct protoent *pent =
404 			getprotobynumber(cs->fw.ip.proto);
405 		char protonum[sizeof("65535")];
406 		const char *name = protonum;
407 
408 		snprintf(protonum, sizeof(protonum), "%u",
409 			 cs->fw.ip.proto);
410 
411 		if (!pent || !xlate_find_match(cs, pent->p_name)) {
412 			if (pent)
413 				name = pent->p_name;
414 			xt_xlate_add(xl, "ip protocol %s%s ",
415 				   cs->fw.ip.invflags & IPT_INV_PROTO ?
416 					"!= " : "", name);
417 		}
418 	}
419 
420 	if (cs->fw.ip.src.s_addr != 0) {
421 		xt_xlate_add(xl, "ip saddr %s%s%s ",
422 			   cs->fw.ip.invflags & IPT_INV_SRCIP ? "!= " : "",
423 			   inet_ntoa(cs->fw.ip.src),
424 			   xtables_ipmask_to_numeric(&cs->fw.ip.smsk));
425 	}
426 	if (cs->fw.ip.dst.s_addr != 0) {
427 		xt_xlate_add(xl, "ip daddr %s%s%s ",
428 			   cs->fw.ip.invflags & IPT_INV_DSTIP ? "!= " : "",
429 			   inet_ntoa(cs->fw.ip.dst),
430 			   xtables_ipmask_to_numeric(&cs->fw.ip.dmsk));
431 	}
432 
433 	ret = xlate_matches(cs, xl);
434 	if (!ret)
435 		return ret;
436 
437 	/* Always add counters per rule, as in iptables */
438 	xt_xlate_add(xl, "counter");
439 	ret = xlate_action(cs, !!(cs->fw.ip.flags & IPT_F_GOTO), xl);
440 
441 	comment = xt_xlate_get_comment(xl);
442 	if (comment)
443 		xt_xlate_add(xl, " comment %s", comment);
444 
445 	return ret;
446 }
447 
448 struct nft_family_ops nft_family_ops_ipv4 = {
449 	.add			= nft_ipv4_add,
450 	.is_same		= nft_ipv4_is_same,
451 	.parse_meta		= nft_ipv4_parse_meta,
452 	.parse_payload		= nft_ipv4_parse_payload,
453 	.parse_immediate	= nft_ipv4_parse_immediate,
454 	.print_header		= print_header,
455 	.print_rule		= nft_ipv4_print_rule,
456 	.save_rule		= nft_ipv4_save_rule,
457 	.save_chain		= nft_ipv46_save_chain,
458 	.proto_parse		= nft_ipv4_proto_parse,
459 	.post_parse		= nft_ipv4_post_parse,
460 	.parse_target		= nft_ipv46_parse_target,
461 	.rule_to_cs		= nft_rule_to_iptables_command_state,
462 	.clear_cs		= nft_clear_iptables_command_state,
463 	.xlate			= nft_ipv4_xlate,
464 };
465