xref: /aosp_15_r20/external/iptables/iptables/nft-cache.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  * (C) 2012 by Pablo Neira Ayuso <[email protected]>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published
6  * by the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
10  */
11 
12 #include <assert.h>
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <xtables.h>
17 
18 #include <linux/netfilter/nf_tables.h>
19 
20 #include <libmnl/libmnl.h>
21 #include <libnftnl/gen.h>
22 #include <libnftnl/set.h>
23 #include <libnftnl/table.h>
24 
25 #include "nft.h"
26 #include "nft-cache.h"
27 #include "nft-chain.h"
28 
29 /* users may define NDEBUG */
assert_nft_restart(struct nft_handle * h)30 static void assert_nft_restart(struct nft_handle *h)
31 {
32 	int rc = nft_restart(h);
33 
34 	assert(rc >= 0);
35 }
36 
cache_chain_list_insert(struct list_head * list,const char * name)37 static void cache_chain_list_insert(struct list_head *list, const char *name)
38 {
39 	struct cache_chain *pos = NULL, *new;
40 
41 	list_for_each_entry(pos, list, head) {
42 		int cmp = strcmp(pos->name, name);
43 
44 		if (!cmp)
45 			return;
46 		if (cmp > 0)
47 			break;
48 	}
49 
50 	new = xtables_malloc(sizeof(*new));
51 	new->name = xtables_strdup(name);
52 	list_add_tail(&new->head, pos ? &pos->head : list);
53 }
54 
nft_cache_level_set(struct nft_handle * h,int level,const struct nft_cmd * cmd)55 void nft_cache_level_set(struct nft_handle *h, int level,
56 			 const struct nft_cmd *cmd)
57 {
58 	struct nft_cache_req *req = &h->cache_req;
59 
60 	if (level > req->level)
61 		req->level = level;
62 
63 	if (!cmd || !cmd->table || req->all_chains)
64 		return;
65 
66 	if (!req->table)
67 		req->table = xtables_strdup(cmd->table);
68 	else
69 		assert(!strcmp(req->table, cmd->table));
70 
71 	if (!cmd->chain) {
72 		req->all_chains = true;
73 		return;
74 	}
75 
76 	cache_chain_list_insert(&req->chain_list, cmd->chain);
77 	if (cmd->rename)
78 		cache_chain_list_insert(&req->chain_list, cmd->rename);
79 	if (cmd->jumpto)
80 		cache_chain_list_insert(&req->chain_list, cmd->jumpto);
81 }
82 
genid_cb(const struct nlmsghdr * nlh,void * data)83 static int genid_cb(const struct nlmsghdr *nlh, void *data)
84 {
85 	uint32_t *genid = data;
86 	struct nftnl_gen *gen;
87 
88 	gen = nftnl_gen_alloc();
89 	if (!gen)
90 		return MNL_CB_ERROR;
91 
92 	if (nftnl_gen_nlmsg_parse(nlh, gen) < 0)
93 		goto out;
94 
95 	*genid = nftnl_gen_get_u32(gen, NFTNL_GEN_ID);
96 
97 	nftnl_gen_free(gen);
98 	return MNL_CB_STOP;
99 out:
100 	nftnl_gen_free(gen);
101 	return MNL_CB_ERROR;
102 }
103 
mnl_genid_get(struct nft_handle * h,uint32_t * genid)104 static void mnl_genid_get(struct nft_handle *h, uint32_t *genid)
105 {
106 	char buf[MNL_SOCKET_BUFFER_SIZE];
107 	struct nlmsghdr *nlh;
108 	int ret;
109 
110 	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETGEN, 0, 0, h->seq);
111 	ret = mnl_talk(h, nlh, genid_cb, genid);
112 	if (ret == 0)
113 		return;
114 
115 	xtables_error(RESOURCE_PROBLEM,
116 		      "Could not fetch rule set generation id: %s",
117 		      nft_strerror(errno));
118 }
119 
nftnl_table_list_cb(const struct nlmsghdr * nlh,void * data)120 static int nftnl_table_list_cb(const struct nlmsghdr *nlh, void *data)
121 {
122 	struct nftnl_table *nftnl = nftnl_table_alloc();
123 	const struct builtin_table *t;
124 	struct nft_handle *h = data;
125 	const char *name;
126 
127 	if (!nftnl)
128 		return MNL_CB_OK;
129 
130 	if (nftnl_table_nlmsg_parse(nlh, nftnl) < 0)
131 		goto out;
132 
133 	name = nftnl_table_get_str(nftnl, NFTNL_TABLE_NAME);
134 	if (!name)
135 		goto out;
136 
137 	t = nft_table_builtin_find(h, name);
138 	if (!t)
139 		goto out;
140 
141 	h->cache->table[t->type].exists = true;
142 out:
143 	nftnl_table_free(nftnl);
144 	return MNL_CB_OK;
145 }
146 
fetch_table_cache(struct nft_handle * h)147 static int fetch_table_cache(struct nft_handle *h)
148 {
149 	struct nlmsghdr *nlh;
150 	char buf[16536];
151 	int i, ret;
152 
153 	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
154 				    NLM_F_DUMP, h->seq);
155 
156 	ret = mnl_talk(h, nlh, nftnl_table_list_cb, h);
157 	if (ret < 0 && errno == EINTR)
158 		assert_nft_restart(h);
159 
160 	for (i = 0; i < NFT_TABLE_MAX; i++) {
161 		enum nft_table_type type = h->tables[i].type;
162 
163 		if (!h->tables[i].name)
164 			continue;
165 
166 		h->cache->table[type].chains = nft_chain_list_alloc();
167 
168 		h->cache->table[type].sets = nftnl_set_list_alloc();
169 		if (!h->cache->table[type].sets)
170 			return 0;
171 	}
172 
173 	return 1;
174 }
175 
djb_hash(const char * key)176 static uint32_t djb_hash(const char *key)
177 {
178 	uint32_t i, hash = 5381;
179 
180 	for (i = 0; i < strlen(key); i++)
181 		hash = ((hash << 5) + hash) + key[i];
182 
183 	return hash;
184 }
185 
chain_name_hlist(struct nft_handle * h,const struct builtin_table * t,const char * chain)186 static struct hlist_head *chain_name_hlist(struct nft_handle *h,
187 					   const struct builtin_table *t,
188 					   const char *chain)
189 {
190 	int key = djb_hash(chain) % CHAIN_NAME_HSIZE;
191 
192 	return &h->cache->table[t->type].chains->names[key];
193 }
194 
195 struct nft_chain *
nft_chain_find(struct nft_handle * h,const char * table,const char * chain)196 nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
197 {
198 	const struct builtin_table *t;
199 	struct hlist_node *node;
200 	struct nft_chain *c;
201 
202 	t = nft_table_builtin_find(h, table);
203 	if (!t)
204 		return NULL;
205 
206 	hlist_for_each_entry(c, node, chain_name_hlist(h, t, chain), hnode) {
207 		if (!strcmp(nftnl_chain_get_str(c->nftnl, NFTNL_CHAIN_NAME),
208 			    chain))
209 			return c;
210 	}
211 	return NULL;
212 }
213 
214 static int
nft_cache_add_base_chain(struct nft_handle * h,const struct builtin_table * t,struct nft_chain * nc)215 nft_cache_add_base_chain(struct nft_handle *h, const struct builtin_table *t,
216 			 struct nft_chain *nc)
217 {
218 	uint32_t hooknum = nftnl_chain_get_u32(nc->nftnl, NFTNL_CHAIN_HOOKNUM);
219 	const char *name = nftnl_chain_get_str(nc->nftnl, NFTNL_CHAIN_NAME);
220 	const char *type = nftnl_chain_get_str(nc->nftnl, NFTNL_CHAIN_TYPE);
221 	uint32_t prio = nftnl_chain_get_u32(nc->nftnl, NFTNL_CHAIN_PRIO);
222 	const struct builtin_chain *bc = NULL;
223 	int i;
224 
225 	for (i = 0; i < NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
226 		if (hooknum == t->chains[i].hook) {
227 			bc = &t->chains[i];
228 			break;
229 		}
230 	}
231 
232 	if (!bc ||
233 	    prio != bc->prio ||
234 	    strcmp(name, bc->name) ||
235 	    strcmp(type, bc->type))
236 		return -EINVAL;
237 
238 	nc->base_slot = &h->cache->table[t->type].base_chains[hooknum];
239 	if (*nc->base_slot)
240 		return -EEXIST;
241 
242 	*nc->base_slot = nc;
243 	return 0;
244 }
245 
nft_cache_add_chain(struct nft_handle * h,const struct builtin_table * t,struct nftnl_chain * c)246 int nft_cache_add_chain(struct nft_handle *h, const struct builtin_table *t,
247 			struct nftnl_chain *c)
248 {
249 	const char *cname = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
250 	struct nft_chain *nc = nft_chain_alloc(c);
251 	int ret;
252 
253 	if (nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM)) {
254 		ret = nft_cache_add_base_chain(h, t, nc);
255 		if (ret) {
256 			h->cache->table[t->type].tainted = true;
257 			nft_chain_free(nc);
258 			return ret;
259 		}
260 	} else {
261 		list_add_tail(&nc->head,
262 			      &h->cache->table[t->type].chains->list);
263 	}
264 	hlist_add_head(&nc->hnode, chain_name_hlist(h, t, cname));
265 	return 0;
266 }
267 
__nft_chain_list_sort(struct list_head * list,int (* cmp)(struct nft_chain * a,struct nft_chain * b))268 static void __nft_chain_list_sort(struct list_head *list,
269 				  int (*cmp)(struct nft_chain *a,
270 					     struct nft_chain *b))
271 {
272 	struct nft_chain *pivot, *cur, *sav;
273 	LIST_HEAD(sublist);
274 
275 	if (list_empty(list))
276 		return;
277 
278 	/* grab first item as pivot (dividing) value */
279 	pivot = list_entry(list->next, struct nft_chain, head);
280 	list_del(&pivot->head);
281 
282 	/* move any smaller value into sublist */
283 	list_for_each_entry_safe(cur, sav, list, head) {
284 		if (cmp(pivot, cur) > 0) {
285 			list_del(&cur->head);
286 			list_add_tail(&cur->head, &sublist);
287 		}
288 	}
289 	/* conquer divided */
290 	__nft_chain_list_sort(&sublist, cmp);
291 	__nft_chain_list_sort(list, cmp);
292 
293 	/* merge divided and pivot again */
294 	list_add_tail(&pivot->head, &sublist);
295 	list_splice(&sublist, list);
296 }
297 
nft_chain_cmp_byname(struct nft_chain * a,struct nft_chain * b)298 static int nft_chain_cmp_byname(struct nft_chain *a, struct nft_chain *b)
299 {
300 	const char *aname = nftnl_chain_get_str(a->nftnl, NFTNL_CHAIN_NAME);
301 	const char *bname = nftnl_chain_get_str(b->nftnl, NFTNL_CHAIN_NAME);
302 
303 	return strcmp(aname, bname);
304 }
305 
nft_cache_sort_chains(struct nft_handle * h,const char * table)306 int nft_cache_sort_chains(struct nft_handle *h, const char *table)
307 {
308 	const struct builtin_table *t = nft_table_builtin_find(h, table);
309 
310 	if (!t)
311 		return -1;
312 
313 	if (h->cache->table[t->type].sorted)
314 		return 0;
315 
316 	__nft_chain_list_sort(&h->cache->table[t->type].chains->list,
317 			      nft_chain_cmp_byname);
318 	h->cache->table[t->type].sorted = true;
319 	return 0;
320 }
321 
322 struct nftnl_chain_list_cb_data {
323 	struct nft_handle *h;
324 	const struct builtin_table *t;
325 };
326 
nftnl_chain_list_cb(const struct nlmsghdr * nlh,void * data)327 static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
328 {
329 	struct nftnl_chain_list_cb_data *d = data;
330 	const struct builtin_table *t = d->t;
331 	struct nft_handle *h = d->h;
332 	struct nftnl_chain *c;
333 	const char *tname;
334 
335 	c = nftnl_chain_alloc();
336 	if (c == NULL)
337 		goto err;
338 
339 	if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
340 		goto out;
341 
342 	tname = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
343 
344 	if (!t) {
345 		t = nft_table_builtin_find(h, tname);
346 		if (!t)
347 			goto out;
348 	} else if (strcmp(t->name, tname)) {
349 		goto out;
350 	}
351 
352 	nft_cache_add_chain(h, t, c);
353 	return MNL_CB_OK;
354 out:
355 	nftnl_chain_free(c);
356 err:
357 	return MNL_CB_OK;
358 }
359 
360 struct nftnl_set_list_cb_data {
361 	struct nft_handle *h;
362 	const struct builtin_table *t;
363 };
364 
nftnl_set_list_cb(const struct nlmsghdr * nlh,void * data)365 static int nftnl_set_list_cb(const struct nlmsghdr *nlh, void *data)
366 {
367 	struct nftnl_set_list_cb_data *d = data;
368 	const struct builtin_table *t = d->t;
369 	struct nftnl_set_list *list;
370 	struct nft_handle *h = d->h;
371 	const char *tname, *sname;
372 	struct nftnl_set *s;
373 
374 	s = nftnl_set_alloc();
375 	if (s == NULL)
376 		return MNL_CB_OK;
377 
378 	if (nftnl_set_nlmsg_parse(nlh, s) < 0)
379 		goto out_free;
380 
381 	tname = nftnl_set_get_str(s, NFTNL_SET_TABLE);
382 
383 	if (!t)
384 		t = nft_table_builtin_find(h, tname);
385 	else if (strcmp(t->name, tname))
386 		goto out_free;
387 
388 	if (!t)
389 		goto out_free;
390 
391 	list = h->cache->table[t->type].sets;
392 	sname = nftnl_set_get_str(s, NFTNL_SET_NAME);
393 
394 	if (nftnl_set_list_lookup_byname(list, sname))
395 		goto out_free;
396 
397 	nftnl_set_list_add_tail(s, list);
398 
399 	return MNL_CB_OK;
400 out_free:
401 	nftnl_set_free(s);
402 	return MNL_CB_OK;
403 }
404 
set_elem_cb(const struct nlmsghdr * nlh,void * data)405 static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
406 {
407 	return nftnl_set_elems_nlmsg_parse(nlh, data) ? -1 : MNL_CB_OK;
408 }
409 
set_has_elements(struct nftnl_set * s)410 static bool set_has_elements(struct nftnl_set *s)
411 {
412 	struct nftnl_set_elems_iter *iter;
413 	bool ret = false;
414 
415 	iter = nftnl_set_elems_iter_create(s);
416 	if (iter) {
417 		ret = !!nftnl_set_elems_iter_cur(iter);
418 		nftnl_set_elems_iter_destroy(iter);
419 	}
420 	return ret;
421 }
422 
set_fetch_elem_cb(struct nftnl_set * s,void * data)423 static int set_fetch_elem_cb(struct nftnl_set *s, void *data)
424 {
425 	char buf[MNL_SOCKET_BUFFER_SIZE];
426 	struct nft_handle *h = data;
427 	struct nlmsghdr *nlh;
428 	int ret;
429 
430 	if (set_has_elements(s))
431 		return 0;
432 
433 	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM, h->family,
434 				    NLM_F_DUMP, h->seq);
435 	nftnl_set_elems_nlmsg_build_payload(nlh, s);
436 
437 	ret = mnl_talk(h, nlh, set_elem_cb, s);
438 
439 	if (!ret && h->verbose > 1) {
440 		fprintf(stdout, "set ");
441 		nftnl_set_fprintf(stdout, s, 0, 0);
442 		fprintf(stdout, "\n");
443 	}
444 	return ret;
445 }
446 
fetch_set_cache(struct nft_handle * h,const struct builtin_table * t,const char * set)447 static int fetch_set_cache(struct nft_handle *h,
448 			   const struct builtin_table *t, const char *set)
449 {
450 	struct nftnl_set_list_cb_data d = {
451 		.h = h,
452 		.t = t,
453 	};
454 	uint16_t flags = NLM_F_DUMP;
455 	struct nftnl_set *s = NULL;
456 	struct nlmsghdr *nlh;
457 	char buf[16536];
458 	int i, ret;
459 
460 	if (t) {
461 		s = nftnl_set_alloc();
462 		if (!s)
463 			return -1;
464 
465 		nftnl_set_set_str(s, NFTNL_SET_TABLE, t->name);
466 
467 		if (set) {
468 			nftnl_set_set_str(s, NFTNL_SET_NAME, set);
469 			flags = NLM_F_ACK;
470 		}
471 	}
472 
473 	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET,
474 				    h->family, flags, h->seq);
475 
476 	if (s) {
477 		nftnl_set_nlmsg_build_payload(nlh, s);
478 		nftnl_set_free(s);
479 	}
480 
481 	ret = mnl_talk(h, nlh, nftnl_set_list_cb, &d);
482 	if (ret < 0 && errno == EINTR) {
483 		assert_nft_restart(h);
484 		return ret;
485 	}
486 
487 	if (t) {
488 		nftnl_set_list_foreach(h->cache->table[t->type].sets,
489 				       set_fetch_elem_cb, h);
490 	} else {
491 		for (i = 0; i < NFT_TABLE_MAX; i++) {
492 			enum nft_table_type type = h->tables[i].type;
493 
494 			if (!h->tables[i].name)
495 				continue;
496 
497 			nftnl_set_list_foreach(h->cache->table[type].sets,
498 					       set_fetch_elem_cb, h);
499 		}
500 	}
501 	return ret;
502 }
503 
__fetch_chain_cache(struct nft_handle * h,const struct builtin_table * t,const struct nftnl_chain * c)504 static int __fetch_chain_cache(struct nft_handle *h,
505 			       const struct builtin_table *t,
506 			       const struct nftnl_chain *c)
507 {
508 	struct nftnl_chain_list_cb_data d = {
509 		.h = h,
510 		.t = t,
511 	};
512 	char buf[16536];
513 	struct nlmsghdr *nlh;
514 	int ret;
515 
516 	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
517 				    c ? NLM_F_ACK : NLM_F_DUMP, h->seq);
518 	if (c)
519 		nftnl_chain_nlmsg_build_payload(nlh, c);
520 
521 	ret = mnl_talk(h, nlh, nftnl_chain_list_cb, &d);
522 	if (ret < 0 && errno == EINTR)
523 		assert_nft_restart(h);
524 
525 	return ret;
526 }
527 
fetch_chain_cache(struct nft_handle * h,const struct builtin_table * t,struct list_head * chains)528 static int fetch_chain_cache(struct nft_handle *h,
529 			     const struct builtin_table *t,
530 			     struct list_head *chains)
531 {
532 	struct cache_chain *cc;
533 	struct nftnl_chain *c;
534 	int rc, ret = 0;
535 
536 	if (!chains)
537 		return __fetch_chain_cache(h, t, NULL);
538 
539 	assert(t);
540 
541 	c = nftnl_chain_alloc();
542 	if (!c)
543 		return -1;
544 
545 	nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, t->name);
546 
547 	list_for_each_entry(cc, chains, head) {
548 		nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, cc->name);
549 		rc = __fetch_chain_cache(h, t, c);
550 		if (rc)
551 			ret = rc;
552 	}
553 
554 	nftnl_chain_free(c);
555 	return ret;
556 }
557 
558 struct rule_list_cb_data {
559 	struct nftnl_chain *chain;
560 	int verbose;
561 };
562 
nftnl_rule_list_cb(const struct nlmsghdr * nlh,void * data)563 static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
564 {
565 	struct rule_list_cb_data *rld = data;
566 	struct nftnl_chain *c = rld->chain;
567 	struct nftnl_rule *r;
568 
569 	r = nftnl_rule_alloc();
570 	if (r == NULL)
571 		return MNL_CB_OK;
572 
573 	if (nftnl_rule_nlmsg_parse(nlh, r) < 0) {
574 		nftnl_rule_free(r);
575 		return MNL_CB_OK;
576 	}
577 
578 	if (rld->verbose > 1) {
579 		nftnl_rule_fprintf(stdout, r, 0, 0);
580 		fprintf(stdout, "\n");
581 	}
582 	nftnl_chain_rule_add_tail(r, c);
583 	return MNL_CB_OK;
584 }
585 
nft_rule_list_update(struct nft_chain * nc,void * data)586 static int nft_rule_list_update(struct nft_chain *nc, void *data)
587 {
588 	struct nftnl_chain *c = nc->nftnl;
589 	struct nft_handle *h = data;
590 	struct rule_list_cb_data rld = {
591 		.chain = c,
592 		.verbose = h->verbose,
593 	};
594 	char buf[16536];
595 	struct nlmsghdr *nlh;
596 	struct nftnl_rule *rule;
597 	int ret;
598 
599 	if (nftnl_rule_lookup_byindex(c, 0))
600 		return 0;
601 
602 	rule = nftnl_rule_alloc();
603 	if (!rule)
604 		return -1;
605 
606 	nftnl_rule_set_str(rule, NFTNL_RULE_TABLE,
607 			   nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE));
608 	nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN,
609 			   nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
610 
611 	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
612 				    NLM_F_DUMP, h->seq);
613 	nftnl_rule_nlmsg_build_payload(nlh, rule);
614 
615 	ret = mnl_talk(h, nlh, nftnl_rule_list_cb, &rld);
616 	if (ret < 0 && errno == EINTR)
617 		assert_nft_restart(h);
618 
619 	nftnl_rule_free(rule);
620 
621 	if (h->family == NFPROTO_BRIDGE)
622 		nft_bridge_chain_postprocess(h, c);
623 
624 	return 0;
625 }
626 
fetch_rule_cache(struct nft_handle * h,const struct builtin_table * t)627 static int fetch_rule_cache(struct nft_handle *h,
628 			    const struct builtin_table *t)
629 {
630 	int i;
631 
632 	if (t)
633 		return nft_chain_foreach(h, t->name, nft_rule_list_update, h);
634 
635 	for (i = 0; i < NFT_TABLE_MAX; i++) {
636 
637 		if (!h->tables[i].name)
638 			continue;
639 
640 		if (nft_chain_foreach(h, h->tables[i].name,
641 				      nft_rule_list_update, h))
642 			return -1;
643 	}
644 	return 0;
645 }
646 
647 static int flush_cache(struct nft_handle *h, struct nft_cache *c,
648 		       const char *tablename);
649 
650 static void
__nft_build_cache(struct nft_handle * h)651 __nft_build_cache(struct nft_handle *h)
652 {
653 	struct nft_cache_req *req = &h->cache_req;
654 	const struct builtin_table *t = NULL;
655 	struct list_head *chains = NULL;
656 	uint32_t genid_check;
657 
658 	if (h->cache_init)
659 		return;
660 
661 	if (req->table) {
662 		t = nft_table_builtin_find(h, req->table);
663 		if (!req->all_chains)
664 			chains = &req->chain_list;
665 	}
666 
667 	h->cache_init = true;
668 retry:
669 	mnl_genid_get(h, &h->nft_genid);
670 
671 	if (req->level >= NFT_CL_TABLES)
672 		fetch_table_cache(h);
673 	if (req->level == NFT_CL_FAKE)
674 		goto genid_check;
675 	if (req->level >= NFT_CL_CHAINS)
676 		fetch_chain_cache(h, t, chains);
677 	if (req->level >= NFT_CL_SETS)
678 		fetch_set_cache(h, t, NULL);
679 	if (req->level >= NFT_CL_RULES)
680 		fetch_rule_cache(h, t);
681 genid_check:
682 	mnl_genid_get(h, &genid_check);
683 	if (h->nft_genid != genid_check) {
684 		flush_cache(h, h->cache, NULL);
685 		goto retry;
686 	}
687 }
688 
__nft_flush_cache(struct nft_handle * h)689 static void __nft_flush_cache(struct nft_handle *h)
690 {
691 	if (!h->cache_index) {
692 		h->cache_index++;
693 		h->cache = &h->__cache[h->cache_index];
694 	} else {
695 		flush_chain_cache(h, NULL);
696 	}
697 }
698 
____flush_rule_cache(struct nftnl_rule * r,void * data)699 static int ____flush_rule_cache(struct nftnl_rule *r, void *data)
700 {
701 	nftnl_rule_list_del(r);
702 	nftnl_rule_free(r);
703 
704 	return 0;
705 }
706 
__flush_rule_cache(struct nft_chain * c,void * data)707 static int __flush_rule_cache(struct nft_chain *c, void *data)
708 {
709 	return nftnl_rule_foreach(c->nftnl, ____flush_rule_cache, NULL);
710 }
711 
flush_rule_cache(struct nft_handle * h,const char * table,struct nft_chain * c)712 int flush_rule_cache(struct nft_handle *h, const char *table,
713 		     struct nft_chain *c)
714 {
715 	if (c)
716 		return __flush_rule_cache(c, NULL);
717 
718 	nft_chain_foreach(h, table, __flush_rule_cache, NULL);
719 	return 0;
720 }
721 
__flush_chain_cache(struct nft_chain * c,void * data)722 static int __flush_chain_cache(struct nft_chain *c, void *data)
723 {
724 	nft_chain_list_del(c);
725 	nft_chain_free(c);
726 
727 	return 0;
728 }
729 
__flush_set_cache(struct nftnl_set * s,void * data)730 static int __flush_set_cache(struct nftnl_set *s, void *data)
731 {
732 	nftnl_set_list_del(s);
733 	nftnl_set_free(s);
734 
735 	return 0;
736 }
737 
flush_base_chain_cache(struct nft_chain ** base_chains)738 static void flush_base_chain_cache(struct nft_chain **base_chains)
739 {
740 	int i;
741 
742 	for (i = 0; i < NF_INET_NUMHOOKS; i++) {
743 		if (!base_chains[i])
744 			continue;
745 		hlist_del(&base_chains[i]->hnode);
746 		nft_chain_free(base_chains[i]);
747 		base_chains[i] = NULL;
748 	}
749 }
750 
flush_cache(struct nft_handle * h,struct nft_cache * c,const char * tablename)751 static int flush_cache(struct nft_handle *h, struct nft_cache *c,
752 		       const char *tablename)
753 {
754 	const struct builtin_table *table;
755 	int i;
756 
757 	if (tablename) {
758 		table = nft_table_builtin_find(h, tablename);
759 		if (!table)
760 			return 0;
761 
762 		flush_base_chain_cache(c->table[table->type].base_chains);
763 		nft_chain_foreach(h, tablename, __flush_chain_cache, NULL);
764 		c->table[table->type].sorted = false;
765 
766 		if (c->table[table->type].sets)
767 			nftnl_set_list_foreach(c->table[table->type].sets,
768 					       __flush_set_cache, NULL);
769 		return 0;
770 	}
771 
772 	for (i = 0; i < NFT_TABLE_MAX; i++) {
773 		if (h->tables[i].name == NULL)
774 			continue;
775 
776 		flush_base_chain_cache(c->table[i].base_chains);
777 		if (c->table[i].chains) {
778 			nft_chain_list_free(c->table[i].chains);
779 			c->table[i].chains = NULL;
780 			c->table[i].sorted = false;
781 		}
782 
783 		if (c->table[i].sets) {
784 			nftnl_set_list_free(c->table[i].sets);
785 			c->table[i].sets = NULL;
786 		}
787 
788 		c->table[i].exists = false;
789 	}
790 
791 	return 1;
792 }
793 
flush_chain_cache(struct nft_handle * h,const char * tablename)794 void flush_chain_cache(struct nft_handle *h, const char *tablename)
795 {
796 	if (!h->cache_init)
797 		return;
798 
799 	if (flush_cache(h, h->cache, tablename))
800 		h->cache_init = false;
801 }
802 
nft_rebuild_cache(struct nft_handle * h)803 void nft_rebuild_cache(struct nft_handle *h)
804 {
805 	if (h->cache_init) {
806 		__nft_flush_cache(h);
807 		h->cache_init = false;
808 	}
809 
810 	__nft_build_cache(h);
811 }
812 
nft_cache_build(struct nft_handle * h)813 void nft_cache_build(struct nft_handle *h)
814 {
815 	struct nft_cache_req *req = &h->cache_req;
816 	const struct builtin_table *t = NULL;
817 	int i;
818 
819 	if (req->table)
820 		t = nft_table_builtin_find(h, req->table);
821 
822 	/* fetch builtin chains as well (if existing) so nft_xt_builtin_init()
823 	 * doesn't override policies by accident */
824 	if (t && !req->all_chains) {
825 		for (i = 0; i < NF_INET_NUMHOOKS; i++) {
826 			const char *cname = t->chains[i].name;
827 
828 			if (!cname)
829 				break;
830 			cache_chain_list_insert(&req->chain_list, cname);
831 		}
832 	}
833 
834 	__nft_build_cache(h);
835 }
836 
nft_release_cache(struct nft_handle * h)837 void nft_release_cache(struct nft_handle *h)
838 {
839 	struct nft_cache_req *req = &h->cache_req;
840 	struct cache_chain *cc, *cc_tmp;
841 
842 	while (h->cache_index)
843 		flush_cache(h, &h->__cache[h->cache_index--], NULL);
844 	flush_cache(h, &h->__cache[0], NULL);
845 	h->cache = &h->__cache[0];
846 	h->cache_init = false;
847 
848 	if (req->level != NFT_CL_FAKE)
849 		req->level = NFT_CL_TABLES;
850 	if (req->table) {
851 		free(req->table);
852 		req->table = NULL;
853 	}
854 	req->all_chains = false;
855 	list_for_each_entry_safe(cc, cc_tmp, &req->chain_list, head) {
856 		list_del(&cc->head);
857 		free(cc->name);
858 		free(cc);
859 	}
860 }
861 
862 struct nftnl_set_list *
nft_set_list_get(struct nft_handle * h,const char * table,const char * set)863 nft_set_list_get(struct nft_handle *h, const char *table, const char *set)
864 {
865 	const struct builtin_table *t;
866 
867 	t = nft_table_builtin_find(h, table);
868 	if (!t)
869 		return NULL;
870 
871 	return h->cache->table[t->type].sets;
872 }
873