From patchwork Tue Jan 23 12:10:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pablo Neira Ayuso X-Patchwork-Id: 864738 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=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zQnDs20PYz9t3l for ; Tue, 23 Jan 2018 23:10:21 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751321AbeAWMKS (ORCPT ); Tue, 23 Jan 2018 07:10:18 -0500 Received: from mail.us.es ([193.147.175.20]:39544 "EHLO mail.us.es" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751086AbeAWMKR (ORCPT ); Tue, 23 Jan 2018 07:10:17 -0500 Received: from antivirus1-rhel7.int (unknown [192.168.2.11]) by mail.us.es (Postfix) with ESMTP id 7CA6A210566 for ; Tue, 23 Jan 2018 13:10:15 +0100 (CET) Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id 5E0D0DA842 for ; Tue, 23 Jan 2018 13:10:15 +0100 (CET) Received: by antivirus1-rhel7.int (Postfix, from userid 99) id 532F0DA811; Tue, 23 Jan 2018 13:10:15 +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,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 9E642DA861 for ; Tue, 23 Jan 2018 13:10:07 +0100 (CET) Received: from 192.168.1.97 (192.168.1.97) by antivirus1-rhel7.int (F-Secure/fsigk_smtp/550/antivirus1-rhel7.int); Tue, 23 Jan 2018 13:10:07 +0100 (CET) X-Virus-Status: clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int) Received: from salvia.here (129.166.216.87.static.jazztel.es [87.216.166.129]) (Authenticated sender: pneira@us.es) by entrada.int (Postfix) with ESMTPA id 116134265A2F for ; Tue, 23 Jan 2018 13:10:06 +0100 (CET) X-SMTPAUTHUS: auth mail.us.es From: Pablo Neira Ayuso To: netfilter-devel@vger.kernel.org Subject: [PATCH libnftnl 1/2] src: add flowtable support Date: Tue, 23 Jan 2018 13:10:02 +0100 Message-Id: <20180123121003.16064-1-pablo@netfilter.org> X-Mailer: git-send-email 2.11.0 X-Virus-Scanned: ClamAV using ClamSMTP Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org This patch allows you to add, delete and list flowtable through the existing netlink interface. Signed-off-by: Pablo Neira Ayuso --- examples/Makefile.am | 12 + examples/nft-flowtable-add.c | 136 +++++++ examples/nft-flowtable-del.c | 122 ++++++ examples/nft-flowtable-get.c | 121 ++++++ include/libnftnl/Makefile.am | 1 + include/libnftnl/flowtable.h | 81 ++++ include/linux/netfilter/nf_tables.h | 53 +++ src/Makefile.am | 1 + src/flowtable.c | 793 ++++++++++++++++++++++++++++++++++++ src/libnftnl.map | 31 ++ 10 files changed, 1351 insertions(+) create mode 100644 examples/nft-flowtable-add.c create mode 100644 examples/nft-flowtable-del.c create mode 100644 examples/nft-flowtable-get.c create mode 100644 include/libnftnl/flowtable.h create mode 100644 src/flowtable.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 48bc7a16c9c6..c0d84ebbcbff 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -25,6 +25,9 @@ check_PROGRAMS = nft-table-add \ nft-obj-add \ nft-obj-get \ nft-obj-del \ + nft-flowtable-add \ + nft-flowtable-del \ + nft-flowtable-get \ nft-ruleset-get \ nft-ruleset-parse-file \ nft-compat-get @@ -104,6 +107,15 @@ nft_obj_del_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} nft_obj_get_SOURCES = nft-obj-get.c nft_obj_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} +nft_flowtable_add_SOURCES = nft-flowtable-add.c +nft_flowtable_add_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + +nft_flowtable_del_SOURCES = nft-flowtable-del.c +nft_flowtable_del_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + +nft_flowtable_get_SOURCES = nft-flowtable-get.c +nft_flowtable_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} + nft_ruleset_get_SOURCES = nft-ruleset-get.c nft_ruleset_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS} diff --git a/examples/nft-flowtable-add.c b/examples/nft-flowtable-add.c new file mode 100644 index 000000000000..bd6cd0f59b79 --- /dev/null +++ b/examples/nft-flowtable-add.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +static struct nftnl_flowtable *flowtable_add_parse(int argc, char *argv[]) +{ + const char *dev_array[] = { "eth0", "tap0", NULL }; + struct nftnl_flowtable *t; + int hooknum = 0; + + if (strcmp(argv[4], "ingress") == 0) + hooknum = NF_NETDEV_INGRESS; + else { + fprintf(stderr, "Unknown hook: %s\n", argv[4]); + return NULL; + } + + t = nftnl_flowtable_alloc(); + if (t == NULL) { + perror("OOM"); + return NULL; + } + nftnl_flowtable_set(t, NFTNL_FLOWTABLE_TABLE, argv[2]); + nftnl_flowtable_set(t, NFTNL_FLOWTABLE_NAME, argv[3]); + if (argc == 6) { + nftnl_flowtable_set_u32(t, NFTNL_FLOWTABLE_HOOKNUM, hooknum); + nftnl_flowtable_set_u32(t, NFTNL_FLOWTABLE_PRIO, atoi(argv[5])); + } + nftnl_flowtable_set_array(t, NFTNL_FLOWTABLE_DEVICES, dev_array); + + return t; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, flowtable_seq; + int ret, family; + struct nftnl_flowtable *t; + struct mnl_nlmsg_batch *batch; + int batching; + + if (argc != 6) { + fprintf(stderr, "Usage: %s \n", + argv[0]); + exit(EXIT_FAILURE); + } + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else if (strcmp(argv[1], "bridge") == 0) + family = NFPROTO_BRIDGE; + else if (strcmp(argv[1], "arp") == 0) + family = NFPROTO_ARP; + else { + fprintf(stderr, "Unknown family: ip, ip6, bridge, arp\n"); + exit(EXIT_FAILURE); + } + + t = flowtable_add_parse(argc, argv); + if (t == NULL) + exit(EXIT_FAILURE); + + batching = nftnl_batch_is_supported(); + if (batching < 0) { + perror("cannot talk to nfnetlink"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + if (batching) { + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + + flowtable_seq = seq; + nlh = nftnl_flowtable_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWFLOWTABLE, family, + NLM_F_CREATE|NLM_F_ACK, seq++); + nftnl_flowtable_nlmsg_build_payload(nlh, t); + nftnl_flowtable_free(t); + mnl_nlmsg_batch_next(batch); + + if (batching) { + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch)) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + mnl_nlmsg_batch_stop(batch); + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, flowtable_seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); + + return EXIT_SUCCESS; +} diff --git a/examples/nft-flowtable-del.c b/examples/nft-flowtable-del.c new file mode 100644 index 000000000000..b25f041a6f6d --- /dev/null +++ b/examples/nft-flowtable-del.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +static struct nftnl_flowtable *flowtable_del_parse(int argc, char *argv[]) +{ + struct nftnl_flowtable *t; + + t = nftnl_flowtable_alloc(); + if (t == NULL) { + perror("OOM"); + return NULL; + } + + nftnl_flowtable_set(t, NFTNL_FLOWTABLE_TABLE, argv[2]); + nftnl_flowtable_set(t, NFTNL_FLOWTABLE_NAME, argv[3]); + + return t; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + struct mnl_nlmsg_batch *batch; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, flowtable_seq; + struct nftnl_flowtable *t; + int ret, family, batching; + + if (argc != 4) { + fprintf(stderr, "Usage: %s
\n", + argv[0]); + exit(EXIT_FAILURE); + } + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else if (strcmp(argv[1], "bridge") == 0) + family = NFPROTO_BRIDGE; + else if (strcmp(argv[1], "arp") == 0) + family = NFPROTO_ARP; + else { + fprintf(stderr, "Unknown family: ip, ip6, bridge, arp\n"); + exit(EXIT_FAILURE); + } + + t = flowtable_del_parse(argc, argv); + if (t == NULL) + exit(EXIT_FAILURE); + + batching = nftnl_batch_is_supported(); + if (batching < 0) { + perror("cannot talk to nfnetlink"); + exit(EXIT_FAILURE); + } + + seq = time(NULL); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + if (batching) { + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + + flowtable_seq = seq; + nlh = nftnl_flowtable_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_DELFLOWTABLE, family, + NLM_F_ACK, seq++); + nftnl_flowtable_nlmsg_build_payload(nlh, t); + nftnl_flowtable_free(t); + mnl_nlmsg_batch_next(batch); + + if (batching) { + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + } + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch)) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + mnl_nlmsg_batch_stop(batch); + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, flowtable_seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); + + return EXIT_SUCCESS; +} diff --git a/examples/nft-flowtable-get.c b/examples/nft-flowtable-get.c new file mode 100644 index 000000000000..37cfadf0b1cd --- /dev/null +++ b/examples/nft-flowtable-get.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +static int table_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_flowtable *t; + char buf[4096]; + uint32_t *type = data; + + t = nftnl_flowtable_alloc(); + if (t == NULL) { + perror("OOM"); + goto err; + } + + if (nftnl_flowtable_nlmsg_parse(nlh, t) < 0) { + perror("nftnl_flowtable_nlmsg_parse"); + goto err_free; + } + + nftnl_flowtable_snprintf(buf, sizeof(buf), t, *type, 0); + printf("%s\n", buf); + +err_free: + nftnl_flowtable_free(t); +err: + return MNL_CB_OK; +} + +int main(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, type = NFTNL_OUTPUT_DEFAULT; + struct nftnl_flowtable *t = NULL; + int ret, family; + + seq = time(NULL); + + if (argc < 2 || argc > 5) { + fprintf(stderr, "Usage: %s [
] [json]\n", + argv[0]); + exit(EXIT_FAILURE); + } + + if (strcmp(argv[1], "ip") == 0) + family = NFPROTO_IPV4; + else if (strcmp(argv[1], "ip6") == 0) + family = NFPROTO_IPV6; + else if (strcmp(argv[1], "bridge") == 0) + family = NFPROTO_BRIDGE; + else if (strcmp(argv[1], "arp") == 0) + family = NFPROTO_ARP; + else if (strcmp(argv[1], "unspec") == 0) + family = NFPROTO_UNSPEC; + else { + fprintf(stderr, "Unknown family: ip, ip6, bridge, arp, unspec\n"); + exit(EXIT_FAILURE); + } + + if (argc >= 4) { + t = nftnl_flowtable_alloc(); + if (t == NULL) { + perror("OOM"); + exit(EXIT_FAILURE); + } + nlh = nftnl_flowtable_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family, + NLM_F_ACK, seq); + nftnl_flowtable_set(t, NFTNL_FLOWTABLE_TABLE, argv[2]); + nftnl_flowtable_set(t, NFTNL_FLOWTABLE_NAME, argv[3]); + nftnl_flowtable_nlmsg_build_payload(nlh, t); + nftnl_flowtable_free(t); + } else if (argc >= 2) { + nlh = nftnl_flowtable_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family, + NLM_F_DUMP, seq); + } + + if (strcmp(argv[argc-1], "json") == 0) + type = NFTNL_OUTPUT_JSON; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_send"); + exit(EXIT_FAILURE); + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, table_cb, &type); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + perror("error"); + exit(EXIT_FAILURE); + } + mnl_socket_close(nl); + + return EXIT_SUCCESS; +} diff --git a/include/libnftnl/Makefile.am b/include/libnftnl/Makefile.am index 6dc7b2b38590..d846a574f438 100644 --- a/include/libnftnl/Makefile.am +++ b/include/libnftnl/Makefile.am @@ -6,6 +6,7 @@ pkginclude_HEADERS = batch.h \ rule.h \ expr.h \ set.h \ + flowtable.h \ ruleset.h \ common.h \ udata.h \ diff --git a/include/libnftnl/flowtable.h b/include/libnftnl/flowtable.h new file mode 100644 index 000000000000..0f8f3252da18 --- /dev/null +++ b/include/libnftnl/flowtable.h @@ -0,0 +1,81 @@ +#ifndef _LIBNFTNL_FLOWTABLE_H_ +#define _LIBNFTNL_FLOWTABLE_H_ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nftnl_flowtable; + +struct nftnl_flowtable *nftnl_flowtable_alloc(void); +void nftnl_flowtable_free(const struct nftnl_flowtable *); + +enum nftnl_flowtable_attr { + NFTNL_FLOWTABLE_NAME = 0, + NFTNL_FLOWTABLE_FAMILY, + NFTNL_FLOWTABLE_TABLE, + NFTNL_FLOWTABLE_HOOKNUM, + NFTNL_FLOWTABLE_PRIO = 4, + NFTNL_FLOWTABLE_USE, + NFTNL_FLOWTABLE_DEVICES, + __NFTNL_FLOWTABLE_MAX +}; +#define NFTNL_FLOWTABLE_MAX (__NFTNL_FLOWTABLE_MAX - 1) + +bool nftnl_flowtable_is_set(const struct nftnl_flowtable *c, uint16_t attr); +void nftnl_flowtable_unset(struct nftnl_flowtable *c, uint16_t attr); +void nftnl_flowtable_set(struct nftnl_flowtable *t, uint16_t attr, const void *data); +int nftnl_flowtable_set_data(struct nftnl_flowtable *t, uint16_t attr, + const void *data, uint32_t data_len); +void nftnl_flowtable_set_u32(struct nftnl_flowtable *t, uint16_t attr, uint32_t data); +void nftnl_flowtable_set_s32(struct nftnl_flowtable *t, uint16_t attr, int32_t data); +int nftnl_flowtable_set_str(struct nftnl_flowtable *t, uint16_t attr, const char *str); +void nftnl_flowtable_set_array(struct nftnl_flowtable *t, uint16_t attr, const char **data); + +const void *nftnl_flowtable_get(const struct nftnl_flowtable *c, uint16_t attr); +const void *nftnl_flowtable_get_data(const struct nftnl_flowtable *c, uint16_t attr, + uint32_t *data_len); +const char *nftnl_flowtable_get_str(const struct nftnl_flowtable *c, uint16_t attr); +uint32_t nftnl_flowtable_get_u32(const struct nftnl_flowtable *c, uint16_t attr); +int32_t nftnl_flowtable_get_s32(const struct nftnl_flowtable *c, uint16_t attr); +const char **nftnl_flowtable_get_array(const struct nftnl_flowtable *t, uint16_t attr); + +struct nlmsghdr; + +void nftnl_flowtable_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nftnl_flowtable *t); + +int nftnl_flowtable_parse(struct nftnl_flowtable *c, enum nftnl_parse_type type, + const char *data, struct nftnl_parse_err *err); +int nftnl_flowtable_parse_file(struct nftnl_flowtable *c, enum nftnl_parse_type type, + FILE *fp, struct nftnl_parse_err *err); +int nftnl_flowtable_snprintf(char *buf, size_t size, const struct nftnl_flowtable *t, uint32_t type, uint32_t flags); +int nftnl_flowtable_fprintf(FILE *fp, const struct nftnl_flowtable *c, uint32_t type, uint32_t flags); + +#define nftnl_flowtable_nlmsg_build_hdr nftnl_nlmsg_build_hdr +int nftnl_flowtable_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_flowtable *t); + +struct nftnl_flowtable_list; + +struct nftnl_flowtable_list *nftnl_flowtable_list_alloc(void); +void nftnl_flowtable_list_free(struct nftnl_flowtable_list *list); +int nftnl_flowtable_list_is_empty(const struct nftnl_flowtable_list *list); +void nftnl_flowtable_list_add(struct nftnl_flowtable *s, + struct nftnl_flowtable_list *list); +void nftnl_flowtable_list_add_tail(struct nftnl_flowtable *s, + struct nftnl_flowtable_list *list); +void nftnl_flowtable_list_del(struct nftnl_flowtable *s); +int nftnl_flowtable_list_foreach(struct nftnl_flowtable_list *flowtable_list, + int (*cb)(struct nftnl_flowtable *t, void *data), void *data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _LIBNFTNL_FLOWTABLE_H_ */ diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 874fa3f239eb..c525e67a665d 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -90,6 +90,9 @@ enum nft_verdicts { * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes) + * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes) + * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes) + * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -114,6 +117,9 @@ enum nf_tables_msg_types { NFT_MSG_GETOBJ, NFT_MSG_DELOBJ, NFT_MSG_GETOBJ_RESET, + NFT_MSG_NEWFLOWTABLE, + NFT_MSG_GETFLOWTABLE, + NFT_MSG_DELFLOWTABLE, NFT_MSG_MAX, }; @@ -1303,6 +1309,53 @@ enum nft_object_attributes { #define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1) /** + * enum nft_flowtable_attributes - nf_tables flow table netlink attributes + * + * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING) + * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING) + * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32) + * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32) + */ +enum nft_flowtable_attributes { + NFTA_FLOWTABLE_UNSPEC, + NFTA_FLOWTABLE_TABLE, + NFTA_FLOWTABLE_NAME, + NFTA_FLOWTABLE_HOOK, + NFTA_FLOWTABLE_USE, + __NFTA_FLOWTABLE_MAX +}; +#define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1) + +/** + * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes + * + * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32) + * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32) + * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED) + */ +enum nft_flowtable_hook_attributes { + NFTA_FLOWTABLE_HOOK_UNSPEC, + NFTA_FLOWTABLE_HOOK_NUM, + NFTA_FLOWTABLE_HOOK_PRIORITY, + NFTA_FLOWTABLE_HOOK_DEVS, + __NFTA_FLOWTABLE_HOOK_MAX +}; +#define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1) + +/** + * enum nft_device_attributes - nf_tables device netlink attributes + * + * @NFTA_DEVICE_NAME: name of this device (NLA_STRING) + */ +enum nft_devices_attributes { + NFTA_DEVICE_UNSPEC, + NFTA_DEVICE_NAME, + __NFTA_DEVICE_MAX +}; +#define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1) + + +/** * enum nft_trace_attributes - nf_tables trace netlink attributes * * @NFTA_TRACE_TABLE: name of the table (NLA_STRING) diff --git a/src/Makefile.am b/src/Makefile.am index 59ddf6a249a2..7708c279c9da 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,7 @@ libnftnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnftnl.map \ libnftnl_la_SOURCES = utils.c \ batch.c \ buffer.c \ + flowtable.c \ common.c \ gen.c \ table.c \ diff --git a/src/flowtable.c b/src/flowtable.c new file mode 100644 index 000000000000..61f18044c491 --- /dev/null +++ b/src/flowtable.c @@ -0,0 +1,793 @@ +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +struct nftnl_flowtable { + struct list_head head; + const char *name; + const char *table; + int family; + uint32_t hooknum; + int32_t prio; + const char **dev_array; + uint32_t dev_array_len; + uint32_t use; + uint32_t flags; +}; + +struct nftnl_flowtable *nftnl_flowtable_alloc(void) +{ + return calloc(1, sizeof(struct nftnl_flowtable)); +} +EXPORT_SYMBOL(nftnl_flowtable_alloc); + +void nftnl_flowtable_free(const struct nftnl_flowtable *c) +{ + int i; + + if (c->flags & (1 << NFTNL_FLOWTABLE_NAME)) + xfree(c->name); + if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE)) + xfree(c->table); + if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) { + for (i = 0; i < c->dev_array_len; i++) + xfree(c->dev_array[i]); + + xfree(c->dev_array); + } + xfree(c); +} +EXPORT_SYMBOL(nftnl_flowtable_free); + +bool nftnl_flowtable_is_set(const struct nftnl_flowtable *c, uint16_t attr) +{ + return c->flags & (1 << attr); +} +EXPORT_SYMBOL(nftnl_flowtable_is_set); + +void nftnl_flowtable_unset(struct nftnl_flowtable *c, uint16_t attr) +{ + int i; + + if (!(c->flags & (1 << attr))) + return; + + switch (attr) { + case NFTNL_FLOWTABLE_NAME: + xfree(c->name); + break; + case NFTNL_FLOWTABLE_TABLE: + xfree(c->table); + break; + case NFTNL_FLOWTABLE_HOOKNUM: + case NFTNL_FLOWTABLE_PRIO: + case NFTNL_FLOWTABLE_USE: + case NFTNL_FLOWTABLE_FAMILY: + break; + case NFTNL_FLOWTABLE_DEVICES: + for (i = 0; i < c->dev_array_len; i++) { + xfree(c->dev_array[i]); + xfree(c->dev_array); + } + break; + default: + return; + } + + c->flags &= ~(1 << attr); +} +EXPORT_SYMBOL(nftnl_flowtable_unset); + +static uint32_t nftnl_flowtable_validate[NFTNL_FLOWTABLE_MAX + 1] = { + [NFTNL_FLOWTABLE_HOOKNUM] = sizeof(uint32_t), + [NFTNL_FLOWTABLE_PRIO] = sizeof(int32_t), + [NFTNL_FLOWTABLE_FAMILY] = sizeof(uint32_t), +}; + +int nftnl_flowtable_set_data(struct nftnl_flowtable *c, uint16_t attr, + const void *data, uint32_t data_len) +{ + const char **dev_array; + int len = 0, i; + + nftnl_assert_attr_exists(attr, NFTNL_FLOWTABLE_MAX); + nftnl_assert_validate(data, nftnl_flowtable_validate, attr, data_len); + + switch(attr) { + case NFTNL_FLOWTABLE_NAME: + if (c->flags & (1 << NFTNL_FLOWTABLE_NAME)) + xfree(c->name); + + c->name = strdup(data); + if (!c->name) + return -1; + break; + case NFTNL_FLOWTABLE_TABLE: + if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE)) + xfree(c->table); + + c->table = strdup(data); + if (!c->table) + return -1; + break; + case NFTNL_FLOWTABLE_HOOKNUM: + memcpy(&c->hooknum, data, sizeof(c->hooknum)); + break; + case NFTNL_FLOWTABLE_PRIO: + memcpy(&c->prio, data, sizeof(c->prio)); + break; + case NFTNL_FLOWTABLE_FAMILY: + memcpy(&c->family, data, sizeof(c->family)); + break; + case NFTNL_FLOWTABLE_DEVICES: + dev_array = (const char **)data; + while (dev_array[len] != NULL) + len++; + + if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) { + for (i = 0; i < c->dev_array_len; i++) { + xfree(c->dev_array[i]); + xfree(c->dev_array); + } + } + + c->dev_array = calloc(len + 1, sizeof(char *)); + if (!c->dev_array) + return -1; + + for (i = 0; i < len; i++) + c->dev_array[i] = strdup(dev_array[i]); + + c->dev_array_len = len; + break; + } + c->flags |= (1 << attr); + return 0; +} +EXPORT_SYMBOL(nftnl_flowtable_set_data); + +void nftnl_flowtable_set(struct nftnl_flowtable *c, uint16_t attr, const void *data) +{ + nftnl_flowtable_set_data(c, attr, data, nftnl_flowtable_validate[attr]); +} +EXPORT_SYMBOL(nftnl_flowtable_set); + +void nftnl_flowtable_set_array(struct nftnl_flowtable *c, uint16_t attr, const char **data) +{ + nftnl_flowtable_set_data(c, attr, &data[0], nftnl_flowtable_validate[attr]); +} +EXPORT_SYMBOL(nftnl_flowtable_set_array); + +void nftnl_flowtable_set_u32(struct nftnl_flowtable *c, uint16_t attr, uint32_t data) +{ + nftnl_flowtable_set_data(c, attr, &data, sizeof(uint32_t)); +} +EXPORT_SYMBOL(nftnl_flowtable_set_u32); + +void nftnl_flowtable_set_s32(struct nftnl_flowtable *c, uint16_t attr, int32_t data) +{ + nftnl_flowtable_set_data(c, attr, &data, sizeof(int32_t)); +} +EXPORT_SYMBOL(nftnl_flowtable_set_s32); + +int nftnl_flowtable_set_str(struct nftnl_flowtable *c, uint16_t attr, const char *str) +{ + return nftnl_flowtable_set_data(c, attr, str, strlen(str) + 1); +} +EXPORT_SYMBOL(nftnl_flowtable_set_str); + +const void *nftnl_flowtable_get_data(const struct nftnl_flowtable *c, + uint16_t attr, uint32_t *data_len) +{ + if (!(c->flags & (1 << attr))) + return NULL; + + switch(attr) { + case NFTNL_FLOWTABLE_NAME: + *data_len = strlen(c->name) + 1; + return c->name; + case NFTNL_FLOWTABLE_TABLE: + *data_len = strlen(c->table) + 1; + return c->table; + case NFTNL_FLOWTABLE_HOOKNUM: + *data_len = sizeof(uint32_t); + return &c->hooknum; + case NFTNL_FLOWTABLE_PRIO: + *data_len = sizeof(int32_t); + return &c->prio; + case NFTNL_FLOWTABLE_FAMILY: + *data_len = sizeof(int32_t); + return &c->family; + case NFTNL_FLOWTABLE_DEVICES: + return &c->dev_array[0]; + } + return NULL; +} +EXPORT_SYMBOL(nftnl_flowtable_get_data); + +const void *nftnl_flowtable_get(const struct nftnl_flowtable *c, uint16_t attr) +{ + uint32_t data_len; + return nftnl_flowtable_get_data(c, attr, &data_len); +} +EXPORT_SYMBOL(nftnl_flowtable_get); + +const char *nftnl_flowtable_get_str(const struct nftnl_flowtable *c, uint16_t attr) +{ + return nftnl_flowtable_get(c, attr); +} +EXPORT_SYMBOL(nftnl_flowtable_get_str); + +uint32_t nftnl_flowtable_get_u32(const struct nftnl_flowtable *c, uint16_t attr) +{ + uint32_t data_len; + const uint32_t *val = nftnl_flowtable_get_data(c, attr, &data_len); + + nftnl_assert(val, attr, data_len == sizeof(uint32_t)); + + return val ? *val : 0; +} +EXPORT_SYMBOL(nftnl_flowtable_get_u32); + +int32_t nftnl_flowtable_get_s32(const struct nftnl_flowtable *c, uint16_t attr) +{ + uint32_t data_len; + const int32_t *val = nftnl_flowtable_get_data(c, attr, &data_len); + + nftnl_assert(val, attr, data_len == sizeof(int32_t)); + + return val ? *val : 0; +} +EXPORT_SYMBOL(nftnl_flowtable_get_s32); + +const char **nftnl_flowtable_get_array(const struct nftnl_flowtable *c, uint16_t attr) +{ + return (const char **)nftnl_flowtable_get(c, attr); +} +EXPORT_SYMBOL(nftnl_flowtable_get_array); + +void nftnl_flowtable_nlmsg_build_payload(struct nlmsghdr *nlh, + const struct nftnl_flowtable *c) +{ + int i; + + if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE)) + mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, c->table); + if (c->flags & (1 << NFTNL_FLOWTABLE_NAME)) + mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, c->name); + if ((c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM)) && + (c->flags & (1 << NFTNL_FLOWTABLE_PRIO))) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK); + mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_NUM, htonl(c->hooknum)); + mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(c->prio)); + if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) { + struct nlattr *nest_dev; + + nest_dev = mnl_attr_nest_start(nlh, + NFTA_FLOWTABLE_HOOK_DEVS); + for (i = 0; i < c->dev_array_len; i++) + mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, + c->dev_array[i]); + mnl_attr_nest_end(nlh, nest_dev); + } + mnl_attr_nest_end(nlh, nest); + } + if (c->flags & (1 << NFTNL_FLOWTABLE_USE)) + mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_USE, htonl(c->use)); +} +EXPORT_SYMBOL(nftnl_flowtable_nlmsg_build_payload); + +static int nftnl_flowtable_parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_FLOWTABLE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_FLOWTABLE_NAME: + case NFTA_FLOWTABLE_TABLE: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + case NFTA_FLOWTABLE_HOOK: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + abi_breakage(); + break; + case NFTA_FLOWTABLE_USE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nftnl_flowtable_parse_hook_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_FLOWTABLE_HOOK_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_FLOWTABLE_HOOK_NUM: + case NFTA_FLOWTABLE_HOOK_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_FLOWTABLE_HOOK_DEVS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nftnl_flowtable_parse_devs(struct nlattr *nest, + struct nftnl_flowtable *c) +{ + struct nlattr *attr; + char *dev_array[8]; + int len = 0, i; + + mnl_attr_for_each_nested(attr, nest) { + if (mnl_attr_get_type(attr) != NFTA_DEVICE_NAME) + return -1; + dev_array[len++] = strdup(mnl_attr_get_str(attr)); + if (len >= 8) + break; + } + + if (!len) + return -1; + + c->dev_array = calloc(len + 1, sizeof(char *)); + if (!c->dev_array) + return -1; + + c->dev_array_len = len; + + for (i = 0; i < len; i++) + c->dev_array[i] = strdup(dev_array[i]); + + return 0; +} + +static int nftnl_flowtable_parse_hook(struct nlattr *attr, struct nftnl_flowtable *c) +{ + struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1] = {}; + int ret; + + if (mnl_attr_parse_nested(attr, nftnl_flowtable_parse_hook_cb, tb) < 0) + return -1; + + if (tb[NFTA_FLOWTABLE_HOOK_NUM]) { + c->hooknum = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_HOOK_NUM])); + c->flags |= (1 << NFTNL_FLOWTABLE_HOOKNUM); + } + if (tb[NFTA_FLOWTABLE_HOOK_PRIORITY]) { + c->prio = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY])); + c->flags |= (1 << NFTNL_FLOWTABLE_PRIO); + } + if (tb[NFTA_FLOWTABLE_HOOK_DEVS]) { + ret = nftnl_flowtable_parse_devs(tb[NFTA_FLOWTABLE_HOOK_DEVS], c); + if (ret < 0) + return -1; + c->flags |= (1 << NFTNL_FLOWTABLE_DEVICES); + } + + return 0; +} + +int nftnl_flowtable_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_flowtable *c) +{ + struct nlattr *tb[NFTA_FLOWTABLE_MAX + 1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + int ret = 0; + + if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_flowtable_parse_attr_cb, tb) < 0) + return -1; + + if (tb[NFTA_FLOWTABLE_NAME]) { + if (c->flags & (1 << NFTNL_FLOWTABLE_NAME)) + xfree(c->name); + c->name = strdup(mnl_attr_get_str(tb[NFTA_FLOWTABLE_NAME])); + if (!c->name) + return -1; + c->flags |= (1 << NFTNL_FLOWTABLE_NAME); + } + if (tb[NFTA_FLOWTABLE_TABLE]) { + if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE)) + xfree(c->table); + c->table = strdup(mnl_attr_get_str(tb[NFTA_FLOWTABLE_TABLE])); + if (!c->table) + return -1; + c->flags |= (1 << NFTNL_FLOWTABLE_TABLE); + } + if (tb[NFTA_FLOWTABLE_HOOK]) { + ret = nftnl_flowtable_parse_hook(tb[NFTA_FLOWTABLE_HOOK], c); + if (ret < 0) + return ret; + } + if (tb[NFTA_FLOWTABLE_USE]) { + c->use = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_USE])); + c->flags |= (1 << NFTNL_FLOWTABLE_USE); + } + + c->family = nfg->nfgen_family; + c->flags |= (1 << NFTNL_FLOWTABLE_FAMILY); + + return ret; +} +EXPORT_SYMBOL(nftnl_flowtable_nlmsg_parse); + +static const char *nftnl_hooknum2str(int family, int hooknum) +{ + switch (family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + case NFPROTO_BRIDGE: + switch (hooknum) { + case NF_INET_PRE_ROUTING: + return "prerouting"; + case NF_INET_LOCAL_IN: + return "input"; + case NF_INET_FORWARD: + return "forward"; + case NF_INET_LOCAL_OUT: + return "output"; + case NF_INET_POST_ROUTING: + return "postrouting"; + } + break; + case NFPROTO_ARP: + switch (hooknum) { + case NF_ARP_IN: + return "input"; + case NF_ARP_OUT: + return "output"; + case NF_ARP_FORWARD: + return "forward"; + } + break; + case NFPROTO_NETDEV: + switch (hooknum) { + case NF_NETDEV_INGRESS: + return "ingress"; + } + break; + } + return "unknown"; +} + +static inline int nftnl_str2hooknum(int family, const char *hook) +{ + int hooknum; + + for (hooknum = 0; hooknum < NF_INET_NUMHOOKS; hooknum++) { + if (strcmp(hook, nftnl_hooknum2str(family, hooknum)) == 0) + return hooknum; + } + return -1; +} + +#ifdef JSON_PARSING +static int nftnl_jansson_parse_flowtable(struct nftnl_flowtable *c, + json_t *tree, + struct nftnl_parse_err *err) +{ + const char *name, *table, *hooknum_str; + int32_t family, prio, hooknum; + json_t *root; + + root = nftnl_jansson_get_node(tree, "flowtable", err); + if (root == NULL) + return -1; + + name = nftnl_jansson_parse_str(root, "name", err); + if (name != NULL) + nftnl_flowtable_set_str(c, NFTNL_FLOWTABLE_NAME, name); + + if (nftnl_jansson_parse_family(root, &family, err) == 0) + nftnl_flowtable_set_u32(c, NFTNL_FLOWTABLE_FAMILY, family); + + table = nftnl_jansson_parse_str(root, "table", err); + + if (table != NULL) + nftnl_flowtable_set_str(c, NFTNL_FLOWTABLE_TABLE, table); + + if (nftnl_jansson_node_exist(root, "hooknum")) { + if (nftnl_jansson_parse_val(root, "prio", NFTNL_TYPE_S32, + &prio, err) == 0) + nftnl_flowtable_set_s32(c, NFTNL_FLOWTABLE_PRIO, prio); + + hooknum_str = nftnl_jansson_parse_str(root, "hooknum", err); + if (hooknum_str != NULL) { + hooknum = nftnl_str2hooknum(c->family, hooknum_str); + if (hooknum == -1) + return -1; + nftnl_flowtable_set_u32(c, NFTNL_FLOWTABLE_HOOKNUM, + hooknum); + } + } + + return 0; +} +#endif + +static int nftnl_flowtable_json_parse(struct nftnl_flowtable *c, + const void *json, + struct nftnl_parse_err *err, + enum nftnl_parse_input input) +{ +#ifdef JSON_PARSING + json_t *tree; + json_error_t error; + int ret; + + tree = nftnl_jansson_create_root(json, &error, err, input); + if (tree == NULL) + return -1; + + ret = nftnl_jansson_parse_flowtable(c, tree, err); + + nftnl_jansson_free_root(tree); + + return ret; +#else + errno = EOPNOTSUPP; + return -1; +#endif +} + +static int nftnl_flowtable_do_parse(struct nftnl_flowtable *c, + enum nftnl_parse_type type, + const void *data, + struct nftnl_parse_err *err, + enum nftnl_parse_input input) +{ + int ret; + struct nftnl_parse_err perr = {}; + + switch (type) { + case NFTNL_PARSE_JSON: + ret = nftnl_flowtable_json_parse(c, data, &perr, input); + break; + case NFTNL_PARSE_XML: + default: + ret = -1; + errno = EOPNOTSUPP; + break; + } + + if (err != NULL) + *err = perr; + + return ret; +} + +int nftnl_flowtable_parse(struct nftnl_flowtable *c, enum nftnl_parse_type type, + const char *data, struct nftnl_parse_err *err) +{ + return nftnl_flowtable_do_parse(c, type, data, err, NFTNL_PARSE_BUFFER); +} +EXPORT_SYMBOL(nftnl_flowtable_parse); + +int nftnl_flowtable_parse_file(struct nftnl_flowtable *c, + enum nftnl_parse_type type, + FILE *fp, struct nftnl_parse_err *err) +{ + return nftnl_flowtable_do_parse(c, type, fp, err, NFTNL_PARSE_FILE); +} +EXPORT_SYMBOL(nftnl_flowtable_parse_file); + +static int nftnl_flowtable_export(char *buf, size_t size, + const struct nftnl_flowtable *c, int type) +{ + NFTNL_BUF_INIT(b, buf, size); + + nftnl_buf_open(&b, type, CHAIN); + if (c->flags & (1 << NFTNL_FLOWTABLE_NAME)) + nftnl_buf_str(&b, type, c->name, NAME); + if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE)) + nftnl_buf_str(&b, type, c->table, TABLE); + if (c->flags & (1 << NFTNL_FLOWTABLE_FAMILY)) + nftnl_buf_str(&b, type, nftnl_family2str(c->family), FAMILY); + if (c->flags & (1 << NFTNL_FLOWTABLE_USE)) + nftnl_buf_u32(&b, type, c->use, USE); + if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM)) { + if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM)) + nftnl_buf_str(&b, type, nftnl_hooknum2str(c->family, + c->hooknum), HOOKNUM); + if (c->flags & (1 << NFTNL_FLOWTABLE_PRIO)) + nftnl_buf_s32(&b, type, c->prio, PRIO); + } + + nftnl_buf_close(&b, type, CHAIN); + + return nftnl_buf_done(&b); +} + +static int nftnl_flowtable_snprintf_default(char *buf, size_t size, + const struct nftnl_flowtable *c) +{ + int ret, remain = size, offset = 0, i; + + ret = snprintf(buf, remain, "flow table %s %s use %u", + c->table, c->name, c->use); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM)) { + ret = snprintf(buf + offset, remain, " hook %s prio %d", + nftnl_hooknum2str(c->family, c->hooknum), + c->prio); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) { + ret = snprintf(buf + offset, remain, " dev { "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + for (i = 0; i < c->dev_array_len; i++) { + ret = snprintf(buf + offset, remain, " %s ", + c->dev_array[i]); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + ret = snprintf(buf + offset, remain, " } "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + } + + return offset; +} + +static int nftnl_flowtable_cmd_snprintf(char *buf, size_t size, + const struct nftnl_flowtable *c, + uint32_t cmd, uint32_t type, + uint32_t flags) +{ + int ret, remain = size, offset = 0; + + ret = nftnl_cmd_header_snprintf(buf + offset, remain, cmd, type, flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + switch (type) { + case NFTNL_OUTPUT_DEFAULT: + ret = nftnl_flowtable_snprintf_default(buf + offset, remain, c); + break; + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + ret = nftnl_flowtable_export(buf + offset, remain, c, type); + break; + default: + return -1; + } + + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_cmd_footer_snprintf(buf + offset, remain, cmd, type, flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +int nftnl_flowtable_snprintf(char *buf, size_t size, const struct nftnl_flowtable *c, + uint32_t type, uint32_t flags) +{ + if (size) + buf[0] = '\0'; + + return nftnl_flowtable_cmd_snprintf(buf, size, c, nftnl_flag2cmd(flags), + type, flags); +} +EXPORT_SYMBOL(nftnl_flowtable_snprintf); + +static int nftnl_flowtable_do_snprintf(char *buf, size_t size, const void *c, + uint32_t cmd, uint32_t type, uint32_t flags) +{ + return nftnl_flowtable_snprintf(buf, size, c, type, flags); +} + +int nftnl_flowtable_fprintf(FILE *fp, const struct nftnl_flowtable *c, + uint32_t type, uint32_t flags) +{ + return nftnl_fprintf(fp, c, NFTNL_CMD_UNSPEC, type, flags, + nftnl_flowtable_do_snprintf); +} +EXPORT_SYMBOL(nftnl_flowtable_fprintf); + +struct nftnl_flowtable_list { + struct list_head list; +}; + +struct nftnl_flowtable_list *nftnl_flowtable_list_alloc(void) +{ + struct nftnl_flowtable_list *list; + + list = calloc(1, sizeof(struct nftnl_flowtable_list)); + if (list == NULL) + return NULL; + + INIT_LIST_HEAD(&list->list); + + return list; +} +EXPORT_SYMBOL(nftnl_flowtable_list_alloc); + +void nftnl_flowtable_list_free(struct nftnl_flowtable_list *list) +{ + struct nftnl_flowtable *s, *tmp; + + list_for_each_entry_safe(s, tmp, &list->list, head) { + list_del(&s->head); + nftnl_flowtable_free(s); + } + xfree(list); +} +EXPORT_SYMBOL(nftnl_flowtable_list_free); + +int nftnl_flowtable_list_is_empty(const struct nftnl_flowtable_list *list) +{ + return list_empty(&list->list); +} +EXPORT_SYMBOL(nftnl_flowtable_list_is_empty); + +void nftnl_flowtable_list_add(struct nftnl_flowtable *s, + struct nftnl_flowtable_list *list) +{ + list_add(&s->head, &list->list); +} +EXPORT_SYMBOL(nftnl_flowtable_list_add); + +void nftnl_flowtable_list_add_tail(struct nftnl_flowtable *s, + struct nftnl_flowtable_list *list) +{ + list_add_tail(&s->head, &list->list); +} +EXPORT_SYMBOL(nftnl_flowtable_list_add_tail); + +void nftnl_flowtable_list_del(struct nftnl_flowtable *s) +{ + list_del(&s->head); +} +EXPORT_SYMBOL(nftnl_flowtable_list_del); + +int nftnl_flowtable_list_foreach(struct nftnl_flowtable_list *flowtable_list, + int (*cb)(struct nftnl_flowtable *t, void *data), void *data) +{ + struct nftnl_flowtable *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &flowtable_list->list, head) { + ret = cb(cur, data); + if (ret < 0) + return ret; + } + return 0; +} +EXPORT_SYMBOL(nftnl_flowtable_list_foreach); diff --git a/src/libnftnl.map b/src/libnftnl.map index d59e80269604..cfe74e2aebfc 100644 --- a/src/libnftnl.map +++ b/src/libnftnl.map @@ -311,3 +311,34 @@ local: *; LIBNFTNL_6 { nftnl_expr_fprintf; } LIBNFTNL_5; + +LIBNFTNL_7 { + nftnl_flowtable_alloc; + nftnl_flowtable_free; + nftnl_flowtable_is_set; + nftnl_flowtable_unset; + nftnl_flowtable_set; + nftnl_flowtable_set_u32; + nftnl_flowtable_set_s32; + nftnl_flowtable_set_array; + nftnl_flowtable_set_str; + nftnl_flowtable_get; + nftnl_flowtable_get_u32; + nftnl_flowtable_get_s32; + nftnl_flowtable_get_array; + nftnl_flowtable_get_str; + nftnl_flowtable_parse; + nftnl_flowtable_parse_file; + nftnl_flowtable_snprintf; + nftnl_flowtable_fprintf; + nftnl_flowtable_nlmsg_build_payload; + nftnl_flowtable_nlmsg_parse; + nftnl_flowtable_list_alloc; + nftnl_flowtable_list_free; + nftnl_flowtable_list_is_empty; + nftnl_flowtable_list_add; + nftnl_flowtable_list_add_tail; + nftnl_flowtable_list_del; + nftnl_flowtable_list_foreach; + +} LIBNFTNL_6; From patchwork Tue Jan 23 12:10:03 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pablo Neira Ayuso X-Patchwork-Id: 864737 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=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zQnDp5syKz9t3J for ; Tue, 23 Jan 2018 23:10:18 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751318AbeAWMKR (ORCPT ); Tue, 23 Jan 2018 07:10:17 -0500 Received: from mail.us.es ([193.147.175.20]:39542 "EHLO mail.us.es" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751056AbeAWMKR (ORCPT ); Tue, 23 Jan 2018 07:10:17 -0500 Received: from antivirus1-rhel7.int (unknown [192.168.2.11]) by mail.us.es (Postfix) with ESMTP id 6D04B210564 for ; Tue, 23 Jan 2018 13:10:15 +0100 (CET) Received: from antivirus1-rhel7.int (localhost [127.0.0.1]) by antivirus1-rhel7.int (Postfix) with ESMTP id 5BEA3DA818 for ; Tue, 23 Jan 2018 13:10:15 +0100 (CET) Received: by antivirus1-rhel7.int (Postfix, from userid 99) id 51391DA80C; Tue, 23 Jan 2018 13:10:15 +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,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 36E35DA86B for ; Tue, 23 Jan 2018 13:10: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); Tue, 23 Jan 2018 13:10:09 +0100 (CET) X-Virus-Status: clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int) Received: from salvia.here (129.166.216.87.static.jazztel.es [87.216.166.129]) (Authenticated sender: pneira@us.es) by entrada.int (Postfix) with ESMTPA id F21344265A2F for ; Tue, 23 Jan 2018 13:10:08 +0100 (CET) X-SMTPAUTHUS: auth mail.us.es From: Pablo Neira Ayuso To: netfilter-devel@vger.kernel.org Subject: [PATCH libnftnl 2/2] expr: add flow offload expression Date: Tue, 23 Jan 2018 13:10:03 +0100 Message-Id: <20180123121003.16064-2-pablo@netfilter.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180123121003.16064-1-pablo@netfilter.org> References: <20180123121003.16064-1-pablo@netfilter.org> X-Virus-Scanned: ClamAV using ClamSMTP Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org This patch adds the new "flow_offload" expression to select what flows are offloaded to an existing flowtable. Signed-off-by: Pablo Neira Ayuso --- include/libnftnl/expr.h | 4 + include/linux/netfilter/nf_tables.h | 11 +++ src/Makefile.am | 1 + src/expr/flow_offload.c | 184 ++++++++++++++++++++++++++++++++++++ src/expr_ops.c | 2 + 5 files changed, 202 insertions(+) create mode 100644 src/expr/flow_offload.c diff --git a/include/libnftnl/expr.h b/include/libnftnl/expr.h index f37f50963d6c..76df94276c46 100644 --- a/include/libnftnl/expr.h +++ b/include/libnftnl/expr.h @@ -221,6 +221,10 @@ enum { }; enum { + NFTNL_EXPR_FLOW_TABLE_NAME = NFTNL_EXPR_BASE, +}; + +enum { NFTNL_EXPR_FWD_SREG_DEV = NFTNL_EXPR_BASE, }; diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index c525e67a665d..2edccfee9506 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -952,6 +952,17 @@ enum nft_ct_attributes { }; #define NFTA_CT_MAX (__NFTA_CT_MAX - 1) +/** + * enum nft_flow_attributes - ct offload expression attributes + * @NFTA_FLOW_TABLE_NAME: flow table name (NLA_STRING) + */ +enum nft_offload_attributes { + NFTA_FLOW_UNSPEC, + NFTA_FLOW_TABLE_NAME, + __NFTA_FLOW_MAX, +}; +#define NFTA_FLOW_MAX (__NFTA_FLOW_MAX - 1) + enum nft_limit_type { NFT_LIMIT_PKTS, NFT_LIMIT_PKT_BYTES diff --git a/src/Makefile.am b/src/Makefile.am index 7708c279c9da..578b7d36d8a9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,6 +32,7 @@ libnftnl_la_SOURCES = utils.c \ expr/data_reg.c \ expr/dup.c \ expr/exthdr.c \ + expr/flow_offload.c \ expr/fib.c \ expr/fwd.c \ expr/limit.c \ diff --git a/src/expr/flow_offload.c b/src/expr/flow_offload.c new file mode 100644 index 000000000000..a2001c9ae9db --- /dev/null +++ b/src/expr/flow_offload.c @@ -0,0 +1,184 @@ +#include "internal.h" + +#include +#include +#include /* for memcpy */ +#include +#include +#include +#include +#include +#include + +struct nftnl_expr_flow { + char *table_name; +}; + +static int nftnl_expr_flow_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_FLOW_TABLE_NAME: + flow->table_name = strdup((const char *)data); + if (!flow->table_name) + return -1; + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_flow_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_FLOW_TABLE_NAME: + *data_len = strlen(flow->table_name) + 1; + return flow->table_name; + } + return NULL; +} + +static int nftnl_expr_flow_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_FLOW_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_FLOW_TABLE_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void nftnl_expr_flow_build(struct nlmsghdr *nlh, + const struct nftnl_expr *e) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_FLOW_TABLE_NAME)) + mnl_attr_put_strz(nlh, NFTA_FLOW_TABLE_NAME, flow->table_name); +} + +static int nftnl_expr_flow_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + struct nlattr *tb[NFTA_FLOW_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_flow_cb, tb) < 0) + return -1; + + if (tb[NFTA_FLOW_TABLE_NAME]) { + flow->table_name = + strdup(mnl_attr_get_str(tb[NFTA_FLOW_TABLE_NAME])); + if (!flow->table_name) + return -1; + e->flags |= (1 << NFTNL_EXPR_FLOW_TABLE_NAME); + } + + return ret; +} + +static int +nftnl_expr_flow_json_parse(struct nftnl_expr *e, json_t *root, + struct nftnl_parse_err *err) +{ +#ifdef JSON_PARSING + const char *table_name; + + table_name = nftnl_jansson_parse_str(root, "flowtable", err); + if (table_name != NULL) + nftnl_expr_set_str(e, NFTNL_EXPR_FLOW_TABLE_NAME, table_name); + + return 0; +#else + errno = EOPNOTSUPP; + return -1; +#endif +} + +static int nftnl_expr_flow_export(char *buf, size_t size, + const struct nftnl_expr *e, int type) +{ + struct nftnl_expr_flow *l = nftnl_expr_data(e); + NFTNL_BUF_INIT(b, buf, size); + + if (e->flags & (1 << NFTNL_EXPR_FLOW_TABLE_NAME)) + nftnl_buf_str(&b, type, l->table_name, SET); + + return nftnl_buf_done(&b); +} + +static int nftnl_expr_flow_snprintf_default(char *buf, size_t size, + const struct nftnl_expr *e) +{ + int remain = size, offset = 0, ret; + struct nftnl_expr_flow *l = nftnl_expr_data(e); + + ret = snprintf(buf, remain, "flowtable %s ", l->table_name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +static int nftnl_expr_flow_snprintf(char *buf, size_t size, uint32_t type, + uint32_t flags, const struct nftnl_expr *e) +{ + switch(type) { + case NFTNL_OUTPUT_DEFAULT: + return nftnl_expr_flow_snprintf_default(buf, size, e); + case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + return nftnl_expr_flow_export(buf, size, e, type); + default: + break; + } + return -1; +} + +static void nftnl_expr_flow_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + xfree(flow->table_name); +} + +static bool nftnl_expr_flow_cmp(const struct nftnl_expr *e1, + const struct nftnl_expr *e2) +{ + struct nftnl_expr_flow *l1 = nftnl_expr_data(e1); + struct nftnl_expr_flow *l2 = nftnl_expr_data(e2); + bool eq = true; + + if (e1->flags & (1 << NFTNL_EXPR_FLOW_TABLE_NAME)) + eq &= !strcmp(l1->table_name, l2->table_name); + + return eq; +} + +struct expr_ops expr_ops_flow = { + .name = "flow_offload", + .alloc_len = sizeof(struct nftnl_expr_flow), + .max_attr = NFTA_FLOW_MAX, + .free = nftnl_expr_flow_free, + .cmp = nftnl_expr_flow_cmp, + .set = nftnl_expr_flow_set, + .get = nftnl_expr_flow_get, + .parse = nftnl_expr_flow_parse, + .build = nftnl_expr_flow_build, + .snprintf = nftnl_expr_flow_snprintf, + .json_parse = nftnl_expr_flow_json_parse, +}; diff --git a/src/expr_ops.c b/src/expr_ops.c index 7a0e1e3d0a26..041bd357d370 100644 --- a/src/expr_ops.c +++ b/src/expr_ops.c @@ -33,6 +33,7 @@ extern struct expr_ops expr_ops_target; extern struct expr_ops expr_ops_dynset; extern struct expr_ops expr_ops_hash; extern struct expr_ops expr_ops_fib; +extern struct expr_ops expr_ops_flow; static struct expr_ops expr_ops_notrack = { .name = "notrack", @@ -69,6 +70,7 @@ static struct expr_ops *expr_ops[] = { &expr_ops_hash, &expr_ops_fib, &expr_ops_objref, + &expr_ops_flow, NULL, };