libnftnl 1.3.1
flowtable.c
1#include "internal.h"
2
3#include <time.h>
4#include <endian.h>
5#include <stdint.h>
6#include <stdlib.h>
7#include <limits.h>
8#include <string.h>
9#include <netinet/in.h>
10#include <errno.h>
11#include <inttypes.h>
12
13#include <libmnl/libmnl.h>
14#include <linux/netfilter/nfnetlink.h>
15#include <linux/netfilter/nf_tables.h>
16#include <linux/netfilter.h>
17#include <linux/netfilter_arp.h>
18
19#include <libnftnl/flowtable.h>
20
22 struct list_head head;
23 const char *name;
24 const char *table;
25 int family;
26 uint32_t hooknum;
27 int32_t prio;
28 uint32_t size;
29 struct nftnl_str_array dev_array;
30 uint32_t ft_flags;
31 uint32_t use;
32 uint32_t flags;
33 uint64_t handle;
34};
35
36EXPORT_SYMBOL(nftnl_flowtable_alloc);
37struct nftnl_flowtable *nftnl_flowtable_alloc(void)
38{
39 return calloc(1, sizeof(struct nftnl_flowtable));
40}
41
42EXPORT_SYMBOL(nftnl_flowtable_free);
43void nftnl_flowtable_free(const struct nftnl_flowtable *c)
44{
45 if (c->flags & (1 << NFTNL_FLOWTABLE_NAME))
46 xfree(c->name);
47 if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE))
48 xfree(c->table);
49 if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES))
50 nftnl_str_array_clear((struct nftnl_str_array *)&c->dev_array);
51 xfree(c);
52}
53
54EXPORT_SYMBOL(nftnl_flowtable_is_set);
55bool nftnl_flowtable_is_set(const struct nftnl_flowtable *c, uint16_t attr)
56{
57 return c->flags & (1 << attr);
58}
59
60EXPORT_SYMBOL(nftnl_flowtable_unset);
61void nftnl_flowtable_unset(struct nftnl_flowtable *c, uint16_t attr)
62{
63 if (!(c->flags & (1 << attr)))
64 return;
65
66 switch (attr) {
67 case NFTNL_FLOWTABLE_NAME:
68 xfree(c->name);
69 break;
70 case NFTNL_FLOWTABLE_TABLE:
71 xfree(c->table);
72 break;
73 case NFTNL_FLOWTABLE_HOOKNUM:
74 case NFTNL_FLOWTABLE_PRIO:
75 case NFTNL_FLOWTABLE_USE:
76 case NFTNL_FLOWTABLE_FAMILY:
77 case NFTNL_FLOWTABLE_FLAGS:
78 case NFTNL_FLOWTABLE_HANDLE:
79 break;
80 case NFTNL_FLOWTABLE_DEVICES:
81 nftnl_str_array_clear(&c->dev_array);
82 break;
83 default:
84 return;
85 }
86
87 c->flags &= ~(1 << attr);
88}
89
90static uint32_t nftnl_flowtable_validate[NFTNL_FLOWTABLE_MAX + 1] = {
91 [NFTNL_FLOWTABLE_HOOKNUM] = sizeof(uint32_t),
92 [NFTNL_FLOWTABLE_PRIO] = sizeof(int32_t),
93 [NFTNL_FLOWTABLE_FAMILY] = sizeof(uint32_t),
94 [NFTNL_FLOWTABLE_SIZE] = sizeof(uint32_t),
95 [NFTNL_FLOWTABLE_FLAGS] = sizeof(uint32_t),
96 [NFTNL_FLOWTABLE_HANDLE] = sizeof(uint64_t),
97};
98
99EXPORT_SYMBOL(nftnl_flowtable_set_data);
100int nftnl_flowtable_set_data(struct nftnl_flowtable *c, uint16_t attr,
101 const void *data, uint32_t data_len)
102{
103 nftnl_assert_attr_exists(attr, NFTNL_FLOWTABLE_MAX);
104 nftnl_assert_validate(data, nftnl_flowtable_validate, attr, data_len);
105
106 switch(attr) {
107 case NFTNL_FLOWTABLE_NAME:
108 return nftnl_set_str_attr(&c->name, &c->flags,
109 attr, data, data_len);
110 case NFTNL_FLOWTABLE_TABLE:
111 return nftnl_set_str_attr(&c->table, &c->flags,
112 attr, data, data_len);
113 break;
114 case NFTNL_FLOWTABLE_HOOKNUM:
115 memcpy(&c->hooknum, data, sizeof(c->hooknum));
116 break;
117 case NFTNL_FLOWTABLE_PRIO:
118 memcpy(&c->prio, data, sizeof(c->prio));
119 break;
120 case NFTNL_FLOWTABLE_FAMILY:
121 memcpy(&c->family, data, sizeof(c->family));
122 break;
123 case NFTNL_FLOWTABLE_DEVICES:
124 if (nftnl_str_array_set(&c->dev_array, data) < 0)
125 return -1;
126 break;
127 case NFTNL_FLOWTABLE_SIZE:
128 memcpy(&c->size, data, sizeof(c->size));
129 break;
130 case NFTNL_FLOWTABLE_FLAGS:
131 memcpy(&c->ft_flags, data, sizeof(c->ft_flags));
132 break;
133 case NFTNL_FLOWTABLE_HANDLE:
134 memcpy(&c->handle, data, sizeof(c->handle));
135 break;
136 }
137 c->flags |= (1 << attr);
138 return 0;
139}
140
141void nftnl_flowtable_set(struct nftnl_flowtable *c, uint16_t attr, const void *data) __visible;
142void nftnl_flowtable_set(struct nftnl_flowtable *c, uint16_t attr, const void *data)
143{
144 nftnl_flowtable_set_data(c, attr, data, nftnl_flowtable_validate[attr]);
145}
146
147EXPORT_SYMBOL(nftnl_flowtable_set_u32);
148void nftnl_flowtable_set_u32(struct nftnl_flowtable *c, uint16_t attr, uint32_t data)
149{
150 nftnl_flowtable_set_data(c, attr, &data, sizeof(uint32_t));
151}
152
153EXPORT_SYMBOL(nftnl_flowtable_set_s32);
154void nftnl_flowtable_set_s32(struct nftnl_flowtable *c, uint16_t attr, int32_t data)
155{
156 nftnl_flowtable_set_data(c, attr, &data, sizeof(int32_t));
157}
158
159EXPORT_SYMBOL(nftnl_flowtable_set_str);
160int nftnl_flowtable_set_str(struct nftnl_flowtable *c, uint16_t attr, const char *str)
161{
162 return nftnl_flowtable_set_data(c, attr, str, strlen(str) + 1);
163}
164
165EXPORT_SYMBOL(nftnl_flowtable_set_u64);
166void nftnl_flowtable_set_u64(struct nftnl_flowtable *c, uint16_t attr, uint64_t data)
167{
168 nftnl_flowtable_set_data(c, attr, &data, sizeof(uint64_t));
169}
170
171EXPORT_SYMBOL(nftnl_flowtable_set_array);
172int nftnl_flowtable_set_array(struct nftnl_flowtable *c, uint16_t attr,
173 const char **data)
174{
175 return nftnl_flowtable_set_data(c, attr, data, 0);
176}
177
178EXPORT_SYMBOL(nftnl_flowtable_get_data);
179const void *nftnl_flowtable_get_data(const struct nftnl_flowtable *c,
180 uint16_t attr, uint32_t *data_len)
181{
182 if (!(c->flags & (1 << attr)))
183 return NULL;
184
185 switch(attr) {
186 case NFTNL_FLOWTABLE_NAME:
187 *data_len = strlen(c->name) + 1;
188 return c->name;
189 case NFTNL_FLOWTABLE_TABLE:
190 *data_len = strlen(c->table) + 1;
191 return c->table;
192 case NFTNL_FLOWTABLE_HOOKNUM:
193 *data_len = sizeof(uint32_t);
194 return &c->hooknum;
195 case NFTNL_FLOWTABLE_PRIO:
196 *data_len = sizeof(int32_t);
197 return &c->prio;
198 case NFTNL_FLOWTABLE_FAMILY:
199 *data_len = sizeof(int32_t);
200 return &c->family;
201 case NFTNL_FLOWTABLE_DEVICES:
202 *data_len = 0;
203 return c->dev_array.array;
204 case NFTNL_FLOWTABLE_SIZE:
205 *data_len = sizeof(int32_t);
206 return &c->size;
207 case NFTNL_FLOWTABLE_FLAGS:
208 *data_len = sizeof(int32_t);
209 return &c->ft_flags;
210 case NFTNL_FLOWTABLE_HANDLE:
211 *data_len = sizeof(uint64_t);
212 return &c->handle;
213 }
214 return NULL;
215}
216
217EXPORT_SYMBOL(nftnl_flowtable_get);
218const void *nftnl_flowtable_get(const struct nftnl_flowtable *c, uint16_t attr)
219{
220 uint32_t data_len;
221 return nftnl_flowtable_get_data(c, attr, &data_len);
222}
223
224EXPORT_SYMBOL(nftnl_flowtable_get_str);
225const char *nftnl_flowtable_get_str(const struct nftnl_flowtable *c, uint16_t attr)
226{
227 return nftnl_flowtable_get(c, attr);
228}
229
230EXPORT_SYMBOL(nftnl_flowtable_get_u32);
231uint32_t nftnl_flowtable_get_u32(const struct nftnl_flowtable *c, uint16_t attr)
232{
233 uint32_t data_len = 0;
234 const uint32_t *val = nftnl_flowtable_get_data(c, attr, &data_len);
235
236 nftnl_assert(val, attr, data_len == sizeof(uint32_t));
237
238 return val ? *val : 0;
239}
240
241EXPORT_SYMBOL(nftnl_flowtable_get_u64);
242uint64_t nftnl_flowtable_get_u64(const struct nftnl_flowtable *c, uint16_t attr)
243{
244 uint32_t data_len = 0;
245 const uint64_t *val = nftnl_flowtable_get_data(c, attr, &data_len);
246
247 nftnl_assert(val, attr, data_len == sizeof(uint64_t));
248
249 return val ? *val : 0;
250}
251
252EXPORT_SYMBOL(nftnl_flowtable_get_s32);
253int32_t nftnl_flowtable_get_s32(const struct nftnl_flowtable *c, uint16_t attr)
254{
255 uint32_t data_len = 0;
256 const int32_t *val = nftnl_flowtable_get_data(c, attr, &data_len);
257
258 nftnl_assert(val, attr, data_len == sizeof(int32_t));
259
260 return val ? *val : 0;
261}
262
263EXPORT_SYMBOL(nftnl_flowtable_get_array);
264const char *const *nftnl_flowtable_get_array(const struct nftnl_flowtable *c, uint16_t attr)
265{
266 uint32_t data_len;
267 const char * const *val = nftnl_flowtable_get_data(c, attr, &data_len);
268
269 nftnl_assert(val, attr, attr == NFTNL_FLOWTABLE_DEVICES);
270
271 return val;
272}
273
274EXPORT_SYMBOL(nftnl_flowtable_nlmsg_build_payload);
275void nftnl_flowtable_nlmsg_build_payload(struct nlmsghdr *nlh,
276 const struct nftnl_flowtable *c)
277{
278 struct nlattr *nest = NULL;
279 int i;
280
281 if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE))
282 mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, c->table);
283 if (c->flags & (1 << NFTNL_FLOWTABLE_NAME))
284 mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, c->name);
285
286 if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM) ||
287 c->flags & (1 << NFTNL_FLOWTABLE_PRIO) ||
288 c->flags & (1 << NFTNL_FLOWTABLE_DEVICES))
289 nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK);
290
291 if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM))
292 mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_NUM, htonl(c->hooknum));
293 if (c->flags & (1 << NFTNL_FLOWTABLE_PRIO))
294 mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(c->prio));
295
296 if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) {
297 struct nlattr *nest_dev;
298 const char *dev;
299
300 nest_dev = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK_DEVS);
301 nftnl_str_array_foreach(dev, &c->dev_array, i)
302 nftnl_attr_put_ifname(nlh, dev);
303 mnl_attr_nest_end(nlh, nest_dev);
304 }
305
306 if (nest)
307 mnl_attr_nest_end(nlh, nest);
308
309 if (c->flags & (1 << NFTNL_FLOWTABLE_FLAGS))
310 mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_FLAGS, htonl(c->ft_flags));
311 if (c->flags & (1 << NFTNL_FLOWTABLE_USE))
312 mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_USE, htonl(c->use));
313 if (c->flags & (1 << NFTNL_FLOWTABLE_HANDLE))
314 mnl_attr_put_u64(nlh, NFTA_FLOWTABLE_HANDLE, htobe64(c->handle));
315}
316
317static int nftnl_flowtable_parse_attr_cb(const struct nlattr *attr, void *data)
318{
319 const struct nlattr **tb = data;
320 int type = mnl_attr_get_type(attr);
321
322 if (mnl_attr_type_valid(attr, NFTA_FLOWTABLE_MAX) < 0)
323 return MNL_CB_OK;
324
325 switch(type) {
326 case NFTA_FLOWTABLE_NAME:
327 case NFTA_FLOWTABLE_TABLE:
328 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
329 abi_breakage();
330 break;
331 case NFTA_FLOWTABLE_HOOK:
332 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
333 abi_breakage();
334 break;
335 case NFTA_FLOWTABLE_FLAGS:
336 case NFTA_FLOWTABLE_USE:
337 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
338 abi_breakage();
339 break;
340 case NFTA_FLOWTABLE_HANDLE:
341 if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
342 abi_breakage();
343 break;
344 }
345
346 tb[type] = attr;
347 return MNL_CB_OK;
348}
349
350static int nftnl_flowtable_parse_hook_cb(const struct nlattr *attr, void *data)
351{
352 const struct nlattr **tb = data;
353 int type = mnl_attr_get_type(attr);
354
355 if (mnl_attr_type_valid(attr, NFTA_FLOWTABLE_HOOK_MAX) < 0)
356 return MNL_CB_OK;
357
358 switch(type) {
359 case NFTA_FLOWTABLE_HOOK_NUM:
360 case NFTA_FLOWTABLE_HOOK_PRIORITY:
361 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
362 abi_breakage();
363 break;
364 case NFTA_FLOWTABLE_HOOK_DEVS:
365 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
366 abi_breakage();
367 break;
368 }
369
370 tb[type] = attr;
371 return MNL_CB_OK;
372}
373
374static int nftnl_flowtable_parse_hook(struct nlattr *attr, struct nftnl_flowtable *c)
375{
376 struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1] = {};
377 int ret;
378
379 if (mnl_attr_parse_nested(attr, nftnl_flowtable_parse_hook_cb, tb) < 0)
380 return -1;
381
382 if (tb[NFTA_FLOWTABLE_HOOK_NUM]) {
383 c->hooknum = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_HOOK_NUM]));
384 c->flags |= (1 << NFTNL_FLOWTABLE_HOOKNUM);
385 }
386 if (tb[NFTA_FLOWTABLE_HOOK_PRIORITY]) {
387 c->prio = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY]));
388 c->flags |= (1 << NFTNL_FLOWTABLE_PRIO);
389 }
390 if (tb[NFTA_FLOWTABLE_HOOK_DEVS]) {
391 ret = nftnl_parse_devs(&c->dev_array,
392 tb[NFTA_FLOWTABLE_HOOK_DEVS]);
393 if (ret < 0)
394 return -1;
395 c->flags |= (1 << NFTNL_FLOWTABLE_DEVICES);
396 }
397
398 return 0;
399}
400
401EXPORT_SYMBOL(nftnl_flowtable_nlmsg_parse);
402int nftnl_flowtable_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_flowtable *c)
403{
404 struct nlattr *tb[NFTA_FLOWTABLE_MAX + 1] = {};
405 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
406 int ret = 0;
407
408 if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_flowtable_parse_attr_cb, tb) < 0)
409 return -1;
410
411 if (nftnl_parse_str_attr(tb[NFTA_FLOWTABLE_NAME],
412 NFTNL_FLOWTABLE_NAME,
413 &c->name, &c->flags) < 0)
414 return -1;
415 if (nftnl_parse_str_attr(tb[NFTA_FLOWTABLE_TABLE],
416 NFTNL_FLOWTABLE_TABLE,
417 &c->table, &c->flags) < 0)
418 return -1;
419 if (tb[NFTA_FLOWTABLE_HOOK]) {
420 ret = nftnl_flowtable_parse_hook(tb[NFTA_FLOWTABLE_HOOK], c);
421 if (ret < 0)
422 return ret;
423 }
424 if (tb[NFTA_FLOWTABLE_FLAGS]) {
425 c->ft_flags = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_FLAGS]));
426 c->flags |= (1 << NFTNL_FLOWTABLE_FLAGS);
427 }
428 if (tb[NFTA_FLOWTABLE_USE]) {
429 c->use = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_USE]));
430 c->flags |= (1 << NFTNL_FLOWTABLE_USE);
431 }
432 if (tb[NFTA_FLOWTABLE_HANDLE]) {
433 c->handle = be64toh(mnl_attr_get_u64(tb[NFTA_FLOWTABLE_HANDLE]));
434 c->flags |= (1 << NFTNL_FLOWTABLE_HANDLE);
435 }
436
437 c->family = nfg->nfgen_family;
438 c->flags |= (1 << NFTNL_FLOWTABLE_FAMILY);
439
440 return ret;
441}
442
443static const char *nftnl_hooknum2str(int family, int hooknum)
444{
445 switch (family) {
446 case NFPROTO_IPV4:
447 case NFPROTO_IPV6:
448 case NFPROTO_INET:
449 case NFPROTO_BRIDGE:
450 switch (hooknum) {
451 case NF_INET_PRE_ROUTING:
452 return "prerouting";
453 case NF_INET_LOCAL_IN:
454 return "input";
455 case NF_INET_FORWARD:
456 return "forward";
457 case NF_INET_LOCAL_OUT:
458 return "output";
459 case NF_INET_POST_ROUTING:
460 return "postrouting";
461 }
462 break;
463 case NFPROTO_ARP:
464 switch (hooknum) {
465 case NF_ARP_IN:
466 return "input";
467 case NF_ARP_OUT:
468 return "output";
469 case NF_ARP_FORWARD:
470 return "forward";
471 }
472 break;
473 case NFPROTO_NETDEV:
474 switch (hooknum) {
475 case NF_NETDEV_INGRESS:
476 return "ingress";
477 }
478 break;
479 }
480 return "unknown";
481}
482
483EXPORT_SYMBOL(nftnl_flowtable_parse);
484int nftnl_flowtable_parse(struct nftnl_flowtable *c, enum nftnl_parse_type type,
485 const char *data, struct nftnl_parse_err *err)
486{
487 errno = EOPNOTSUPP;
488 return -1;
489}
490
491EXPORT_SYMBOL(nftnl_flowtable_parse_file);
492int nftnl_flowtable_parse_file(struct nftnl_flowtable *c,
493 enum nftnl_parse_type type,
494 FILE *fp, struct nftnl_parse_err *err)
495{
496 errno = EOPNOTSUPP;
497 return -1;
498}
499
500static int nftnl_flowtable_snprintf_default(char *buf, size_t remain,
501 const struct nftnl_flowtable *c)
502{
503 int ret, offset = 0, i;
504 const char *dev;
505
506 ret = snprintf(buf, remain, "flow table %s %s use %u size %u flags %x",
507 c->table, c->name, c->use, c->size, c->ft_flags);
508 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
509
510 if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM)) {
511 ret = snprintf(buf + offset, remain, " hook %s prio %d ",
512 nftnl_hooknum2str(c->family, c->hooknum),
513 c->prio);
514 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
515
516 if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) {
517 ret = snprintf(buf + offset, remain, " dev { ");
518 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
519
520 nftnl_str_array_foreach(dev, &c->dev_array, i) {
521 ret = snprintf(buf + offset, remain, " %s ",
522 dev);
523 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
524 }
525 ret = snprintf(buf + offset, remain, " } ");
526 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
527 }
528 }
529
530 return offset;
531}
532
533static int nftnl_flowtable_cmd_snprintf(char *buf, size_t remain,
534 const struct nftnl_flowtable *c,
535 uint32_t cmd, uint32_t type,
536 uint32_t flags)
537{
538 int ret, offset = 0;
539
540 if (type != NFTNL_OUTPUT_DEFAULT)
541 return -1;
542
543 ret = nftnl_flowtable_snprintf_default(buf + offset, remain, c);
544 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
545 return offset;
546}
547
548EXPORT_SYMBOL(nftnl_flowtable_snprintf);
549int nftnl_flowtable_snprintf(char *buf, size_t size, const struct nftnl_flowtable *c,
550 uint32_t type, uint32_t flags)
551{
552 if (size)
553 buf[0] = '\0';
554
555 return nftnl_flowtable_cmd_snprintf(buf, size, c, nftnl_flag2cmd(flags),
556 type, flags);
557}
558
559static int nftnl_flowtable_do_snprintf(char *buf, size_t size, const void *c,
560 uint32_t cmd, uint32_t type, uint32_t flags)
561{
562 return nftnl_flowtable_snprintf(buf, size, c, type, flags);
563}
564
565EXPORT_SYMBOL(nftnl_flowtable_fprintf);
566int nftnl_flowtable_fprintf(FILE *fp, const struct nftnl_flowtable *c,
567 uint32_t type, uint32_t flags)
568{
569 return nftnl_fprintf(fp, c, NFTNL_CMD_UNSPEC, type, flags,
570 nftnl_flowtable_do_snprintf);
571}
572
574 struct list_head list;
575};
576
577EXPORT_SYMBOL(nftnl_flowtable_list_alloc);
578struct nftnl_flowtable_list *nftnl_flowtable_list_alloc(void)
579{
580 struct nftnl_flowtable_list *list;
581
582 list = calloc(1, sizeof(struct nftnl_flowtable_list));
583 if (list == NULL)
584 return NULL;
585
586 INIT_LIST_HEAD(&list->list);
587
588 return list;
589}
590
591EXPORT_SYMBOL(nftnl_flowtable_list_free);
592void nftnl_flowtable_list_free(struct nftnl_flowtable_list *list)
593{
594 struct nftnl_flowtable *s, *tmp;
595
596 list_for_each_entry_safe(s, tmp, &list->list, head) {
597 list_del(&s->head);
598 nftnl_flowtable_free(s);
599 }
600 xfree(list);
601}
602
603EXPORT_SYMBOL(nftnl_flowtable_list_is_empty);
604int nftnl_flowtable_list_is_empty(const struct nftnl_flowtable_list *list)
605{
606 return list_empty(&list->list);
607}
608
609EXPORT_SYMBOL(nftnl_flowtable_list_add);
610void nftnl_flowtable_list_add(struct nftnl_flowtable *s,
611 struct nftnl_flowtable_list *list)
612{
613 list_add(&s->head, &list->list);
614}
615
616EXPORT_SYMBOL(nftnl_flowtable_list_add_tail);
617void nftnl_flowtable_list_add_tail(struct nftnl_flowtable *s,
618 struct nftnl_flowtable_list *list)
619{
620 list_add_tail(&s->head, &list->list);
621}
622
623EXPORT_SYMBOL(nftnl_flowtable_list_del);
624void nftnl_flowtable_list_del(struct nftnl_flowtable *s)
625{
626 list_del(&s->head);
627}
628
629EXPORT_SYMBOL(nftnl_flowtable_list_foreach);
630int nftnl_flowtable_list_foreach(struct nftnl_flowtable_list *flowtable_list,
631 int (*cb)(struct nftnl_flowtable *t, void *data), void *data)
632{
633 struct nftnl_flowtable *cur, *tmp;
634 int ret;
635
636 list_for_each_entry_safe(cur, tmp, &flowtable_list->list, head) {
637 ret = cb(cur, data);
638 if (ret < 0)
639 return ret;
640 }
641 return 0;
642}