• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 2012-2013 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 #include <stdlib.h>
16 #include <stdbool.h>
17 #include <netdb.h>
18 #include <errno.h>
19 #include <inttypes.h>
20 
21 #include <xtables.h>
22 
23 #include <linux/netfilter/xt_comment.h>
24 #include <linux/netfilter/xt_limit.h>
25 
26 #include <libmnl/libmnl.h>
27 #include <libnftnl/rule.h>
28 #include <libnftnl/expr.h>
29 
30 #include "nft-shared.h"
31 #include "nft-bridge.h"
32 #include "xshared.h"
33 #include "nft.h"
34 
35 extern struct nft_family_ops nft_family_ops_ipv4;
36 extern struct nft_family_ops nft_family_ops_ipv6;
37 extern struct nft_family_ops nft_family_ops_arp;
38 extern struct nft_family_ops nft_family_ops_bridge;
39 
add_meta(struct nftnl_rule * r,uint32_t key)40 void add_meta(struct nftnl_rule *r, uint32_t key)
41 {
42 	struct nftnl_expr *expr;
43 
44 	expr = nftnl_expr_alloc("meta");
45 	if (expr == NULL)
46 		return;
47 
48 	nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, key);
49 	nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, NFT_REG_1);
50 
51 	nftnl_rule_add_expr(r, expr);
52 }
53 
add_payload(struct nftnl_rule * r,int offset,int len,uint32_t base)54 void add_payload(struct nftnl_rule *r, int offset, int len, uint32_t base)
55 {
56 	struct nftnl_expr *expr;
57 
58 	expr = nftnl_expr_alloc("payload");
59 	if (expr == NULL)
60 		return;
61 
62 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
63 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, NFT_REG_1);
64 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
65 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
66 
67 	nftnl_rule_add_expr(r, expr);
68 }
69 
70 /* bitwise operation is = sreg & mask ^ xor */
add_bitwise_u16(struct nftnl_rule * r,uint16_t mask,uint16_t xor)71 void add_bitwise_u16(struct nftnl_rule *r, uint16_t mask, uint16_t xor)
72 {
73 	struct nftnl_expr *expr;
74 
75 	expr = nftnl_expr_alloc("bitwise");
76 	if (expr == NULL)
77 		return;
78 
79 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_1);
80 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_1);
81 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, sizeof(uint16_t));
82 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t));
83 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t));
84 
85 	nftnl_rule_add_expr(r, expr);
86 }
87 
add_bitwise(struct nftnl_rule * r,uint8_t * mask,size_t len)88 void add_bitwise(struct nftnl_rule *r, uint8_t *mask, size_t len)
89 {
90 	struct nftnl_expr *expr;
91 	uint32_t xor[4] = { 0 };
92 
93 	expr = nftnl_expr_alloc("bitwise");
94 	if (expr == NULL)
95 		return;
96 
97 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_1);
98 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_1);
99 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
100 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
101 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, len);
102 
103 	nftnl_rule_add_expr(r, expr);
104 }
105 
add_cmp_ptr(struct nftnl_rule * r,uint32_t op,void * data,size_t len)106 void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len)
107 {
108 	struct nftnl_expr *expr;
109 
110 	expr = nftnl_expr_alloc("cmp");
111 	if (expr == NULL)
112 		return;
113 
114 	nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, NFT_REG_1);
115 	nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op);
116 	nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, len);
117 
118 	nftnl_rule_add_expr(r, expr);
119 }
120 
add_cmp_u8(struct nftnl_rule * r,uint8_t val,uint32_t op)121 void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op)
122 {
123 	add_cmp_ptr(r, op, &val, sizeof(val));
124 }
125 
add_cmp_u16(struct nftnl_rule * r,uint16_t val,uint32_t op)126 void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op)
127 {
128 	add_cmp_ptr(r, op, &val, sizeof(val));
129 }
130 
add_cmp_u32(struct nftnl_rule * r,uint32_t val,uint32_t op)131 void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op)
132 {
133 	add_cmp_ptr(r, op, &val, sizeof(val));
134 }
135 
add_iniface(struct nftnl_rule * r,char * iface,uint32_t op)136 void add_iniface(struct nftnl_rule *r, char *iface, uint32_t op)
137 {
138 	int iface_len;
139 
140 	iface_len = strlen(iface);
141 
142 	add_meta(r, NFT_META_IIFNAME);
143 	if (iface[iface_len - 1] == '+') {
144 		if (iface_len > 1)
145 			add_cmp_ptr(r, op, iface, iface_len - 1);
146 	} else
147 		add_cmp_ptr(r, op, iface, iface_len + 1);
148 }
149 
add_outiface(struct nftnl_rule * r,char * iface,uint32_t op)150 void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op)
151 {
152 	int iface_len;
153 
154 	iface_len = strlen(iface);
155 
156 	add_meta(r, NFT_META_OIFNAME);
157 	if (iface[iface_len - 1] == '+') {
158 		if (iface_len > 1)
159 			add_cmp_ptr(r, op, iface, iface_len - 1);
160 	} else
161 		add_cmp_ptr(r, op, iface, iface_len + 1);
162 }
163 
add_addr(struct nftnl_rule * r,enum nft_payload_bases base,int offset,void * data,void * mask,size_t len,uint32_t op)164 void add_addr(struct nftnl_rule *r, enum nft_payload_bases base, int offset,
165 	      void *data, void *mask, size_t len, uint32_t op)
166 {
167 	const unsigned char *m = mask;
168 	bool bitwise = false;
169 	int i;
170 
171 	for (i = 0; i < len; i++) {
172 		if (m[i] != 0xff) {
173 			bitwise = m[i] != 0;
174 			break;
175 		}
176 	}
177 
178 	if (!bitwise)
179 		len = i;
180 
181 	add_payload(r, offset, len, base);
182 
183 	if (bitwise)
184 		add_bitwise(r, mask, len);
185 
186 	add_cmp_ptr(r, op, data, len);
187 }
188 
add_proto(struct nftnl_rule * r,int offset,size_t len,uint8_t proto,uint32_t op)189 void add_proto(struct nftnl_rule *r, int offset, size_t len,
190 	       uint8_t proto, uint32_t op)
191 {
192 	add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER);
193 	add_cmp_u8(r, proto, op);
194 }
195 
add_l4proto(struct nftnl_rule * r,uint8_t proto,uint32_t op)196 void add_l4proto(struct nftnl_rule *r, uint8_t proto, uint32_t op)
197 {
198 	add_meta(r, NFT_META_L4PROTO);
199 	add_cmp_u8(r, proto, op);
200 }
201 
is_same_interfaces(const char * a_iniface,const char * a_outiface,unsigned const char * a_iniface_mask,unsigned const char * a_outiface_mask,const char * b_iniface,const char * b_outiface,unsigned const char * b_iniface_mask,unsigned const char * b_outiface_mask)202 bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
203 			unsigned const char *a_iniface_mask,
204 			unsigned const char *a_outiface_mask,
205 			const char *b_iniface, const char *b_outiface,
206 			unsigned const char *b_iniface_mask,
207 			unsigned const char *b_outiface_mask)
208 {
209 	int i;
210 
211 	for (i = 0; i < IFNAMSIZ; i++) {
212 		if (a_iniface_mask[i] != b_iniface_mask[i]) {
213 			DEBUGP("different iniface mask %x, %x (%d)\n",
214 			a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i);
215 			return false;
216 		}
217 		if ((a_iniface[i] & a_iniface_mask[i])
218 		    != (b_iniface[i] & b_iniface_mask[i])) {
219 			DEBUGP("different iniface\n");
220 			return false;
221 		}
222 		if (a_outiface_mask[i] != b_outiface_mask[i]) {
223 			DEBUGP("different outiface mask\n");
224 			return false;
225 		}
226 		if ((a_outiface[i] & a_outiface_mask[i])
227 		    != (b_outiface[i] & b_outiface_mask[i])) {
228 			DEBUGP("different outiface\n");
229 			return false;
230 		}
231 	}
232 
233 	return true;
234 }
235 
parse_ifname(const char * name,unsigned int len,char * dst,unsigned char * mask)236 static void parse_ifname(const char *name, unsigned int len, char *dst, unsigned char *mask)
237 {
238 	if (len == 0)
239 		return;
240 
241 	memcpy(dst, name, len);
242 	if (name[len - 1] == '\0') {
243 		if (mask)
244 			memset(mask, 0xff, len);
245 		return;
246 	}
247 
248 	if (len >= IFNAMSIZ)
249 		return;
250 
251 	/* wildcard */
252 	dst[len++] = '+';
253 	if (len >= IFNAMSIZ)
254 		return;
255 	dst[len++] = 0;
256 	if (mask)
257 		memset(mask, 0xff, len - 2);
258 }
259 
parse_meta(struct nftnl_expr * e,uint8_t key,char * iniface,unsigned char * iniface_mask,char * outiface,unsigned char * outiface_mask,uint8_t * invflags)260 int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface,
261 		unsigned char *iniface_mask, char *outiface,
262 		unsigned char *outiface_mask, uint8_t *invflags)
263 {
264 	uint32_t value;
265 	const void *ifname;
266 	uint32_t len;
267 
268 	switch(key) {
269 	case NFT_META_IIF:
270 		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
271 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
272 			*invflags |= IPT_INV_VIA_IN;
273 
274 		if_indextoname(value, iniface);
275 
276 		memset(iniface_mask, 0xff, strlen(iniface)+1);
277 		break;
278 	case NFT_META_OIF:
279 		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
280 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
281 			*invflags |= IPT_INV_VIA_OUT;
282 
283 		if_indextoname(value, outiface);
284 
285 		memset(outiface_mask, 0xff, strlen(outiface)+1);
286 		break;
287 	case NFT_META_BRI_IIFNAME:
288 	case NFT_META_IIFNAME:
289 		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
290 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
291 			*invflags |= IPT_INV_VIA_IN;
292 
293 		parse_ifname(ifname, len, iniface, iniface_mask);
294 		break;
295 	case NFT_META_BRI_OIFNAME:
296 	case NFT_META_OIFNAME:
297 		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
298 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
299 			*invflags |= IPT_INV_VIA_OUT;
300 
301 		parse_ifname(ifname, len, outiface, outiface_mask);
302 		break;
303 	default:
304 		return -1;
305 	}
306 
307 	return 0;
308 }
309 
nft_parse_target(struct nft_xt_ctx * ctx,struct nftnl_expr * e)310 static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
311 {
312 	uint32_t tg_len;
313 	const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
314 	const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
315 	struct xtables_target *target;
316 	struct xt_entry_target *t;
317 	size_t size;
318 	void *data = ctx->cs;
319 
320 	target = xtables_find_target(targname, XTF_TRY_LOAD);
321 	if (target == NULL)
322 		return;
323 
324 	size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
325 
326 	t = xtables_calloc(1, size);
327 	memcpy(&t->data, targinfo, tg_len);
328 	t->u.target_size = size;
329 	t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
330 	strcpy(t->u.user.name, target->name);
331 
332 	target->t = t;
333 
334 	ctx->h->ops->parse_target(target, data);
335 }
336 
nft_parse_match(struct nft_xt_ctx * ctx,struct nftnl_expr * e)337 static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
338 {
339 	uint32_t mt_len;
340 	const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
341 	const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
342 	struct xtables_match *match;
343 	struct xtables_rule_match **matches;
344 	struct xt_entry_match *m;
345 
346 	switch (ctx->h->family) {
347 	case NFPROTO_IPV4:
348 	case NFPROTO_IPV6:
349 	case NFPROTO_BRIDGE:
350 		matches = &ctx->cs->matches;
351 		break;
352 	default:
353 		fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
354 			ctx->h->family);
355 		exit(EXIT_FAILURE);
356 	}
357 
358 	match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
359 	if (match == NULL)
360 		return;
361 
362 	m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
363 	memcpy(&m->data, mt_info, mt_len);
364 	m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
365 	m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
366 	strcpy(m->u.user.name, match->name);
367 
368 	match->m = m;
369 
370 	if (ctx->h->ops->parse_match != NULL)
371 		ctx->h->ops->parse_match(match, ctx->cs);
372 }
373 
print_proto(uint16_t proto,int invert)374 void print_proto(uint16_t proto, int invert)
375 {
376 	const struct protoent *pent = getprotobynumber(proto);
377 
378 	if (invert)
379 		printf("! ");
380 
381 	if (pent) {
382 		printf("-p %s ", pent->p_name);
383 		return;
384 	}
385 
386 	printf("-p %u ", proto);
387 }
388 
get_cmp_data(struct nftnl_expr * e,void * data,size_t dlen,bool * inv)389 void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
390 {
391 	uint32_t len;
392 	uint8_t op;
393 
394 	memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen);
395 	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
396 	if (op == NFT_CMP_NEQ)
397 		*inv = true;
398 	else
399 		*inv = false;
400 }
401 
nft_meta_set_to_target(struct nft_xt_ctx * ctx)402 static void nft_meta_set_to_target(struct nft_xt_ctx *ctx)
403 {
404 	struct xtables_target *target;
405 	struct xt_entry_target *t;
406 	unsigned int size;
407 	const char *targname;
408 
409 	switch (ctx->meta.key) {
410 	case NFT_META_NFTRACE:
411 		if (ctx->immediate.data[0] == 0)
412 			return;
413 		targname = "TRACE";
414 		break;
415 	default:
416 		return;
417 	}
418 
419 	target = xtables_find_target(targname, XTF_TRY_LOAD);
420 	if (target == NULL)
421 		return;
422 
423 	size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size;
424 
425 	t = xtables_calloc(1, size);
426 	t->u.target_size = size;
427 	t->u.user.revision = target->revision;
428 	strcpy(t->u.user.name, targname);
429 
430 	target->t = t;
431 
432 	ctx->h->ops->parse_target(target, ctx->cs);
433 }
434 
nft_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e)435 static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
436 {
437 	ctx->meta.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
438 
439 	if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG) &&
440 	    (ctx->flags & NFT_XT_CTX_IMMEDIATE) &&
441 	     nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG) == ctx->immediate.reg) {
442 		ctx->flags &= ~NFT_XT_CTX_IMMEDIATE;
443 		nft_meta_set_to_target(ctx);
444 		return;
445 	}
446 
447 	ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
448 	ctx->flags |= NFT_XT_CTX_META;
449 }
450 
nft_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e)451 static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
452 {
453 	if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
454 		memcpy(&ctx->prev_payload, &ctx->payload,
455 		       sizeof(ctx->prev_payload));
456 		ctx->flags |= NFT_XT_CTX_PREV_PAYLOAD;
457 	}
458 
459 	ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
460 	ctx->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
461 	ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
462 	ctx->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
463 	ctx->flags |= NFT_XT_CTX_PAYLOAD;
464 }
465 
nft_parse_bitwise(struct nft_xt_ctx * ctx,struct nftnl_expr * e)466 static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
467 {
468 	uint32_t reg, len;
469 	const void *data;
470 
471 	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
472 	if (ctx->reg && reg != ctx->reg)
473 		return;
474 
475 	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
476 	memcpy(ctx->bitwise.xor, data, len);
477 	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
478 	memcpy(ctx->bitwise.mask, data, len);
479 	ctx->flags |= NFT_XT_CTX_BITWISE;
480 }
481 
nft_parse_cmp(struct nft_xt_ctx * ctx,struct nftnl_expr * e)482 static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
483 {
484 	void *data = ctx->cs;
485 	uint32_t reg;
486 
487 	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
488 	if (ctx->reg && reg != ctx->reg)
489 		return;
490 
491 	if (ctx->flags & NFT_XT_CTX_META) {
492 		ctx->h->ops->parse_meta(ctx, e, data);
493 		ctx->flags &= ~NFT_XT_CTX_META;
494 	}
495 	/* bitwise context is interpreted from payload */
496 	if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
497 		ctx->h->ops->parse_payload(ctx, e, data);
498 		ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
499 	}
500 }
501 
nft_parse_counter(struct nftnl_expr * e,struct xt_counters * counters)502 static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
503 {
504 	counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
505 	counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
506 }
507 
nft_parse_immediate(struct nft_xt_ctx * ctx,struct nftnl_expr * e)508 static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
509 {
510 	const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
511 	const char *jumpto = NULL;
512 	bool nft_goto = false;
513 	void *data = ctx->cs;
514 	int verdict;
515 
516 	if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
517 		const void *imm_data;
518 		uint32_t len;
519 
520 		imm_data = nftnl_expr_get_data(e, NFTNL_EXPR_IMM_DATA, &len);
521 
522 		if (len > sizeof(ctx->immediate.data))
523 			return;
524 
525 		memcpy(ctx->immediate.data, imm_data, len);
526 		ctx->immediate.len = len;
527 		ctx->immediate.reg = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG);
528 		ctx->flags |= NFT_XT_CTX_IMMEDIATE;
529 		return;
530 	}
531 
532 	verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
533 	/* Standard target? */
534 	switch(verdict) {
535 	case NF_ACCEPT:
536 		jumpto = "ACCEPT";
537 		break;
538 	case NF_DROP:
539 		jumpto = "DROP";
540 		break;
541 	case NFT_RETURN:
542 		jumpto = "RETURN";
543 		break;;
544 	case NFT_GOTO:
545 		nft_goto = true;
546 		/* fall through */
547 	case NFT_JUMP:
548 		jumpto = chain;
549 		break;
550 	}
551 
552 	ctx->h->ops->parse_immediate(jumpto, nft_goto, data);
553 }
554 
nft_parse_limit(struct nft_xt_ctx * ctx,struct nftnl_expr * e)555 static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
556 {
557 	__u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
558 	__u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
559 	__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
560 	struct xtables_rule_match **matches;
561 	struct xtables_match *match;
562 	struct xt_rateinfo *rinfo;
563 	size_t size;
564 
565 	switch (ctx->h->family) {
566 	case NFPROTO_IPV4:
567 	case NFPROTO_IPV6:
568 	case NFPROTO_BRIDGE:
569 		matches = &ctx->cs->matches;
570 		break;
571 	default:
572 		fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
573 			ctx->h->family);
574 		exit(EXIT_FAILURE);
575 	}
576 
577 	match = xtables_find_match("limit", XTF_TRY_LOAD, matches);
578 	if (match == NULL)
579 		return;
580 
581 	size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
582 	match->m = xtables_calloc(1, size);
583 	match->m->u.match_size = size;
584 	strcpy(match->m->u.user.name, match->name);
585 	match->m->u.user.revision = match->revision;
586 	xs_init_match(match);
587 
588 	rinfo = (void *)match->m->data;
589 	rinfo->avg = XT_LIMIT_SCALE * unit / rate;
590 	rinfo->burst = burst;
591 
592 	if (ctx->h->ops->parse_match != NULL)
593 		ctx->h->ops->parse_match(match, ctx->cs);
594 }
595 
nft_parse_lookup(struct nft_xt_ctx * ctx,struct nft_handle * h,struct nftnl_expr * e)596 static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
597 			     struct nftnl_expr *e)
598 {
599 	if (ctx->h->ops->parse_lookup)
600 		ctx->h->ops->parse_lookup(ctx, e, NULL);
601 }
602 
nft_rule_to_iptables_command_state(struct nft_handle * h,const struct nftnl_rule * r,struct iptables_command_state * cs)603 void nft_rule_to_iptables_command_state(struct nft_handle *h,
604 					const struct nftnl_rule *r,
605 					struct iptables_command_state *cs)
606 {
607 	struct nftnl_expr_iter *iter;
608 	struct nftnl_expr *expr;
609 	struct nft_xt_ctx ctx = {
610 		.cs = cs,
611 		.h = h,
612 		.table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
613 	};
614 
615 	iter = nftnl_expr_iter_create(r);
616 	if (iter == NULL)
617 		return;
618 
619 	ctx.iter = iter;
620 	expr = nftnl_expr_iter_next(iter);
621 	while (expr != NULL) {
622 		const char *name =
623 			nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
624 
625 		if (strcmp(name, "counter") == 0)
626 			nft_parse_counter(expr, &ctx.cs->counters);
627 		else if (strcmp(name, "payload") == 0)
628 			nft_parse_payload(&ctx, expr);
629 		else if (strcmp(name, "meta") == 0)
630 			nft_parse_meta(&ctx, expr);
631 		else if (strcmp(name, "bitwise") == 0)
632 			nft_parse_bitwise(&ctx, expr);
633 		else if (strcmp(name, "cmp") == 0)
634 			nft_parse_cmp(&ctx, expr);
635 		else if (strcmp(name, "immediate") == 0)
636 			nft_parse_immediate(&ctx, expr);
637 		else if (strcmp(name, "match") == 0)
638 			nft_parse_match(&ctx, expr);
639 		else if (strcmp(name, "target") == 0)
640 			nft_parse_target(&ctx, expr);
641 		else if (strcmp(name, "limit") == 0)
642 			nft_parse_limit(&ctx, expr);
643 		else if (strcmp(name, "lookup") == 0)
644 			nft_parse_lookup(&ctx, h, expr);
645 
646 		expr = nftnl_expr_iter_next(iter);
647 	}
648 
649 	nftnl_expr_iter_destroy(iter);
650 
651 	if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
652 		const void *data;
653 		uint32_t len, size;
654 		const char *comment;
655 
656 		data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
657 		comment = get_comment(data, len);
658 		if (comment) {
659 			struct xtables_match *match;
660 			struct xt_entry_match *m;
661 
662 			match = xtables_find_match("comment", XTF_TRY_LOAD,
663 						   &cs->matches);
664 			if (match == NULL)
665 				return;
666 
667 			size = XT_ALIGN(sizeof(struct xt_entry_match))
668 				+ match->size;
669 			m = xtables_calloc(1, size);
670 
671 			strncpy((char *)m->data, comment, match->size - 1);
672 			m->u.match_size = size;
673 			m->u.user.revision = 0;
674 			strcpy(m->u.user.name, match->name);
675 
676 			match->m = m;
677 		}
678 	}
679 
680 	if (cs->target != NULL) {
681 		cs->jumpto = cs->target->name;
682 	} else if (cs->jumpto != NULL) {
683 		struct xt_entry_target *t;
684 		uint32_t size;
685 
686 		cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
687 		if (!cs->target)
688 			return;
689 
690 		size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
691 		t = xtables_calloc(1, size);
692 		t->u.target_size = size;
693 		t->u.user.revision = cs->target->revision;
694 		strcpy(t->u.user.name, cs->jumpto);
695 		cs->target->t = t;
696 	} else {
697 		cs->jumpto = "";
698 	}
699 }
700 
nft_clear_iptables_command_state(struct iptables_command_state * cs)701 void nft_clear_iptables_command_state(struct iptables_command_state *cs)
702 {
703 	xtables_rule_matches_free(&cs->matches);
704 	if (cs->target) {
705 		free(cs->target->t);
706 		cs->target->t = NULL;
707 
708 		if (cs->target == cs->target->next) {
709 			free(cs->target);
710 			cs->target = NULL;
711 		}
712 	}
713 }
714 
print_header(unsigned int format,const char * chain,const char * pol,const struct xt_counters * counters,bool basechain,uint32_t refs,uint32_t entries)715 void print_header(unsigned int format, const char *chain, const char *pol,
716 		  const struct xt_counters *counters, bool basechain,
717 		  uint32_t refs, uint32_t entries)
718 {
719 	printf("Chain %s", chain);
720 	if (basechain) {
721 		printf(" (policy %s", pol);
722 		if (!(format & FMT_NOCOUNTS)) {
723 			fputc(' ', stdout);
724 			xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
725 			fputs("packets, ", stdout);
726 			xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
727 			fputs("bytes", stdout);
728 		}
729 		printf(")\n");
730 	} else {
731 		printf(" (%u references)\n", refs);
732 	}
733 
734 	if (format & FMT_LINENUMBERS)
735 		printf(FMT("%-4s ", "%s "), "num");
736 	if (!(format & FMT_NOCOUNTS)) {
737 		if (format & FMT_KILOMEGAGIGA) {
738 			printf(FMT("%5s ","%s "), "pkts");
739 			printf(FMT("%5s ","%s "), "bytes");
740 		} else {
741 			printf(FMT("%8s ","%s "), "pkts");
742 			printf(FMT("%10s ","%s "), "bytes");
743 		}
744 	}
745 	if (!(format & FMT_NOTARGET))
746 		printf(FMT("%-9s ","%s "), "target");
747 	fputs(" prot ", stdout);
748 	if (format & FMT_OPTIONS)
749 		fputs("opt", stdout);
750 	if (format & FMT_VIA) {
751 		printf(FMT(" %-6s ","%s "), "in");
752 		printf(FMT("%-6s ","%s "), "out");
753 	}
754 	printf(FMT(" %-19s ","%s "), "source");
755 	printf(FMT(" %-19s "," %s "), "destination");
756 	printf("\n");
757 }
758 
print_rule_details(const struct iptables_command_state * cs,const char * targname,uint8_t flags,uint8_t invflags,uint8_t proto,unsigned int num,unsigned int format)759 void print_rule_details(const struct iptables_command_state *cs,
760 			const char *targname, uint8_t flags,
761 			uint8_t invflags, uint8_t proto,
762 			unsigned int num, unsigned int format)
763 {
764 	if (format & FMT_LINENUMBERS)
765 		printf(FMT("%-4u ", "%u "), num);
766 
767 	if (!(format & FMT_NOCOUNTS)) {
768 		xtables_print_num(cs->counters.pcnt, format);
769 		xtables_print_num(cs->counters.bcnt, format);
770 	}
771 
772 	if (!(format & FMT_NOTARGET))
773 		printf(FMT("%-9s ", "%s "), targname ? targname : "");
774 
775 	fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout);
776 	{
777 		const char *pname =
778 			proto_to_name(proto, format&FMT_NUMERIC);
779 		if (pname)
780 			printf(FMT("%-5s", "%s "), pname);
781 		else
782 			printf(FMT("%-5hu", "%hu "), proto);
783 	}
784 }
785 
786 static void
print_iface(char letter,const char * iface,const unsigned char * mask,int inv)787 print_iface(char letter, const char *iface, const unsigned char *mask, int inv)
788 {
789 	unsigned int i;
790 
791 	if (mask[0] == 0)
792 		return;
793 
794 	printf("%s-%c ", inv ? "! " : "", letter);
795 
796 	for (i = 0; i < IFNAMSIZ; i++) {
797 		if (mask[i] != 0) {
798 			if (iface[i] != '\0')
799 				printf("%c", iface[i]);
800 			} else {
801 				if (iface[i-1] != '\0')
802 					printf("+");
803 				break;
804 		}
805 	}
806 
807 	printf(" ");
808 }
809 
save_rule_details(const struct iptables_command_state * cs,uint8_t invflags,uint16_t proto,const char * iniface,unsigned const char * iniface_mask,const char * outiface,unsigned const char * outiface_mask)810 void save_rule_details(const struct iptables_command_state *cs,
811 		       uint8_t invflags, uint16_t proto,
812 		       const char *iniface,
813 		       unsigned const char *iniface_mask,
814 		       const char *outiface,
815 		       unsigned const char *outiface_mask)
816 {
817 	if (iniface != NULL) {
818 		print_iface('i', iniface, iniface_mask,
819 			    invflags & IPT_INV_VIA_IN);
820 	}
821 	if (outiface != NULL) {
822 		print_iface('o', outiface, outiface_mask,
823 			    invflags & IPT_INV_VIA_OUT);
824 	}
825 
826 	if (proto > 0) {
827 		const struct protoent *pent = getprotobynumber(proto);
828 
829 		if (invflags & XT_INV_PROTO)
830 			printf("! ");
831 
832 		if (pent)
833 			printf("-p %s ", pent->p_name);
834 		else
835 			printf("-p %u ", proto);
836 	}
837 }
838 
nft_ipv46_save_chain(const struct nftnl_chain * c,const char * policy)839 void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
840 {
841 	const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
842 	uint64_t pkts = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS);
843 	uint64_t bytes = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES);
844 
845 	printf(":%s %s [%"PRIu64":%"PRIu64"]\n",
846 	       chain, policy ?: "-", pkts, bytes);
847 }
848 
save_matches_and_target(const struct iptables_command_state * cs,bool goto_flag,const void * fw,unsigned int format)849 void save_matches_and_target(const struct iptables_command_state *cs,
850 			     bool goto_flag, const void *fw,
851 			     unsigned int format)
852 {
853 	struct xtables_rule_match *matchp;
854 
855 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
856 		if (matchp->match->alias) {
857 			printf("-m %s",
858 			       matchp->match->alias(matchp->match->m));
859 		} else
860 			printf("-m %s", matchp->match->name);
861 
862 		if (matchp->match->save != NULL) {
863 			/* cs->fw union makes the trick */
864 			matchp->match->save(fw, matchp->match->m);
865 		}
866 		printf(" ");
867 	}
868 
869 	if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS)
870 		printf("-c %llu %llu ",
871 		       (unsigned long long)cs->counters.pcnt,
872 		       (unsigned long long)cs->counters.bcnt);
873 
874 	if (cs->target != NULL) {
875 		if (cs->target->alias) {
876 			printf("-j %s", cs->target->alias(cs->target->t));
877 		} else
878 			printf("-j %s", cs->jumpto);
879 
880 		if (cs->target->save != NULL)
881 			cs->target->save(fw, cs->target->t);
882 	} else if (strlen(cs->jumpto) > 0) {
883 		printf("-%c %s", goto_flag ? 'g' : 'j', cs->jumpto);
884 	}
885 
886 	printf("\n");
887 }
888 
print_matches_and_target(struct iptables_command_state * cs,unsigned int format)889 void print_matches_and_target(struct iptables_command_state *cs,
890 			      unsigned int format)
891 {
892 	struct xtables_rule_match *matchp;
893 
894 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
895 		if (matchp->match->print != NULL) {
896 			matchp->match->print(&cs->fw, matchp->match->m,
897 					     format & FMT_NUMERIC);
898 		}
899 	}
900 
901 	if (cs->target != NULL) {
902 		if (cs->target->print != NULL) {
903 			cs->target->print(&cs->fw, cs->target->t,
904 					  format & FMT_NUMERIC);
905 		}
906 	}
907 }
908 
nft_family_ops_lookup(int family)909 struct nft_family_ops *nft_family_ops_lookup(int family)
910 {
911 	switch (family) {
912 	case AF_INET:
913 		return &nft_family_ops_ipv4;
914 	case AF_INET6:
915 		return &nft_family_ops_ipv6;
916 	case NFPROTO_ARP:
917 		return &nft_family_ops_arp;
918 	case NFPROTO_BRIDGE:
919 		return &nft_family_ops_bridge;
920 	default:
921 		break;
922 	}
923 
924 	return NULL;
925 }
926 
compare_matches(struct xtables_rule_match * mt1,struct xtables_rule_match * mt2)927 bool compare_matches(struct xtables_rule_match *mt1,
928 		     struct xtables_rule_match *mt2)
929 {
930 	struct xtables_rule_match *mp1;
931 	struct xtables_rule_match *mp2;
932 
933 	for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) {
934 		struct xt_entry_match *m1 = mp1->match->m;
935 		struct xt_entry_match *m2 = mp2->match->m;
936 
937 		if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
938 			DEBUGP("mismatching match name\n");
939 			return false;
940 		}
941 
942 		if (m1->u.user.match_size != m2->u.user.match_size) {
943 			DEBUGP("mismatching match size\n");
944 			return false;
945 		}
946 
947 		if (memcmp(m1->data, m2->data,
948 			   mp1->match->userspacesize) != 0) {
949 			DEBUGP("mismatch match data\n");
950 			return false;
951 		}
952 	}
953 
954 	/* Both cursors should be NULL */
955 	if (mp1 != mp2) {
956 		DEBUGP("mismatch matches amount\n");
957 		return false;
958 	}
959 
960 	return true;
961 }
962 
compare_targets(struct xtables_target * tg1,struct xtables_target * tg2)963 bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
964 {
965 	if (tg1 == NULL && tg2 == NULL)
966 		return true;
967 
968 	if (tg1 == NULL || tg2 == NULL)
969 		return false;
970 	if (tg1->userspacesize != tg2->userspacesize)
971 		return false;
972 
973 	if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
974 		return false;
975 
976 	if (memcmp(tg1->t->data, tg2->t->data, tg1->userspacesize) != 0)
977 		return false;
978 
979 	return true;
980 }
981 
nft_ipv46_parse_target(struct xtables_target * t,void * data)982 void nft_ipv46_parse_target(struct xtables_target *t, void *data)
983 {
984 	struct iptables_command_state *cs = data;
985 
986 	cs->target = t;
987 }
988 
nft_check_xt_legacy(int family,bool is_ipt_save)989 void nft_check_xt_legacy(int family, bool is_ipt_save)
990 {
991 	static const char tables6[] = "/proc/net/ip6_tables_names";
992 	static const char tables4[] = "/proc/net/ip_tables_names";
993 	const char *prefix = "ip";
994 	FILE *fp = NULL;
995 	char buf[1024];
996 
997 	switch (family) {
998 	case NFPROTO_IPV4:
999 		fp = fopen(tables4, "r");
1000 		break;
1001 	case NFPROTO_IPV6:
1002 		fp = fopen(tables6, "r");
1003 		prefix = "ip6";
1004 		break;
1005 	default:
1006 		break;
1007 	}
1008 
1009 	if (!fp)
1010 		return;
1011 
1012 	if (fgets(buf, sizeof(buf), fp))
1013 		fprintf(stderr, "# Warning: %stables-legacy tables present, use %stables-legacy%s to see them\n",
1014 			prefix, prefix, is_ipt_save ? "-save" : "");
1015 	fclose(fp);
1016 }
1017