xref: /aosp_15_r20/external/iptables/iptables/nft-shared.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  * (C) 2012-2013 by Pablo Neira Ayuso <[email protected]>
3  * (C) 2013 by Tomasz Bursztyka <[email protected]>
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 <assert.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdbool.h>
18 #include <netdb.h>
19 #include <errno.h>
20 #include <inttypes.h>
21 
22 #include <xtables.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 
xt_nftnl_expr_alloc(const char * name)38 static struct nftnl_expr *xt_nftnl_expr_alloc(const char *name)
39 {
40 	struct nftnl_expr *expr = nftnl_expr_alloc(name);
41 
42 	if (expr)
43 		return expr;
44 
45 	xtables_error(RESOURCE_PROBLEM,
46 		      "Failed to allocate nftnl expression '%s'", name);
47 }
48 
add_meta(struct nft_handle * h,struct nftnl_rule * r,uint32_t key,uint8_t * dreg)49 void add_meta(struct nft_handle *h, struct nftnl_rule *r, uint32_t key,
50 	      uint8_t *dreg)
51 {
52 	struct nftnl_expr *expr;
53 	uint8_t reg;
54 
55 	expr = xt_nftnl_expr_alloc("meta");
56 
57 	reg = NFT_REG_1;
58 	nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, key);
59 	nftnl_expr_set_u32(expr, NFTNL_EXPR_META_DREG, reg);
60 	nftnl_rule_add_expr(r, expr);
61 
62 	*dreg = reg;
63 }
64 
add_payload(struct nft_handle * h,struct nftnl_rule * r,int offset,int len,uint32_t base,uint8_t * dreg)65 void add_payload(struct nft_handle *h, struct nftnl_rule *r,
66 		 int offset, int len, uint32_t base, uint8_t *dreg)
67 {
68 	struct nftnl_expr *expr;
69 	uint8_t reg;
70 
71 	expr = xt_nftnl_expr_alloc("payload");
72 
73 	reg = NFT_REG_1;
74 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_BASE, base);
75 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_DREG, reg);
76 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
77 	nftnl_expr_set_u32(expr, NFTNL_EXPR_PAYLOAD_LEN, len);
78 	nftnl_rule_add_expr(r, expr);
79 
80 	*dreg = reg;
81 }
82 
83 /* bitwise operation is = sreg & mask ^ xor */
add_bitwise_u16(struct nft_handle * h,struct nftnl_rule * r,uint16_t mask,uint16_t xor,uint8_t sreg,uint8_t * dreg)84 void add_bitwise_u16(struct nft_handle *h, struct nftnl_rule *r,
85 		     uint16_t mask, uint16_t xor, uint8_t sreg, uint8_t *dreg)
86 {
87 	struct nftnl_expr *expr;
88 	uint8_t reg;
89 
90 	expr = xt_nftnl_expr_alloc("bitwise");
91 
92 	reg = NFT_REG_1;
93 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, sreg);
94 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg);
95 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, sizeof(uint16_t));
96 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t));
97 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t));
98 	nftnl_rule_add_expr(r, expr);
99 
100 	*dreg = reg;
101 }
102 
add_bitwise(struct nft_handle * h,struct nftnl_rule * r,uint8_t * mask,size_t len,uint8_t sreg,uint8_t * dreg)103 void add_bitwise(struct nft_handle *h, struct nftnl_rule *r,
104 		 uint8_t *mask, size_t len, uint8_t sreg, uint8_t *dreg)
105 {
106 	struct nftnl_expr *expr;
107 	uint32_t xor[4] = { 0 };
108 	uint8_t reg = *dreg;
109 
110 	expr = xt_nftnl_expr_alloc("bitwise");
111 
112 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, sreg);
113 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, reg);
114 	nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, len);
115 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_MASK, mask, len);
116 	nftnl_expr_set(expr, NFTNL_EXPR_BITWISE_XOR, &xor, len);
117 	nftnl_rule_add_expr(r, expr);
118 
119 	*dreg = reg;
120 }
121 
add_cmp_ptr(struct nftnl_rule * r,uint32_t op,void * data,size_t len,uint8_t sreg)122 void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len,
123 		 uint8_t sreg)
124 {
125 	struct nftnl_expr *expr;
126 
127 	expr = xt_nftnl_expr_alloc("cmp");
128 
129 	nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_SREG, sreg);
130 	nftnl_expr_set_u32(expr, NFTNL_EXPR_CMP_OP, op);
131 	nftnl_expr_set(expr, NFTNL_EXPR_CMP_DATA, data, len);
132 	nftnl_rule_add_expr(r, expr);
133 }
134 
add_cmp_u8(struct nftnl_rule * r,uint8_t val,uint32_t op,uint8_t sreg)135 void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op, uint8_t sreg)
136 {
137 	add_cmp_ptr(r, op, &val, sizeof(val), sreg);
138 }
139 
add_cmp_u16(struct nftnl_rule * r,uint16_t val,uint32_t op,uint8_t sreg)140 void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op, uint8_t sreg)
141 {
142 	add_cmp_ptr(r, op, &val, sizeof(val), sreg);
143 }
144 
add_cmp_u32(struct nftnl_rule * r,uint32_t val,uint32_t op,uint8_t sreg)145 void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op, uint8_t sreg)
146 {
147 	add_cmp_ptr(r, op, &val, sizeof(val), sreg);
148 }
149 
add_iface(struct nft_handle * h,struct nftnl_rule * r,char * iface,uint32_t key,uint32_t op)150 void add_iface(struct nft_handle *h, struct nftnl_rule *r,
151 	       char *iface, uint32_t key, uint32_t op)
152 {
153 	int iface_len = strlen(iface);
154 	uint8_t reg;
155 
156 
157 	if (iface[iface_len - 1] == '+') {
158 		if (iface_len > 1) {
159 			iface_len -= 1;
160 		} else if (op != NFT_CMP_EQ) {
161 			op = NFT_CMP_EQ;
162 			iface = "INVAL/D";
163 			iface_len = strlen(iface) + 1;
164 		} else {
165 			return; /* -o + */
166 		}
167 	} else {
168 		iface_len += 1;
169 	}
170 
171 	add_meta(h, r, key, &reg);
172 	add_cmp_ptr(r, op, iface, iface_len, reg);
173 }
174 
add_addr(struct nft_handle * h,struct nftnl_rule * r,enum nft_payload_bases base,int offset,void * data,void * mask,size_t len,uint32_t op)175 void add_addr(struct nft_handle *h, struct nftnl_rule *r,
176 	      enum nft_payload_bases base, int offset,
177 	      void *data, void *mask, size_t len, uint32_t op)
178 {
179 	const unsigned char *m = mask;
180 	bool bitwise = false;
181 	uint8_t reg;
182 	int i, j;
183 
184 	for (i = 0; i < len; i++) {
185 		if (m[i] != 0xff) {
186 			bitwise = m[i] != 0;
187 			break;
188 		}
189 	}
190 	for (j = i + 1; !bitwise && j < len; j++)
191 		bitwise = !!m[j];
192 
193 	if (!bitwise)
194 		len = i;
195 
196 	add_payload(h, r, offset, len, base, &reg);
197 
198 	if (bitwise)
199 		add_bitwise(h, r, mask, len, reg, &reg);
200 
201 	add_cmp_ptr(r, op, data, len, reg);
202 }
203 
add_proto(struct nft_handle * h,struct nftnl_rule * r,int offset,size_t len,uint8_t proto,uint32_t op)204 void add_proto(struct nft_handle *h, struct nftnl_rule *r,
205 	       int offset, size_t len, uint8_t proto, uint32_t op)
206 {
207 	uint8_t reg;
208 
209 	add_payload(h, r, offset, len, NFT_PAYLOAD_NETWORK_HEADER, &reg);
210 	add_cmp_u8(r, proto, op, reg);
211 }
212 
add_l4proto(struct nft_handle * h,struct nftnl_rule * r,uint8_t proto,uint32_t op)213 void add_l4proto(struct nft_handle *h, struct nftnl_rule *r,
214 		 uint8_t proto, uint32_t op)
215 {
216 	uint8_t reg;
217 
218 	add_meta(h, r, NFT_META_L4PROTO, &reg);
219 	add_cmp_u8(r, proto, op, reg);
220 }
221 
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)222 bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
223 			unsigned const char *a_iniface_mask,
224 			unsigned const char *a_outiface_mask,
225 			const char *b_iniface, const char *b_outiface,
226 			unsigned const char *b_iniface_mask,
227 			unsigned const char *b_outiface_mask)
228 {
229 	int i;
230 
231 	for (i = 0; i < IFNAMSIZ; i++) {
232 		if (a_iniface_mask[i] != b_iniface_mask[i]) {
233 			DEBUGP("different iniface mask %x, %x (%d)\n",
234 			a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i);
235 			return false;
236 		}
237 		if ((a_iniface[i] & a_iniface_mask[i])
238 		    != (b_iniface[i] & b_iniface_mask[i])) {
239 			DEBUGP("different iniface\n");
240 			return false;
241 		}
242 		if (a_outiface_mask[i] != b_outiface_mask[i]) {
243 			DEBUGP("different outiface mask\n");
244 			return false;
245 		}
246 		if ((a_outiface[i] & a_outiface_mask[i])
247 		    != (b_outiface[i] & b_outiface_mask[i])) {
248 			DEBUGP("different outiface\n");
249 			return false;
250 		}
251 	}
252 
253 	return true;
254 }
255 
__get_cmp_data(struct nftnl_expr * e,void * data,size_t dlen,uint8_t * op)256 void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op)
257 {
258 	uint32_t len;
259 
260 	memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen);
261 	*op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
262 }
263 
get_cmp_data(struct nftnl_expr * e,void * data,size_t dlen,bool * inv)264 void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
265 {
266 	uint8_t op;
267 
268 	__get_cmp_data(e, data, dlen, &op);
269 	*inv = (op == NFT_CMP_NEQ);
270 }
271 
nft_ipv46_save_chain(const struct nftnl_chain * c,const char * policy)272 void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
273 {
274 	const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
275 	uint64_t pkts = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS);
276 	uint64_t bytes = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES);
277 
278 	printf(":%s %s [%"PRIu64":%"PRIu64"]\n",
279 	       chain, policy ?: "-", pkts, bytes);
280 }
281 
save_matches_and_target(const struct iptables_command_state * cs,bool goto_flag,const void * fw,unsigned int format)282 void save_matches_and_target(const struct iptables_command_state *cs,
283 			     bool goto_flag, const void *fw,
284 			     unsigned int format)
285 {
286 	struct xtables_rule_match *matchp;
287 
288 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
289 		if (matchp->match->alias) {
290 			printf(" -m %s",
291 			       matchp->match->alias(matchp->match->m));
292 		} else
293 			printf(" -m %s", matchp->match->name);
294 
295 		if (matchp->match->save != NULL) {
296 			/* cs->fw union makes the trick */
297 			matchp->match->save(fw, matchp->match->m);
298 		}
299 	}
300 
301 	if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS)
302 		printf(" -c %llu %llu",
303 		       (unsigned long long)cs->counters.pcnt,
304 		       (unsigned long long)cs->counters.bcnt);
305 
306 	if (cs->target != NULL) {
307 		if (cs->target->alias) {
308 			printf(" -j %s", cs->target->alias(cs->target->t));
309 		} else
310 			printf(" -j %s", cs->jumpto);
311 
312 		if (cs->target->save != NULL) {
313 			cs->target->save(fw, cs->target->t);
314 		}
315 	} else if (strlen(cs->jumpto) > 0) {
316 		printf(" -%c %s", goto_flag ? 'g' : 'j', cs->jumpto);
317 	}
318 
319 	printf("\n");
320 }
321 
print_matches_and_target(struct iptables_command_state * cs,unsigned int format)322 void print_matches_and_target(struct iptables_command_state *cs,
323 			      unsigned int format)
324 {
325 	struct xtables_rule_match *matchp;
326 
327 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
328 		if (matchp->match->print != NULL) {
329 			matchp->match->print(&cs->fw, matchp->match->m,
330 					     format & FMT_NUMERIC);
331 		}
332 	}
333 
334 	if (cs->target != NULL) {
335 		if (cs->target->print != NULL) {
336 			cs->target->print(&cs->fw, cs->target->t,
337 					  format & FMT_NUMERIC);
338 		}
339 	}
340 }
341 
nft_family_ops_lookup(int family)342 struct nft_family_ops *nft_family_ops_lookup(int family)
343 {
344 	switch (family) {
345 	case AF_INET:
346 		return &nft_family_ops_ipv4;
347 	case AF_INET6:
348 		return &nft_family_ops_ipv6;
349 	case NFPROTO_ARP:
350 		return &nft_family_ops_arp;
351 	case NFPROTO_BRIDGE:
352 		return &nft_family_ops_bridge;
353 	default:
354 		break;
355 	}
356 
357 	return NULL;
358 }
359 
compare_matches(struct xtables_rule_match * mt1,struct xtables_rule_match * mt2)360 bool compare_matches(struct xtables_rule_match *mt1,
361 		     struct xtables_rule_match *mt2)
362 {
363 	struct xtables_rule_match *mp1;
364 	struct xtables_rule_match *mp2;
365 
366 	for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) {
367 		struct xt_entry_match *m1 = mp1->match->m;
368 		struct xt_entry_match *m2 = mp2->match->m;
369 		size_t cmplen = mp1->match->userspacesize;
370 
371 		if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
372 			DEBUGP("mismatching match name\n");
373 			return false;
374 		}
375 
376 		if (m1->u.user.match_size != m2->u.user.match_size) {
377 			DEBUGP("mismatching match size\n");
378 			return false;
379 		}
380 
381 		if (!strcmp(m1->u.user.name, "among"))
382 			cmplen = m1->u.match_size - sizeof(*m1);
383 
384 		if (memcmp(m1->data, m2->data, cmplen) != 0) {
385 			DEBUGP("mismatch match data\n");
386 			DEBUG_HEXDUMP("m1->data", m1->data, cmplen);
387 			DEBUG_HEXDUMP("m2->data", m2->data, cmplen);
388 			return false;
389 		}
390 	}
391 
392 	/* Both cursors should be NULL */
393 	if (mp1 != mp2) {
394 		DEBUGP("mismatch matches amount\n");
395 		return false;
396 	}
397 
398 	return true;
399 }
400 
compare_targets(struct xtables_target * tg1,struct xtables_target * tg2)401 bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
402 {
403 	if (tg1 == NULL && tg2 == NULL)
404 		return true;
405 
406 	if (tg1 == NULL || tg2 == NULL)
407 		return false;
408 	if (tg1->userspacesize != tg2->userspacesize)
409 		return false;
410 
411 	if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
412 		return false;
413 
414 	if (memcmp(tg1->t->data, tg2->t->data, tg1->userspacesize) != 0)
415 		return false;
416 
417 	return true;
418 }
419 
nft_check_xt_legacy(int family,bool is_ipt_save)420 void nft_check_xt_legacy(int family, bool is_ipt_save)
421 {
422 	static const char tables6[] = "/proc/net/ip6_tables_names";
423 	static const char tables4[] = "/proc/net/ip_tables_names";
424 	static const char tablesa[] = "/proc/net/arp_tables_names";
425 	const char *prefix = "ip";
426 	FILE *fp = NULL;
427 	char buf[1024];
428 
429 	switch (family) {
430 	case NFPROTO_IPV4:
431 		fp = fopen(tables4, "r");
432 		break;
433 	case NFPROTO_IPV6:
434 		fp = fopen(tables6, "r");
435 		prefix = "ip6";
436 		break;
437 	case NFPROTO_ARP:
438 		fp = fopen(tablesa, "r");
439 		prefix = "arp";
440 		break;
441 	default:
442 		break;
443 	}
444 
445 	if (!fp)
446 		return;
447 
448 	if (fgets(buf, sizeof(buf), fp))
449 		fprintf(stderr, "# Warning: %stables-legacy tables present, use %stables-legacy%s to see them\n",
450 			prefix, prefix, is_ipt_save ? "-save" : "");
451 	fclose(fp);
452 }
453 
nft_get_next_reg(enum nft_registers reg,size_t size)454 enum nft_registers nft_get_next_reg(enum nft_registers reg, size_t size)
455 {
456 	/* convert size to NETLINK_ALIGN-sized chunks */
457 	size = (size + NETLINK_ALIGN - 1) / NETLINK_ALIGN;
458 
459 	/* map 16byte reg to 4byte one */
460 	if (reg < __NFT_REG_MAX)
461 		reg = NFT_REG32_00 + (reg - 1) * NFT_REG_SIZE / NFT_REG32_SIZE;
462 
463 	reg += size;
464 	assert(reg <= NFT_REG32_15);
465 
466 	return reg;
467 }
468