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