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
33extern struct nft_family_ops nft_family_ops_ipv4;
34extern struct nft_family_ops nft_family_ops_ipv6;
35extern struct nft_family_ops nft_family_ops_arp;
36extern struct nft_family_ops nft_family_ops_bridge;
37
38void 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
52void 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 */
69void 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
86static 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
104void 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
119void 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
124void 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
129void 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
134void 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
147void 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
160void 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
169void 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
176bool 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
210int 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
274static 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
290void 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
323void 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
369void 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
384void 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
397void 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
404void 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
411void 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
427void 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
448void 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
454void 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
485void 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
562void 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
606void 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
633void 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
672static void
673print_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
696void 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
725void save_counters(uint64_t pcnt, uint64_t bcnt)
726{
727	printf("[%llu:%llu] ", (unsigned long long)pcnt,
728			       (unsigned long long)bcnt);
729}
730
731void 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
762void 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
782struct 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
800bool 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
836bool 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
853bool 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