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