Home | History | Annotate | Download | only in iptables
      1 /*
      2  * (C) 2012-2013 by Pablo Neira Ayuso <pablo (at) netfilter.org>
      3  * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka (at) 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 
     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 
     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 */
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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