From patchwork Thu Dec 17 16:38:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pablo Neira Ayuso X-Patchwork-Id: 1417781 X-Patchwork-Delegate: pablo@netfilter.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=netfilter.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4Cxd4f4s4mz9sW1 for ; Fri, 18 Dec 2020 03:39:30 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728158AbgLQQjN (ORCPT ); Thu, 17 Dec 2020 11:39:13 -0500 Received: from correo.us.es ([193.147.175.20]:58540 "EHLO mail.us.es" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726773AbgLQQjN (ORCPT ); Thu, 17 Dec 2020 11:39:13 -0500 Received: from antivirus1-rhel7.int (unknown [192.168.2.11]) by mail.us.es (Postfix) with ESMTP id D8DA6C1065 for ; Thu, 17 Dec 2020 17:38:11 +0100 (CET) Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id C8AABDA844 for ; Thu, 17 Dec 2020 17:38:11 +0100 (CET) Received: by antivirus1-rhel7.int (Postfix, from userid 99) id BE3DBDA840; Thu, 17 Dec 2020 17:38:11 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on antivirus1-rhel7.int X-Spam-Level: X-Spam-Status: No, score=-108.2 required=7.5 tests=ALL_TRUSTED,BAYES_50, SMTPAUTH_US2,URIBL_BLOCKED,USER_IN_WELCOMELIST,USER_IN_WHITELIST autolearn=disabled version=3.4.1 Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id 4979FDA704 for ; Thu, 17 Dec 2020 17:38:09 +0100 (CET) Received: from 192.168.1.97 (192.168.1.97) by antivirus1-rhel7.int (F-Secure/fsigk_smtp/550/antivirus1-rhel7.int); Thu, 17 Dec 2020 17:38:09 +0100 (CET) X-Virus-Status: clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int) Received: from localhost.localdomain (unknown [90.77.255.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: pneira@us.es) by entrada.int (Postfix) with ESMTPSA id 26580426CC85 for ; Thu, 17 Dec 2020 17:38:09 +0100 (CET) X-SMTPAUTHUS: auth mail.us.es From: Pablo Neira Ayuso To: netfilter-devel@vger.kernel.org Subject: [PATCH 1/3 libnftnl,v2] src: add NFTNL_SET_ELEM_EXPRESSIONS Date: Thu, 17 Dec 2020 17:38:21 +0100 Message-Id: <20201217163823.24180-1-pablo@netfilter.org> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 X-Virus-Scanned: ClamAV using ClamSMTP Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org NFTNL_SET_ELEM_EXPR defines the stateful expression type that this element stores. This is useful to restore runtime set element stateful expressions (when saving, then reboot and restore). This patch adds support for the set element expression list, which generalizes NFTNL_SET_ELEM_EXPR. This patch also adds nftnl_set_elem_add_expr() to add new expressions to set elements and nftnl_set_elem_expr_foreach() to iterate over the list of expressions. Signed-off-by: Pablo Neira Ayuso --- v2: no changes. include/libnftnl/set.h | 7 ++ include/linux/netfilter/nf_tables.h | 3 + include/set_elem.h | 2 +- src/libnftnl.map | 5 ++ src/set_elem.c | 111 +++++++++++++++++++++++----- 5 files changed, 110 insertions(+), 18 deletions(-) diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h index 961ce5d7d71d..1804850dc92b 100644 --- a/include/libnftnl/set.h +++ b/include/libnftnl/set.h @@ -107,6 +107,7 @@ enum { NFTNL_SET_ELEM_EXPR, NFTNL_SET_ELEM_OBJREF, NFTNL_SET_ELEM_KEY_END, + NFTNL_SET_ELEM_EXPRESSIONS, __NFTNL_SET_ELEM_MAX }; #define NFTNL_SET_ELEM_MAX (__NFTNL_SET_ELEM_MAX - 1) @@ -144,6 +145,12 @@ int nftnl_set_elem_parse_file(struct nftnl_set_elem *e, enum nftnl_parse_type ty int nftnl_set_elem_snprintf(char *buf, size_t size, const struct nftnl_set_elem *s, uint32_t type, uint32_t flags); int nftnl_set_elem_fprintf(FILE *fp, const struct nftnl_set_elem *se, uint32_t type, uint32_t flags); +struct nftnl_expr; +void nftnl_set_elem_add_expr(struct nftnl_set_elem *e, struct nftnl_expr *expr); +int nftnl_set_elem_expr_foreach(struct nftnl_set_elem *e, + int (*cb)(struct nftnl_expr *e, void *data), + void *data); + int nftnl_set_elem_foreach(struct nftnl_set *s, int (*cb)(struct nftnl_set_elem *e, void *data), void *data); struct nftnl_set_elems_iter; diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index e4cdf78f25e2..5cf3faf4b66f 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -367,6 +367,7 @@ enum nft_set_attributes { NFTA_SET_OBJ_TYPE, NFTA_SET_HANDLE, NFTA_SET_EXPR, + NFTA_SET_EXPRESSIONS, __NFTA_SET_MAX }; #define NFTA_SET_MAX (__NFTA_SET_MAX - 1) @@ -405,6 +406,7 @@ enum nft_set_elem_attributes { NFTA_SET_ELEM_PAD, NFTA_SET_ELEM_OBJREF, NFTA_SET_ELEM_KEY_END, + NFTA_SET_ELEM_EXPRESSIONS, __NFTA_SET_ELEM_MAX }; #define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1) @@ -712,6 +714,7 @@ enum nft_dynset_attributes { NFTA_DYNSET_EXPR, NFTA_DYNSET_PAD, NFTA_DYNSET_FLAGS, + NFTA_DYNSET_EXPRESSIONS, __NFTA_DYNSET_MAX, }; #define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1) diff --git a/include/set_elem.h b/include/set_elem.h index 52f185aa11be..9239557469fe 100644 --- a/include/set_elem.h +++ b/include/set_elem.h @@ -10,7 +10,7 @@ struct nftnl_set_elem { union nftnl_data_reg key; union nftnl_data_reg key_end; union nftnl_data_reg data; - struct nftnl_expr *expr; + struct list_head expr_list; uint64_t timeout; uint64_t expiration; const char *objref; diff --git a/src/libnftnl.map b/src/libnftnl.map index 2d35ace0355b..ce1c0820de2d 100644 --- a/src/libnftnl.map +++ b/src/libnftnl.map @@ -374,3 +374,8 @@ LIBNFTNL_15 { nftnl_expr_build_payload; nftnl_rule_del_expr; } LIBNFTNL_14; + +LIBNFTNL_16 { + nftnl_set_elem_add_expr; + nftnl_set_elem_expr_foreach; +} LIBNFTNL_15; diff --git a/src/set_elem.c b/src/set_elem.c index 46bb0623a3bb..8f634e756a1b 100644 --- a/src/set_elem.c +++ b/src/set_elem.c @@ -36,17 +36,21 @@ struct nftnl_set_elem *nftnl_set_elem_alloc(void) if (s == NULL) return NULL; + INIT_LIST_HEAD(&s->expr_list); + return s; } EXPORT_SYMBOL(nftnl_set_elem_free); void nftnl_set_elem_free(struct nftnl_set_elem *s) { + struct nftnl_expr *e, *tmp; + if (s->flags & (1 << NFTNL_SET_ELEM_CHAIN)) xfree(s->data.chain); - if (s->flags & (1 << NFTNL_SET_ELEM_EXPR)) - nftnl_expr_free(s->expr); + list_for_each_entry_safe(e, tmp, &s->expr_list, head) + nftnl_expr_free(e); if (s->flags & (1 << NFTNL_SET_ELEM_USERDATA)) xfree(s->user.data); @@ -66,6 +70,8 @@ bool nftnl_set_elem_is_set(const struct nftnl_set_elem *s, uint16_t attr) EXPORT_SYMBOL(nftnl_set_elem_unset); void nftnl_set_elem_unset(struct nftnl_set_elem *s, uint16_t attr) { + struct nftnl_expr *expr, *tmp; + if (!(s->flags & (1 << attr))) return; @@ -85,7 +91,9 @@ void nftnl_set_elem_unset(struct nftnl_set_elem *s, uint16_t attr) xfree(s->user.data); break; case NFTNL_SET_ELEM_EXPR: - nftnl_expr_free(s->expr); + case NFTNL_SET_ELEM_EXPRESSIONS: + list_for_each_entry_safe(expr, tmp, &s->expr_list, head) + nftnl_expr_free(expr); break; case NFTNL_SET_ELEM_OBJREF: xfree(s->objref); @@ -108,6 +116,8 @@ EXPORT_SYMBOL(nftnl_set_elem_set); int nftnl_set_elem_set(struct nftnl_set_elem *s, uint16_t attr, const void *data, uint32_t data_len) { + struct nftnl_expr *expr, *tmp; + nftnl_assert_attr_exists(attr, NFTNL_SET_ELEM_MAX); nftnl_assert_validate(data, nftnl_set_elem_validate, attr, data_len); @@ -163,10 +173,11 @@ int nftnl_set_elem_set(struct nftnl_set_elem *s, uint16_t attr, return -1; break; case NFTNL_SET_ELEM_EXPR: - if (s->flags & (1 << NFTNL_SET_ELEM_EXPR)) - nftnl_expr_free(s->expr); + list_for_each_entry_safe(expr, tmp, &s->expr_list, head) + nftnl_expr_free(expr); - s->expr = (void *)data; + expr = (void *)data; + list_add(&expr->head, &s->expr_list); break; } s->flags |= (1 << attr); @@ -194,6 +205,8 @@ int nftnl_set_elem_set_str(struct nftnl_set_elem *s, uint16_t attr, const char * EXPORT_SYMBOL(nftnl_set_elem_get); const void *nftnl_set_elem_get(struct nftnl_set_elem *s, uint16_t attr, uint32_t *data_len) { + struct nftnl_expr *expr; + if (!(s->flags & (1 << attr))) return NULL; @@ -226,7 +239,9 @@ const void *nftnl_set_elem_get(struct nftnl_set_elem *s, uint16_t attr, uint32_t *data_len = s->user.len; return s->user.data; case NFTNL_SET_ELEM_EXPR: - return s->expr; + list_for_each_entry(expr, &s->expr_list, head) + break; + return expr; case NFTNL_SET_ELEM_OBJREF: *data_len = strlen(s->objref) + 1; return s->objref; @@ -288,6 +303,9 @@ err: void nftnl_set_elem_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_set_elem *e) { + struct nftnl_expr *expr; + int num_exprs = 0; + if (e->flags & (1 << NFTNL_SET_ELEM_FLAGS)) mnl_attr_put_u32(nlh, NFTA_SET_ELEM_FLAGS, htonl(e->set_elem_flags)); if (e->flags & (1 << NFTNL_SET_ELEM_TIMEOUT)) @@ -332,12 +350,30 @@ void nftnl_set_elem_nlmsg_build_payload(struct nlmsghdr *nlh, mnl_attr_put(nlh, NFTA_SET_ELEM_USERDATA, e->user.len, e->user.data); if (e->flags & (1 << NFTNL_SET_ELEM_OBJREF)) mnl_attr_put_strz(nlh, NFTA_SET_ELEM_OBJREF, e->objref); - if (e->flags & (1 << NFTNL_SET_ELEM_EXPR)) { - struct nlattr *nest1; - nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_EXPR); - nftnl_expr_build_payload(nlh, e->expr); - mnl_attr_nest_end(nlh, nest1); + if (!list_empty(&e->expr_list)) { + list_for_each_entry(expr, &e->expr_list, head) + num_exprs++; + + if (num_exprs == 1) { + struct nlattr *nest1; + + nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_EXPR); + list_for_each_entry(expr, &e->expr_list, head) + nftnl_expr_build_payload(nlh, expr); + + mnl_attr_nest_end(nlh, nest1); + } else if (num_exprs > 1) { + struct nlattr *nest1, *nest2; + + nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_EXPRESSIONS); + list_for_each_entry(expr, &e->expr_list, head) { + nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); + nftnl_expr_build_payload(nlh, expr); + mnl_attr_nest_end(nlh, nest2); + } + mnl_attr_nest_end(nlh, nest1); + } } } @@ -383,6 +419,28 @@ void nftnl_set_elems_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_set mnl_attr_nest_end(nlh, nest1); } +EXPORT_SYMBOL(nftnl_set_elem_add_expr); +void nftnl_set_elem_add_expr(struct nftnl_set_elem *e, struct nftnl_expr *expr) +{ + list_add_tail(&expr->head, &e->expr_list); +} + +EXPORT_SYMBOL(nftnl_set_elem_expr_foreach); +int nftnl_set_elem_expr_foreach(struct nftnl_set_elem *e, + int (*cb)(struct nftnl_expr *e, void *data), + void *data) +{ + struct nftnl_expr *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &e->expr_list, head) { + ret = cb(cur, data); + if (ret < 0) + return ret; + } + return 0; +} + static int nftnl_set_elem_parse_attr_cb(const struct nlattr *attr, void *data) { const struct nlattr **tb = data; @@ -405,6 +463,7 @@ static int nftnl_set_elem_parse_attr_cb(const struct nlattr *attr, void *data) case NFTA_SET_ELEM_KEY_END: case NFTA_SET_ELEM_DATA: case NFTA_SET_ELEM_EXPR: + case NFTA_SET_ELEM_EXPRESSIONS: if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) abi_breakage(); break; @@ -476,12 +535,32 @@ static int nftnl_set_elems_parse2(struct nftnl_set *s, const struct nlattr *nest } } if (tb[NFTA_SET_ELEM_EXPR]) { - e->expr = nftnl_expr_parse(tb[NFTA_SET_ELEM_EXPR]); - if (e->expr == NULL) { + struct nftnl_expr *expr; + + expr = nftnl_expr_parse(tb[NFTA_SET_ELEM_EXPR]); + if (expr == NULL) { ret = -1; goto out_set_elem; } + list_add_tail(&expr->head, &e->expr_list); e->flags |= (1 << NFTNL_SET_ELEM_EXPR); + } else if (tb[NFTA_SET_ELEM_EXPRESSIONS]) { + struct nftnl_expr *expr; + struct nlattr *attr; + + mnl_attr_for_each_nested(attr, tb[NFTA_SET_ELEM_EXPRESSIONS]) { + if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM) { + ret = -1; + goto out_set_elem; + } + expr = nftnl_expr_parse(attr); + if (expr == NULL) { + ret = -1; + goto out_set_elem; + } + list_add_tail(&expr->head, &e->expr_list); + } + e->flags |= (1 << NFTNL_SET_ELEM_EXPRESSIONS); } if (tb[NFTA_SET_ELEM_USERDATA]) { const void *udata = @@ -494,7 +573,7 @@ static int nftnl_set_elems_parse2(struct nftnl_set *s, const struct nlattr *nest e->user.data = malloc(e->user.len); if (e->user.data == NULL) { ret = -1; - goto out_expr; + goto out_set_elem; } memcpy(e->user.data, udata, e->user.len); e->flags |= (1 << NFTNL_RULE_USERDATA); @@ -512,8 +591,6 @@ static int nftnl_set_elems_parse2(struct nftnl_set *s, const struct nlattr *nest list_add_tail(&e->head, &s->element_list); return 0; -out_expr: - nftnl_expr_free(e->expr); out_set_elem: nftnl_set_elem_free(e); return ret; From patchwork Thu Dec 17 16:38:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pablo Neira Ayuso X-Patchwork-Id: 1417782 X-Patchwork-Delegate: pablo@netfilter.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=netfilter.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4Cxd4h3ZNXz9sW8 for ; Fri, 18 Dec 2020 03:39:32 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728160AbgLQQjN (ORCPT ); Thu, 17 Dec 2020 11:39:13 -0500 Received: from correo.us.es ([193.147.175.20]:58546 "EHLO mail.us.es" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727857AbgLQQjN (ORCPT ); Thu, 17 Dec 2020 11:39:13 -0500 Received: from antivirus1-rhel7.int (unknown [192.168.2.11]) by mail.us.es (Postfix) with ESMTP id 13252C1060 for ; Thu, 17 Dec 2020 17:38:13 +0100 (CET) Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id 03EC0DA844 for ; Thu, 17 Dec 2020 17:38:13 +0100 (CET) Received: by antivirus1-rhel7.int (Postfix, from userid 99) id ED896DA840; Thu, 17 Dec 2020 17:38:12 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on antivirus1-rhel7.int X-Spam-Level: X-Spam-Status: No, score=-108.2 required=7.5 tests=ALL_TRUSTED,BAYES_50, SMTPAUTH_US2,URIBL_BLOCKED,USER_IN_WELCOMELIST,USER_IN_WHITELIST autolearn=disabled version=3.4.1 Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id 87221DA73D for ; Thu, 17 Dec 2020 17:38:10 +0100 (CET) Received: from 192.168.1.97 (192.168.1.97) by antivirus1-rhel7.int (F-Secure/fsigk_smtp/550/antivirus1-rhel7.int); Thu, 17 Dec 2020 17:38:10 +0100 (CET) X-Virus-Status: clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int) Received: from localhost.localdomain (unknown [90.77.255.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: pneira@us.es) by entrada.int (Postfix) with ESMTPSA id 68DCC426CC85 for ; Thu, 17 Dec 2020 17:38:10 +0100 (CET) X-SMTPAUTHUS: auth mail.us.es From: Pablo Neira Ayuso To: netfilter-devel@vger.kernel.org Subject: [PATCH 2/3 libnftnl,v2] src: add NFTNL_SET_EXPRESSIONS Date: Thu, 17 Dec 2020 17:38:22 +0100 Message-Id: <20201217163823.24180-2-pablo@netfilter.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201217163823.24180-1-pablo@netfilter.org> References: <20201217163823.24180-1-pablo@netfilter.org> MIME-Version: 1.0 X-Virus-Scanned: ClamAV using ClamSMTP Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org NFTNL_SET_EXPR defines the stateful expression type that this set stores in each element. This provides the set definition in terms of stateful expressions. The expression that is passed via NFNTL_SET_ELEM_EXPR must equal to this set stateful expression type, otherwise the kernel bails out. This patch adds support for the set expression list, which generalizes NFTNL_SET_EXPR. This patch also adds nftnl_set_add_expr() to add new expressions to a set and nftnl_set_elem_expr_foreach() to iterate over the list of expressions. Signed-off-by: Pablo Neira Ayuso --- v2: constify first parameter of _foreach function include/libnftnl/set.h | 7 +++ include/set.h | 2 +- src/libnftnl.map | 2 + src/set.c | 111 +++++++++++++++++++++++++++++++++++------ 4 files changed, 105 insertions(+), 17 deletions(-) diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h index 1804850dc92b..1eae024c8523 100644 --- a/include/libnftnl/set.h +++ b/include/libnftnl/set.h @@ -31,6 +31,7 @@ enum nftnl_set_attr { NFTNL_SET_HANDLE, NFTNL_SET_DESC_CONCAT, NFTNL_SET_EXPR, + NFTNL_SET_EXPRESSIONS, __NFTNL_SET_MAX }; #define NFTNL_SET_MAX (__NFTNL_SET_MAX - 1) @@ -80,6 +81,12 @@ int nftnl_set_list_foreach(struct nftnl_set_list *set_list, int (*cb)(struct nft struct nftnl_set *nftnl_set_list_lookup_byname(struct nftnl_set_list *set_list, const char *set); +struct nftnl_expr; +void nftnl_set_add_expr(struct nftnl_set *s, struct nftnl_expr *expr); +int nftnl_set_expr_foreach(const struct nftnl_set *s, + int (*cb)(struct nftnl_expr *e, void *data), + void *data); + struct nftnl_set_list_iter; struct nftnl_set_list_iter *nftnl_set_list_iter_create(const struct nftnl_set_list *l); struct nftnl_set *nftnl_set_list_iter_cur(const struct nftnl_set_list_iter *iter); diff --git a/include/set.h b/include/set.h index 66ac129836de..55018b6b9ba9 100644 --- a/include/set.h +++ b/include/set.h @@ -33,7 +33,7 @@ struct nftnl_set { uint32_t flags; uint32_t gc_interval; uint64_t timeout; - struct nftnl_expr *expr; + struct list_head expr_list; }; struct nftnl_set_list; diff --git a/src/libnftnl.map b/src/libnftnl.map index ce1c0820de2d..7078a5d38ba8 100644 --- a/src/libnftnl.map +++ b/src/libnftnl.map @@ -376,6 +376,8 @@ LIBNFTNL_15 { } LIBNFTNL_14; LIBNFTNL_16 { + nftnl_set_add_expr; + nftnl_set_expr_foreach; nftnl_set_elem_add_expr; nftnl_set_elem_expr_foreach; } LIBNFTNL_15; diff --git a/src/set.c b/src/set.c index 15fa29d5f02c..8c5025d16206 100644 --- a/src/set.c +++ b/src/set.c @@ -37,6 +37,7 @@ struct nftnl_set *nftnl_set_alloc(void) return NULL; INIT_LIST_HEAD(&s->element_list); + INIT_LIST_HEAD(&s->expr_list); return s; } @@ -44,6 +45,7 @@ EXPORT_SYMBOL(nftnl_set_free); void nftnl_set_free(const struct nftnl_set *s) { struct nftnl_set_elem *elem, *tmp; + struct nftnl_expr *expr, *next; if (s->flags & (1 << NFTNL_SET_TABLE)) xfree(s->table); @@ -51,8 +53,9 @@ void nftnl_set_free(const struct nftnl_set *s) xfree(s->name); if (s->flags & (1 << NFTNL_SET_USERDATA)) xfree(s->user.data); - if (s->flags & (1 << NFTNL_SET_EXPR)) - nftnl_expr_free(s->expr); + + list_for_each_entry_safe(expr, next, &s->expr_list, head) + nftnl_expr_free(expr); list_for_each_entry_safe(elem, tmp, &s->element_list, head) { list_del(&elem->head); @@ -70,6 +73,8 @@ bool nftnl_set_is_set(const struct nftnl_set *s, uint16_t attr) EXPORT_SYMBOL(nftnl_set_unset); void nftnl_set_unset(struct nftnl_set *s, uint16_t attr) { + struct nftnl_expr *expr, *tmp; + if (!(s->flags & (1 << attr))) return; @@ -99,7 +104,9 @@ void nftnl_set_unset(struct nftnl_set *s, uint16_t attr) xfree(s->user.data); break; case NFTNL_SET_EXPR: - nftnl_expr_free(s->expr); + case NFTNL_SET_EXPRESSIONS: + list_for_each_entry_safe(expr, tmp, &s->expr_list, head) + nftnl_expr_free(expr); break; default: return; @@ -127,6 +134,8 @@ EXPORT_SYMBOL(nftnl_set_set_data); int nftnl_set_set_data(struct nftnl_set *s, uint16_t attr, const void *data, uint32_t data_len) { + struct nftnl_expr *expr, *tmp; + nftnl_assert_attr_exists(attr, NFTNL_SET_MAX); nftnl_assert_validate(data, nftnl_set_validate, attr, data_len); @@ -201,10 +210,11 @@ int nftnl_set_set_data(struct nftnl_set *s, uint16_t attr, const void *data, s->user.len = data_len; break; case NFTNL_SET_EXPR: - if (s->flags & (1 << NFTNL_SET_EXPR)) - nftnl_expr_free(s->expr); + list_for_each_entry_safe(expr, tmp, &s->expr_list, head) + nftnl_expr_free(expr); - s->expr = (void *)data; + expr = (void *)data; + list_add(&expr->head, &s->expr_list); break; } s->flags |= (1 << attr); @@ -239,6 +249,8 @@ EXPORT_SYMBOL(nftnl_set_get_data); const void *nftnl_set_get_data(const struct nftnl_set *s, uint16_t attr, uint32_t *data_len) { + struct nftnl_expr *expr; + if (!(s->flags & (1 << attr))) return NULL; @@ -295,7 +307,9 @@ const void *nftnl_set_get_data(const struct nftnl_set *s, uint16_t attr, *data_len = s->user.len; return s->user.data; case NFTNL_SET_EXPR: - return s->expr; + list_for_each_entry(expr, &s->expr_list, head) + break; + return expr; } return NULL; } @@ -414,6 +428,8 @@ nftnl_set_nlmsg_build_desc_payload(struct nlmsghdr *nlh, struct nftnl_set *s) EXPORT_SYMBOL(nftnl_set_nlmsg_build_payload); void nftnl_set_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_set *s) { + int num_exprs = 0; + if (s->flags & (1 << NFTNL_SET_TABLE)) mnl_attr_put_strz(nlh, NFTA_SET_TABLE, s->table); if (s->flags & (1 << NFTNL_SET_NAME)) @@ -445,15 +461,55 @@ void nftnl_set_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_set *s) mnl_attr_put_u32(nlh, NFTA_SET_GC_INTERVAL, htonl(s->gc_interval)); if (s->flags & (1 << NFTNL_SET_USERDATA)) mnl_attr_put(nlh, NFTA_SET_USERDATA, s->user.len, s->user.data); - if (s->flags & (1 << NFTNL_SET_EXPR)) { - struct nlattr *nest1; - - nest1 = mnl_attr_nest_start(nlh, NFTA_SET_EXPR); - nftnl_expr_build_payload(nlh, s->expr); - mnl_attr_nest_end(nlh, nest1); + if (!list_empty(&s->expr_list)) { + struct nftnl_expr *expr; + + list_for_each_entry(expr, &s->expr_list, head) + num_exprs++; + + if (num_exprs == 1) { + struct nlattr *nest1; + + nest1 = mnl_attr_nest_start(nlh, NFTA_SET_EXPR); + list_for_each_entry(expr, &s->expr_list, head) + nftnl_expr_build_payload(nlh, expr); + + mnl_attr_nest_end(nlh, nest1); + } else if (num_exprs > 1) { + struct nlattr *nest1, *nest2; + + nest1 = mnl_attr_nest_start(nlh, NFTA_SET_EXPRESSIONS); + list_for_each_entry(expr, &s->expr_list, head) { + nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); + nftnl_expr_build_payload(nlh, expr); + mnl_attr_nest_end(nlh, nest2); + } + mnl_attr_nest_end(nlh, nest1); + } } } +EXPORT_SYMBOL(nftnl_set_add_expr); +void nftnl_set_add_expr(struct nftnl_set *s, struct nftnl_expr *expr) +{ + list_add_tail(&expr->head, &s->expr_list); +} + +EXPORT_SYMBOL(nftnl_set_expr_foreach); +int nftnl_set_expr_foreach(const struct nftnl_set *s, + int (*cb)(struct nftnl_expr *e, void *data), + void *data) +{ + struct nftnl_expr *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &s->expr_list, head) { + ret = cb(cur, data); + if (ret < 0) + return ret; + } + return 0; +} static int nftnl_set_parse_attr_cb(const struct nlattr *attr, void *data) { @@ -493,6 +549,8 @@ static int nftnl_set_parse_attr_cb(const struct nlattr *attr, void *data) abi_breakage(); break; case NFTA_SET_DESC: + case NFTA_SET_EXPR: + case NFTA_SET_EXPRESSIONS: if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) abi_breakage(); break; @@ -578,6 +636,7 @@ int nftnl_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_set *s) { struct nlattr *tb[NFTA_SET_MAX+1] = {}; struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + struct nftnl_expr *expr, *next; int ret; if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_set_parse_attr_cb, tb) < 0) @@ -656,17 +715,37 @@ int nftnl_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_set *s) return ret; } if (tb[NFTA_SET_EXPR]) { - s->expr = nftnl_expr_parse(tb[NFTA_SET_EXPR]); - if (!s->expr) - return -1; + expr = nftnl_expr_parse(tb[NFTA_SET_EXPR]); + if (!expr) + goto out_set_expr; + list_add(&expr->head, &s->expr_list); s->flags |= (1 << NFTNL_SET_EXPR); + } else if (tb[NFTA_SET_EXPRESSIONS]) { + struct nlattr *attr; + + mnl_attr_for_each_nested(attr, tb[NFTA_SET_EXPRESSIONS]) { + if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM) + goto out_set_expr; + + expr = nftnl_expr_parse(attr); + if (expr == NULL) + goto out_set_expr; + + list_add_tail(&expr->head, &s->expr_list); + } + s->flags |= (1 << NFTNL_SET_EXPRESSIONS); } s->family = nfg->nfgen_family; s->flags |= (1 << NFTNL_SET_FAMILY); return 0; +out_set_expr: + list_for_each_entry_safe(expr, next, &s->expr_list, head) + nftnl_expr_free(expr); + + return -1; } static int nftnl_set_do_parse(struct nftnl_set *s, enum nftnl_parse_type type, From patchwork Thu Dec 17 16:38:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pablo Neira Ayuso X-Patchwork-Id: 1417783 X-Patchwork-Delegate: pablo@netfilter.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=netfilter.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4Cxd4h6RtMz9sWK for ; Fri, 18 Dec 2020 03:39:32 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728161AbgLQQjO (ORCPT ); Thu, 17 Dec 2020 11:39:14 -0500 Received: from correo.us.es ([193.147.175.20]:58548 "EHLO mail.us.es" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728132AbgLQQjO (ORCPT ); Thu, 17 Dec 2020 11:39:14 -0500 Received: from antivirus1-rhel7.int (unknown [192.168.2.11]) by mail.us.es (Postfix) with ESMTP id 96364C1061 for ; Thu, 17 Dec 2020 17:38:13 +0100 (CET) Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id 84D07DA840 for ; Thu, 17 Dec 2020 17:38:13 +0100 (CET) Received: by antivirus1-rhel7.int (Postfix, from userid 99) id 7A50BDA72F; Thu, 17 Dec 2020 17:38:13 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on antivirus1-rhel7.int X-Spam-Level: X-Spam-Status: No, score=-108.2 required=7.5 tests=ALL_TRUSTED,BAYES_50, SMTPAUTH_US2,URIBL_BLOCKED,USER_IN_WELCOMELIST,USER_IN_WHITELIST autolearn=disabled version=3.4.1 Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id 0EC95DA72F for ; Thu, 17 Dec 2020 17:38:11 +0100 (CET) Received: from 192.168.1.97 (192.168.1.97) by antivirus1-rhel7.int (F-Secure/fsigk_smtp/550/antivirus1-rhel7.int); Thu, 17 Dec 2020 17:38:11 +0100 (CET) X-Virus-Status: clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int) Received: from localhost.localdomain (unknown [90.77.255.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: pneira@us.es) by entrada.int (Postfix) with ESMTPSA id EA7A4426CC85 for ; Thu, 17 Dec 2020 17:38:10 +0100 (CET) X-SMTPAUTHUS: auth mail.us.es From: Pablo Neira Ayuso To: netfilter-devel@vger.kernel.org Subject: [PATCH 3/3 libnftnl,v2] src: add NFTNL_EXPR_DYNSET_EXPRESSIONS Date: Thu, 17 Dec 2020 17:38:23 +0100 Message-Id: <20201217163823.24180-3-pablo@netfilter.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201217163823.24180-1-pablo@netfilter.org> References: <20201217163823.24180-1-pablo@netfilter.org> MIME-Version: 1.0 X-Virus-Scanned: ClamAV using ClamSMTP Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org NFTNL_EXPR_DYNSET_EXPR defines the stateful expression type that an element stores when added from the packet path. This patch adds support for the set expression list, which generalizes NFTNL_EXPR_DYNSET_EXPR. This patch also adds nftnl_expr_add_expr() to add new expressions to elements and nftnl_set_expr_expr_foreach() to iterate over the list of expressions. Signed-off-by: Pablo Neira Ayuso --- v2: constify first parameter of _foreach function include/expr_ops.h | 1 + include/libnftnl/expr.h | 7 +++ src/expr.c | 3 ++ src/expr/dynset.c | 111 +++++++++++++++++++++++++++++++++++----- src/libnftnl.map | 2 + 5 files changed, 110 insertions(+), 14 deletions(-) diff --git a/include/expr_ops.h b/include/expr_ops.h index a7f1b9a6abfd..5237ac791588 100644 --- a/include/expr_ops.h +++ b/include/expr_ops.h @@ -12,6 +12,7 @@ struct expr_ops { const char *name; uint32_t alloc_len; int max_attr; + void (*init)(const struct nftnl_expr *e); void (*free)(const struct nftnl_expr *e); int (*set)(struct nftnl_expr *e, uint16_t type, const void *data, uint32_t data_len); const void *(*get)(const struct nftnl_expr *e, uint16_t type, uint32_t *data_len); diff --git a/include/libnftnl/expr.h b/include/libnftnl/expr.h index c2b2d8644bcd..13c55e70b743 100644 --- a/include/libnftnl/expr.h +++ b/include/libnftnl/expr.h @@ -38,6 +38,12 @@ const char *nftnl_expr_get_str(const struct nftnl_expr *expr, uint16_t type); void nftnl_expr_build_payload(struct nlmsghdr *nlh, struct nftnl_expr *expr); +/* For dynset expressions. */ +void nftnl_expr_add_expr(struct nftnl_expr *expr, uint32_t type, struct nftnl_expr *e); +int nftnl_expr_expr_foreach(const struct nftnl_expr *e, + int (*cb)(struct nftnl_expr *e, void *data), + void *data); + int nftnl_expr_snprintf(char *buf, size_t buflen, const struct nftnl_expr *expr, uint32_t type, uint32_t flags); int nftnl_expr_fprintf(FILE *fp, const struct nftnl_expr *expr, uint32_t type, uint32_t flags); @@ -167,6 +173,7 @@ enum { NFTNL_EXPR_DYNSET_SET_NAME, NFTNL_EXPR_DYNSET_SET_ID, NFTNL_EXPR_DYNSET_EXPR, + NFTNL_EXPR_DYNSET_EXPRESSIONS, }; enum { diff --git a/src/expr.c b/src/expr.c index ed2f60e1429f..8e0bce2643b1 100644 --- a/src/expr.c +++ b/src/expr.c @@ -42,6 +42,9 @@ struct nftnl_expr *nftnl_expr_alloc(const char *name) expr->flags |= (1 << NFTNL_EXPR_NAME); expr->ops = ops; + if (ops->init) + ops->init(expr); + return expr; } diff --git a/src/expr/dynset.c b/src/expr/dynset.c index 91dbea930715..f349a17a8701 100644 --- a/src/expr/dynset.c +++ b/src/expr/dynset.c @@ -26,7 +26,7 @@ struct nftnl_expr_dynset { enum nft_registers sreg_data; enum nft_dynset_ops op; uint64_t timeout; - struct nftnl_expr *expr; + struct list_head expr_list; char *set_name; uint32_t set_id; }; @@ -36,6 +36,7 @@ nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type, const void *data, uint32_t data_len) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *expr, *next; switch (type) { case NFTNL_EXPR_DYNSET_SREG_KEY: @@ -59,7 +60,11 @@ nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type, memcpy(&dynset->set_id, data, sizeof(dynset->set_id)); break; case NFTNL_EXPR_DYNSET_EXPR: - dynset->expr = (void *)data; + list_for_each_entry_safe(expr, next, &dynset->expr_list, head) + nftnl_expr_free(expr); + + expr = (void *)data; + list_add(&expr->head, &dynset->expr_list); break; default: return -1; @@ -72,6 +77,7 @@ nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type, uint32_t *data_len) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *expr; switch (type) { case NFTNL_EXPR_DYNSET_SREG_KEY: @@ -93,7 +99,9 @@ nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type, *data_len = sizeof(dynset->set_id); return &dynset->set_id; case NFTNL_EXPR_DYNSET_EXPR: - return dynset->expr; + list_for_each_entry(expr, &dynset->expr_list, head) + break; + return expr; } return NULL; } @@ -137,6 +145,7 @@ nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nlattr *nest; + int num_exprs = 0; if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY)) mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_KEY, htonl(dynset->sreg_key)); @@ -150,11 +159,55 @@ nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) mnl_attr_put_strz(nlh, NFTA_DYNSET_SET_NAME, dynset->set_name); if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID)) mnl_attr_put_u32(nlh, NFTA_DYNSET_SET_ID, htonl(dynset->set_id)); - if (e->flags & (1 << NFTNL_EXPR_DYNSET_EXPR)) { - nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR); - nftnl_expr_build_payload(nlh, dynset->expr); - mnl_attr_nest_end(nlh, nest); + if (!list_empty(&dynset->expr_list)) { + struct nftnl_expr *expr; + + list_for_each_entry(expr, &dynset->expr_list, head) + num_exprs++; + + if (num_exprs == 1) { + nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR); + list_for_each_entry(expr, &dynset->expr_list, head) + nftnl_expr_build_payload(nlh, expr); + mnl_attr_nest_end(nlh, nest); + } else if (num_exprs > 1) { + struct nlattr *nest1, *nest2; + + nest1 = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPRESSIONS); + list_for_each_entry(expr, &dynset->expr_list, head) { + nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); + nftnl_expr_build_payload(nlh, expr); + mnl_attr_nest_end(nlh, nest2); + } + mnl_attr_nest_end(nlh, nest1); + } + } +} + +EXPORT_SYMBOL(nftnl_expr_add_expr); +void nftnl_expr_add_expr(struct nftnl_expr *e, uint32_t type, + struct nftnl_expr *expr) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + + list_add_tail(&expr->head, &dynset->expr_list); +} + +EXPORT_SYMBOL(nftnl_expr_expr_foreach); +int nftnl_expr_expr_foreach(const struct nftnl_expr *e, + int (*cb)(struct nftnl_expr *e, void *data), + void *data) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &dynset->expr_list, head) { + ret = cb(cur, data); + if (ret < 0) + return ret; } + return 0; } static int @@ -162,6 +215,7 @@ nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nlattr *tb[NFTA_SET_MAX+1] = {}; + struct nftnl_expr *expr, *next; int ret = 0; if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0) @@ -195,13 +249,34 @@ nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr) e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_ID); } if (tb[NFTA_DYNSET_EXPR]) { - e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR); - dynset->expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]); - if (dynset->expr == NULL) + expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]); + if (expr == NULL) return -1; + + list_add(&expr->head, &dynset->expr_list); + e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR); + } else if (tb[NFTA_DYNSET_EXPRESSIONS]) { + struct nlattr *attr2; + + mnl_attr_for_each_nested(attr2, tb[NFTA_DYNSET_EXPRESSIONS]) { + if (mnl_attr_get_type(attr2) != NFTA_LIST_ELEM) + goto out_dynset_expr; + + expr = nftnl_expr_parse(attr2); + if (!expr) + goto out_dynset_expr; + + list_add_tail(&expr->head, &dynset->expr_list); + } + e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPRESSIONS); } return ret; +out_dynset_expr: + list_for_each_entry_safe(expr, next, &dynset->expr_list, head) + nftnl_expr_free(expr); + + return -1; } static const char *op2str_array[] = { @@ -239,8 +314,7 @@ nftnl_expr_dynset_snprintf_default(char *buf, size_t size, dynset->timeout); SNPRINTF_BUFFER_SIZE(ret, remain, offset); } - if (e->flags & (1 << NFTNL_EXPR_DYNSET_EXPR)) { - expr = dynset->expr; + list_for_each_entry(expr, &dynset->expr_list, head) { ret = snprintf(buf + offset, remain, "expr [ %s ", expr->ops->name); SNPRINTF_BUFFER_SIZE(ret, remain, offset); @@ -272,19 +346,28 @@ nftnl_expr_dynset_snprintf(char *buf, size_t size, uint32_t type, return -1; } +static void nftnl_expr_dynset_init(const struct nftnl_expr *e) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + + INIT_LIST_HEAD(&dynset->expr_list); +} + static void nftnl_expr_dynset_free(const struct nftnl_expr *e) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *expr, *next; xfree(dynset->set_name); - if (dynset->expr) - nftnl_expr_free(dynset->expr); + list_for_each_entry_safe(expr, next, &dynset->expr_list, head) + nftnl_expr_free(expr); } struct expr_ops expr_ops_dynset = { .name = "dynset", .alloc_len = sizeof(struct nftnl_expr_dynset), .max_attr = NFTA_DYNSET_MAX, + .init = nftnl_expr_dynset_init, .free = nftnl_expr_dynset_free, .set = nftnl_expr_dynset_set, .get = nftnl_expr_dynset_get, diff --git a/src/libnftnl.map b/src/libnftnl.map index 7078a5d38ba8..e707b89cfdfd 100644 --- a/src/libnftnl.map +++ b/src/libnftnl.map @@ -380,4 +380,6 @@ LIBNFTNL_16 { nftnl_set_expr_foreach; nftnl_set_elem_add_expr; nftnl_set_elem_expr_foreach; + nftnl_expr_add_expr; + nftnl_expr_expr_foreach; } LIBNFTNL_15;