From fd2d6bc4c2b1857663178357d5f37d762e58a03d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 15 Nov 2020 19:42:03 -0800 Subject: netfilter: nft_reject_bridge: fix build errors due to code movement Fix build errors in net/bridge/netfilter/nft_reject_bridge.ko by selecting NF_REJECT_IPV4, which provides the missing symbols. ERROR: modpost: "nf_reject_skb_v4_tcp_reset" [net/bridge/netfilter/nft_reject_bridge.ko] undefined! ERROR: modpost: "nf_reject_skb_v4_unreach" [net/bridge/netfilter/nft_reject_bridge.ko] undefined! Fixes: fa538f7cf05a ("netfilter: nf_reject: add reject skbuff creation helpers") Reported-by: kernel test robot Signed-off-by: Randy Dunlap Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index e4d287afc2c9..ac5372121e60 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -18,6 +18,8 @@ config NFT_BRIDGE_META config NFT_BRIDGE_REJECT tristate "Netfilter nf_tables bridge reject support" depends on NFT_REJECT + depends on NF_REJECT_IPV4 + depends on NF_REJECT_IPV6 help Add support to reject packets. -- cgit v1.2.3 From 988187e8810364dc02f3ceb7cab497f4a2e9003f Mon Sep 17 00:00:00 2001 From: Yejune Deng Date: Mon, 16 Nov 2020 16:01:47 +0800 Subject: ipvs: replace atomic_add_return() atomic_inc_return() looks better Signed-off-by: Yejune Deng Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_core.c | 2 +- net/netfilter/ipvs/ip_vs_sync.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index c0b8215ab3d4..54e086c65721 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -2137,7 +2137,7 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int if (cp->flags & IP_VS_CONN_F_ONE_PACKET) pkts = sysctl_sync_threshold(ipvs); else - pkts = atomic_add_return(1, &cp->in_pkts); + pkts = atomic_inc_return(&cp->in_pkts); if (ipvs->sync_state & IP_VS_STATE_MASTER) ip_vs_sync_conn(ipvs, cp, pkts); diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 16b48064f715..9d43277b8b4f 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -615,7 +615,7 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, cp = cp->control; if (cp) { if (cp->flags & IP_VS_CONN_F_TEMPLATE) - pkts = atomic_add_return(1, &cp->in_pkts); + pkts = atomic_inc_return(&cp->in_pkts); else pkts = sysctl_sync_threshold(ipvs); ip_vs_sync_conn(ipvs, cp, pkts); @@ -776,7 +776,7 @@ control: if (!cp) return; if (cp->flags & IP_VS_CONN_F_TEMPLATE) - pkts = atomic_add_return(1, &cp->in_pkts); + pkts = atomic_inc_return(&cp->in_pkts); else pkts = sysctl_sync_threshold(ipvs); goto sloop; -- cgit v1.2.3 From 0ef083d51ba1f4a2e890807d49d53ad20eee0ffd Mon Sep 17 00:00:00 2001 From: Kaixu Xia Date: Fri, 6 Nov 2020 16:20:13 +0800 Subject: netfilter: Remove unnecessary conversion to bool Here we could use the '!=' expression to fix the following coccicheck warning: ./net/netfilter/xt_nfacct.c:30:41-46: WARNING: conversion to bool not needed here Reported-by: Tosk Robot Signed-off-by: Kaixu Xia Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_nfacct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_nfacct.c b/net/netfilter/xt_nfacct.c index a97c2259bbc8..7c6bf1c16813 100644 --- a/net/netfilter/xt_nfacct.c +++ b/net/netfilter/xt_nfacct.c @@ -27,7 +27,7 @@ static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par) overquota = nfnl_acct_overquota(xt_net(par), info->nfacct); - return overquota == NFACCT_UNDERQUOTA ? false : true; + return overquota != NFACCT_UNDERQUOTA; } static int -- cgit v1.2.3 From f7583f02a538bdb6d5191e82bacc7b85a27d4b0a Mon Sep 17 00:00:00 2001 From: Wang Shanker Date: Mon, 16 Nov 2020 12:17:24 +0800 Subject: netfilter: nfnl_acct: remove data from struct net This patch removes nfnl_acct_list from struct net to reduce the default memory footprint for the netns structure. Signed-off-by: Miao Wang Signed-off-by: Pablo Neira Ayuso --- include/net/net_namespace.h | 3 --- net/netfilter/nfnetlink_acct.c | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 22bc07f4b043..dc20a47e3828 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -151,9 +151,6 @@ struct net { #endif struct sock *nfnl; struct sock *nfnl_stash; -#if IS_ENABLED(CONFIG_NETFILTER_NETLINK_ACCT) - struct list_head nfnl_acct_list; -#endif #if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) struct list_head nfct_timeout_list; #endif diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index 5e511df8d709..0fa1653b5f19 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,17 @@ struct nfacct_filter { u32 mask; }; +struct nfnl_acct_net { + struct list_head nfnl_acct_list; +}; + +static unsigned int nfnl_acct_net_id __read_mostly; + +static inline struct nfnl_acct_net *nfnl_acct_pernet(struct net *net) +{ + return net_generic(net, nfnl_acct_net_id); +} + #define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) #define NFACCT_OVERQUOTA_BIT 2 /* NFACCT_F_OVERQUOTA */ @@ -49,6 +61,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl, const struct nlattr * const tb[], struct netlink_ext_ack *extack) { + struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); struct nf_acct *nfacct, *matching = NULL; char *acct_name; unsigned int size = 0; @@ -61,7 +74,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl, if (strlen(acct_name) == 0) return -EINVAL; - list_for_each_entry(nfacct, &net->nfnl_acct_list, head) { + list_for_each_entry(nfacct, &nfnl_acct_net->nfnl_acct_list, head) { if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) continue; @@ -123,7 +136,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl, be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); } refcount_set(&nfacct->refcnt, 1); - list_add_tail_rcu(&nfacct->head, &net->nfnl_acct_list); + list_add_tail_rcu(&nfacct->head, &nfnl_acct_net->nfnl_acct_list); return 0; } @@ -188,6 +201,7 @@ static int nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); + struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); struct nf_acct *cur, *last; const struct nfacct_filter *filter = cb->data; @@ -199,7 +213,7 @@ nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) cb->args[1] = 0; rcu_read_lock(); - list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) { + list_for_each_entry_rcu(cur, &nfnl_acct_net->nfnl_acct_list, head) { if (last) { if (cur != last) continue; @@ -269,6 +283,7 @@ static int nfnl_acct_get(struct net *net, struct sock *nfnl, const struct nlattr * const tb[], struct netlink_ext_ack *extack) { + struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); int ret = -ENOENT; struct nf_acct *cur; char *acct_name; @@ -288,7 +303,7 @@ static int nfnl_acct_get(struct net *net, struct sock *nfnl, return -EINVAL; acct_name = nla_data(tb[NFACCT_NAME]); - list_for_each_entry(cur, &net->nfnl_acct_list, head) { + list_for_each_entry(cur, &nfnl_acct_net->nfnl_acct_list, head) { struct sk_buff *skb2; if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) @@ -342,19 +357,20 @@ static int nfnl_acct_del(struct net *net, struct sock *nfnl, const struct nlattr * const tb[], struct netlink_ext_ack *extack) { + struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); struct nf_acct *cur, *tmp; int ret = -ENOENT; char *acct_name; if (!tb[NFACCT_NAME]) { - list_for_each_entry_safe(cur, tmp, &net->nfnl_acct_list, head) + list_for_each_entry_safe(cur, tmp, &nfnl_acct_net->nfnl_acct_list, head) nfnl_acct_try_del(cur); return 0; } acct_name = nla_data(tb[NFACCT_NAME]); - list_for_each_entry(cur, &net->nfnl_acct_list, head) { + list_for_each_entry(cur, &nfnl_acct_net->nfnl_acct_list, head) { if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) continue; @@ -402,10 +418,11 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); struct nf_acct *nfnl_acct_find_get(struct net *net, const char *acct_name) { + struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); struct nf_acct *cur, *acct = NULL; rcu_read_lock(); - list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) { + list_for_each_entry_rcu(cur, &nfnl_acct_net->nfnl_acct_list, head) { if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) continue; @@ -488,16 +505,17 @@ EXPORT_SYMBOL_GPL(nfnl_acct_overquota); static int __net_init nfnl_acct_net_init(struct net *net) { - INIT_LIST_HEAD(&net->nfnl_acct_list); + INIT_LIST_HEAD(&nfnl_acct_pernet(net)->nfnl_acct_list); return 0; } static void __net_exit nfnl_acct_net_exit(struct net *net) { + struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); struct nf_acct *cur, *tmp; - list_for_each_entry_safe(cur, tmp, &net->nfnl_acct_list, head) { + list_for_each_entry_safe(cur, tmp, &nfnl_acct_net->nfnl_acct_list, head) { list_del_rcu(&cur->head); if (refcount_dec_and_test(&cur->refcnt)) @@ -508,6 +526,8 @@ static void __net_exit nfnl_acct_net_exit(struct net *net) static struct pernet_operations nfnl_acct_ops = { .init = nfnl_acct_net_init, .exit = nfnl_acct_net_exit, + .id = &nfnl_acct_net_id, + .size = sizeof(struct nfnl_acct_net), }; static int __init nfnl_acct_init(void) -- cgit v1.2.3 From 04295878beac396dae47ba93141cae0d9386e7ef Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 21 Nov 2020 12:11:51 +0100 Subject: netfilter: use actual socket sk for REJECT action True to the message of commit v5.10-rc1-105-g46d6c5ae953c, _do_ actually make use of state->sk when possible, such as in the REJECT modules. Reported-by: Minqiang Chen Cc: Jason A. Donenfeld Signed-off-by: Jan Engelhardt Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_reject.h | 4 ++-- include/net/netfilter/ipv6/nf_reject.h | 5 ++--- net/ipv4/netfilter/ipt_REJECT.c | 3 ++- net/ipv4/netfilter/nf_reject_ipv4.c | 6 +++--- net/ipv4/netfilter/nft_reject_ipv4.c | 3 ++- net/ipv6/netfilter/ip6t_REJECT.c | 2 +- net/ipv6/netfilter/nf_reject_ipv6.c | 5 +++-- net/ipv6/netfilter/nft_reject_ipv6.c | 3 ++- net/netfilter/nft_reject_inet.c | 6 ++++-- 9 files changed, 21 insertions(+), 16 deletions(-) diff --git a/include/net/netfilter/ipv4/nf_reject.h b/include/net/netfilter/ipv4/nf_reject.h index 0d8ff84a2588..c653fcb88354 100644 --- a/include/net/netfilter/ipv4/nf_reject.h +++ b/include/net/netfilter/ipv4/nf_reject.h @@ -8,8 +8,8 @@ #include void nf_send_unreach(struct sk_buff *skb_in, int code, int hook); -void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook); - +void nf_send_reset(struct net *net, struct sock *, struct sk_buff *oldskb, + int hook); const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, struct tcphdr *_oth, int hook); struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, diff --git a/include/net/netfilter/ipv6/nf_reject.h b/include/net/netfilter/ipv6/nf_reject.h index edcf6d1cd316..d729344ba644 100644 --- a/include/net/netfilter/ipv6/nf_reject.h +++ b/include/net/netfilter/ipv6/nf_reject.h @@ -7,9 +7,8 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code, unsigned int hooknum); - -void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook); - +void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, + int hook); const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb, struct tcphdr *otcph, unsigned int *otcplen, int hook); diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index e16b98ee6266..4b8840734762 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -56,7 +56,8 @@ reject_tg(struct sk_buff *skb, const struct xt_action_param *par) nf_send_unreach(skb, ICMP_PKT_FILTERED, hook); break; case IPT_TCP_RESET: - nf_send_reset(xt_net(par), skb, hook); + nf_send_reset(xt_net(par), par->state->sk, skb, hook); + break; case IPT_ICMP_ECHOREPLY: /* Doesn't happen. */ break; diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index 4109055cdea6..4eed5afca392 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -234,7 +234,8 @@ static int nf_reject_fill_skb_dst(struct sk_buff *skb_in) } /* Send RST reply */ -void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook) +void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb, + int hook) { struct net_device *br_indev __maybe_unused; struct sk_buff *nskb; @@ -267,8 +268,7 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook) niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, ip4_dst_hoplimit(skb_dst(nskb))); nf_reject_ip_tcphdr_put(nskb, oldskb, oth); - - if (ip_route_me_harder(net, nskb->sk, nskb, RTN_UNSPEC)) + if (ip_route_me_harder(net, sk, nskb, RTN_UNSPEC)) goto free_nskb; niph = ip_hdr(nskb); diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c index e408f813f5d8..ff437e4ed6db 100644 --- a/net/ipv4/netfilter/nft_reject_ipv4.c +++ b/net/ipv4/netfilter/nft_reject_ipv4.c @@ -27,7 +27,8 @@ static void nft_reject_ipv4_eval(const struct nft_expr *expr, nf_send_unreach(pkt->skb, priv->icmp_code, nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt)); + nf_send_reset(nft_net(pkt), pkt->xt.state->sk, pkt->skb, + nft_hook(pkt)); break; default: break; diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index 3ac5485049f0..a35019d2e480 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -61,7 +61,7 @@ reject_tg6(struct sk_buff *skb, const struct xt_action_param *par) /* Do nothing */ break; case IP6T_TCP_RESET: - nf_send_reset6(net, skb, xt_hooknum(par)); + nf_send_reset6(net, par->state->sk, skb, xt_hooknum(par)); break; case IP6T_ICMP6_POLICY_FAIL: nf_send_unreach6(net, skb, ICMPV6_POLICY_FAIL, xt_hooknum(par)); diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index aa35e6e37c1f..570d1d76c44d 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -275,7 +275,8 @@ static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in) return 0; } -void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) +void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, + int hook) { struct net_device *br_indev __maybe_unused; struct sk_buff *nskb; @@ -367,7 +368,7 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) dev_queue_xmit(nskb); } else #endif - ip6_local_out(net, nskb->sk, nskb); + ip6_local_out(net, sk, nskb); } EXPORT_SYMBOL_GPL(nf_send_reset6); diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c index c1098a1968e1..7969d1f3018d 100644 --- a/net/ipv6/netfilter/nft_reject_ipv6.c +++ b/net/ipv6/netfilter/nft_reject_ipv6.c @@ -28,7 +28,8 @@ static void nft_reject_ipv6_eval(const struct nft_expr *expr, nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt)); + nf_send_reset6(nft_net(pkt), pkt->xt.state->sk, pkt->skb, + nft_hook(pkt)); break; default: break; diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c index 32f3ea398ddf..95090186ee90 100644 --- a/net/netfilter/nft_reject_inet.c +++ b/net/netfilter/nft_reject_inet.c @@ -28,7 +28,8 @@ static void nft_reject_inet_eval(const struct nft_expr *expr, nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt)); + nf_send_reset(nft_net(pkt), pkt->xt.state->sk, + pkt->skb, nft_hook(pkt)); break; case NFT_REJECT_ICMPX_UNREACH: nf_send_unreach(pkt->skb, @@ -44,7 +45,8 @@ static void nft_reject_inet_eval(const struct nft_expr *expr, priv->icmp_code, nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt)); + nf_send_reset6(nft_net(pkt), pkt->xt.state->sk, + pkt->skb, nft_hook(pkt)); break; case NFT_REJECT_ICMPX_UNREACH: nf_send_unreach6(nft_net(pkt), pkt->skb, -- cgit v1.2.3 From 86d21fc7474563cb5d054ff001d8ad7b69206717 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Dec 2020 14:43:23 +0100 Subject: netfilter: ctnetlink: add timeout and protoinfo to destroy events DESTROY events do not include the remaining timeout. Add the timeout if the entry was removed explicitly. This can happen when a conntrack gets deleted prematurely, e.g. due to a tcp reset, module removal, netdev notifier (nat/masquerade device went down), ctnetlink and so on. Add the protocol state too for the destroy message to check for abnormal state on connection termination. Joint work with Pablo. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 2 +- net/netfilter/nf_conntrack_netlink.c | 31 ++++++++++++++++++---------- net/netfilter/nf_conntrack_proto_dccp.c | 13 +++++++++--- net/netfilter/nf_conntrack_proto_sctp.c | 13 ++++++++---- net/netfilter/nf_conntrack_proto_tcp.c | 13 ++++++++---- 5 files changed, 49 insertions(+), 23 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 9be7320b994f..96f9cf81f46b 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -32,7 +32,7 @@ struct nf_conntrack_l4proto { /* convert protoinfo to nfnetink attributes */ int (*to_nlattr)(struct sk_buff *skb, struct nlattr *nla, - struct nf_conn *ct); + struct nf_conn *ct, bool destroy); /* convert nfnetlink attributes to protoinfo */ int (*from_nlattr)(struct nlattr *tb[], struct nf_conn *ct); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 3d0fd33be018..84caf3316946 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -167,10 +167,14 @@ nla_put_failure: return -1; } -static int ctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct) +static int ctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct, + bool skip_zero) { long timeout = nf_ct_expires(ct) / HZ; + if (skip_zero && timeout == 0) + return 0; + if (nla_put_be32(skb, CTA_TIMEOUT, htonl(timeout))) goto nla_put_failure; return 0; @@ -179,7 +183,8 @@ nla_put_failure: return -1; } -static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct) +static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct, + bool destroy) { const struct nf_conntrack_l4proto *l4proto; struct nlattr *nest_proto; @@ -193,7 +198,7 @@ static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct) if (!nest_proto) goto nla_put_failure; - ret = l4proto->to_nlattr(skb, nest_proto, ct); + ret = l4proto->to_nlattr(skb, nest_proto, ct, destroy); nla_nest_end(skb, nest_proto); @@ -537,8 +542,8 @@ static int ctnetlink_dump_info(struct sk_buff *skb, struct nf_conn *ct) return -1; if (!test_bit(IPS_OFFLOAD_BIT, &ct->status) && - (ctnetlink_dump_timeout(skb, ct) < 0 || - ctnetlink_dump_protoinfo(skb, ct) < 0)) + (ctnetlink_dump_timeout(skb, ct, false) < 0 || + ctnetlink_dump_protoinfo(skb, ct, false) < 0)) return -1; return 0; @@ -780,15 +785,19 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) goto nla_put_failure; if (events & (1 << IPCT_DESTROY)) { + if (ctnetlink_dump_timeout(skb, ct, true) < 0) + goto nla_put_failure; + if (ctnetlink_dump_acct(skb, ct, type) < 0 || - ctnetlink_dump_timestamp(skb, ct) < 0) + ctnetlink_dump_timestamp(skb, ct) < 0 || + ctnetlink_dump_protoinfo(skb, ct, true) < 0) goto nla_put_failure; } else { - if (ctnetlink_dump_timeout(skb, ct) < 0) + if (ctnetlink_dump_timeout(skb, ct, false) < 0) goto nla_put_failure; - if (events & (1 << IPCT_PROTOINFO) - && ctnetlink_dump_protoinfo(skb, ct) < 0) + if (events & (1 << IPCT_PROTOINFO) && + ctnetlink_dump_protoinfo(skb, ct, false) < 0) goto nla_put_failure; if ((events & (1 << IPCT_HELPER) || nfct_help(ct)) @@ -2720,10 +2729,10 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct) if (ctnetlink_dump_status(skb, ct) < 0) goto nla_put_failure; - if (ctnetlink_dump_timeout(skb, ct) < 0) + if (ctnetlink_dump_timeout(skb, ct, false) < 0) goto nla_put_failure; - if (ctnetlink_dump_protoinfo(skb, ct) < 0) + if (ctnetlink_dump_protoinfo(skb, ct, false) < 0) goto nla_put_failure; if (ctnetlink_dump_helpinfo(skb, ct) < 0) diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index b3f4a334f9d7..db7479db8512 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -589,7 +589,7 @@ static void dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct) #if IS_ENABLED(CONFIG_NF_CT_NETLINK) static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, - struct nf_conn *ct) + struct nf_conn *ct, bool destroy) { struct nlattr *nest_parms; @@ -597,15 +597,22 @@ static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP); if (!nest_parms) goto nla_put_failure; - if (nla_put_u8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state) || - nla_put_u8(skb, CTA_PROTOINFO_DCCP_ROLE, + if (nla_put_u8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state)) + goto nla_put_failure; + + if (destroy) + goto skip_state; + + if (nla_put_u8(skb, CTA_PROTOINFO_DCCP_ROLE, ct->proto.dccp.role[IP_CT_DIR_ORIGINAL]) || nla_put_be64(skb, CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ, cpu_to_be64(ct->proto.dccp.handshake_seq), CTA_PROTOINFO_DCCP_PAD)) goto nla_put_failure; +skip_state: nla_nest_end(skb, nest_parms); spin_unlock_bh(&ct->lock); + return 0; nla_put_failure: diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 810cca24b399..fb8dc02e502f 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -543,7 +543,7 @@ static bool sctp_can_early_drop(const struct nf_conn *ct) #include static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, - struct nf_conn *ct) + struct nf_conn *ct, bool destroy) { struct nlattr *nest_parms; @@ -552,15 +552,20 @@ static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, if (!nest_parms) goto nla_put_failure; - if (nla_put_u8(skb, CTA_PROTOINFO_SCTP_STATE, ct->proto.sctp.state) || - nla_put_be32(skb, CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + if (nla_put_u8(skb, CTA_PROTOINFO_SCTP_STATE, ct->proto.sctp.state)) + goto nla_put_failure; + + if (destroy) + goto skip_state; + + if (nla_put_be32(skb, CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL]) || nla_put_be32(skb, CTA_PROTOINFO_SCTP_VTAG_REPLY, ct->proto.sctp.vtag[IP_CT_DIR_REPLY])) goto nla_put_failure; +skip_state: spin_unlock_bh(&ct->lock); - nla_nest_end(skb, nest_parms); return 0; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 811c6c9b59e1..1d7e1c595546 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1186,7 +1186,7 @@ static bool tcp_can_early_drop(const struct nf_conn *ct) #include static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, - struct nf_conn *ct) + struct nf_conn *ct, bool destroy) { struct nlattr *nest_parms; struct nf_ct_tcp_flags tmp = {}; @@ -1196,8 +1196,13 @@ static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, if (!nest_parms) goto nla_put_failure; - if (nla_put_u8(skb, CTA_PROTOINFO_TCP_STATE, ct->proto.tcp.state) || - nla_put_u8(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, + if (nla_put_u8(skb, CTA_PROTOINFO_TCP_STATE, ct->proto.tcp.state)) + goto nla_put_failure; + + if (destroy) + goto skip_state; + + if (nla_put_u8(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, ct->proto.tcp.seen[0].td_scale) || nla_put_u8(skb, CTA_PROTOINFO_TCP_WSCALE_REPLY, ct->proto.tcp.seen[1].td_scale)) @@ -1212,8 +1217,8 @@ static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, if (nla_put(skb, CTA_PROTOINFO_TCP_FLAGS_REPLY, sizeof(struct nf_ct_tcp_flags), &tmp)) goto nla_put_failure; +skip_state: spin_unlock_bh(&ct->lock); - nla_nest_end(skb, nest_parms); return 0; -- cgit v1.2.3 From 8cfd9b0f8515e7c361bba27e2a2684cbd427fe01 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 7 Dec 2020 17:37:01 +0100 Subject: netfilter: nftables: generalize set expressions support Currently, the set infrastucture allows for one single expressions per element. This patch extends the existing infrastructure to allow for up to two expressions. This is not updating the netlink API yet, this is coming as an initial preparation patch. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 5 ++- net/netfilter/nf_tables_api.c | 90 ++++++++++++++++++++++++++++----------- net/netfilter/nft_dynset.c | 3 +- 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 55b4cadf290a..aad7e1381200 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -396,6 +396,8 @@ struct nft_set_type { }; #define to_set_type(o) container_of(o, struct nft_set_type, ops) +#define NFT_SET_EXPR_MAX 2 + /** * struct nft_set - nf_tables set instance * @@ -448,13 +450,14 @@ struct nft_set { u16 policy; u16 udlen; unsigned char *udata; - struct nft_expr *expr; /* runtime data below here */ const struct nft_set_ops *ops ____cacheline_aligned; u16 flags:14, genmask:2; u8 klen; u8 dlen; + u8 num_exprs; + struct nft_expr *exprs[NFT_SET_EXPR_MAX]; unsigned char data[] __attribute__((aligned(__alignof__(u64)))); }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 65aa98fc5eb6..ade10cd23acc 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3841,9 +3841,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, nla_nest_end(skb, nest); - if (set->expr) { + if (set->num_exprs == 1) { nest = nla_nest_start_noflag(skb, NFTA_SET_EXPR); - if (nf_tables_fill_expr_info(skb, set->expr) < 0) + if (nf_tables_fill_expr_info(skb, set->exprs[0]) < 0) goto nla_put_failure; nla_nest_end(skb, nest); @@ -4279,6 +4279,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, err = PTR_ERR(expr); goto err_set_alloc_name; } + set->exprs[0] = expr; + set->num_exprs++; } udata = NULL; @@ -4296,7 +4298,6 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, set->dtype = dtype; set->objtype = objtype; set->dlen = desc.dlen; - set->expr = expr; set->flags = flags; set->size = desc.size; set->policy = policy; @@ -4325,8 +4326,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, err_set_trans: ops->destroy(set); err_set_init: - if (expr) - nft_expr_destroy(&ctx, expr); + for (i = 0; i < set->num_exprs; i++) + nft_expr_destroy(&ctx, set->exprs[i]); err_set_alloc_name: kfree(set->name); err_set_name: @@ -4336,11 +4337,13 @@ err_set_name: static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) { + int i; + if (WARN_ON(set->use > 0)) return; - if (set->expr) - nft_expr_destroy(ctx, set->expr); + for (i = 0; i < set->num_exprs; i++) + nft_expr_destroy(ctx, set->exprs[i]); set->ops->destroy(set); kfree(set->name); @@ -5139,9 +5142,39 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, kfree(elem); } +static int nft_set_elem_expr_clone(const struct nft_ctx *ctx, + struct nft_set *set, + struct nft_expr *expr_array[]) +{ + struct nft_expr *expr; + int err, i, k; + + for (i = 0; i < set->num_exprs; i++) { + expr = kzalloc(set->exprs[i]->ops->size, GFP_KERNEL); + if (!expr) + goto err_expr; + + err = nft_expr_clone(expr, set->exprs[i]); + if (err < 0) { + nft_expr_destroy(ctx, expr); + goto err_expr; + } + expr_array[i] = expr; + } + + return 0; + +err_expr: + for (k = i - 1; k >= 0; k++) + nft_expr_destroy(ctx, expr_array[i]); + + return -ENOMEM; +} + static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr, u32 nlmsg_flags) { + struct nft_expr *expr_array[NFT_SET_EXPR_MAX] = {}; struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; u8 genmask = nft_genmask_next(ctx->net); struct nft_set_ext_tmpl tmpl; @@ -5149,7 +5182,6 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, struct nft_set_elem elem; struct nft_set_binding *binding; struct nft_object *obj = NULL; - struct nft_expr *expr = NULL; struct nft_userdata *udata; struct nft_data_desc desc; enum nft_registers dreg; @@ -5158,7 +5190,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, u64 timeout; u64 expiration; u8 ulen; - int err; + int err, i; err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr, nft_set_elem_policy, NULL); @@ -5216,23 +5248,27 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, return err; } - if (nla[NFTA_SET_ELEM_EXPR] != NULL) { + if (nla[NFTA_SET_ELEM_EXPR]) { + struct nft_expr *expr; + + if (set->num_exprs != 1) + return -EOPNOTSUPP; + expr = nft_set_elem_expr_alloc(ctx, set, nla[NFTA_SET_ELEM_EXPR]); if (IS_ERR(expr)) return PTR_ERR(expr); - err = -EOPNOTSUPP; - if (set->expr && set->expr->ops != expr->ops) - goto err_set_elem_expr; - } else if (set->expr) { - expr = kzalloc(set->expr->ops->size, GFP_KERNEL); - if (!expr) - return -ENOMEM; + expr_array[0] = expr; - err = nft_expr_clone(expr, set->expr); - if (err < 0) + if (set->exprs[0] && set->exprs[0]->ops != expr->ops) { + err = -EOPNOTSUPP; goto err_set_elem_expr; + } + } else if (set->num_exprs > 0) { + err = nft_set_elem_expr_clone(ctx, set, expr_array); + if (err < 0) + goto err_set_elem_expr_clone; } err = nft_setelem_parse_key(ctx, set, &elem.key.val, @@ -5257,9 +5293,9 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT); } - if (expr) + if (set->num_exprs == 1) nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPR, - expr->ops->size); + expr_array[0]->ops->size); if (nla[NFTA_SET_ELEM_OBJREF] != NULL) { if (!(set->flags & NFT_SET_OBJECT)) { @@ -5341,10 +5377,12 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, *nft_set_ext_obj(ext) = obj; obj->use++; } - if (expr) { + if (set->num_exprs == 1) { + struct nft_expr *expr = expr_array[0]; + memcpy(nft_set_ext_expr(ext), expr, expr->ops->size); kfree(expr); - expr = NULL; + expr_array[0] = NULL; } trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); @@ -5406,9 +5444,9 @@ err_parse_key_end: err_parse_key: nft_data_release(&elem.key.val, NFT_DATA_VALUE); err_set_elem_expr: - if (expr != NULL) - nft_expr_destroy(ctx, expr); - + for (i = 0; i < set->num_exprs && expr_array[i]; i++) + nft_expr_destroy(ctx, expr_array[i]); +err_set_elem_expr_clone: return err; } diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 64ca13a1885b..4353e47c30fc 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -188,7 +188,8 @@ static int nft_dynset_init(const struct nft_ctx *ctx, if (IS_ERR(priv->expr)) return PTR_ERR(priv->expr); - if (set->expr && set->expr->ops != priv->expr->ops) { + if (set->num_exprs == 1 && + set->exprs[0]->ops != priv->expr->ops) { err = -EOPNOTSUPP; goto err_expr_free; } -- cgit v1.2.3 From 92b211a28992b82a693547e3fe5ff97646961785 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 7 Dec 2020 17:37:05 +0100 Subject: netfilter: nftables: move nft_expr before nft_set Move the nft_expr structure definition before nft_set. Expressions are used by rules and sets, remove unnecessary forward declarations. This comes as preparation to support for multiple expressions per set element. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 54 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index aad7e1381200..0f4ae16a0c42 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -305,8 +305,33 @@ struct nft_set_estimate { enum nft_set_class space; }; +#define NFT_EXPR_MAXATTR 16 +#define NFT_EXPR_SIZE(size) (sizeof(struct nft_expr) + \ + ALIGN(size, __alignof__(struct nft_expr))) + +/** + * struct nft_expr - nf_tables expression + * + * @ops: expression ops + * @data: expression private data + */ +struct nft_expr { + const struct nft_expr_ops *ops; + unsigned char data[] + __attribute__((aligned(__alignof__(u64)))); +}; + +static inline void *nft_expr_priv(const struct nft_expr *expr) +{ + return (void *)expr->data; +} + +int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src); +void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); +int nft_expr_dump(struct sk_buff *skb, unsigned int attr, + const struct nft_expr *expr); + struct nft_set_ext; -struct nft_expr; /** * struct nft_set_ops - nf_tables set operations @@ -797,7 +822,6 @@ struct nft_offload_ctx; * @validate: validate expression, called during loop detection * @data: extra data to attach to this expression operation */ -struct nft_expr; struct nft_expr_ops { void (*eval)(const struct nft_expr *expr, struct nft_regs *regs, @@ -833,32 +857,6 @@ struct nft_expr_ops { void *data; }; -#define NFT_EXPR_MAXATTR 16 -#define NFT_EXPR_SIZE(size) (sizeof(struct nft_expr) + \ - ALIGN(size, __alignof__(struct nft_expr))) - -/** - * struct nft_expr - nf_tables expression - * - * @ops: expression ops - * @data: expression private data - */ -struct nft_expr { - const struct nft_expr_ops *ops; - unsigned char data[] - __attribute__((aligned(__alignof__(u64)))); -}; - -static inline void *nft_expr_priv(const struct nft_expr *expr) -{ - return (void *)expr->data; -} - -int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src); -void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); -int nft_expr_dump(struct sk_buff *skb, unsigned int attr, - const struct nft_expr *expr); - /** * struct nft_rule - nf_tables rule * -- cgit v1.2.3 From 563125a73ac30d7036ae69ca35c40500562c1de4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 9 Dec 2020 20:10:27 +0100 Subject: netfilter: nftables: generalize set extension to support for several expressions This patch replaces NFT_SET_EXPR by NFT_SET_EXT_EXPRESSIONS. This new extension allows to attach several expressions to one set element (not only one single expression as NFT_SET_EXPR provides). This patch prepares for support for several expressions per set element in the netlink userspace API. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 36 ++++++++++--- net/netfilter/nf_tables_api.c | 85 +++++++++++++++++++++++-------- net/netfilter/nft_dynset.c | 103 ++++++++++++++++++++++++++++++-------- net/netfilter/nft_set_hash.c | 27 +++++++--- 4 files changed, 196 insertions(+), 55 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 0f4ae16a0c42..bba56f2ce60c 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -421,6 +421,20 @@ struct nft_set_type { }; #define to_set_type(o) container_of(o, struct nft_set_type, ops) +struct nft_set_elem_expr { + u8 size; + unsigned char data[] + __attribute__((aligned(__alignof__(struct nft_expr)))); +}; + +#define nft_setelem_expr_at(__elem_expr, __offset) \ + ((struct nft_expr *)&__elem_expr->data[__offset]) + +#define nft_setelem_expr_foreach(__expr, __elem_expr, __size) \ + for (__expr = nft_setelem_expr_at(__elem_expr, 0), __size = 0; \ + __size < (__elem_expr)->size; \ + __size += (__expr)->ops->size, __expr = ((void *)(__expr)) + (__expr)->ops->size) + #define NFT_SET_EXPR_MAX 2 /** @@ -547,7 +561,7 @@ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set); * @NFT_SET_EXT_TIMEOUT: element timeout * @NFT_SET_EXT_EXPIRATION: element expiration time * @NFT_SET_EXT_USERDATA: user data associated with the element - * @NFT_SET_EXT_EXPR: expression assiociated with the element + * @NFT_SET_EXT_EXPRESSIONS: expressions assiciated with the element * @NFT_SET_EXT_OBJREF: stateful object reference associated with element * @NFT_SET_EXT_NUM: number of extension types */ @@ -559,7 +573,7 @@ enum nft_set_extensions { NFT_SET_EXT_TIMEOUT, NFT_SET_EXT_EXPIRATION, NFT_SET_EXT_USERDATA, - NFT_SET_EXT_EXPR, + NFT_SET_EXT_EXPRESSIONS, NFT_SET_EXT_OBJREF, NFT_SET_EXT_NUM }; @@ -677,9 +691,9 @@ static inline struct nft_userdata *nft_set_ext_userdata(const struct nft_set_ext return nft_set_ext(ext, NFT_SET_EXT_USERDATA); } -static inline struct nft_expr *nft_set_ext_expr(const struct nft_set_ext *ext) +static inline struct nft_set_elem_expr *nft_set_ext_expr(const struct nft_set_ext *ext) { - return nft_set_ext(ext, NFT_SET_EXT_EXPR); + return nft_set_ext(ext, NFT_SET_EXT_EXPRESSIONS); } static inline bool nft_set_elem_expired(const struct nft_set_ext *ext) @@ -909,11 +923,17 @@ static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext, struct nft_regs *regs, const struct nft_pktinfo *pkt) { + struct nft_set_elem_expr *elem_expr; struct nft_expr *expr; - - if (__nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) { - expr = nft_set_ext_expr(ext); - expr->ops->eval(expr, regs, pkt); + u32 size; + + if (__nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) { + elem_expr = nft_set_ext_expr(ext); + nft_setelem_expr_foreach(expr, elem_expr, size) { + expr->ops->eval(expr, regs, pkt); + if (regs->verdict.code == NFT_BREAK) + return; + } } } diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index ade10cd23acc..a3d5014dd246 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4496,8 +4496,8 @@ const struct nft_set_ext_type nft_set_ext_types[] = { [NFT_SET_EXT_DATA] = { .align = __alignof__(u32), }, - [NFT_SET_EXT_EXPR] = { - .align = __alignof__(struct nft_expr), + [NFT_SET_EXT_EXPRESSIONS] = { + .align = __alignof__(struct nft_set_elem_expr), }, [NFT_SET_EXT_OBJREF] = { .len = sizeof(struct nft_object *), @@ -4573,6 +4573,29 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net, return 0; } +static int nft_set_elem_expr_dump(struct sk_buff *skb, + const struct nft_set *set, + const struct nft_set_ext *ext) +{ + struct nft_set_elem_expr *elem_expr; + u32 size, num_exprs = 0; + struct nft_expr *expr; + + elem_expr = nft_set_ext_expr(ext); + nft_setelem_expr_foreach(expr, elem_expr, size) + num_exprs++; + + if (num_exprs == 1) { + expr = nft_setelem_expr_at(elem_expr, 0); + if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr) < 0) + return -1; + + return 0; + } + + return 0; +} + static int nf_tables_fill_setelem(struct sk_buff *skb, const struct nft_set *set, const struct nft_set_elem *elem) @@ -4600,8 +4623,8 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, set->dlen) < 0) goto nla_put_failure; - if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR) && - nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0) + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS) && + nft_set_elem_expr_dump(skb, set, ext)) goto nla_put_failure; if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) && @@ -5096,8 +5119,8 @@ void *nft_set_elem_init(const struct nft_set *set, return elem; } -static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, - struct nft_expr *expr) +static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx, + struct nft_expr *expr) { if (expr->ops->destroy_clone) { expr->ops->destroy_clone(ctx, expr); @@ -5107,6 +5130,16 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, } } +static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, + struct nft_set_elem_expr *elem_expr) +{ + struct nft_expr *expr; + u32 size; + + nft_setelem_expr_foreach(expr, elem_expr, size) + __nft_set_elem_expr_destroy(ctx, expr); +} + void nft_set_elem_destroy(const struct nft_set *set, void *elem, bool destroy_expr) { @@ -5119,7 +5152,7 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem, nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE); if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) nft_data_release(nft_set_ext_data(ext), set->dtype); - if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) + if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext)); if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF)) @@ -5136,7 +5169,7 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, { struct nft_set_ext *ext = nft_set_elem_ext(set, elem); - if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext)); kfree(elem); @@ -5171,6 +5204,18 @@ err_expr: return -ENOMEM; } +static void nft_set_elem_expr_setup(const struct nft_set_ext *ext, int i, + struct nft_expr *expr_array[]) +{ + struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); + struct nft_expr *expr = nft_setelem_expr_at(elem_expr, elem_expr->size); + + memcpy(expr, expr_array[i], expr_array[i]->ops->size); + elem_expr->size += expr_array[i]->ops->size; + kfree(expr_array[i]); + expr_array[i] = NULL; +} + static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr, u32 nlmsg_flags) { @@ -5186,11 +5231,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, struct nft_data_desc desc; enum nft_registers dreg; struct nft_trans *trans; - u32 flags = 0; + u32 flags = 0, size = 0; u64 timeout; u64 expiration; - u8 ulen; int err, i; + u8 ulen; err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr, nft_set_elem_policy, NULL); @@ -5293,9 +5338,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT); } - if (set->num_exprs == 1) - nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPR, - expr_array[0]->ops->size); + if (set->num_exprs) { + for (i = 0; i < set->num_exprs; i++) + size += expr_array[i]->ops->size; + + nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPRESSIONS, + sizeof(struct nft_set_elem_expr) + + size); + } if (nla[NFTA_SET_ELEM_OBJREF] != NULL) { if (!(set->flags & NFT_SET_OBJECT)) { @@ -5377,13 +5427,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, *nft_set_ext_obj(ext) = obj; obj->use++; } - if (set->num_exprs == 1) { - struct nft_expr *expr = expr_array[0]; - - memcpy(nft_set_ext_expr(ext), expr, expr->ops->size); - kfree(expr); - expr_array[0] = NULL; - } + for (i = 0; i < set->num_exprs; i++) + nft_set_elem_expr_setup(ext, i, expr_array); trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); if (trans == NULL) diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 4353e47c30fc..d9e609b2e5d4 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -19,11 +19,30 @@ struct nft_dynset { enum nft_registers sreg_key:8; enum nft_registers sreg_data:8; bool invert; + u8 num_exprs; u64 timeout; - struct nft_expr *expr; + struct nft_expr *expr_array[NFT_SET_EXPR_MAX]; struct nft_set_binding binding; }; +static int nft_dynset_expr_setup(const struct nft_dynset *priv, + const struct nft_set_ext *ext) +{ + struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); + struct nft_expr *expr; + int i; + + for (i = 0; i < priv->num_exprs; i++) { + expr = nft_setelem_expr_at(elem_expr, elem_expr->size); + if (nft_expr_clone(expr, priv->expr_array[i]) < 0) + return -1; + + elem_expr->size += priv->expr_array[i]->ops->size; + } + + return 0; +} + static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, struct nft_regs *regs) { @@ -44,8 +63,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, goto err1; ext = nft_set_elem_ext(set, elem); - if (priv->expr != NULL && - nft_expr_clone(nft_set_ext_expr(ext), priv->expr) < 0) + if (priv->num_exprs && nft_dynset_expr_setup(priv, ext) < 0) goto err2; return elem; @@ -90,6 +108,41 @@ void nft_dynset_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } +static void nft_dynset_ext_add_expr(struct nft_dynset *priv) +{ + u8 size = 0; + int i; + + for (i = 0; i < priv->num_exprs; i++) + size += priv->expr_array[i]->ops->size; + + nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPRESSIONS, + sizeof(struct nft_set_elem_expr) + size); +} + +static struct nft_expr * +nft_dynset_expr_alloc(const struct nft_ctx *ctx, const struct nft_set *set, + const struct nlattr *attr, int pos) +{ + struct nft_expr *expr; + int err; + + expr = nft_set_elem_expr_alloc(ctx, set, attr); + if (IS_ERR(expr)) + return expr; + + if (set->exprs[pos] && set->exprs[pos]->ops != expr->ops) { + err = -EOPNOTSUPP; + goto err_dynset_expr; + } + + return expr; + +err_dynset_expr: + nft_expr_destroy(ctx, expr); + return ERR_PTR(err); +} + static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = { [NFTA_DYNSET_SET_NAME] = { .type = NLA_STRING, .len = NFT_SET_MAXNAMELEN - 1 }, @@ -110,7 +163,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx, u8 genmask = nft_genmask_next(ctx->net); struct nft_set *set; u64 timeout; - int err; + int err, i; lockdep_assert_held(&ctx->net->nft.commit_mutex); @@ -179,17 +232,23 @@ static int nft_dynset_init(const struct nft_ctx *ctx, } else if (set->flags & NFT_SET_MAP) return -EINVAL; - if (tb[NFTA_DYNSET_EXPR] != NULL) { + if (tb[NFTA_DYNSET_EXPR]) { + struct nft_expr *dynset_expr; + if (!(set->flags & NFT_SET_EVAL)) return -EINVAL; - priv->expr = nft_set_elem_expr_alloc(ctx, set, - tb[NFTA_DYNSET_EXPR]); - if (IS_ERR(priv->expr)) - return PTR_ERR(priv->expr); + dynset_expr = nft_dynset_expr_alloc(ctx, set, + tb[NFTA_DYNSET_EXPR], 0); + if (IS_ERR(dynset_expr)) + return PTR_ERR(dynset_expr); - if (set->num_exprs == 1 && - set->exprs[0]->ops != priv->expr->ops) { + priv->num_exprs++; + priv->expr_array[0] = dynset_expr; + + if (set->num_exprs > 1 || + (set->num_exprs == 1 && + dynset_expr->ops != set->exprs[0]->ops)) { err = -EOPNOTSUPP; goto err_expr_free; } @@ -199,9 +258,10 @@ static int nft_dynset_init(const struct nft_ctx *ctx, nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen); if (set->flags & NFT_SET_MAP) nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen); - if (priv->expr != NULL) - nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPR, - priv->expr->ops->size); + + if (priv->num_exprs) + nft_dynset_ext_add_expr(priv); + if (set->flags & NFT_SET_TIMEOUT) { if (timeout || set->timeout) nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION); @@ -220,8 +280,8 @@ static int nft_dynset_init(const struct nft_ctx *ctx, return 0; err_expr_free: - if (priv->expr != NULL) - nft_expr_destroy(ctx, priv->expr); + for (i = 0; i < priv->num_exprs; i++) + nft_expr_destroy(ctx, priv->expr_array[i]); return err; } @@ -246,9 +306,10 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) { struct nft_dynset *priv = nft_expr_priv(expr); + int i; - if (priv->expr != NULL) - nft_expr_destroy(ctx, priv->expr); + for (i = 0; i < priv->num_exprs; i++) + nft_expr_destroy(ctx, priv->expr_array[i]); nf_tables_destroy_set(ctx, priv->set); } @@ -271,8 +332,10 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr) cpu_to_be64(jiffies_to_msecs(priv->timeout)), NFTA_DYNSET_PAD)) goto nla_put_failure; - if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr)) - goto nla_put_failure; + if (priv->num_exprs == 1) { + if (nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr_array[0])) + goto nla_put_failure; + } if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags))) goto nla_put_failure; return 0; diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 4d3f147e8d8d..bf618b7ec1ae 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -293,6 +293,22 @@ cont: rhashtable_walk_exit(&hti); } +static bool nft_rhash_expr_needs_gc_run(const struct nft_set *set, + struct nft_set_ext *ext) +{ + struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); + struct nft_expr *expr; + u32 size; + + nft_setelem_expr_foreach(expr, elem_expr, size) { + if (expr->ops->gc && + expr->ops->gc(read_pnet(&set->net), expr)) + return true; + } + + return false; +} + static void nft_rhash_gc(struct work_struct *work) { struct nft_set *set; @@ -314,16 +330,13 @@ static void nft_rhash_gc(struct work_struct *work) continue; } - if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPR)) { - struct nft_expr *expr = nft_set_ext_expr(&he->ext); + if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPRESSIONS) && + nft_rhash_expr_needs_gc_run(set, &he->ext)) + goto needs_gc_run; - if (expr->ops->gc && - expr->ops->gc(read_pnet(&set->net), expr)) - goto gc; - } if (!nft_set_elem_expired(&he->ext)) continue; -gc: +needs_gc_run: if (nft_set_elem_mark_busy(&he->ext)) continue; -- cgit v1.2.3 From 48b0ae046ee96eac999839f6d26c624b8c93ed66 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 7 Dec 2020 17:37:14 +0100 Subject: netfilter: nftables: netlink support for several set element expressions This patch adds three new netlink attributes to encapsulate a list of expressions per set elements: - NFTA_SET_EXPRESSIONS: this attribute provides the set definition in terms of expressions. New set elements get attached the list of expressions that is specified by this new netlink attribute. - NFTA_SET_ELEM_EXPRESSIONS: this attribute allows users to restore (or initialize) the stateful information of set elements when adding an element to the set. - NFTA_DYNSET_EXPRESSIONS: this attribute specifies the list of expressions that the set element gets when it is inserted from the packet path. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 6 +++ net/netfilter/nf_tables_api.c | 93 ++++++++++++++++++++++++++++++-- net/netfilter/nft_dynset.c | 56 +++++++++++++++++-- 3 files changed, 149 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 98272cb5f617..28b6ee53305f 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -361,6 +361,7 @@ enum nft_set_field_attributes { * @NFTA_SET_OBJ_TYPE: stateful object type (NLA_U32: NFT_OBJECT_*) * @NFTA_SET_HANDLE: set handle (NLA_U64) * @NFTA_SET_EXPR: set expression (NLA_NESTED: nft_expr_attributes) + * @NFTA_SET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes) */ enum nft_set_attributes { NFTA_SET_UNSPEC, @@ -381,6 +382,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) @@ -406,6 +408,7 @@ enum nft_set_elem_flags { * @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes) * @NFTA_SET_ELEM_OBJREF: stateful object reference (NLA_STRING) * @NFTA_SET_ELEM_KEY_END: closing key value (NLA_NESTED: nft_data) + * @NFTA_SET_ELEM_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes) */ enum nft_set_elem_attributes { NFTA_SET_ELEM_UNSPEC, @@ -419,6 +422,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) @@ -715,6 +719,7 @@ enum nft_dynset_flags { * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64) * @NFTA_DYNSET_EXPR: expression (NLA_NESTED: nft_expr_attributes) * @NFTA_DYNSET_FLAGS: flags (NLA_U32) + * @NFTA_DYNSET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes) */ enum nft_dynset_attributes { NFTA_DYNSET_UNSPEC, @@ -727,6 +732,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/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a3d5014dd246..243e3c2c7629 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3566,6 +3566,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_OBJ_TYPE] = { .type = NLA_U32 }, [NFTA_SET_HANDLE] = { .type = NLA_U64 }, [NFTA_SET_EXPR] = { .type = NLA_NESTED }, + [NFTA_SET_EXPRESSIONS] = { .type = NLA_NESTED }, }; static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { @@ -3773,6 +3774,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, u32 portid = ctx->portid; struct nlattr *nest; u32 seq = ctx->seq; + int i; event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), @@ -3847,6 +3849,17 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, goto nla_put_failure; nla_nest_end(skb, nest); + } else if (set->num_exprs > 1) { + nest = nla_nest_start_noflag(skb, NFTA_SET_EXPRESSIONS); + if (nest == NULL) + goto nla_put_failure; + + for (i = 0; i < set->num_exprs; i++) { + if (nft_expr_dump(skb, NFTA_LIST_ELEM, + set->exprs[i]) < 0) + goto nla_put_failure; + } + nla_nest_end(skb, nest); } nlmsg_end(skb, nlh); @@ -4215,7 +4228,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, return err; } - if (nla[NFTA_SET_EXPR]) + if (nla[NFTA_SET_EXPR] || nla[NFTA_SET_EXPRESSIONS]) desc.expr = true; table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family, genmask); @@ -4281,6 +4294,29 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, } set->exprs[0] = expr; set->num_exprs++; + } else if (nla[NFTA_SET_EXPRESSIONS]) { + struct nft_expr *expr; + struct nlattr *tmp; + int left; + + i = 0; + nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) { + if (i == NFT_SET_EXPR_MAX) { + err = -E2BIG; + goto err_set_init; + } + if (nla_type(tmp) != NFTA_LIST_ELEM) { + err = -EINVAL; + goto err_set_init; + } + expr = nft_set_elem_expr_alloc(&ctx, set, tmp); + if (IS_ERR(expr)) { + err = PTR_ERR(expr); + goto err_set_init; + } + set->exprs[i++] = expr; + set->num_exprs++; + } } udata = NULL; @@ -4540,6 +4576,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { [NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING, .len = NFT_OBJ_MAXNAMELEN - 1 }, [NFTA_SET_ELEM_KEY_END] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_EXPRESSIONS] = { .type = NLA_NESTED }, }; static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { @@ -4580,6 +4617,7 @@ static int nft_set_elem_expr_dump(struct sk_buff *skb, struct nft_set_elem_expr *elem_expr; u32 size, num_exprs = 0; struct nft_expr *expr; + struct nlattr *nest; elem_expr = nft_set_ext_expr(ext); nft_setelem_expr_foreach(expr, elem_expr, size) @@ -4591,9 +4629,22 @@ static int nft_set_elem_expr_dump(struct sk_buff *skb, return -1; return 0; - } + } else if (num_exprs > 1) { + nest = nla_nest_start_noflag(skb, NFTA_SET_ELEM_EXPRESSIONS); + if (nest == NULL) + goto nla_put_failure; + nft_setelem_expr_foreach(expr, elem_expr, size) { + expr = nft_setelem_expr_at(elem_expr, size); + if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr) < 0) + goto nla_put_failure; + } + nla_nest_end(skb, nest); + } return 0; + +nla_put_failure: + return -1; } static int nf_tables_fill_setelem(struct sk_buff *skb, @@ -5268,7 +5319,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, nla[NFTA_SET_ELEM_TIMEOUT] || nla[NFTA_SET_ELEM_EXPIRATION] || nla[NFTA_SET_ELEM_USERDATA] || - nla[NFTA_SET_ELEM_EXPR])) + nla[NFTA_SET_ELEM_EXPR] || + nla[NFTA_SET_ELEM_EXPRESSIONS])) return -EINVAL; timeout = 0; @@ -5310,6 +5362,41 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, err = -EOPNOTSUPP; goto err_set_elem_expr; } + } else if (nla[NFTA_SET_ELEM_EXPRESSIONS]) { + struct nft_expr *expr; + struct nlattr *tmp; + int left; + + if (set->num_exprs == 0) + return -EOPNOTSUPP; + + i = 0; + nla_for_each_nested(tmp, nla[NFTA_SET_ELEM_EXPRESSIONS], left) { + if (i == set->num_exprs) { + err = -E2BIG; + goto err_set_elem_expr; + } + if (nla_type(tmp) != NFTA_LIST_ELEM) { + err = -EINVAL; + goto err_set_elem_expr; + } + expr = nft_set_elem_expr_alloc(ctx, set, tmp); + if (IS_ERR(expr)) { + err = PTR_ERR(expr); + goto err_set_elem_expr; + } + expr_array[i] = expr; + + if (expr->ops != set->exprs[i]->ops) { + err = -EOPNOTSUPP; + goto err_set_elem_expr; + } + i++; + } + if (set->num_exprs != i) { + err = -EOPNOTSUPP; + goto err_set_elem_expr; + } } else if (set->num_exprs > 0) { err = nft_set_elem_expr_clone(ctx, set, expr_array); if (err < 0) diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index d9e609b2e5d4..13c426d5dcf9 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -153,6 +153,7 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = { [NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 }, [NFTA_DYNSET_EXPR] = { .type = NLA_NESTED }, [NFTA_DYNSET_FLAGS] = { .type = NLA_U32 }, + [NFTA_DYNSET_EXPRESSIONS] = { .type = NLA_NESTED }, }; static int nft_dynset_init(const struct nft_ctx *ctx, @@ -232,12 +233,13 @@ static int nft_dynset_init(const struct nft_ctx *ctx, } else if (set->flags & NFT_SET_MAP) return -EINVAL; + if ((tb[NFTA_DYNSET_EXPR] || tb[NFTA_DYNSET_EXPRESSIONS]) && + !(set->flags & NFT_SET_EVAL)) + return -EINVAL; + if (tb[NFTA_DYNSET_EXPR]) { struct nft_expr *dynset_expr; - if (!(set->flags & NFT_SET_EVAL)) - return -EINVAL; - dynset_expr = nft_dynset_expr_alloc(ctx, set, tb[NFTA_DYNSET_EXPR], 0); if (IS_ERR(dynset_expr)) @@ -252,6 +254,40 @@ static int nft_dynset_init(const struct nft_ctx *ctx, err = -EOPNOTSUPP; goto err_expr_free; } + } else if (tb[NFTA_DYNSET_EXPRESSIONS]) { + struct nft_expr *dynset_expr; + struct nlattr *tmp; + int left; + + i = 0; + nla_for_each_nested(tmp, tb[NFTA_DYNSET_EXPRESSIONS], left) { + if (i == NFT_SET_EXPR_MAX) { + err = -E2BIG; + goto err_expr_free; + } + if (nla_type(tmp) != NFTA_LIST_ELEM) { + err = -EINVAL; + goto err_expr_free; + } + dynset_expr = nft_dynset_expr_alloc(ctx, set, tmp, i); + if (IS_ERR(dynset_expr)) { + err = PTR_ERR(dynset_expr); + goto err_expr_free; + } + priv->expr_array[i] = dynset_expr; + priv->num_exprs++; + + if (set->num_exprs && + dynset_expr->ops != set->exprs[i]->ops) { + err = -EOPNOTSUPP; + goto err_expr_free; + } + i++; + } + if (set->num_exprs && set->num_exprs != i) { + err = -EOPNOTSUPP; + goto err_expr_free; + } } nft_set_ext_prepare(&priv->tmpl); @@ -318,6 +354,7 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr) { const struct nft_dynset *priv = nft_expr_priv(expr); u32 flags = priv->invert ? NFT_DYNSET_F_INV : 0; + int i; if (nft_dump_register(skb, NFTA_DYNSET_SREG_KEY, priv->sreg_key)) goto nla_put_failure; @@ -335,6 +372,19 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr) if (priv->num_exprs == 1) { if (nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr_array[0])) goto nla_put_failure; + } else if (priv->num_exprs > 1) { + struct nlattr *nest; + + nest = nla_nest_start_noflag(skb, NFTA_DYNSET_EXPRESSIONS); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < priv->num_exprs; i++) { + if (nft_expr_dump(skb, NFTA_LIST_ELEM, + priv->expr_array[i])) + goto nla_put_failure; + } + nla_nest_end(skb, nest); } if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags))) goto nla_put_failure; -- cgit v1.2.3