* [PATCH 1/2 v3] netfilter: nfnetlink_queue: get rid of nfnetlink_queue_ct.c @ 2015-10-01 18:43 Pablo Neira Ayuso 2015-10-01 18:43 ` [PATCH 2/2 v3] netfilter: rename nfnetlink_queue_core.c to nfnetlink_queue.c Pablo Neira Ayuso 0 siblings, 1 reply; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-01 18:43 UTC (permalink / raw) To: netfilter-devel; +Cc: chamaken The original intention was to avoid dependencies between nfnetlink_queue and conntrack without ifdef pollution. However, we can achieve this by moving the conntrack dependent code into ctnetlink and keep some glue code to access the nfq_ct indirection from nfqueue. After this patch, the nfq_ct indirection is always compiled in the netfilter core to avoid polluting nfqueue with ifdefs. Thus, if nf_conntrack is not compiled this results in only 8-bytes of memory waste in x86_64. This patch also adds ctnetlink_nfqueue_seqadj() to avoid that the nf_conn structure layout if exposed to nf_queue, which creates another dependency with nf_conntrack at compilation time. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> --- v3: fix typo when rebasing without the patch to consolidate the netlink message size calculation. include/linux/netfilter.h | 12 ++-- include/net/netfilter/nfnetlink_queue.h | 51 -------------- net/netfilter/Makefile | 1 - net/netfilter/core.c | 9 ++- net/netfilter/nf_conntrack_netlink.c | 52 ++++++++++++++- net/netfilter/nfnetlink_queue_core.c | 52 +++++++++++---- net/netfilter/nfnetlink_queue_ct.c | 113 -------------------------------- 7 files changed, 103 insertions(+), 187 deletions(-) delete mode 100644 include/net/netfilter/nfnetlink_queue.h delete mode 100644 net/netfilter/nfnetlink_queue_ct.c diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 165ab2d..3e5e8f2 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -369,14 +369,21 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) extern void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) __rcu; void nf_ct_attach(struct sk_buff *, const struct sk_buff *); extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; +#else +static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} +#endif struct nf_conn; enum ip_conntrack_info; struct nlattr; struct nfq_ct_hook { + struct nf_conn *(*get_ct)(struct sk_buff *skb, + enum ip_conntrack_info *ctinfo); size_t (*build_size)(const struct nf_conn *ct); - int (*build)(struct sk_buff *skb, struct nf_conn *ct); + int (*build)(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + u_int16_t ct_attr, u_int16_t ct_info_attr); int (*parse)(const struct nlattr *attr, struct nf_conn *ct); int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, u32 portid, u32 report); @@ -384,9 +391,6 @@ struct nfq_ct_hook { enum ip_conntrack_info ctinfo, s32 off); }; extern struct nfq_ct_hook __rcu *nfq_ct_hook; -#else -static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} -#endif /** * nf_skb_duplicated - TEE target has sent a packet diff --git a/include/net/netfilter/nfnetlink_queue.h b/include/net/netfilter/nfnetlink_queue.h deleted file mode 100644 index aff88ba..0000000 --- a/include/net/netfilter/nfnetlink_queue.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef _NET_NFNL_QUEUE_H_ -#define _NET_NFNL_QUEUE_H_ - -#include <linux/netfilter/nf_conntrack_common.h> - -struct nf_conn; - -#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT -struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size, - enum ip_conntrack_info *ctinfo); -struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb, - const struct nlattr *attr, - enum ip_conntrack_info *ctinfo); -int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo); -void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, int diff); -int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, - u32 portid, u32 report); -#else -inline struct nf_conn * -nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) -{ - return NULL; -} - -inline struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb, - const struct nlattr *attr, - enum ip_conntrack_info *ctinfo) -{ - return NULL; -} - -inline int -nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) -{ - return 0; -} - -inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, int diff) -{ -} - -inline int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, - u32 portid, u32 report) -{ - return 0; -} -#endif /* NF_CONNTRACK */ -#endif diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 70d026d..4d68e72 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_NETFILTER) = netfilter.o obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o nfnetlink_queue-y := nfnetlink_queue_core.o -nfnetlink_queue-$(CONFIG_NETFILTER_NETLINK_QUEUE_CT) += nfnetlink_queue_ct.o obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 2e90733..1412e36 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -348,6 +348,12 @@ int skb_make_writable(struct sk_buff *skb, unsigned int writable_len) } EXPORT_SYMBOL(skb_make_writable); +/* This needs to be compiled in any case to avoid dependencies between the + * nfnetlink_queue code and nf_conntrack. + */ +struct nfq_ct_hook __rcu *nfq_ct_hook __read_mostly; +EXPORT_SYMBOL_GPL(nfq_ct_hook); + #if IS_ENABLED(CONFIG_NF_CONNTRACK) /* This does not belong here, but locally generated errors need it if connection tracking in use: without this, connection may not be in hash table, and hence @@ -385,9 +391,6 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct) } EXPORT_SYMBOL(nf_conntrack_destroy); -struct nfq_ct_hook __rcu *nfq_ct_hook __read_mostly; -EXPORT_SYMBOL_GPL(nfq_ct_hook); - /* Built-in default zone used e.g. by modules. */ const struct nf_conntrack_zone nf_ct_zone_dflt = { .id = NF_CT_DEFAULT_ZONE_ID, diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 94a6654..eb67bf8 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2162,8 +2162,19 @@ ctnetlink_nfqueue_build_size(const struct nf_conn *ct) ; } -static int -ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) +static struct nf_conn *ctnetlink_nfqueue_get_ct(struct sk_buff *skb, + enum ip_conntrack_info *ctinfo) +{ + struct nf_conn *ct; + + ct = nf_ct_get(skb, ctinfo); + if (ct && nf_ct_is_untracked(ct)) + ct = NULL; + + return ct; +} + +static int __ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) { const struct nf_conntrack_zone *zone; struct nlattr *nest_parms; @@ -2236,6 +2247,31 @@ nla_put_failure: } static int +ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + u_int16_t ct_attr, u_int16_t ct_info_attr) +{ + struct nlattr *nest_parms; + + nest_parms = nla_nest_start(skb, ct_attr | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + if (__ctnetlink_nfqueue_build(skb, ct) < 0) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms); + + if (nla_put_be32(skb, ct_info_attr, htonl(ctinfo))) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static int ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) { int err; @@ -2350,12 +2386,22 @@ ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, return 0; } +static void ctnetlink_nfqueue_seqadj(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int diff) +{ + if (!(ct->status & IPS_NAT_MASK)) + return; + + nf_ct_tcp_seqadj_set(skb, ct, ctinfo, diff); +} + static struct nfq_ct_hook ctnetlink_nfqueue_hook = { + .get_ct = ctnetlink_nfqueue_get_ct, .build_size = ctnetlink_nfqueue_build_size, .build = ctnetlink_nfqueue_build, .parse = ctnetlink_nfqueue_parse, .attach_expect = ctnetlink_nfqueue_attach_expect, - .seq_adjust = nf_ct_tcp_seqadj_set, + .seq_adjust = ctnetlink_nfqueue_seqadj, }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 41583e3..b1f1c74 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -28,12 +28,12 @@ #include <linux/netfilter_bridge.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_queue.h> +#include <linux/netfilter/nf_conntrack_common.h> #include <linux/list.h> #include <net/sock.h> #include <net/tcp_states.h> #include <net/netfilter/nf_queue.h> #include <net/netns/generic.h> -#include <net/netfilter/nfnetlink_queue.h> #include <linux/atomic.h> @@ -313,6 +313,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct net_device *outdev; struct nf_conn *ct = NULL; enum ip_conntrack_info uninitialized_var(ctinfo); + struct nfq_ct_hook *nfq_ct; bool csum_verify; char *secdata = NULL; u32 seclen = 0; @@ -364,8 +365,14 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, break; } - if (queue->flags & NFQA_CFG_F_CONNTRACK) - ct = nfqnl_ct_get(entskb, &size, &ctinfo); + if (queue->flags & NFQA_CFG_F_CONNTRACK) { + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct != NULL) { + ct = nfq_ct->get_ct(entskb, &ctinfo); + if (ct != NULL) + size += nfq_ct->build_size(ct); + } + } if (queue->flags & NFQA_CFG_F_UID_GID) { size += (nla_total_size(sizeof(u_int32_t)) /* uid */ @@ -508,7 +515,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, if (seclen && nla_put(skb, NFQA_SECCTX, seclen, secdata)) goto nla_put_failure; - if (ct && nfqnl_ct_put(skb, ct, ctinfo) < 0) + if (ct && nfq_ct->build(skb, ct, ctinfo, NFQA_CT, NFQA_CT_INFO) < 0) goto nla_put_failure; if (cap_len > data_len && @@ -1001,6 +1008,28 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, return 0; } +static struct nf_conn *nfqnl_ct_parse(struct nfq_ct_hook *nfq_ct, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[], + struct nf_queue_entry *entry, + enum ip_conntrack_info *ctinfo) +{ + struct nf_conn *ct; + + ct = nfq_ct->get_ct(entry->skb, ctinfo); + if (ct == NULL) + return NULL; + + if (nfq_ct->parse(nfqa[NFQA_CT], ct) < 0) + return NULL; + + if (nfqa[NFQA_EXP]) + nfq_ct->attach_expect(nfqa[NFQA_EXP], ct, + NETLINK_CB(entry->skb).portid, + nlmsg_report(nlh)); + return ct; +} + static int nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, @@ -1014,6 +1043,7 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, unsigned int verdict; struct nf_queue_entry *entry; enum ip_conntrack_info uninitialized_var(ctinfo); + struct nfq_ct_hook *nfq_ct; struct nf_conn *ct = NULL; struct net *net = sock_net(ctnl); @@ -1037,12 +1067,10 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, return -ENOENT; if (nfqa[NFQA_CT]) { - ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); - if (ct && nfqa[NFQA_EXP]) { - nfqnl_attach_expect(ct, nfqa[NFQA_EXP], - NETLINK_CB(skb).portid, - nlmsg_report(nlh)); - } + /* rcu lock already held from nfnl->call_rcu. */ + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct != NULL) + ct = nfqnl_ct_parse(nfq_ct, nlh, nfqa, entry, &ctinfo); } if (nfqa[NFQA_PAYLOAD]) { @@ -1053,8 +1081,8 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, payload_len, entry, diff) < 0) verdict = NF_DROP; - if (ct) - nfqnl_ct_seq_adjust(entry->skb, ct, ctinfo, diff); + if (ct && diff) + nfq_ct->seq_adjust(entry->skb, ct, ctinfo, diff); } if (nfqa[NFQA_MARK]) diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c deleted file mode 100644 index 96cac50..0000000 --- a/net/netfilter/nfnetlink_queue_ct.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/skbuff.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nfnetlink.h> -#include <linux/netfilter/nfnetlink_queue.h> -#include <net/netfilter/nf_conntrack.h> -#include <net/netfilter/nfnetlink_queue.h> - -struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size, - enum ip_conntrack_info *ctinfo) -{ - struct nfq_ct_hook *nfq_ct; - struct nf_conn *ct; - - /* rcu_read_lock()ed by __nf_queue already. */ - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct == NULL) - return NULL; - - ct = nf_ct_get(entskb, ctinfo); - if (ct) { - if (!nf_ct_is_untracked(ct)) - *size += nfq_ct->build_size(ct); - else - ct = NULL; - } - return ct; -} - -struct nf_conn * -nfqnl_ct_parse(const struct sk_buff *skb, const struct nlattr *attr, - enum ip_conntrack_info *ctinfo) -{ - struct nfq_ct_hook *nfq_ct; - struct nf_conn *ct; - - /* rcu_read_lock()ed by __nf_queue already. */ - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct == NULL) - return NULL; - - ct = nf_ct_get(skb, ctinfo); - if (ct && !nf_ct_is_untracked(ct)) - nfq_ct->parse(attr, ct); - - return ct; -} - -int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo) -{ - struct nfq_ct_hook *nfq_ct; - struct nlattr *nest_parms; - u_int32_t tmp; - - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct == NULL) - return 0; - - nest_parms = nla_nest_start(skb, NFQA_CT | NLA_F_NESTED); - if (!nest_parms) - goto nla_put_failure; - - if (nfq_ct->build(skb, ct) < 0) - goto nla_put_failure; - - nla_nest_end(skb, nest_parms); - - tmp = ctinfo; - if (nla_put_be32(skb, NFQA_CT_INFO, htonl(tmp))) - goto nla_put_failure; - - return 0; - -nla_put_failure: - return -1; -} - -void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, int diff) -{ - struct nfq_ct_hook *nfq_ct; - - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct == NULL) - return; - - if ((ct->status & IPS_NAT_MASK) && diff) - nfq_ct->seq_adjust(skb, ct, ctinfo, diff); -} - -int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, - u32 portid, u32 report) -{ - struct nfq_ct_hook *nfq_ct; - - if (nf_ct_is_untracked(ct)) - return 0; - - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct == NULL) - return -EOPNOTSUPP; - - return nfq_ct->attach_expect(attr, ct, portid, report); -} -- 2.1.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 2/2 v3] netfilter: rename nfnetlink_queue_core.c to nfnetlink_queue.c 2015-10-01 18:43 [PATCH 1/2 v3] netfilter: nfnetlink_queue: get rid of nfnetlink_queue_ct.c Pablo Neira Ayuso @ 2015-10-01 18:43 ` Pablo Neira Ayuso 2015-10-05 2:44 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Ken-ichirou MATSUZAWA 0 siblings, 1 reply; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-01 18:43 UTC (permalink / raw) To: netfilter-devel; +Cc: chamaken Now that we have integrated the ct glue code into nfnetlink_queue without introducing dependencies with the conntrack code. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> --- v3: no changes net/netfilter/Makefile | 1 - net/netfilter/nfnetlink_queue.c | 1443 ++++++++++++++++++++++++++++++++++ net/netfilter/nfnetlink_queue_core.c | 1443 ---------------------------------- 3 files changed, 1443 insertions(+), 1444 deletions(-) create mode 100644 net/netfilter/nfnetlink_queue.c delete mode 100644 net/netfilter/nfnetlink_queue_core.c diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 4d68e72..7638c36 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_NETFILTER) = netfilter.o obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o -nfnetlink_queue-y := nfnetlink_queue_core.o obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c new file mode 100644 index 0000000..b1f1c74 --- /dev/null +++ b/net/netfilter/nfnetlink_queue.c @@ -0,0 +1,1443 @@ +/* + * This is a module which is used for queueing packets and communicating with + * userspace via nfnetlink. + * + * (C) 2005 by Harald Welte <laforge@netfilter.org> + * (C) 2007 by Patrick McHardy <kaber@trash.net> + * + * Based on the old ipv4-only ip_queue.c: + * (C) 2000-2002 James Morris <jmorris@intercode.com.au> + * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/module.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/notifier.h> +#include <linux/netdevice.h> +#include <linux/netfilter.h> +#include <linux/proc_fs.h> +#include <linux/netfilter_ipv4.h> +#include <linux/netfilter_ipv6.h> +#include <linux/netfilter_bridge.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_queue.h> +#include <linux/netfilter/nf_conntrack_common.h> +#include <linux/list.h> +#include <net/sock.h> +#include <net/tcp_states.h> +#include <net/netfilter/nf_queue.h> +#include <net/netns/generic.h> + +#include <linux/atomic.h> + +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +#include "../bridge/br_private.h" +#endif + +#define NFQNL_QMAX_DEFAULT 1024 + +/* We're using struct nlattr which has 16bit nla_len. Note that nla_len + * includes the header length. Thus, the maximum packet length that we + * support is 65531 bytes. We send truncated packets if the specified length + * is larger than that. Userspace can check for presence of NFQA_CAP_LEN + * attribute to detect truncation. + */ +#define NFQNL_MAX_COPY_RANGE (0xffff - NLA_HDRLEN) + +struct nfqnl_instance { + struct hlist_node hlist; /* global list of queues */ + struct rcu_head rcu; + + u32 peer_portid; + unsigned int queue_maxlen; + unsigned int copy_range; + unsigned int queue_dropped; + unsigned int queue_user_dropped; + + + u_int16_t queue_num; /* number of this queue */ + u_int8_t copy_mode; + u_int32_t flags; /* Set using NFQA_CFG_FLAGS */ +/* + * Following fields are dirtied for each queued packet, + * keep them in same cache line if possible. + */ + spinlock_t lock; + unsigned int queue_total; + unsigned int id_sequence; /* 'sequence' of pkt ids */ + struct list_head queue_list; /* packets in queue */ +}; + +typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); + +static int nfnl_queue_net_id __read_mostly; + +#define INSTANCE_BUCKETS 16 +struct nfnl_queue_net { + spinlock_t instances_lock; + struct hlist_head instance_table[INSTANCE_BUCKETS]; +}; + +static struct nfnl_queue_net *nfnl_queue_pernet(struct net *net) +{ + return net_generic(net, nfnl_queue_net_id); +} + +static inline u_int8_t instance_hashfn(u_int16_t queue_num) +{ + return ((queue_num >> 8) ^ queue_num) % INSTANCE_BUCKETS; +} + +static struct nfqnl_instance * +instance_lookup(struct nfnl_queue_net *q, u_int16_t queue_num) +{ + struct hlist_head *head; + struct nfqnl_instance *inst; + + head = &q->instance_table[instance_hashfn(queue_num)]; + hlist_for_each_entry_rcu(inst, head, hlist) { + if (inst->queue_num == queue_num) + return inst; + } + return NULL; +} + +static struct nfqnl_instance * +instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid) +{ + struct nfqnl_instance *inst; + unsigned int h; + int err; + + spin_lock(&q->instances_lock); + if (instance_lookup(q, queue_num)) { + err = -EEXIST; + goto out_unlock; + } + + inst = kzalloc(sizeof(*inst), GFP_ATOMIC); + if (!inst) { + err = -ENOMEM; + goto out_unlock; + } + + inst->queue_num = queue_num; + inst->peer_portid = portid; + inst->queue_maxlen = NFQNL_QMAX_DEFAULT; + inst->copy_range = NFQNL_MAX_COPY_RANGE; + inst->copy_mode = NFQNL_COPY_NONE; + spin_lock_init(&inst->lock); + INIT_LIST_HEAD(&inst->queue_list); + + if (!try_module_get(THIS_MODULE)) { + err = -EAGAIN; + goto out_free; + } + + h = instance_hashfn(queue_num); + hlist_add_head_rcu(&inst->hlist, &q->instance_table[h]); + + spin_unlock(&q->instances_lock); + + return inst; + +out_free: + kfree(inst); +out_unlock: + spin_unlock(&q->instances_lock); + return ERR_PTR(err); +} + +static void nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, + unsigned long data); + +static void +instance_destroy_rcu(struct rcu_head *head) +{ + struct nfqnl_instance *inst = container_of(head, struct nfqnl_instance, + rcu); + + nfqnl_flush(inst, NULL, 0); + kfree(inst); + module_put(THIS_MODULE); +} + +static void +__instance_destroy(struct nfqnl_instance *inst) +{ + hlist_del_rcu(&inst->hlist); + call_rcu(&inst->rcu, instance_destroy_rcu); +} + +static void +instance_destroy(struct nfnl_queue_net *q, struct nfqnl_instance *inst) +{ + spin_lock(&q->instances_lock); + __instance_destroy(inst); + spin_unlock(&q->instances_lock); +} + +static inline void +__enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) +{ + list_add_tail(&entry->list, &queue->queue_list); + queue->queue_total++; +} + +static void +__dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) +{ + list_del(&entry->list); + queue->queue_total--; +} + +static struct nf_queue_entry * +find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) +{ + struct nf_queue_entry *entry = NULL, *i; + + spin_lock_bh(&queue->lock); + + list_for_each_entry(i, &queue->queue_list, list) { + if (i->id == id) { + entry = i; + break; + } + } + + if (entry) + __dequeue_entry(queue, entry); + + spin_unlock_bh(&queue->lock); + + return entry; +} + +static void +nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) +{ + struct nf_queue_entry *entry, *next; + + spin_lock_bh(&queue->lock); + list_for_each_entry_safe(entry, next, &queue->queue_list, list) { + if (!cmpfn || cmpfn(entry, data)) { + list_del(&entry->list); + queue->queue_total--; + nf_reinject(entry, NF_DROP); + } + } + spin_unlock_bh(&queue->lock); +} + +static int +nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, + bool csum_verify) +{ + __u32 flags = 0; + + if (packet->ip_summed == CHECKSUM_PARTIAL) + flags = NFQA_SKB_CSUMNOTREADY; + else if (csum_verify) + flags = NFQA_SKB_CSUM_NOTVERIFIED; + + if (skb_is_gso(packet)) + flags |= NFQA_SKB_GSO; + + return flags ? nla_put_be32(nlskb, NFQA_SKB_INFO, htonl(flags)) : 0; +} + +static int nfqnl_put_sk_uidgid(struct sk_buff *skb, struct sock *sk) +{ + const struct cred *cred; + + if (!sk_fullsock(sk)) + return 0; + + read_lock_bh(&sk->sk_callback_lock); + if (sk->sk_socket && sk->sk_socket->file) { + cred = sk->sk_socket->file->f_cred; + if (nla_put_be32(skb, NFQA_UID, + htonl(from_kuid_munged(&init_user_ns, cred->fsuid)))) + goto nla_put_failure; + if (nla_put_be32(skb, NFQA_GID, + htonl(from_kgid_munged(&init_user_ns, cred->fsgid)))) + goto nla_put_failure; + } + read_unlock_bh(&sk->sk_callback_lock); + return 0; + +nla_put_failure: + read_unlock_bh(&sk->sk_callback_lock); + return -1; +} + +static u32 nfqnl_get_sk_secctx(struct sk_buff *skb, char **secdata) +{ + u32 seclen = 0; +#if IS_ENABLED(CONFIG_NETWORK_SECMARK) + if (!skb || !sk_fullsock(skb->sk)) + return 0; + + read_lock_bh(&skb->sk->sk_callback_lock); + + if (skb->secmark) + security_secid_to_secctx(skb->secmark, secdata, &seclen); + + read_unlock_bh(&skb->sk->sk_callback_lock); +#endif + return seclen; +} + +static struct sk_buff * +nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, + struct nf_queue_entry *entry, + __be32 **packet_id_ptr) +{ + size_t size; + size_t data_len = 0, cap_len = 0, rem_len = 0; + unsigned int hlen = 0; + struct sk_buff *skb; + struct nlattr *nla; + struct nfqnl_msg_packet_hdr *pmsg; + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + struct sk_buff *entskb = entry->skb; + struct net_device *indev; + struct net_device *outdev; + struct nf_conn *ct = NULL; + enum ip_conntrack_info uninitialized_var(ctinfo); + struct nfq_ct_hook *nfq_ct; + bool csum_verify; + char *secdata = NULL; + u32 seclen = 0; + + size = nlmsg_total_size(sizeof(struct nfgenmsg)) + + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ +#endif + + nla_total_size(sizeof(u_int32_t)) /* mark */ + + nla_total_size(sizeof(struct nfqnl_msg_packet_hw)) + + nla_total_size(sizeof(u_int32_t)) /* skbinfo */ + + nla_total_size(sizeof(u_int32_t)); /* cap_len */ + + if (entskb->tstamp.tv64) + size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); + + if (entry->state.hook <= NF_INET_FORWARD || + (entry->state.hook == NF_INET_POST_ROUTING && entskb->sk == NULL)) + csum_verify = !skb_csum_unnecessary(entskb); + else + csum_verify = false; + + outdev = entry->state.out; + + switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) { + case NFQNL_COPY_META: + case NFQNL_COPY_NONE: + break; + + case NFQNL_COPY_PACKET: + if (!(queue->flags & NFQA_CFG_F_GSO) && + entskb->ip_summed == CHECKSUM_PARTIAL && + skb_checksum_help(entskb)) + return NULL; + + data_len = ACCESS_ONCE(queue->copy_range); + if (data_len > entskb->len) + data_len = entskb->len; + + hlen = skb_zerocopy_headlen(entskb); + hlen = min_t(unsigned int, hlen, data_len); + size += sizeof(struct nlattr) + hlen; + cap_len = entskb->len; + rem_len = data_len - hlen; + break; + } + + if (queue->flags & NFQA_CFG_F_CONNTRACK) { + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct != NULL) { + ct = nfq_ct->get_ct(entskb, &ctinfo); + if (ct != NULL) + size += nfq_ct->build_size(ct); + } + } + + if (queue->flags & NFQA_CFG_F_UID_GID) { + size += (nla_total_size(sizeof(u_int32_t)) /* uid */ + + nla_total_size(sizeof(u_int32_t))); /* gid */ + } + + if ((queue->flags & NFQA_CFG_F_SECCTX) && entskb->sk) { + seclen = nfqnl_get_sk_secctx(entskb, &secdata); + if (seclen) + size += nla_total_size(seclen); + } + + skb = __netlink_alloc_skb(net->nfnl, size, rem_len, queue->peer_portid, + GFP_ATOMIC); + if (!skb) { + skb_tx_error(entskb); + return NULL; + } + + nlh = nlmsg_put(skb, 0, 0, + NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, + sizeof(struct nfgenmsg), 0); + if (!nlh) { + skb_tx_error(entskb); + kfree_skb(skb); + return NULL; + } + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = entry->state.pf; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = htons(queue->queue_num); + + nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); + pmsg = nla_data(nla); + pmsg->hw_protocol = entskb->protocol; + pmsg->hook = entry->state.hook; + *packet_id_ptr = &pmsg->packet_id; + + indev = entry->state.in; + if (indev) { +#if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex))) + goto nla_put_failure; +#else + if (entry->state.pf == PF_BRIDGE) { + /* Case 1: indev is physical input device, we need to + * look for bridge group (when called from + * netfilter_bridge) */ + if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, + htonl(indev->ifindex)) || + /* this is the bridge group "brX" */ + /* rcu_read_lock()ed by __nf_queue */ + nla_put_be32(skb, NFQA_IFINDEX_INDEV, + htonl(br_port_get_rcu(indev)->br->dev->ifindex))) + goto nla_put_failure; + } else { + int physinif; + + /* Case 2: indev is bridge group, we need to look for + * physical device (when called from ipv4) */ + if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, + htonl(indev->ifindex))) + goto nla_put_failure; + + physinif = nf_bridge_get_physinif(entskb); + if (physinif && + nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, + htonl(physinif))) + goto nla_put_failure; + } +#endif + } + + if (outdev) { +#if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex))) + goto nla_put_failure; +#else + if (entry->state.pf == PF_BRIDGE) { + /* Case 1: outdev is physical output device, we need to + * look for bridge group (when called from + * netfilter_bridge) */ + if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, + htonl(outdev->ifindex)) || + /* this is the bridge group "brX" */ + /* rcu_read_lock()ed by __nf_queue */ + nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, + htonl(br_port_get_rcu(outdev)->br->dev->ifindex))) + goto nla_put_failure; + } else { + int physoutif; + + /* Case 2: outdev is bridge group, we need to look for + * physical output device (when called from ipv4) */ + if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, + htonl(outdev->ifindex))) + goto nla_put_failure; + + physoutif = nf_bridge_get_physoutif(entskb); + if (physoutif && + nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, + htonl(physoutif))) + goto nla_put_failure; + } +#endif + } + + if (entskb->mark && + nla_put_be32(skb, NFQA_MARK, htonl(entskb->mark))) + goto nla_put_failure; + + if (indev && entskb->dev && + entskb->mac_header != entskb->network_header) { + struct nfqnl_msg_packet_hw phw; + int len; + + memset(&phw, 0, sizeof(phw)); + len = dev_parse_header(entskb, phw.hw_addr); + if (len) { + phw.hw_addrlen = htons(len); + if (nla_put(skb, NFQA_HWADDR, sizeof(phw), &phw)) + goto nla_put_failure; + } + } + + if (entskb->tstamp.tv64) { + struct nfqnl_msg_packet_timestamp ts; + struct timeval tv = ktime_to_timeval(entskb->tstamp); + ts.sec = cpu_to_be64(tv.tv_sec); + ts.usec = cpu_to_be64(tv.tv_usec); + + if (nla_put(skb, NFQA_TIMESTAMP, sizeof(ts), &ts)) + goto nla_put_failure; + } + + if ((queue->flags & NFQA_CFG_F_UID_GID) && entskb->sk && + nfqnl_put_sk_uidgid(skb, entskb->sk) < 0) + goto nla_put_failure; + + if (seclen && nla_put(skb, NFQA_SECCTX, seclen, secdata)) + goto nla_put_failure; + + if (ct && nfq_ct->build(skb, ct, ctinfo, NFQA_CT, NFQA_CT_INFO) < 0) + goto nla_put_failure; + + if (cap_len > data_len && + nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len))) + goto nla_put_failure; + + if (nfqnl_put_packet_info(skb, entskb, csum_verify)) + goto nla_put_failure; + + if (data_len) { + struct nlattr *nla; + + if (skb_tailroom(skb) < sizeof(*nla) + hlen) + goto nla_put_failure; + + nla = (struct nlattr *)skb_put(skb, sizeof(*nla)); + nla->nla_type = NFQA_PAYLOAD; + nla->nla_len = nla_attr_size(data_len); + + if (skb_zerocopy(skb, entskb, data_len, hlen)) + goto nla_put_failure; + } + + nlh->nlmsg_len = skb->len; + return skb; + +nla_put_failure: + skb_tx_error(entskb); + kfree_skb(skb); + net_err_ratelimited("nf_queue: error creating packet message\n"); + return NULL; +} + +static int +__nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, + struct nf_queue_entry *entry) +{ + struct sk_buff *nskb; + int err = -ENOBUFS; + __be32 *packet_id_ptr; + int failopen = 0; + + nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr); + if (nskb == NULL) { + err = -ENOMEM; + goto err_out; + } + spin_lock_bh(&queue->lock); + + if (queue->queue_total >= queue->queue_maxlen) { + if (queue->flags & NFQA_CFG_F_FAIL_OPEN) { + failopen = 1; + err = 0; + } else { + queue->queue_dropped++; + net_warn_ratelimited("nf_queue: full at %d entries, dropping packets(s)\n", + queue->queue_total); + } + goto err_out_free_nskb; + } + entry->id = ++queue->id_sequence; + *packet_id_ptr = htonl(entry->id); + + /* nfnetlink_unicast will either free the nskb or add it to a socket */ + err = nfnetlink_unicast(nskb, net, queue->peer_portid, MSG_DONTWAIT); + if (err < 0) { + queue->queue_user_dropped++; + goto err_out_unlock; + } + + __enqueue_entry(queue, entry); + + spin_unlock_bh(&queue->lock); + return 0; + +err_out_free_nskb: + kfree_skb(nskb); +err_out_unlock: + spin_unlock_bh(&queue->lock); + if (failopen) + nf_reinject(entry, NF_ACCEPT); +err_out: + return err; +} + +static struct nf_queue_entry * +nf_queue_entry_dup(struct nf_queue_entry *e) +{ + struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC); + if (entry) { + if (nf_queue_entry_get_refs(entry)) + return entry; + kfree(entry); + } + return NULL; +} + +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +/* When called from bridge netfilter, skb->data must point to MAC header + * before calling skb_gso_segment(). Else, original MAC header is lost + * and segmented skbs will be sent to wrong destination. + */ +static void nf_bridge_adjust_skb_data(struct sk_buff *skb) +{ + if (skb->nf_bridge) + __skb_push(skb, skb->network_header - skb->mac_header); +} + +static void nf_bridge_adjust_segmented_data(struct sk_buff *skb) +{ + if (skb->nf_bridge) + __skb_pull(skb, skb->network_header - skb->mac_header); +} +#else +#define nf_bridge_adjust_skb_data(s) do {} while (0) +#define nf_bridge_adjust_segmented_data(s) do {} while (0) +#endif + +static void free_entry(struct nf_queue_entry *entry) +{ + nf_queue_entry_release_refs(entry); + kfree(entry); +} + +static int +__nfqnl_enqueue_packet_gso(struct net *net, struct nfqnl_instance *queue, + struct sk_buff *skb, struct nf_queue_entry *entry) +{ + int ret = -ENOMEM; + struct nf_queue_entry *entry_seg; + + nf_bridge_adjust_segmented_data(skb); + + if (skb->next == NULL) { /* last packet, no need to copy entry */ + struct sk_buff *gso_skb = entry->skb; + entry->skb = skb; + ret = __nfqnl_enqueue_packet(net, queue, entry); + if (ret) + entry->skb = gso_skb; + return ret; + } + + skb->next = NULL; + + entry_seg = nf_queue_entry_dup(entry); + if (entry_seg) { + entry_seg->skb = skb; + ret = __nfqnl_enqueue_packet(net, queue, entry_seg); + if (ret) + free_entry(entry_seg); + } + return ret; +} + +static int +nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) +{ + unsigned int queued; + struct nfqnl_instance *queue; + struct sk_buff *skb, *segs; + int err = -ENOBUFS; + struct net *net = entry->state.net; + struct nfnl_queue_net *q = nfnl_queue_pernet(net); + + /* rcu_read_lock()ed by nf_hook_slow() */ + queue = instance_lookup(q, queuenum); + if (!queue) + return -ESRCH; + + if (queue->copy_mode == NFQNL_COPY_NONE) + return -EINVAL; + + skb = entry->skb; + + switch (entry->state.pf) { + case NFPROTO_IPV4: + skb->protocol = htons(ETH_P_IP); + break; + case NFPROTO_IPV6: + skb->protocol = htons(ETH_P_IPV6); + break; + } + + if ((queue->flags & NFQA_CFG_F_GSO) || !skb_is_gso(skb)) + return __nfqnl_enqueue_packet(net, queue, entry); + + nf_bridge_adjust_skb_data(skb); + segs = skb_gso_segment(skb, 0); + /* Does not use PTR_ERR to limit the number of error codes that can be + * returned by nf_queue. For instance, callers rely on -ECANCELED to + * mean 'ignore this hook'. + */ + if (IS_ERR_OR_NULL(segs)) + goto out_err; + queued = 0; + err = 0; + do { + struct sk_buff *nskb = segs->next; + if (err == 0) + err = __nfqnl_enqueue_packet_gso(net, queue, + segs, entry); + if (err == 0) + queued++; + else + kfree_skb(segs); + segs = nskb; + } while (segs); + + if (queued) { + if (err) /* some segments are already queued */ + free_entry(entry); + kfree_skb(skb); + return 0; + } + out_err: + nf_bridge_adjust_segmented_data(skb); + return err; +} + +static int +nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) +{ + struct sk_buff *nskb; + + if (diff < 0) { + if (pskb_trim(e->skb, data_len)) + return -ENOMEM; + } else if (diff > 0) { + if (data_len > 0xFFFF) + return -EINVAL; + if (diff > skb_tailroom(e->skb)) { + nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), + diff, GFP_ATOMIC); + if (!nskb) { + printk(KERN_WARNING "nf_queue: OOM " + "in mangle, dropping packet\n"); + return -ENOMEM; + } + kfree_skb(e->skb); + e->skb = nskb; + } + skb_put(e->skb, diff); + } + if (!skb_make_writable(e->skb, data_len)) + return -ENOMEM; + skb_copy_to_linear_data(e->skb, data, data_len); + e->skb->ip_summed = CHECKSUM_NONE; + return 0; +} + +static int +nfqnl_set_mode(struct nfqnl_instance *queue, + unsigned char mode, unsigned int range) +{ + int status = 0; + + spin_lock_bh(&queue->lock); + switch (mode) { + case NFQNL_COPY_NONE: + case NFQNL_COPY_META: + queue->copy_mode = mode; + queue->copy_range = 0; + break; + + case NFQNL_COPY_PACKET: + queue->copy_mode = mode; + if (range == 0 || range > NFQNL_MAX_COPY_RANGE) + queue->copy_range = NFQNL_MAX_COPY_RANGE; + else + queue->copy_range = range; + break; + + default: + status = -EINVAL; + + } + spin_unlock_bh(&queue->lock); + + return status; +} + +static int +dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) +{ + if (entry->state.in) + if (entry->state.in->ifindex == ifindex) + return 1; + if (entry->state.out) + if (entry->state.out->ifindex == ifindex) + return 1; +#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + if (entry->skb->nf_bridge) { + int physinif, physoutif; + + physinif = nf_bridge_get_physinif(entry->skb); + physoutif = nf_bridge_get_physoutif(entry->skb); + + if (physinif == ifindex || physoutif == ifindex) + return 1; + } +#endif + return 0; +} + +/* drop all packets with either indev or outdev == ifindex from all queue + * instances */ +static void +nfqnl_dev_drop(struct net *net, int ifindex) +{ + int i; + struct nfnl_queue_net *q = nfnl_queue_pernet(net); + + rcu_read_lock(); + + for (i = 0; i < INSTANCE_BUCKETS; i++) { + struct nfqnl_instance *inst; + struct hlist_head *head = &q->instance_table[i]; + + hlist_for_each_entry_rcu(inst, head, hlist) + nfqnl_flush(inst, dev_cmp, ifindex); + } + + rcu_read_unlock(); +} + +static int +nfqnl_rcv_dev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + + /* Drop any packets associated with the downed device */ + if (event == NETDEV_DOWN) + nfqnl_dev_drop(dev_net(dev), dev->ifindex); + return NOTIFY_DONE; +} + +static struct notifier_block nfqnl_dev_notifier = { + .notifier_call = nfqnl_rcv_dev_event, +}; + +static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr) +{ + return entry->elem == (struct nf_hook_ops *)ops_ptr; +} + +static void nfqnl_nf_hook_drop(struct net *net, struct nf_hook_ops *hook) +{ + struct nfnl_queue_net *q = nfnl_queue_pernet(net); + int i; + + rcu_read_lock(); + for (i = 0; i < INSTANCE_BUCKETS; i++) { + struct nfqnl_instance *inst; + struct hlist_head *head = &q->instance_table[i]; + + hlist_for_each_entry_rcu(inst, head, hlist) + nfqnl_flush(inst, nf_hook_cmp, (unsigned long)hook); + } + rcu_read_unlock(); +} + +static int +nfqnl_rcv_nl_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct netlink_notify *n = ptr; + struct nfnl_queue_net *q = nfnl_queue_pernet(n->net); + + if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { + int i; + + /* destroy all instances for this portid */ + spin_lock(&q->instances_lock); + for (i = 0; i < INSTANCE_BUCKETS; i++) { + struct hlist_node *t2; + struct nfqnl_instance *inst; + struct hlist_head *head = &q->instance_table[i]; + + hlist_for_each_entry_safe(inst, t2, head, hlist) { + if (n->portid == inst->peer_portid) + __instance_destroy(inst); + } + } + spin_unlock(&q->instances_lock); + } + return NOTIFY_DONE; +} + +static struct notifier_block nfqnl_rtnl_notifier = { + .notifier_call = nfqnl_rcv_nl_event, +}; + +static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { + [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, + [NFQA_MARK] = { .type = NLA_U32 }, + [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, + [NFQA_CT] = { .type = NLA_UNSPEC }, + [NFQA_EXP] = { .type = NLA_UNSPEC }, +}; + +static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { + [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, + [NFQA_MARK] = { .type = NLA_U32 }, +}; + +static struct nfqnl_instance * +verdict_instance_lookup(struct nfnl_queue_net *q, u16 queue_num, u32 nlportid) +{ + struct nfqnl_instance *queue; + + queue = instance_lookup(q, queue_num); + if (!queue) + return ERR_PTR(-ENODEV); + + if (queue->peer_portid != nlportid) + return ERR_PTR(-EPERM); + + return queue; +} + +static struct nfqnl_msg_verdict_hdr* +verdicthdr_get(const struct nlattr * const nfqa[]) +{ + struct nfqnl_msg_verdict_hdr *vhdr; + unsigned int verdict; + + if (!nfqa[NFQA_VERDICT_HDR]) + return NULL; + + vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]); + verdict = ntohl(vhdr->verdict) & NF_VERDICT_MASK; + if (verdict > NF_MAX_VERDICT || verdict == NF_STOLEN) + return NULL; + return vhdr; +} + +static int nfq_id_after(unsigned int id, unsigned int max) +{ + return (int)(id - max) > 0; +} + +static int +nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + struct nfgenmsg *nfmsg = nlmsg_data(nlh); + struct nf_queue_entry *entry, *tmp; + unsigned int verdict, maxid; + struct nfqnl_msg_verdict_hdr *vhdr; + struct nfqnl_instance *queue; + LIST_HEAD(batch_list); + u16 queue_num = ntohs(nfmsg->res_id); + + struct net *net = sock_net(ctnl); + struct nfnl_queue_net *q = nfnl_queue_pernet(net); + + queue = verdict_instance_lookup(q, queue_num, + NETLINK_CB(skb).portid); + if (IS_ERR(queue)) + return PTR_ERR(queue); + + vhdr = verdicthdr_get(nfqa); + if (!vhdr) + return -EINVAL; + + verdict = ntohl(vhdr->verdict); + maxid = ntohl(vhdr->id); + + spin_lock_bh(&queue->lock); + + list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) { + if (nfq_id_after(entry->id, maxid)) + break; + __dequeue_entry(queue, entry); + list_add_tail(&entry->list, &batch_list); + } + + spin_unlock_bh(&queue->lock); + + if (list_empty(&batch_list)) + return -ENOENT; + + list_for_each_entry_safe(entry, tmp, &batch_list, list) { + if (nfqa[NFQA_MARK]) + entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); + nf_reinject(entry, verdict); + } + return 0; +} + +static struct nf_conn *nfqnl_ct_parse(struct nfq_ct_hook *nfq_ct, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[], + struct nf_queue_entry *entry, + enum ip_conntrack_info *ctinfo) +{ + struct nf_conn *ct; + + ct = nfq_ct->get_ct(entry->skb, ctinfo); + if (ct == NULL) + return NULL; + + if (nfq_ct->parse(nfqa[NFQA_CT], ct) < 0) + return NULL; + + if (nfqa[NFQA_EXP]) + nfq_ct->attach_expect(nfqa[NFQA_EXP], ct, + NETLINK_CB(entry->skb).portid, + nlmsg_report(nlh)); + return ct; +} + +static int +nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u_int16_t queue_num = ntohs(nfmsg->res_id); + + struct nfqnl_msg_verdict_hdr *vhdr; + struct nfqnl_instance *queue; + unsigned int verdict; + struct nf_queue_entry *entry; + enum ip_conntrack_info uninitialized_var(ctinfo); + struct nfq_ct_hook *nfq_ct; + struct nf_conn *ct = NULL; + + struct net *net = sock_net(ctnl); + struct nfnl_queue_net *q = nfnl_queue_pernet(net); + + queue = instance_lookup(q, queue_num); + if (!queue) + queue = verdict_instance_lookup(q, queue_num, + NETLINK_CB(skb).portid); + if (IS_ERR(queue)) + return PTR_ERR(queue); + + vhdr = verdicthdr_get(nfqa); + if (!vhdr) + return -EINVAL; + + verdict = ntohl(vhdr->verdict); + + entry = find_dequeue_entry(queue, ntohl(vhdr->id)); + if (entry == NULL) + return -ENOENT; + + if (nfqa[NFQA_CT]) { + /* rcu lock already held from nfnl->call_rcu. */ + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct != NULL) + ct = nfqnl_ct_parse(nfq_ct, nlh, nfqa, entry, &ctinfo); + } + + if (nfqa[NFQA_PAYLOAD]) { + u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); + int diff = payload_len - entry->skb->len; + + if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), + payload_len, entry, diff) < 0) + verdict = NF_DROP; + + if (ct && diff) + nfq_ct->seq_adjust(entry->skb, ct, ctinfo, diff); + } + + if (nfqa[NFQA_MARK]) + entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); + + nf_reinject(entry, verdict); + return 0; +} + +static int +nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + return -ENOTSUPP; +} + +static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { + [NFQA_CFG_CMD] = { .len = sizeof(struct nfqnl_msg_config_cmd) }, + [NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) }, +}; + +static const struct nf_queue_handler nfqh = { + .outfn = &nfqnl_enqueue_packet, + .nf_hook_drop = &nfqnl_nf_hook_drop, +}; + +static int +nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u_int16_t queue_num = ntohs(nfmsg->res_id); + struct nfqnl_instance *queue; + struct nfqnl_msg_config_cmd *cmd = NULL; + struct net *net = sock_net(ctnl); + struct nfnl_queue_net *q = nfnl_queue_pernet(net); + int ret = 0; + + if (nfqa[NFQA_CFG_CMD]) { + cmd = nla_data(nfqa[NFQA_CFG_CMD]); + + /* Obsolete commands without queue context */ + switch (cmd->command) { + case NFQNL_CFG_CMD_PF_BIND: return 0; + case NFQNL_CFG_CMD_PF_UNBIND: return 0; + } + } + + rcu_read_lock(); + queue = instance_lookup(q, queue_num); + if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { + ret = -EPERM; + goto err_out_unlock; + } + + if (cmd != NULL) { + switch (cmd->command) { + case NFQNL_CFG_CMD_BIND: + if (queue) { + ret = -EBUSY; + goto err_out_unlock; + } + queue = instance_create(q, queue_num, + NETLINK_CB(skb).portid); + if (IS_ERR(queue)) { + ret = PTR_ERR(queue); + goto err_out_unlock; + } + break; + case NFQNL_CFG_CMD_UNBIND: + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + instance_destroy(q, queue); + break; + case NFQNL_CFG_CMD_PF_BIND: + case NFQNL_CFG_CMD_PF_UNBIND: + break; + default: + ret = -ENOTSUPP; + break; + } + } + + if (nfqa[NFQA_CFG_PARAMS]) { + struct nfqnl_msg_config_params *params; + + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + params = nla_data(nfqa[NFQA_CFG_PARAMS]); + nfqnl_set_mode(queue, params->copy_mode, + ntohl(params->copy_range)); + } + + if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { + __be32 *queue_maxlen; + + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); + spin_lock_bh(&queue->lock); + queue->queue_maxlen = ntohl(*queue_maxlen); + spin_unlock_bh(&queue->lock); + } + + if (nfqa[NFQA_CFG_FLAGS]) { + __u32 flags, mask; + + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + + if (!nfqa[NFQA_CFG_MASK]) { + /* A mask is needed to specify which flags are being + * changed. + */ + ret = -EINVAL; + goto err_out_unlock; + } + + flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); + mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); + + if (flags >= NFQA_CFG_F_MAX) { + ret = -EOPNOTSUPP; + goto err_out_unlock; + } +#if !IS_ENABLED(CONFIG_NETWORK_SECMARK) + if (flags & mask & NFQA_CFG_F_SECCTX) { + ret = -EOPNOTSUPP; + goto err_out_unlock; + } +#endif + spin_lock_bh(&queue->lock); + queue->flags &= ~mask; + queue->flags |= flags & mask; + spin_unlock_bh(&queue->lock); + } + +err_out_unlock: + rcu_read_unlock(); + return ret; +} + +static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { + [NFQNL_MSG_PACKET] = { .call_rcu = nfqnl_recv_unsupp, + .attr_count = NFQA_MAX, }, + [NFQNL_MSG_VERDICT] = { .call_rcu = nfqnl_recv_verdict, + .attr_count = NFQA_MAX, + .policy = nfqa_verdict_policy }, + [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, + .attr_count = NFQA_CFG_MAX, + .policy = nfqa_cfg_policy }, + [NFQNL_MSG_VERDICT_BATCH]={ .call_rcu = nfqnl_recv_verdict_batch, + .attr_count = NFQA_MAX, + .policy = nfqa_verdict_batch_policy }, +}; + +static const struct nfnetlink_subsystem nfqnl_subsys = { + .name = "nf_queue", + .subsys_id = NFNL_SUBSYS_QUEUE, + .cb_count = NFQNL_MSG_MAX, + .cb = nfqnl_cb, +}; + +#ifdef CONFIG_PROC_FS +struct iter_state { + struct seq_net_private p; + unsigned int bucket; +}; + +static struct hlist_node *get_first(struct seq_file *seq) +{ + struct iter_state *st = seq->private; + struct net *net; + struct nfnl_queue_net *q; + + if (!st) + return NULL; + + net = seq_file_net(seq); + q = nfnl_queue_pernet(net); + for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { + if (!hlist_empty(&q->instance_table[st->bucket])) + return q->instance_table[st->bucket].first; + } + return NULL; +} + +static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) +{ + struct iter_state *st = seq->private; + struct net *net = seq_file_net(seq); + + h = h->next; + while (!h) { + struct nfnl_queue_net *q; + + if (++st->bucket >= INSTANCE_BUCKETS) + return NULL; + + q = nfnl_queue_pernet(net); + h = q->instance_table[st->bucket].first; + } + return h; +} + +static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) +{ + struct hlist_node *head; + head = get_first(seq); + + if (head) + while (pos && (head = get_next(seq, head))) + pos--; + return pos ? NULL : head; +} + +static void *seq_start(struct seq_file *s, loff_t *pos) + __acquires(nfnl_queue_pernet(seq_file_net(s))->instances_lock) +{ + spin_lock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock); + return get_idx(s, *pos); +} + +static void *seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + (*pos)++; + return get_next(s, v); +} + +static void seq_stop(struct seq_file *s, void *v) + __releases(nfnl_queue_pernet(seq_file_net(s))->instances_lock) +{ + spin_unlock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock); +} + +static int seq_show(struct seq_file *s, void *v) +{ + const struct nfqnl_instance *inst = v; + + seq_printf(s, "%5u %6u %5u %1u %5u %5u %5u %8u %2d\n", + inst->queue_num, + inst->peer_portid, inst->queue_total, + inst->copy_mode, inst->copy_range, + inst->queue_dropped, inst->queue_user_dropped, + inst->id_sequence, 1); + return 0; +} + +static const struct seq_operations nfqnl_seq_ops = { + .start = seq_start, + .next = seq_next, + .stop = seq_stop, + .show = seq_show, +}; + +static int nfqnl_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &nfqnl_seq_ops, + sizeof(struct iter_state)); +} + +static const struct file_operations nfqnl_file_ops = { + .owner = THIS_MODULE, + .open = nfqnl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + +#endif /* PROC_FS */ + +static int __net_init nfnl_queue_net_init(struct net *net) +{ + unsigned int i; + struct nfnl_queue_net *q = nfnl_queue_pernet(net); + + for (i = 0; i < INSTANCE_BUCKETS; i++) + INIT_HLIST_HEAD(&q->instance_table[i]); + + spin_lock_init(&q->instances_lock); + +#ifdef CONFIG_PROC_FS + if (!proc_create("nfnetlink_queue", 0440, + net->nf.proc_netfilter, &nfqnl_file_ops)) + return -ENOMEM; +#endif + return 0; +} + +static void __net_exit nfnl_queue_net_exit(struct net *net) +{ +#ifdef CONFIG_PROC_FS + remove_proc_entry("nfnetlink_queue", net->nf.proc_netfilter); +#endif +} + +static struct pernet_operations nfnl_queue_net_ops = { + .init = nfnl_queue_net_init, + .exit = nfnl_queue_net_exit, + .id = &nfnl_queue_net_id, + .size = sizeof(struct nfnl_queue_net), +}; + +static int __init nfnetlink_queue_init(void) +{ + int status; + + status = register_pernet_subsys(&nfnl_queue_net_ops); + if (status < 0) { + pr_err("nf_queue: failed to register pernet ops\n"); + goto out; + } + + netlink_register_notifier(&nfqnl_rtnl_notifier); + status = nfnetlink_subsys_register(&nfqnl_subsys); + if (status < 0) { + pr_err("nf_queue: failed to create netlink socket\n"); + goto cleanup_netlink_notifier; + } + + register_netdevice_notifier(&nfqnl_dev_notifier); + nf_register_queue_handler(&nfqh); + return status; + +cleanup_netlink_notifier: + netlink_unregister_notifier(&nfqnl_rtnl_notifier); +out: + return status; +} + +static void __exit nfnetlink_queue_fini(void) +{ + nf_unregister_queue_handler(); + unregister_netdevice_notifier(&nfqnl_dev_notifier); + nfnetlink_subsys_unregister(&nfqnl_subsys); + netlink_unregister_notifier(&nfqnl_rtnl_notifier); + unregister_pernet_subsys(&nfnl_queue_net_ops); + + rcu_barrier(); /* Wait for completion of call_rcu()'s */ +} + +MODULE_DESCRIPTION("netfilter packet queue handler"); +MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE); + +module_init(nfnetlink_queue_init); +module_exit(nfnetlink_queue_fini); diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c deleted file mode 100644 index b1f1c74..0000000 --- a/net/netfilter/nfnetlink_queue_core.c +++ /dev/null @@ -1,1443 +0,0 @@ -/* - * This is a module which is used for queueing packets and communicating with - * userspace via nfnetlink. - * - * (C) 2005 by Harald Welte <laforge@netfilter.org> - * (C) 2007 by Patrick McHardy <kaber@trash.net> - * - * Based on the old ipv4-only ip_queue.c: - * (C) 2000-2002 James Morris <jmorris@intercode.com.au> - * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ -#include <linux/module.h> -#include <linux/skbuff.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <linux/notifier.h> -#include <linux/netdevice.h> -#include <linux/netfilter.h> -#include <linux/proc_fs.h> -#include <linux/netfilter_ipv4.h> -#include <linux/netfilter_ipv6.h> -#include <linux/netfilter_bridge.h> -#include <linux/netfilter/nfnetlink.h> -#include <linux/netfilter/nfnetlink_queue.h> -#include <linux/netfilter/nf_conntrack_common.h> -#include <linux/list.h> -#include <net/sock.h> -#include <net/tcp_states.h> -#include <net/netfilter/nf_queue.h> -#include <net/netns/generic.h> - -#include <linux/atomic.h> - -#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -#include "../bridge/br_private.h" -#endif - -#define NFQNL_QMAX_DEFAULT 1024 - -/* We're using struct nlattr which has 16bit nla_len. Note that nla_len - * includes the header length. Thus, the maximum packet length that we - * support is 65531 bytes. We send truncated packets if the specified length - * is larger than that. Userspace can check for presence of NFQA_CAP_LEN - * attribute to detect truncation. - */ -#define NFQNL_MAX_COPY_RANGE (0xffff - NLA_HDRLEN) - -struct nfqnl_instance { - struct hlist_node hlist; /* global list of queues */ - struct rcu_head rcu; - - u32 peer_portid; - unsigned int queue_maxlen; - unsigned int copy_range; - unsigned int queue_dropped; - unsigned int queue_user_dropped; - - - u_int16_t queue_num; /* number of this queue */ - u_int8_t copy_mode; - u_int32_t flags; /* Set using NFQA_CFG_FLAGS */ -/* - * Following fields are dirtied for each queued packet, - * keep them in same cache line if possible. - */ - spinlock_t lock; - unsigned int queue_total; - unsigned int id_sequence; /* 'sequence' of pkt ids */ - struct list_head queue_list; /* packets in queue */ -}; - -typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); - -static int nfnl_queue_net_id __read_mostly; - -#define INSTANCE_BUCKETS 16 -struct nfnl_queue_net { - spinlock_t instances_lock; - struct hlist_head instance_table[INSTANCE_BUCKETS]; -}; - -static struct nfnl_queue_net *nfnl_queue_pernet(struct net *net) -{ - return net_generic(net, nfnl_queue_net_id); -} - -static inline u_int8_t instance_hashfn(u_int16_t queue_num) -{ - return ((queue_num >> 8) ^ queue_num) % INSTANCE_BUCKETS; -} - -static struct nfqnl_instance * -instance_lookup(struct nfnl_queue_net *q, u_int16_t queue_num) -{ - struct hlist_head *head; - struct nfqnl_instance *inst; - - head = &q->instance_table[instance_hashfn(queue_num)]; - hlist_for_each_entry_rcu(inst, head, hlist) { - if (inst->queue_num == queue_num) - return inst; - } - return NULL; -} - -static struct nfqnl_instance * -instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid) -{ - struct nfqnl_instance *inst; - unsigned int h; - int err; - - spin_lock(&q->instances_lock); - if (instance_lookup(q, queue_num)) { - err = -EEXIST; - goto out_unlock; - } - - inst = kzalloc(sizeof(*inst), GFP_ATOMIC); - if (!inst) { - err = -ENOMEM; - goto out_unlock; - } - - inst->queue_num = queue_num; - inst->peer_portid = portid; - inst->queue_maxlen = NFQNL_QMAX_DEFAULT; - inst->copy_range = NFQNL_MAX_COPY_RANGE; - inst->copy_mode = NFQNL_COPY_NONE; - spin_lock_init(&inst->lock); - INIT_LIST_HEAD(&inst->queue_list); - - if (!try_module_get(THIS_MODULE)) { - err = -EAGAIN; - goto out_free; - } - - h = instance_hashfn(queue_num); - hlist_add_head_rcu(&inst->hlist, &q->instance_table[h]); - - spin_unlock(&q->instances_lock); - - return inst; - -out_free: - kfree(inst); -out_unlock: - spin_unlock(&q->instances_lock); - return ERR_PTR(err); -} - -static void nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, - unsigned long data); - -static void -instance_destroy_rcu(struct rcu_head *head) -{ - struct nfqnl_instance *inst = container_of(head, struct nfqnl_instance, - rcu); - - nfqnl_flush(inst, NULL, 0); - kfree(inst); - module_put(THIS_MODULE); -} - -static void -__instance_destroy(struct nfqnl_instance *inst) -{ - hlist_del_rcu(&inst->hlist); - call_rcu(&inst->rcu, instance_destroy_rcu); -} - -static void -instance_destroy(struct nfnl_queue_net *q, struct nfqnl_instance *inst) -{ - spin_lock(&q->instances_lock); - __instance_destroy(inst); - spin_unlock(&q->instances_lock); -} - -static inline void -__enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) -{ - list_add_tail(&entry->list, &queue->queue_list); - queue->queue_total++; -} - -static void -__dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) -{ - list_del(&entry->list); - queue->queue_total--; -} - -static struct nf_queue_entry * -find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) -{ - struct nf_queue_entry *entry = NULL, *i; - - spin_lock_bh(&queue->lock); - - list_for_each_entry(i, &queue->queue_list, list) { - if (i->id == id) { - entry = i; - break; - } - } - - if (entry) - __dequeue_entry(queue, entry); - - spin_unlock_bh(&queue->lock); - - return entry; -} - -static void -nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) -{ - struct nf_queue_entry *entry, *next; - - spin_lock_bh(&queue->lock); - list_for_each_entry_safe(entry, next, &queue->queue_list, list) { - if (!cmpfn || cmpfn(entry, data)) { - list_del(&entry->list); - queue->queue_total--; - nf_reinject(entry, NF_DROP); - } - } - spin_unlock_bh(&queue->lock); -} - -static int -nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, - bool csum_verify) -{ - __u32 flags = 0; - - if (packet->ip_summed == CHECKSUM_PARTIAL) - flags = NFQA_SKB_CSUMNOTREADY; - else if (csum_verify) - flags = NFQA_SKB_CSUM_NOTVERIFIED; - - if (skb_is_gso(packet)) - flags |= NFQA_SKB_GSO; - - return flags ? nla_put_be32(nlskb, NFQA_SKB_INFO, htonl(flags)) : 0; -} - -static int nfqnl_put_sk_uidgid(struct sk_buff *skb, struct sock *sk) -{ - const struct cred *cred; - - if (!sk_fullsock(sk)) - return 0; - - read_lock_bh(&sk->sk_callback_lock); - if (sk->sk_socket && sk->sk_socket->file) { - cred = sk->sk_socket->file->f_cred; - if (nla_put_be32(skb, NFQA_UID, - htonl(from_kuid_munged(&init_user_ns, cred->fsuid)))) - goto nla_put_failure; - if (nla_put_be32(skb, NFQA_GID, - htonl(from_kgid_munged(&init_user_ns, cred->fsgid)))) - goto nla_put_failure; - } - read_unlock_bh(&sk->sk_callback_lock); - return 0; - -nla_put_failure: - read_unlock_bh(&sk->sk_callback_lock); - return -1; -} - -static u32 nfqnl_get_sk_secctx(struct sk_buff *skb, char **secdata) -{ - u32 seclen = 0; -#if IS_ENABLED(CONFIG_NETWORK_SECMARK) - if (!skb || !sk_fullsock(skb->sk)) - return 0; - - read_lock_bh(&skb->sk->sk_callback_lock); - - if (skb->secmark) - security_secid_to_secctx(skb->secmark, secdata, &seclen); - - read_unlock_bh(&skb->sk->sk_callback_lock); -#endif - return seclen; -} - -static struct sk_buff * -nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, - struct nf_queue_entry *entry, - __be32 **packet_id_ptr) -{ - size_t size; - size_t data_len = 0, cap_len = 0, rem_len = 0; - unsigned int hlen = 0; - struct sk_buff *skb; - struct nlattr *nla; - struct nfqnl_msg_packet_hdr *pmsg; - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - struct sk_buff *entskb = entry->skb; - struct net_device *indev; - struct net_device *outdev; - struct nf_conn *ct = NULL; - enum ip_conntrack_info uninitialized_var(ctinfo); - struct nfq_ct_hook *nfq_ct; - bool csum_verify; - char *secdata = NULL; - u32 seclen = 0; - - size = nlmsg_total_size(sizeof(struct nfgenmsg)) - + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ -#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ -#endif - + nla_total_size(sizeof(u_int32_t)) /* mark */ - + nla_total_size(sizeof(struct nfqnl_msg_packet_hw)) - + nla_total_size(sizeof(u_int32_t)) /* skbinfo */ - + nla_total_size(sizeof(u_int32_t)); /* cap_len */ - - if (entskb->tstamp.tv64) - size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); - - if (entry->state.hook <= NF_INET_FORWARD || - (entry->state.hook == NF_INET_POST_ROUTING && entskb->sk == NULL)) - csum_verify = !skb_csum_unnecessary(entskb); - else - csum_verify = false; - - outdev = entry->state.out; - - switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) { - case NFQNL_COPY_META: - case NFQNL_COPY_NONE: - break; - - case NFQNL_COPY_PACKET: - if (!(queue->flags & NFQA_CFG_F_GSO) && - entskb->ip_summed == CHECKSUM_PARTIAL && - skb_checksum_help(entskb)) - return NULL; - - data_len = ACCESS_ONCE(queue->copy_range); - if (data_len > entskb->len) - data_len = entskb->len; - - hlen = skb_zerocopy_headlen(entskb); - hlen = min_t(unsigned int, hlen, data_len); - size += sizeof(struct nlattr) + hlen; - cap_len = entskb->len; - rem_len = data_len - hlen; - break; - } - - if (queue->flags & NFQA_CFG_F_CONNTRACK) { - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct != NULL) { - ct = nfq_ct->get_ct(entskb, &ctinfo); - if (ct != NULL) - size += nfq_ct->build_size(ct); - } - } - - if (queue->flags & NFQA_CFG_F_UID_GID) { - size += (nla_total_size(sizeof(u_int32_t)) /* uid */ - + nla_total_size(sizeof(u_int32_t))); /* gid */ - } - - if ((queue->flags & NFQA_CFG_F_SECCTX) && entskb->sk) { - seclen = nfqnl_get_sk_secctx(entskb, &secdata); - if (seclen) - size += nla_total_size(seclen); - } - - skb = __netlink_alloc_skb(net->nfnl, size, rem_len, queue->peer_portid, - GFP_ATOMIC); - if (!skb) { - skb_tx_error(entskb); - return NULL; - } - - nlh = nlmsg_put(skb, 0, 0, - NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, - sizeof(struct nfgenmsg), 0); - if (!nlh) { - skb_tx_error(entskb); - kfree_skb(skb); - return NULL; - } - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = entry->state.pf; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = htons(queue->queue_num); - - nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); - pmsg = nla_data(nla); - pmsg->hw_protocol = entskb->protocol; - pmsg->hook = entry->state.hook; - *packet_id_ptr = &pmsg->packet_id; - - indev = entry->state.in; - if (indev) { -#if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER) - if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex))) - goto nla_put_failure; -#else - if (entry->state.pf == PF_BRIDGE) { - /* Case 1: indev is physical input device, we need to - * look for bridge group (when called from - * netfilter_bridge) */ - if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, - htonl(indev->ifindex)) || - /* this is the bridge group "brX" */ - /* rcu_read_lock()ed by __nf_queue */ - nla_put_be32(skb, NFQA_IFINDEX_INDEV, - htonl(br_port_get_rcu(indev)->br->dev->ifindex))) - goto nla_put_failure; - } else { - int physinif; - - /* Case 2: indev is bridge group, we need to look for - * physical device (when called from ipv4) */ - if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, - htonl(indev->ifindex))) - goto nla_put_failure; - - physinif = nf_bridge_get_physinif(entskb); - if (physinif && - nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, - htonl(physinif))) - goto nla_put_failure; - } -#endif - } - - if (outdev) { -#if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER) - if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex))) - goto nla_put_failure; -#else - if (entry->state.pf == PF_BRIDGE) { - /* Case 1: outdev is physical output device, we need to - * look for bridge group (when called from - * netfilter_bridge) */ - if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, - htonl(outdev->ifindex)) || - /* this is the bridge group "brX" */ - /* rcu_read_lock()ed by __nf_queue */ - nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, - htonl(br_port_get_rcu(outdev)->br->dev->ifindex))) - goto nla_put_failure; - } else { - int physoutif; - - /* Case 2: outdev is bridge group, we need to look for - * physical output device (when called from ipv4) */ - if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, - htonl(outdev->ifindex))) - goto nla_put_failure; - - physoutif = nf_bridge_get_physoutif(entskb); - if (physoutif && - nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, - htonl(physoutif))) - goto nla_put_failure; - } -#endif - } - - if (entskb->mark && - nla_put_be32(skb, NFQA_MARK, htonl(entskb->mark))) - goto nla_put_failure; - - if (indev && entskb->dev && - entskb->mac_header != entskb->network_header) { - struct nfqnl_msg_packet_hw phw; - int len; - - memset(&phw, 0, sizeof(phw)); - len = dev_parse_header(entskb, phw.hw_addr); - if (len) { - phw.hw_addrlen = htons(len); - if (nla_put(skb, NFQA_HWADDR, sizeof(phw), &phw)) - goto nla_put_failure; - } - } - - if (entskb->tstamp.tv64) { - struct nfqnl_msg_packet_timestamp ts; - struct timeval tv = ktime_to_timeval(entskb->tstamp); - ts.sec = cpu_to_be64(tv.tv_sec); - ts.usec = cpu_to_be64(tv.tv_usec); - - if (nla_put(skb, NFQA_TIMESTAMP, sizeof(ts), &ts)) - goto nla_put_failure; - } - - if ((queue->flags & NFQA_CFG_F_UID_GID) && entskb->sk && - nfqnl_put_sk_uidgid(skb, entskb->sk) < 0) - goto nla_put_failure; - - if (seclen && nla_put(skb, NFQA_SECCTX, seclen, secdata)) - goto nla_put_failure; - - if (ct && nfq_ct->build(skb, ct, ctinfo, NFQA_CT, NFQA_CT_INFO) < 0) - goto nla_put_failure; - - if (cap_len > data_len && - nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len))) - goto nla_put_failure; - - if (nfqnl_put_packet_info(skb, entskb, csum_verify)) - goto nla_put_failure; - - if (data_len) { - struct nlattr *nla; - - if (skb_tailroom(skb) < sizeof(*nla) + hlen) - goto nla_put_failure; - - nla = (struct nlattr *)skb_put(skb, sizeof(*nla)); - nla->nla_type = NFQA_PAYLOAD; - nla->nla_len = nla_attr_size(data_len); - - if (skb_zerocopy(skb, entskb, data_len, hlen)) - goto nla_put_failure; - } - - nlh->nlmsg_len = skb->len; - return skb; - -nla_put_failure: - skb_tx_error(entskb); - kfree_skb(skb); - net_err_ratelimited("nf_queue: error creating packet message\n"); - return NULL; -} - -static int -__nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, - struct nf_queue_entry *entry) -{ - struct sk_buff *nskb; - int err = -ENOBUFS; - __be32 *packet_id_ptr; - int failopen = 0; - - nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr); - if (nskb == NULL) { - err = -ENOMEM; - goto err_out; - } - spin_lock_bh(&queue->lock); - - if (queue->queue_total >= queue->queue_maxlen) { - if (queue->flags & NFQA_CFG_F_FAIL_OPEN) { - failopen = 1; - err = 0; - } else { - queue->queue_dropped++; - net_warn_ratelimited("nf_queue: full at %d entries, dropping packets(s)\n", - queue->queue_total); - } - goto err_out_free_nskb; - } - entry->id = ++queue->id_sequence; - *packet_id_ptr = htonl(entry->id); - - /* nfnetlink_unicast will either free the nskb or add it to a socket */ - err = nfnetlink_unicast(nskb, net, queue->peer_portid, MSG_DONTWAIT); - if (err < 0) { - queue->queue_user_dropped++; - goto err_out_unlock; - } - - __enqueue_entry(queue, entry); - - spin_unlock_bh(&queue->lock); - return 0; - -err_out_free_nskb: - kfree_skb(nskb); -err_out_unlock: - spin_unlock_bh(&queue->lock); - if (failopen) - nf_reinject(entry, NF_ACCEPT); -err_out: - return err; -} - -static struct nf_queue_entry * -nf_queue_entry_dup(struct nf_queue_entry *e) -{ - struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC); - if (entry) { - if (nf_queue_entry_get_refs(entry)) - return entry; - kfree(entry); - } - return NULL; -} - -#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -/* When called from bridge netfilter, skb->data must point to MAC header - * before calling skb_gso_segment(). Else, original MAC header is lost - * and segmented skbs will be sent to wrong destination. - */ -static void nf_bridge_adjust_skb_data(struct sk_buff *skb) -{ - if (skb->nf_bridge) - __skb_push(skb, skb->network_header - skb->mac_header); -} - -static void nf_bridge_adjust_segmented_data(struct sk_buff *skb) -{ - if (skb->nf_bridge) - __skb_pull(skb, skb->network_header - skb->mac_header); -} -#else -#define nf_bridge_adjust_skb_data(s) do {} while (0) -#define nf_bridge_adjust_segmented_data(s) do {} while (0) -#endif - -static void free_entry(struct nf_queue_entry *entry) -{ - nf_queue_entry_release_refs(entry); - kfree(entry); -} - -static int -__nfqnl_enqueue_packet_gso(struct net *net, struct nfqnl_instance *queue, - struct sk_buff *skb, struct nf_queue_entry *entry) -{ - int ret = -ENOMEM; - struct nf_queue_entry *entry_seg; - - nf_bridge_adjust_segmented_data(skb); - - if (skb->next == NULL) { /* last packet, no need to copy entry */ - struct sk_buff *gso_skb = entry->skb; - entry->skb = skb; - ret = __nfqnl_enqueue_packet(net, queue, entry); - if (ret) - entry->skb = gso_skb; - return ret; - } - - skb->next = NULL; - - entry_seg = nf_queue_entry_dup(entry); - if (entry_seg) { - entry_seg->skb = skb; - ret = __nfqnl_enqueue_packet(net, queue, entry_seg); - if (ret) - free_entry(entry_seg); - } - return ret; -} - -static int -nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) -{ - unsigned int queued; - struct nfqnl_instance *queue; - struct sk_buff *skb, *segs; - int err = -ENOBUFS; - struct net *net = entry->state.net; - struct nfnl_queue_net *q = nfnl_queue_pernet(net); - - /* rcu_read_lock()ed by nf_hook_slow() */ - queue = instance_lookup(q, queuenum); - if (!queue) - return -ESRCH; - - if (queue->copy_mode == NFQNL_COPY_NONE) - return -EINVAL; - - skb = entry->skb; - - switch (entry->state.pf) { - case NFPROTO_IPV4: - skb->protocol = htons(ETH_P_IP); - break; - case NFPROTO_IPV6: - skb->protocol = htons(ETH_P_IPV6); - break; - } - - if ((queue->flags & NFQA_CFG_F_GSO) || !skb_is_gso(skb)) - return __nfqnl_enqueue_packet(net, queue, entry); - - nf_bridge_adjust_skb_data(skb); - segs = skb_gso_segment(skb, 0); - /* Does not use PTR_ERR to limit the number of error codes that can be - * returned by nf_queue. For instance, callers rely on -ECANCELED to - * mean 'ignore this hook'. - */ - if (IS_ERR_OR_NULL(segs)) - goto out_err; - queued = 0; - err = 0; - do { - struct sk_buff *nskb = segs->next; - if (err == 0) - err = __nfqnl_enqueue_packet_gso(net, queue, - segs, entry); - if (err == 0) - queued++; - else - kfree_skb(segs); - segs = nskb; - } while (segs); - - if (queued) { - if (err) /* some segments are already queued */ - free_entry(entry); - kfree_skb(skb); - return 0; - } - out_err: - nf_bridge_adjust_segmented_data(skb); - return err; -} - -static int -nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) -{ - struct sk_buff *nskb; - - if (diff < 0) { - if (pskb_trim(e->skb, data_len)) - return -ENOMEM; - } else if (diff > 0) { - if (data_len > 0xFFFF) - return -EINVAL; - if (diff > skb_tailroom(e->skb)) { - nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), - diff, GFP_ATOMIC); - if (!nskb) { - printk(KERN_WARNING "nf_queue: OOM " - "in mangle, dropping packet\n"); - return -ENOMEM; - } - kfree_skb(e->skb); - e->skb = nskb; - } - skb_put(e->skb, diff); - } - if (!skb_make_writable(e->skb, data_len)) - return -ENOMEM; - skb_copy_to_linear_data(e->skb, data, data_len); - e->skb->ip_summed = CHECKSUM_NONE; - return 0; -} - -static int -nfqnl_set_mode(struct nfqnl_instance *queue, - unsigned char mode, unsigned int range) -{ - int status = 0; - - spin_lock_bh(&queue->lock); - switch (mode) { - case NFQNL_COPY_NONE: - case NFQNL_COPY_META: - queue->copy_mode = mode; - queue->copy_range = 0; - break; - - case NFQNL_COPY_PACKET: - queue->copy_mode = mode; - if (range == 0 || range > NFQNL_MAX_COPY_RANGE) - queue->copy_range = NFQNL_MAX_COPY_RANGE; - else - queue->copy_range = range; - break; - - default: - status = -EINVAL; - - } - spin_unlock_bh(&queue->lock); - - return status; -} - -static int -dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) -{ - if (entry->state.in) - if (entry->state.in->ifindex == ifindex) - return 1; - if (entry->state.out) - if (entry->state.out->ifindex == ifindex) - return 1; -#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) - if (entry->skb->nf_bridge) { - int physinif, physoutif; - - physinif = nf_bridge_get_physinif(entry->skb); - physoutif = nf_bridge_get_physoutif(entry->skb); - - if (physinif == ifindex || physoutif == ifindex) - return 1; - } -#endif - return 0; -} - -/* drop all packets with either indev or outdev == ifindex from all queue - * instances */ -static void -nfqnl_dev_drop(struct net *net, int ifindex) -{ - int i; - struct nfnl_queue_net *q = nfnl_queue_pernet(net); - - rcu_read_lock(); - - for (i = 0; i < INSTANCE_BUCKETS; i++) { - struct nfqnl_instance *inst; - struct hlist_head *head = &q->instance_table[i]; - - hlist_for_each_entry_rcu(inst, head, hlist) - nfqnl_flush(inst, dev_cmp, ifindex); - } - - rcu_read_unlock(); -} - -static int -nfqnl_rcv_dev_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - /* Drop any packets associated with the downed device */ - if (event == NETDEV_DOWN) - nfqnl_dev_drop(dev_net(dev), dev->ifindex); - return NOTIFY_DONE; -} - -static struct notifier_block nfqnl_dev_notifier = { - .notifier_call = nfqnl_rcv_dev_event, -}; - -static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr) -{ - return entry->elem == (struct nf_hook_ops *)ops_ptr; -} - -static void nfqnl_nf_hook_drop(struct net *net, struct nf_hook_ops *hook) -{ - struct nfnl_queue_net *q = nfnl_queue_pernet(net); - int i; - - rcu_read_lock(); - for (i = 0; i < INSTANCE_BUCKETS; i++) { - struct nfqnl_instance *inst; - struct hlist_head *head = &q->instance_table[i]; - - hlist_for_each_entry_rcu(inst, head, hlist) - nfqnl_flush(inst, nf_hook_cmp, (unsigned long)hook); - } - rcu_read_unlock(); -} - -static int -nfqnl_rcv_nl_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct netlink_notify *n = ptr; - struct nfnl_queue_net *q = nfnl_queue_pernet(n->net); - - if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { - int i; - - /* destroy all instances for this portid */ - spin_lock(&q->instances_lock); - for (i = 0; i < INSTANCE_BUCKETS; i++) { - struct hlist_node *t2; - struct nfqnl_instance *inst; - struct hlist_head *head = &q->instance_table[i]; - - hlist_for_each_entry_safe(inst, t2, head, hlist) { - if (n->portid == inst->peer_portid) - __instance_destroy(inst); - } - } - spin_unlock(&q->instances_lock); - } - return NOTIFY_DONE; -} - -static struct notifier_block nfqnl_rtnl_notifier = { - .notifier_call = nfqnl_rcv_nl_event, -}; - -static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { - [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, - [NFQA_MARK] = { .type = NLA_U32 }, - [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, - [NFQA_CT] = { .type = NLA_UNSPEC }, - [NFQA_EXP] = { .type = NLA_UNSPEC }, -}; - -static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { - [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, - [NFQA_MARK] = { .type = NLA_U32 }, -}; - -static struct nfqnl_instance * -verdict_instance_lookup(struct nfnl_queue_net *q, u16 queue_num, u32 nlportid) -{ - struct nfqnl_instance *queue; - - queue = instance_lookup(q, queue_num); - if (!queue) - return ERR_PTR(-ENODEV); - - if (queue->peer_portid != nlportid) - return ERR_PTR(-EPERM); - - return queue; -} - -static struct nfqnl_msg_verdict_hdr* -verdicthdr_get(const struct nlattr * const nfqa[]) -{ - struct nfqnl_msg_verdict_hdr *vhdr; - unsigned int verdict; - - if (!nfqa[NFQA_VERDICT_HDR]) - return NULL; - - vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]); - verdict = ntohl(vhdr->verdict) & NF_VERDICT_MASK; - if (verdict > NF_MAX_VERDICT || verdict == NF_STOLEN) - return NULL; - return vhdr; -} - -static int nfq_id_after(unsigned int id, unsigned int max) -{ - return (int)(id - max) > 0; -} - -static int -nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - struct nfgenmsg *nfmsg = nlmsg_data(nlh); - struct nf_queue_entry *entry, *tmp; - unsigned int verdict, maxid; - struct nfqnl_msg_verdict_hdr *vhdr; - struct nfqnl_instance *queue; - LIST_HEAD(batch_list); - u16 queue_num = ntohs(nfmsg->res_id); - - struct net *net = sock_net(ctnl); - struct nfnl_queue_net *q = nfnl_queue_pernet(net); - - queue = verdict_instance_lookup(q, queue_num, - NETLINK_CB(skb).portid); - if (IS_ERR(queue)) - return PTR_ERR(queue); - - vhdr = verdicthdr_get(nfqa); - if (!vhdr) - return -EINVAL; - - verdict = ntohl(vhdr->verdict); - maxid = ntohl(vhdr->id); - - spin_lock_bh(&queue->lock); - - list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) { - if (nfq_id_after(entry->id, maxid)) - break; - __dequeue_entry(queue, entry); - list_add_tail(&entry->list, &batch_list); - } - - spin_unlock_bh(&queue->lock); - - if (list_empty(&batch_list)) - return -ENOENT; - - list_for_each_entry_safe(entry, tmp, &batch_list, list) { - if (nfqa[NFQA_MARK]) - entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); - nf_reinject(entry, verdict); - } - return 0; -} - -static struct nf_conn *nfqnl_ct_parse(struct nfq_ct_hook *nfq_ct, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[], - struct nf_queue_entry *entry, - enum ip_conntrack_info *ctinfo) -{ - struct nf_conn *ct; - - ct = nfq_ct->get_ct(entry->skb, ctinfo); - if (ct == NULL) - return NULL; - - if (nfq_ct->parse(nfqa[NFQA_CT], ct) < 0) - return NULL; - - if (nfqa[NFQA_EXP]) - nfq_ct->attach_expect(nfqa[NFQA_EXP], ct, - NETLINK_CB(entry->skb).portid, - nlmsg_report(nlh)); - return ct; -} - -static int -nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - struct nfgenmsg *nfmsg = nlmsg_data(nlh); - u_int16_t queue_num = ntohs(nfmsg->res_id); - - struct nfqnl_msg_verdict_hdr *vhdr; - struct nfqnl_instance *queue; - unsigned int verdict; - struct nf_queue_entry *entry; - enum ip_conntrack_info uninitialized_var(ctinfo); - struct nfq_ct_hook *nfq_ct; - struct nf_conn *ct = NULL; - - struct net *net = sock_net(ctnl); - struct nfnl_queue_net *q = nfnl_queue_pernet(net); - - queue = instance_lookup(q, queue_num); - if (!queue) - queue = verdict_instance_lookup(q, queue_num, - NETLINK_CB(skb).portid); - if (IS_ERR(queue)) - return PTR_ERR(queue); - - vhdr = verdicthdr_get(nfqa); - if (!vhdr) - return -EINVAL; - - verdict = ntohl(vhdr->verdict); - - entry = find_dequeue_entry(queue, ntohl(vhdr->id)); - if (entry == NULL) - return -ENOENT; - - if (nfqa[NFQA_CT]) { - /* rcu lock already held from nfnl->call_rcu. */ - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct != NULL) - ct = nfqnl_ct_parse(nfq_ct, nlh, nfqa, entry, &ctinfo); - } - - if (nfqa[NFQA_PAYLOAD]) { - u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); - int diff = payload_len - entry->skb->len; - - if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), - payload_len, entry, diff) < 0) - verdict = NF_DROP; - - if (ct && diff) - nfq_ct->seq_adjust(entry->skb, ct, ctinfo, diff); - } - - if (nfqa[NFQA_MARK]) - entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); - - nf_reinject(entry, verdict); - return 0; -} - -static int -nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - return -ENOTSUPP; -} - -static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { - [NFQA_CFG_CMD] = { .len = sizeof(struct nfqnl_msg_config_cmd) }, - [NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) }, -}; - -static const struct nf_queue_handler nfqh = { - .outfn = &nfqnl_enqueue_packet, - .nf_hook_drop = &nfqnl_nf_hook_drop, -}; - -static int -nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - struct nfgenmsg *nfmsg = nlmsg_data(nlh); - u_int16_t queue_num = ntohs(nfmsg->res_id); - struct nfqnl_instance *queue; - struct nfqnl_msg_config_cmd *cmd = NULL; - struct net *net = sock_net(ctnl); - struct nfnl_queue_net *q = nfnl_queue_pernet(net); - int ret = 0; - - if (nfqa[NFQA_CFG_CMD]) { - cmd = nla_data(nfqa[NFQA_CFG_CMD]); - - /* Obsolete commands without queue context */ - switch (cmd->command) { - case NFQNL_CFG_CMD_PF_BIND: return 0; - case NFQNL_CFG_CMD_PF_UNBIND: return 0; - } - } - - rcu_read_lock(); - queue = instance_lookup(q, queue_num); - if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { - ret = -EPERM; - goto err_out_unlock; - } - - if (cmd != NULL) { - switch (cmd->command) { - case NFQNL_CFG_CMD_BIND: - if (queue) { - ret = -EBUSY; - goto err_out_unlock; - } - queue = instance_create(q, queue_num, - NETLINK_CB(skb).portid); - if (IS_ERR(queue)) { - ret = PTR_ERR(queue); - goto err_out_unlock; - } - break; - case NFQNL_CFG_CMD_UNBIND: - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - instance_destroy(q, queue); - break; - case NFQNL_CFG_CMD_PF_BIND: - case NFQNL_CFG_CMD_PF_UNBIND: - break; - default: - ret = -ENOTSUPP; - break; - } - } - - if (nfqa[NFQA_CFG_PARAMS]) { - struct nfqnl_msg_config_params *params; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - params = nla_data(nfqa[NFQA_CFG_PARAMS]); - nfqnl_set_mode(queue, params->copy_mode, - ntohl(params->copy_range)); - } - - if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { - __be32 *queue_maxlen; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); - spin_lock_bh(&queue->lock); - queue->queue_maxlen = ntohl(*queue_maxlen); - spin_unlock_bh(&queue->lock); - } - - if (nfqa[NFQA_CFG_FLAGS]) { - __u32 flags, mask; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - - if (!nfqa[NFQA_CFG_MASK]) { - /* A mask is needed to specify which flags are being - * changed. - */ - ret = -EINVAL; - goto err_out_unlock; - } - - flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); - mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); - - if (flags >= NFQA_CFG_F_MAX) { - ret = -EOPNOTSUPP; - goto err_out_unlock; - } -#if !IS_ENABLED(CONFIG_NETWORK_SECMARK) - if (flags & mask & NFQA_CFG_F_SECCTX) { - ret = -EOPNOTSUPP; - goto err_out_unlock; - } -#endif - spin_lock_bh(&queue->lock); - queue->flags &= ~mask; - queue->flags |= flags & mask; - spin_unlock_bh(&queue->lock); - } - -err_out_unlock: - rcu_read_unlock(); - return ret; -} - -static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { - [NFQNL_MSG_PACKET] = { .call_rcu = nfqnl_recv_unsupp, - .attr_count = NFQA_MAX, }, - [NFQNL_MSG_VERDICT] = { .call_rcu = nfqnl_recv_verdict, - .attr_count = NFQA_MAX, - .policy = nfqa_verdict_policy }, - [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, - .attr_count = NFQA_CFG_MAX, - .policy = nfqa_cfg_policy }, - [NFQNL_MSG_VERDICT_BATCH]={ .call_rcu = nfqnl_recv_verdict_batch, - .attr_count = NFQA_MAX, - .policy = nfqa_verdict_batch_policy }, -}; - -static const struct nfnetlink_subsystem nfqnl_subsys = { - .name = "nf_queue", - .subsys_id = NFNL_SUBSYS_QUEUE, - .cb_count = NFQNL_MSG_MAX, - .cb = nfqnl_cb, -}; - -#ifdef CONFIG_PROC_FS -struct iter_state { - struct seq_net_private p; - unsigned int bucket; -}; - -static struct hlist_node *get_first(struct seq_file *seq) -{ - struct iter_state *st = seq->private; - struct net *net; - struct nfnl_queue_net *q; - - if (!st) - return NULL; - - net = seq_file_net(seq); - q = nfnl_queue_pernet(net); - for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { - if (!hlist_empty(&q->instance_table[st->bucket])) - return q->instance_table[st->bucket].first; - } - return NULL; -} - -static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) -{ - struct iter_state *st = seq->private; - struct net *net = seq_file_net(seq); - - h = h->next; - while (!h) { - struct nfnl_queue_net *q; - - if (++st->bucket >= INSTANCE_BUCKETS) - return NULL; - - q = nfnl_queue_pernet(net); - h = q->instance_table[st->bucket].first; - } - return h; -} - -static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) -{ - struct hlist_node *head; - head = get_first(seq); - - if (head) - while (pos && (head = get_next(seq, head))) - pos--; - return pos ? NULL : head; -} - -static void *seq_start(struct seq_file *s, loff_t *pos) - __acquires(nfnl_queue_pernet(seq_file_net(s))->instances_lock) -{ - spin_lock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock); - return get_idx(s, *pos); -} - -static void *seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - (*pos)++; - return get_next(s, v); -} - -static void seq_stop(struct seq_file *s, void *v) - __releases(nfnl_queue_pernet(seq_file_net(s))->instances_lock) -{ - spin_unlock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock); -} - -static int seq_show(struct seq_file *s, void *v) -{ - const struct nfqnl_instance *inst = v; - - seq_printf(s, "%5u %6u %5u %1u %5u %5u %5u %8u %2d\n", - inst->queue_num, - inst->peer_portid, inst->queue_total, - inst->copy_mode, inst->copy_range, - inst->queue_dropped, inst->queue_user_dropped, - inst->id_sequence, 1); - return 0; -} - -static const struct seq_operations nfqnl_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = seq_show, -}; - -static int nfqnl_open(struct inode *inode, struct file *file) -{ - return seq_open_net(inode, file, &nfqnl_seq_ops, - sizeof(struct iter_state)); -} - -static const struct file_operations nfqnl_file_ops = { - .owner = THIS_MODULE, - .open = nfqnl_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_net, -}; - -#endif /* PROC_FS */ - -static int __net_init nfnl_queue_net_init(struct net *net) -{ - unsigned int i; - struct nfnl_queue_net *q = nfnl_queue_pernet(net); - - for (i = 0; i < INSTANCE_BUCKETS; i++) - INIT_HLIST_HEAD(&q->instance_table[i]); - - spin_lock_init(&q->instances_lock); - -#ifdef CONFIG_PROC_FS - if (!proc_create("nfnetlink_queue", 0440, - net->nf.proc_netfilter, &nfqnl_file_ops)) - return -ENOMEM; -#endif - return 0; -} - -static void __net_exit nfnl_queue_net_exit(struct net *net) -{ -#ifdef CONFIG_PROC_FS - remove_proc_entry("nfnetlink_queue", net->nf.proc_netfilter); -#endif -} - -static struct pernet_operations nfnl_queue_net_ops = { - .init = nfnl_queue_net_init, - .exit = nfnl_queue_net_exit, - .id = &nfnl_queue_net_id, - .size = sizeof(struct nfnl_queue_net), -}; - -static int __init nfnetlink_queue_init(void) -{ - int status; - - status = register_pernet_subsys(&nfnl_queue_net_ops); - if (status < 0) { - pr_err("nf_queue: failed to register pernet ops\n"); - goto out; - } - - netlink_register_notifier(&nfqnl_rtnl_notifier); - status = nfnetlink_subsys_register(&nfqnl_subsys); - if (status < 0) { - pr_err("nf_queue: failed to create netlink socket\n"); - goto cleanup_netlink_notifier; - } - - register_netdevice_notifier(&nfqnl_dev_notifier); - nf_register_queue_handler(&nfqh); - return status; - -cleanup_netlink_notifier: - netlink_unregister_notifier(&nfqnl_rtnl_notifier); -out: - return status; -} - -static void __exit nfnetlink_queue_fini(void) -{ - nf_unregister_queue_handler(); - unregister_netdevice_notifier(&nfqnl_dev_notifier); - nfnetlink_subsys_unregister(&nfqnl_subsys); - netlink_unregister_notifier(&nfqnl_rtnl_notifier); - unregister_pernet_subsys(&nfnl_queue_net_ops); - - rcu_barrier(); /* Wait for completion of call_rcu()'s */ -} - -MODULE_DESCRIPTION("netfilter packet queue handler"); -MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE); - -module_init(nfnetlink_queue_init); -module_exit(nfnetlink_queue_fini); -- 2.1.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information 2015-10-01 18:43 ` [PATCH 2/2 v3] netfilter: rename nfnetlink_queue_core.c to nfnetlink_queue.c Pablo Neira Ayuso @ 2015-10-05 2:44 ` Ken-ichirou MATSUZAWA 2015-10-05 2:47 ` [PATCHv2 nf-next 1/4] netfilter: nfnetlink_queue: rename related to nfqueue attaching conntrack info Ken-ichirou MATSUZAWA ` (4 more replies) 0 siblings, 5 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-05 2:44 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Hi Pablo, I rebased on top of your patches, would you review these? You suggested it might be better to divide ct attaching Kconfig to each of nfqueue and nflog, but I think it makes less sense now because of your rework. is it right? Thanks, ^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCHv2 nf-next 1/4] netfilter: nfnetlink_queue: rename related to nfqueue attaching conntrack info 2015-10-05 2:44 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Ken-ichirou MATSUZAWA @ 2015-10-05 2:47 ` Ken-ichirou MATSUZAWA 2015-10-05 2:48 ` [PATCHv2 nf-next 2/4] netfilter: Kconfig rename QUEUE_CT to GLUE_CT Ken-ichirou MATSUZAWA ` (3 subsequent siblings) 4 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-05 2:47 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel The idea of this series of patch is to attach conntrack information to nflog like nfqueue has already done. nfqueue conntrack info attaching basis is generic, rename those names to generic one, glue. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- include/linux/netfilter.h | 4 +-- net/netfilter/core.c | 4 +-- net/netfilter/nf_conntrack_netlink.c | 60 +++++++++++++++++----------------- net/netfilter/nfnetlink_queue.c | 30 ++++++++--------- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 3e5e8f2..27747de 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -377,7 +377,7 @@ struct nf_conn; enum ip_conntrack_info; struct nlattr; -struct nfq_ct_hook { +struct nfnl_ct_hook { struct nf_conn *(*get_ct)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo); size_t (*build_size)(const struct nf_conn *ct); @@ -390,7 +390,7 @@ struct nfq_ct_hook { void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, s32 off); }; -extern struct nfq_ct_hook __rcu *nfq_ct_hook; +extern struct nfnl_ct_hook __rcu *nfnl_ct_hook; /** * nf_skb_duplicated - TEE target has sent a packet diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 1412e36..32a2894 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -351,8 +351,8 @@ EXPORT_SYMBOL(skb_make_writable); /* This needs to be compiled in any case to avoid dependencies between the * nfnetlink_queue code and nf_conntrack. */ -struct nfq_ct_hook __rcu *nfq_ct_hook __read_mostly; -EXPORT_SYMBOL_GPL(nfq_ct_hook); +struct nfnl_ct_hook __rcu *nfnl_ct_hook __read_mostly; +EXPORT_SYMBOL_GPL(nfnl_ct_hook); #if IS_ENABLED(CONFIG_NF_CONNTRACK) /* This does not belong here, but locally generated errors need it if connection diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index eb67bf8..704f29b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2135,7 +2135,7 @@ ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT static size_t -ctnetlink_nfqueue_build_size(const struct nf_conn *ct) +ctnetlink_glue_build_size(const struct nf_conn *ct) { return 3 * nla_total_size(0) /* CTA_TUPLE_ORIG|REPL|MASTER */ + 3 * nla_total_size(0) /* CTA_TUPLE_IP */ @@ -2162,8 +2162,8 @@ ctnetlink_nfqueue_build_size(const struct nf_conn *ct) ; } -static struct nf_conn *ctnetlink_nfqueue_get_ct(struct sk_buff *skb, - enum ip_conntrack_info *ctinfo) +static struct nf_conn *ctnetlink_glue_get_ct(struct sk_buff *skb, + enum ip_conntrack_info *ctinfo) { struct nf_conn *ct; @@ -2174,7 +2174,7 @@ static struct nf_conn *ctnetlink_nfqueue_get_ct(struct sk_buff *skb, return ct; } -static int __ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) +static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct) { const struct nf_conntrack_zone *zone; struct nlattr *nest_parms; @@ -2247,9 +2247,9 @@ nla_put_failure: } static int -ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - u_int16_t ct_attr, u_int16_t ct_info_attr) +ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + u_int16_t ct_attr, u_int16_t ct_info_attr) { struct nlattr *nest_parms; @@ -2257,7 +2257,7 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct, if (!nest_parms) goto nla_put_failure; - if (__ctnetlink_nfqueue_build(skb, ct) < 0) + if (__ctnetlink_glue_build(skb, ct) < 0) goto nla_put_failure; nla_nest_end(skb, nest_parms); @@ -2272,7 +2272,7 @@ nla_put_failure: } static int -ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) +ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) { int err; @@ -2312,7 +2312,7 @@ ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) } static int -ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) +ctnetlink_glue_parse(const struct nlattr *attr, struct nf_conn *ct) { struct nlattr *cda[CTA_MAX+1]; int ret; @@ -2322,16 +2322,16 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) return ret; spin_lock_bh(&nf_conntrack_expect_lock); - ret = ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct); + ret = ctnetlink_glue_parse_ct((const struct nlattr **)cda, ct); spin_unlock_bh(&nf_conntrack_expect_lock); return ret; } -static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda, - const struct nf_conn *ct, - struct nf_conntrack_tuple *tuple, - struct nf_conntrack_tuple *mask) +static int ctnetlink_glue_exp_parse(const struct nlattr * const *cda, + const struct nf_conn *ct, + struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *mask) { int err; @@ -2345,8 +2345,8 @@ static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda, } static int -ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, - u32 portid, u32 report) +ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, + u32 portid, u32 report) { struct nlattr *cda[CTA_EXPECT_MAX+1]; struct nf_conntrack_tuple tuple, mask; @@ -2358,8 +2358,8 @@ ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, if (err < 0) return err; - err = ctnetlink_nfqueue_exp_parse((const struct nlattr * const *)cda, - ct, &tuple, &mask); + err = ctnetlink_glue_exp_parse((const struct nlattr * const *)cda, + ct, &tuple, &mask); if (err < 0) return err; @@ -2386,8 +2386,8 @@ ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, return 0; } -static void ctnetlink_nfqueue_seqadj(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, int diff) +static void ctnetlink_glue_seqadj(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int diff) { if (!(ct->status & IPS_NAT_MASK)) return; @@ -2395,13 +2395,13 @@ static void ctnetlink_nfqueue_seqadj(struct sk_buff *skb, struct nf_conn *ct, nf_ct_tcp_seqadj_set(skb, ct, ctinfo, diff); } -static struct nfq_ct_hook ctnetlink_nfqueue_hook = { - .get_ct = ctnetlink_nfqueue_get_ct, - .build_size = ctnetlink_nfqueue_build_size, - .build = ctnetlink_nfqueue_build, - .parse = ctnetlink_nfqueue_parse, - .attach_expect = ctnetlink_nfqueue_attach_expect, - .seq_adjust = ctnetlink_nfqueue_seqadj, +static struct nfnl_ct_hook ctnetlink_glue_hook = { + .get_ct = ctnetlink_glue_get_ct, + .build_size = ctnetlink_glue_build_size, + .build = ctnetlink_glue_build, + .parse = ctnetlink_glue_parse, + .attach_expect = ctnetlink_glue_attach_expect, + .seq_adjust = ctnetlink_glue_seqadj, }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ @@ -3389,7 +3389,7 @@ static int __init ctnetlink_init(void) } #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT /* setup interaction between nf_queue and nf_conntrack_netlink. */ - RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook); + RCU_INIT_POINTER(nfnl_ct_hook, &ctnetlink_glue_hook); #endif return 0; @@ -3409,7 +3409,7 @@ static void __exit ctnetlink_exit(void) nfnetlink_subsys_unregister(&ctnl_exp_subsys); nfnetlink_subsys_unregister(&ctnl_subsys); #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT - RCU_INIT_POINTER(nfq_ct_hook, NULL); + RCU_INIT_POINTER(nfnl_ct_hook, NULL); #endif } diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index b1f1c74..789a738 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -313,7 +313,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct net_device *outdev; struct nf_conn *ct = NULL; enum ip_conntrack_info uninitialized_var(ctinfo); - struct nfq_ct_hook *nfq_ct; + struct nfnl_ct_hook *nfnl_ct; bool csum_verify; char *secdata = NULL; u32 seclen = 0; @@ -366,11 +366,11 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, } if (queue->flags & NFQA_CFG_F_CONNTRACK) { - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct != NULL) { - ct = nfq_ct->get_ct(entskb, &ctinfo); + nfnl_ct = rcu_dereference(nfnl_ct_hook); + if (nfnl_ct != NULL) { + ct = nfnl_ct->get_ct(entskb, &ctinfo); if (ct != NULL) - size += nfq_ct->build_size(ct); + size += nfnl_ct->build_size(ct); } } @@ -515,7 +515,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, if (seclen && nla_put(skb, NFQA_SECCTX, seclen, secdata)) goto nla_put_failure; - if (ct && nfq_ct->build(skb, ct, ctinfo, NFQA_CT, NFQA_CT_INFO) < 0) + if (ct && nfnl_ct->build(skb, ct, ctinfo, NFQA_CT, NFQA_CT_INFO) < 0) goto nla_put_failure; if (cap_len > data_len && @@ -1008,7 +1008,7 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, return 0; } -static struct nf_conn *nfqnl_ct_parse(struct nfq_ct_hook *nfq_ct, +static struct nf_conn *nfqnl_ct_parse(struct nfnl_ct_hook *nfnl_ct, const struct nlmsghdr *nlh, const struct nlattr * const nfqa[], struct nf_queue_entry *entry, @@ -1016,15 +1016,15 @@ static struct nf_conn *nfqnl_ct_parse(struct nfq_ct_hook *nfq_ct, { struct nf_conn *ct; - ct = nfq_ct->get_ct(entry->skb, ctinfo); + ct = nfnl_ct->get_ct(entry->skb, ctinfo); if (ct == NULL) return NULL; - if (nfq_ct->parse(nfqa[NFQA_CT], ct) < 0) + if (nfnl_ct->parse(nfqa[NFQA_CT], ct) < 0) return NULL; if (nfqa[NFQA_EXP]) - nfq_ct->attach_expect(nfqa[NFQA_EXP], ct, + nfnl_ct->attach_expect(nfqa[NFQA_EXP], ct, NETLINK_CB(entry->skb).portid, nlmsg_report(nlh)); return ct; @@ -1043,7 +1043,7 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, unsigned int verdict; struct nf_queue_entry *entry; enum ip_conntrack_info uninitialized_var(ctinfo); - struct nfq_ct_hook *nfq_ct; + struct nfnl_ct_hook *nfnl_ct; struct nf_conn *ct = NULL; struct net *net = sock_net(ctnl); @@ -1068,9 +1068,9 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, if (nfqa[NFQA_CT]) { /* rcu lock already held from nfnl->call_rcu. */ - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct != NULL) - ct = nfqnl_ct_parse(nfq_ct, nlh, nfqa, entry, &ctinfo); + nfnl_ct = rcu_dereference(nfnl_ct_hook); + if (nfnl_ct != NULL) + ct = nfqnl_ct_parse(nfnl_ct, nlh, nfqa, entry, &ctinfo); } if (nfqa[NFQA_PAYLOAD]) { @@ -1082,7 +1082,7 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, verdict = NF_DROP; if (ct && diff) - nfq_ct->seq_adjust(entry->skb, ct, ctinfo, diff); + nfnl_ct->seq_adjust(entry->skb, ct, ctinfo, diff); } if (nfqa[NFQA_MARK]) -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCHv2 nf-next 2/4] netfilter: Kconfig rename QUEUE_CT to GLUE_CT 2015-10-05 2:44 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Ken-ichirou MATSUZAWA 2015-10-05 2:47 ` [PATCHv2 nf-next 1/4] netfilter: nfnetlink_queue: rename related to nfqueue attaching conntrack info Ken-ichirou MATSUZAWA @ 2015-10-05 2:48 ` Ken-ichirou MATSUZAWA 2015-10-05 2:49 ` [PATCHv2 nf-next 3/4] netfilter: nf_conntrack_netlink: add const qualifier to nfnl_hook.get_ct Ken-ichirou MATSUZAWA ` (2 subsequent siblings) 4 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-05 2:48 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Conntrack information attaching infrastructure is now generic and update it's name to use `glue' in previous patch. This patch updates Kconfig symbol name and adding NF_CT_NETLINK dependency. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/Kconfig | 10 +++++----- net/netfilter/nf_conntrack_netlink.c | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 3e1b4ab..d287818 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -354,7 +354,7 @@ config NF_CT_NETLINK_HELPER select NETFILTER_NETLINK depends on NF_CT_NETLINK depends on NETFILTER_NETLINK_QUEUE - depends on NETFILTER_NETLINK_QUEUE_CT + depends on NETFILTER_NETLINK_GLUE_CT depends on NETFILTER_ADVANCED help This option enables the user-space connection tracking helpers @@ -362,10 +362,10 @@ config NF_CT_NETLINK_HELPER If unsure, say `N'. -config NETFILTER_NETLINK_QUEUE_CT - bool "NFQUEUE integration with Connection Tracking" - default n - depends on NETFILTER_NETLINK_QUEUE +config NETFILTER_NETLINK_GLUE_CT + bool "NFQUEUE integration with Connection Tracking" + default n + depends on NETFILTER_NETLINK_QUEUE && NF_CT_NETLINK help If this option is enabled, NFQUEUE can include Connection Tracking information together with the packet is the enqueued via NFNETLINK. diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 704f29b..71d6ada 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2133,7 +2133,7 @@ ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask); -#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT +#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT static size_t ctnetlink_glue_build_size(const struct nf_conn *ct) { @@ -2403,7 +2403,7 @@ static struct nfnl_ct_hook ctnetlink_glue_hook = { .attach_expect = ctnetlink_glue_attach_expect, .seq_adjust = ctnetlink_glue_seqadj, }; -#endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ +#endif /* CONFIG_NETFILTER_NETLINK_GLUE_CT */ /*********************************************************************** * EXPECT @@ -3387,7 +3387,7 @@ static int __init ctnetlink_init(void) pr_err("ctnetlink_init: cannot register pernet operations\n"); goto err_unreg_exp_subsys; } -#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT +#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT /* setup interaction between nf_queue and nf_conntrack_netlink. */ RCU_INIT_POINTER(nfnl_ct_hook, &ctnetlink_glue_hook); #endif @@ -3408,7 +3408,7 @@ static void __exit ctnetlink_exit(void) unregister_pernet_subsys(&ctnetlink_net_ops); nfnetlink_subsys_unregister(&ctnl_exp_subsys); nfnetlink_subsys_unregister(&ctnl_subsys); -#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT +#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT RCU_INIT_POINTER(nfnl_ct_hook, NULL); #endif } -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCHv2 nf-next 3/4] netfilter: nf_conntrack_netlink: add const qualifier to nfnl_hook.get_ct 2015-10-05 2:44 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Ken-ichirou MATSUZAWA 2015-10-05 2:47 ` [PATCHv2 nf-next 1/4] netfilter: nfnetlink_queue: rename related to nfqueue attaching conntrack info Ken-ichirou MATSUZAWA 2015-10-05 2:48 ` [PATCHv2 nf-next 2/4] netfilter: Kconfig rename QUEUE_CT to GLUE_CT Ken-ichirou MATSUZAWA @ 2015-10-05 2:49 ` Ken-ichirou MATSUZAWA 2015-10-05 2:50 ` [PATCHv2 nf-next 4/4] netfilter: nfnetlink_log: allow to attach conntrack Ken-ichirou MATSUZAWA 2015-10-05 15:33 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Pablo Neira Ayuso 4 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-05 2:49 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel get_ct as is and will not update its skb argument, and users of nfnl_ct_hook is currently only nfqueue, we can add const qualifier. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- include/linux/netfilter.h | 2 +- net/netfilter/nf_conntrack_netlink.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 27747de..edb3dc3 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -378,7 +378,7 @@ enum ip_conntrack_info; struct nlattr; struct nfnl_ct_hook { - struct nf_conn *(*get_ct)(struct sk_buff *skb, + struct nf_conn *(*get_ct)(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo); size_t (*build_size)(const struct nf_conn *ct); int (*build)(struct sk_buff *skb, struct nf_conn *ct, diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 71d6ada..9f52729 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2162,7 +2162,7 @@ ctnetlink_glue_build_size(const struct nf_conn *ct) ; } -static struct nf_conn *ctnetlink_glue_get_ct(struct sk_buff *skb, +static struct nf_conn *ctnetlink_glue_get_ct(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) { struct nf_conn *ct; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCHv2 nf-next 4/4] netfilter: nfnetlink_log: allow to attach conntrack 2015-10-05 2:44 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Ken-ichirou MATSUZAWA ` (2 preceding siblings ...) 2015-10-05 2:49 ` [PATCHv2 nf-next 3/4] netfilter: nf_conntrack_netlink: add const qualifier to nfnl_hook.get_ct Ken-ichirou MATSUZAWA @ 2015-10-05 2:50 ` Ken-ichirou MATSUZAWA 2015-10-05 15:23 ` Pablo Neira Ayuso 2015-10-05 15:33 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Pablo Neira Ayuso 4 siblings, 1 reply; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-05 2:50 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch enables to include the conntrack information together with the packet that is sent to user-space via NFLOG, then a user-space program can acquire NATed information by this NFULA_CT attribute. Including the conntrack information is optional, you can set it via NFULNL_CFG_F_CONNTRACK flag with the NFULA_CFG_FLAGS attribute like NFQUEUE. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- include/uapi/linux/netfilter/nfnetlink_log.h | 3 ++ net/netfilter/Kconfig | 9 +++--- net/netfilter/nfnetlink_log.c | 40 ++++++++++++++++++++++---- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/include/uapi/linux/netfilter/nfnetlink_log.h b/include/uapi/linux/netfilter/nfnetlink_log.h index 90c2c95..fb21f0c 100644 --- a/include/uapi/linux/netfilter/nfnetlink_log.h +++ b/include/uapi/linux/netfilter/nfnetlink_log.h @@ -51,6 +51,8 @@ enum nfulnl_attr_type { NFULA_HWTYPE, /* hardware type */ NFULA_HWHEADER, /* hardware header */ NFULA_HWLEN, /* hardware header length */ + NFULA_CT, /* nf_conntrack_netlink.h */ + NFULA_CT_INFO, /* enum ip_conntrack_info */ __NFULA_MAX }; @@ -93,5 +95,6 @@ enum nfulnl_attr_config { #define NFULNL_CFG_F_SEQ 0x0001 #define NFULNL_CFG_F_SEQ_GLOBAL 0x0002 +#define NFULNL_CFG_F_CONNTRACK 0x0004 #endif /* _NFNETLINK_LOG_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index d287818..e22349e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -363,12 +363,13 @@ config NF_CT_NETLINK_HELPER If unsure, say `N'. config NETFILTER_NETLINK_GLUE_CT - bool "NFQUEUE integration with Connection Tracking" + bool "NFQUEUE and NFLOG integration with Connection Tracking" default n - depends on NETFILTER_NETLINK_QUEUE && NF_CT_NETLINK + depends on (NETFILTER_NETLINK_QUEUE || NETFILTER_NETLINK_LOG) && NF_CT_NETLINK help - If this option is enabled, NFQUEUE can include Connection Tracking - information together with the packet is the enqueued via NFNETLINK. + If this option is enabled, NFQUEUE and NFLOG can include + Connection Tracking information together with the packet is + the enqueued via NFNETLINK. config NF_NAT tristate diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 4670821..d4b6947 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -27,6 +27,7 @@ #include <net/netlink.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_log.h> +#include <linux/netfilter/nf_conntrack_common.h> #include <linux/spinlock.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> @@ -401,7 +402,9 @@ __build_packet_message(struct nfnl_log_net *log, unsigned int hooknum, const struct net_device *indev, const struct net_device *outdev, - const char *prefix, unsigned int plen) + const char *prefix, unsigned int plen, + const struct nfnl_ct_hook *nfnl_ct, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) { struct nfulnl_msg_packet_hdr pmsg; struct nlmsghdr *nlh; @@ -575,6 +578,10 @@ __build_packet_message(struct nfnl_log_net *log, htonl(atomic_inc_return(&log->global_seq)))) goto nla_put_failure; + if (ct && nfnl_ct->build(inst->skb, ct, ctinfo, + NFULA_CT, NFULA_CT_INFO) < 0) + goto nla_put_failure; + if (data_len) { struct nlattr *nla; int size = nla_attr_size(data_len); @@ -620,12 +627,16 @@ nfulnl_log_packet(struct net *net, const struct nf_loginfo *li_user, const char *prefix) { - unsigned int size, data_len; + size_t size; + unsigned int data_len; struct nfulnl_instance *inst; const struct nf_loginfo *li; unsigned int qthreshold; unsigned int plen; struct nfnl_log_net *log = nfnl_log_pernet(net); + const struct nfnl_ct_hook *nfnl_ct = NULL; + struct nf_conn *ct = NULL; + enum ip_conntrack_info uninitialized_var(ctinfo); if (li_user && li_user->type == NF_LOG_TYPE_ULOG) li = li_user; @@ -671,7 +682,15 @@ nfulnl_log_packet(struct net *net, size += nla_total_size(sizeof(u_int32_t)); if (inst->flags & NFULNL_CFG_F_SEQ_GLOBAL) size += nla_total_size(sizeof(u_int32_t)); - + if (inst->flags & NFULNL_CFG_F_CONNTRACK) { + nfnl_ct = rcu_dereference(nfnl_ct_hook); + if (nfnl_ct != NULL) { + ct = nfnl_ct->get_ct(skb, &ctinfo); + if (ct != NULL) + size += nfnl_ct->build_size(ct); + } + } + qthreshold = inst->qthreshold; /* per-rule qthreshold overrides per-instance */ if (li->u.ulog.qthreshold) @@ -715,7 +734,8 @@ nfulnl_log_packet(struct net *net, inst->qlen++; __build_packet_message(log, inst, skb, data_len, pf, - hooknum, in, out, prefix, plen); + hooknum, in, out, prefix, plen, + nfnl_ct, ct, ctinfo); if (inst->qlen >= qthreshold) __nfulnl_flush(inst); @@ -899,13 +919,21 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, } if (nfula[NFULA_CFG_FLAGS]) { - __be16 flags = nla_get_be16(nfula[NFULA_CFG_FLAGS]); + u16 flags = ntohs(nla_get_be16(nfula[NFULA_CFG_FLAGS])); if (!inst) { ret = -ENODEV; goto out; } - nfulnl_set_flags(inst, ntohs(flags)); + + if (flags & NFULNL_CFG_F_CONNTRACK && + rcu_dereference(nfnl_ct_hook) == NULL) { + ret = -EOPNOTSUPP; + goto out; + + } + + nfulnl_set_flags(inst, flags); } out_put: -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCHv2 nf-next 4/4] netfilter: nfnetlink_log: allow to attach conntrack 2015-10-05 2:50 ` [PATCHv2 nf-next 4/4] netfilter: nfnetlink_log: allow to attach conntrack Ken-ichirou MATSUZAWA @ 2015-10-05 15:23 ` Pablo Neira Ayuso 2015-10-06 2:10 ` [PATCHv2 nf-next 0/1] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA 0 siblings, 1 reply; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-05 15:23 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Mon, Oct 05, 2015 at 11:50:46AM +0900, Ken-ichirou MATSUZAWA wrote: > This patch enables to include the conntrack information together > with the packet that is sent to user-space via NFLOG, then a > user-space program can acquire NATed information by this NFULA_CT > attribute. WARNING: suspect code indent for conditional statements (8, 24) #99: FILE: net/netfilter/nfnetlink_log.c:581: + if (ct && nfnl_ct->build(inst->skb, ct, ctinfo, [...] + goto nla_put_failure; Please, make sure you use tabs next time. ERROR: trailing whitespace #102: FILE: net/netfilter/nfnetlink_log.c:584: ERROR: trailing whitespace #137: FILE: net/netfilter/nfnetlink_log.c:693: Please, run checkpatch.pl on your patches next time to address whitespace errors and wrong indent. And regarding this: ... rcu_dereference(nfnl_ct_hook) == NULL) { it should be: rcu_access_pointer(nfnl_ct_hook) == NULL) { I have fixed these issues here this time, no need to resend. But, would you please resubmit this: http://patchwork.ozlabs.org/patch/516185/ but using rcu_access_pointer() instead? BTW, another thing that would be good to investigate is to autoload nf_conntrack_netlink if the process passes the _F_CONNTRACK flag is set. Would you have a look into that? Thanks. ^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCHv2 nf-next 0/1] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag 2015-10-05 15:23 ` Pablo Neira Ayuso @ 2015-10-06 2:10 ` Ken-ichirou MATSUZAWA 2015-10-06 2:12 ` [PATCHv2 nf-next 1/1] " Ken-ichirou MATSUZAWA 0 siblings, 1 reply; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-06 2:10 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Thank you for advices. I try to fix rcu_dereference() and to autoload not only nf_conntrack_netlink but also l3ct module if nfgen family is specified. Is it unnecessary? I will make same thing to nfnetlink_log after this round. Thanks, ^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCHv2 nf-next 1/1] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag 2015-10-06 2:10 ` [PATCHv2 nf-next 0/1] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA @ 2015-10-06 2:12 ` Ken-ichirou MATSUZAWA 2015-10-06 10:07 ` Pablo Neira Ayuso 0 siblings, 1 reply; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-06 2:12 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch enables to check GLUE_CT is enabled or not when NFQA_CFG_F_CONNTRACK config flag is received. And try to load nf_conntrack_netlink module, and l3proto module if family is specified. Then returns error either case is failed. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index a659e57..99c9c8c 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -34,6 +34,7 @@ #include <net/tcp_states.h> #include <net/netfilter/nf_queue.h> #include <net/netns/generic.h> +#include <net/netfilter/nf_conntrack.h> #include <linux/atomic.h> @@ -1225,6 +1226,24 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, goto err_out_unlock; } #endif + if (flags & mask & NFQA_CFG_F_CONNTRACK) { + if (!rcu_access_pointer(nfnl_ct_hook)) +#ifdef CONFIG_MODULES + if (request_module("ip_conntrack_netlink") || + !rcu_access_pointer(nfnl_ct_hook)) +#endif + { + ret = -EOPNOTSUPP; + goto err_out_unlock; + } + + if (nfmsg->nfgen_family && + nf_ct_l3proto_try_module_get(nfmsg->nfgen_family)) { + ret = -EPROTONOSUPPORT; + goto err_out_unlock; + } + } + spin_lock_bh(&queue->lock); queue->flags &= ~mask; queue->flags |= flags & mask; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCHv2 nf-next 1/1] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag 2015-10-06 2:12 ` [PATCHv2 nf-next 1/1] " Ken-ichirou MATSUZAWA @ 2015-10-06 10:07 ` Pablo Neira Ayuso 2015-10-07 4:20 ` Ken-ichirou MATSUZAWA 0 siblings, 1 reply; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-06 10:07 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Tue, Oct 06, 2015 at 11:12:46AM +0900, Ken-ichirou MATSUZAWA wrote: > This patch enables to check GLUE_CT is enabled or not when > NFQA_CFG_F_CONNTRACK config flag is received. And try to load > nf_conntrack_netlink module, and l3proto module if family is > specified. Then returns error either case is failed. > > Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> > --- > net/netfilter/nfnetlink_queue.c | 19 +++++++++++++++++++ > 1 file changed, 19 insertions(+) > > diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c > index a659e57..99c9c8c 100644 > --- a/net/netfilter/nfnetlink_queue.c > +++ b/net/netfilter/nfnetlink_queue.c > @@ -34,6 +34,7 @@ > #include <net/tcp_states.h> > #include <net/netfilter/nf_queue.h> > #include <net/netns/generic.h> > +#include <net/netfilter/nf_conntrack.h> > > #include <linux/atomic.h> > > @@ -1225,6 +1226,24 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, > goto err_out_unlock; > } > #endif > + if (flags & mask & NFQA_CFG_F_CONNTRACK) { > + if (!rcu_access_pointer(nfnl_ct_hook)) > +#ifdef CONFIG_MODULES > + if (request_module("ip_conntrack_netlink") || nfnl_lock is held while requesting a module, which is something that we should avoid. Please, abort the operation and return -EAGAIN to retry, this is the usual procedure when requesting other modules. > + !rcu_access_pointer(nfnl_ct_hook)) > +#endif > + { > + ret = -EOPNOTSUPP; > + goto err_out_unlock; > + } > + > + if (nfmsg->nfgen_family && > + nf_ct_l3proto_try_module_get(nfmsg->nfgen_family)) { > + ret = -EPROTONOSUPPORT; > + goto err_out_unlock; I think this chunk belongs to nf_conntrack_netlink, the nf_conntrack_{ipv4,ipv6} modules gets loaded when the user tries to create a conntrack/expectation. Otherwise we create a hard dependency between nfnetlink_queue and nf_conntrack, which is what we're trying to avoid ;-) > + } > + } > + > spin_lock_bh(&queue->lock); > queue->flags &= ~mask; > queue->flags |= flags & mask; > -- > 1.7.10.4 > ^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCHv2 nf-next 1/1] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag 2015-10-06 10:07 ` Pablo Neira Ayuso @ 2015-10-07 4:20 ` Ken-ichirou MATSUZAWA 2015-10-07 4:23 ` [PATCHv3 nf-next] " Ken-ichirou MATSUZAWA ` (3 more replies) 0 siblings, 4 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-07 4:20 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel On Tue, Oct 06, 2015 at 12:07:28PM +0200, Pablo Neira Ayuso wrote: > On Tue, Oct 06, 2015 at 11:12:46AM +0900, Ken-ichirou MATSUZAWA wrote: > > + if (flags & mask & NFQA_CFG_F_CONNTRACK) { > > + if (!rcu_access_pointer(nfnl_ct_hook)) > > +#ifdef CONFIG_MODULES > > + if (request_module("ip_conntrack_netlink") || > > nfnl_lock is held while requesting a module, which is something that > we should avoid. Please, abort the operation and return -EAGAIN to > retry, this is the usual procedure when requesting other modules. Thank you as always. I read another request_module() part and I think I can understand, would you review it again? I changed rcu_access_pointer() since rcu_read_lock() is called in nfqnl_recv_config(). I think a few fix seems to be needed at that time, and try to fix it in the following patches. Would you confirm these too? Thanks, ^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCHv3 nf-next] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag 2015-10-07 4:20 ` Ken-ichirou MATSUZAWA @ 2015-10-07 4:23 ` Ken-ichirou MATSUZAWA 2015-10-07 4:25 ` [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module " Ken-ichirou MATSUZAWA ` (2 subsequent siblings) 3 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-07 4:23 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch enables to check if GLUE_CT is enabled and try to load dependent, nf_conntrack_netlink module when NFQA_CFG_F_CONNTRACK config flag is received. Then returns error either case is failed. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index a659e57..583a7a0 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1225,6 +1225,23 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, goto err_out_unlock; } #endif + if (flags & mask & NFQA_CFG_F_CONNTRACK && + !rcu_dereference(nfnl_ct_hook)) { +#ifdef CONFIG_MODULES + rcu_read_unlock(); + nfnl_unlock(NFNL_SUBSYS_QUEUE); + request_module("ip_conntrack_netlink"); + nfnl_lock(NFNL_SUBSYS_QUEUE); + rcu_read_lock(); + if (rcu_dereference(nfnl_ct_hook)) { + ret = -EAGAIN; + goto err_out_unlock; + } +#endif + ret = -EOPNOTSUPP; + goto err_out_unlock; + } + spin_lock_bh(&queue->lock); queue->flags &= ~mask; queue->flags |= flags & mask; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag 2015-10-07 4:20 ` Ken-ichirou MATSUZAWA 2015-10-07 4:23 ` [PATCHv3 nf-next] " Ken-ichirou MATSUZAWA @ 2015-10-07 4:25 ` Ken-ichirou MATSUZAWA 2015-10-12 17:13 ` Pablo Neira Ayuso 2015-10-16 17:05 ` Pablo Neira Ayuso 2015-10-07 4:27 ` [PATCH nf-next] netfilter: nf_conntrack_netlink: fix nf-nat module loaded checking Ken-ichirou MATSUZAWA 2015-10-07 4:30 ` [PATCH nf-next] netfilter: nf_conntrack_netlink: fix locks around helper module loading Ken-ichirou MATSUZAWA 3 siblings, 2 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-07 4:25 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch enables to load nf_conntrack_netlink module if NFULNL_CFG_F_CONNTRACK config flag is specified. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_log.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index e1d1187..f8d9bd8 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -927,7 +927,16 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, } if (flags & NFULNL_CFG_F_CONNTRACK && - rcu_access_pointer(nfnl_ct_hook) == NULL) { + !rcu_access_pointer(nfnl_ct_hook)) { +#ifdef CONFIG_MODULES + nfnl_unlock(NFNL_SUBSYS_ULOG); + request_module("ip_conntrack_netlink"); + nfnl_lock(NFNL_SUBSYS_ULOG); + if (rcu_access_pointer(nfnl_ct_hook)) { + ret = -EAGAIN; + goto out; + } +#endif ret = -EOPNOTSUPP; goto out; } -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag 2015-10-07 4:25 ` [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module " Ken-ichirou MATSUZAWA @ 2015-10-12 17:13 ` Pablo Neira Ayuso 2015-10-12 20:10 ` Pablo Neira Ayuso 2015-10-16 17:05 ` Pablo Neira Ayuso 1 sibling, 1 reply; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-12 17:13 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Wed, Oct 07, 2015 at 01:25:50PM +0900, Ken-ichirou MATSUZAWA wrote: > This patch enables to load nf_conntrack_netlink module if > NFULNL_CFG_F_CONNTRACK config flag is specified. Applied, thanks. ^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag 2015-10-12 17:13 ` Pablo Neira Ayuso @ 2015-10-12 20:10 ` Pablo Neira Ayuso 0 siblings, 0 replies; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-12 20:10 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Mon, Oct 12, 2015 at 07:13:52PM +0200, Pablo Neira Ayuso wrote: > On Wed, Oct 07, 2015 at 01:25:50PM +0900, Ken-ichirou MATSUZAWA wrote: > > This patch enables to load nf_conntrack_netlink module if > > NFULNL_CFG_F_CONNTRACK config flag is specified. > > Applied, thanks. I just noticed we're breaking atomicity when configuring things. I mean, if we fail to configure the flags, we return an error but it's too late, we just create the instance and we have configured everything. I quickly made a couple of patches here, we'll be posting them tomorrow. ^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag 2015-10-07 4:25 ` [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module " Ken-ichirou MATSUZAWA 2015-10-12 17:13 ` Pablo Neira Ayuso @ 2015-10-16 17:05 ` Pablo Neira Ayuso 2015-11-06 0:46 ` Ken-ichirou MATSUZAWA 1 sibling, 1 reply; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-16 17:05 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Wed, Oct 07, 2015 at 01:25:50PM +0900, Ken-ichirou MATSUZAWA wrote: > This patch enables to load nf_conntrack_netlink module if > NFULNL_CFG_F_CONNTRACK config flag is specified. > > Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> > --- > net/netfilter/nfnetlink_log.c | 11 ++++++++++- > 1 file changed, 10 insertions(+), 1 deletion(-) > > diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c > index e1d1187..f8d9bd8 100644 > --- a/net/netfilter/nfnetlink_log.c > +++ b/net/netfilter/nfnetlink_log.c > @@ -927,7 +927,16 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, > } > > if (flags & NFULNL_CFG_F_CONNTRACK && > - rcu_access_pointer(nfnl_ct_hook) == NULL) { > + !rcu_access_pointer(nfnl_ct_hook)) { > +#ifdef CONFIG_MODULES > + nfnl_unlock(NFNL_SUBSYS_ULOG); > + request_module("ip_conntrack_netlink"); > + nfnl_lock(NFNL_SUBSYS_ULOG); > + if (rcu_access_pointer(nfnl_ct_hook)) { > + ret = -EAGAIN; > + goto out; > + } > +#endif I know this is not your fault, but could you have a look to see if we can get resolved the broken atomicity problems in first place when applying configuration updates to nfnetlink_queue in a similar way to what I did for nfnetlink_log? I mean, move code that check for things right up the beginning of the functions, then we pass the preparation phase and we're good, we apply the configuration changes. Again not your fault, but there's another nasty thing in that code: We can destroy a queue in NFQNL_CFG_CMD_UNBIND, then we can keep going on configuring it when it is actually destroyed. This is working only by luck because we're using call_rcu so the object is still there, but this is sloppy and it should be fixed. After having that resolved, we can go back to adding module autoload. Let me know, Thanks. ^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag 2015-10-16 17:05 ` Pablo Neira Ayuso @ 2015-11-06 0:46 ` Ken-ichirou MATSUZAWA 2015-11-06 0:49 ` [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling Ken-ichirou MATSUZAWA ` (2 more replies) 0 siblings, 3 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-11-06 0:46 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Sorry for being late. On Fri, Oct 16, 2015 at 07:05:32PM +0200, Pablo Neira Ayuso wrote: > can get resolved the broken atomicity problems in first place when > applying configuration updates to nfnetlink_queue in a similar way to > what I did for nfnetlink_log? I tried it, would you review the following patches? > can destroy a queue in NFQNL_CFG_CMD_UNBIND, then we can keep going on > configuring it when it is actually destroyed. This is working only by I'm wondering how we handle obsolete commands NFQNL_CFG_CMD_PF_BIND / UNBIND, especially with a valid queue_num and configurations. These patches follows current behavior, do nothing, ignores another nlas and just returns success. Should I update patches to handle configuraion nlas with obsolute commands? Thanks, ^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling 2015-11-06 0:46 ` Ken-ichirou MATSUZAWA @ 2015-11-06 0:49 ` Ken-ichirou MATSUZAWA 2015-11-08 22:14 ` Pablo Neira Ayuso 2015-11-06 0:56 ` [PATCH nf-next 2/3] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity Ken-ichirou MATSUZAWA 2015-11-06 0:58 ` [PATCH nf-next 3/3] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA 2 siblings, 1 reply; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-11-06 0:49 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 7d81d28..f85a3d3 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1116,21 +1116,10 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, struct nfgenmsg *nfmsg = nlmsg_data(nlh); u_int16_t queue_num = ntohs(nfmsg->res_id); struct nfqnl_instance *queue; - struct nfqnl_msg_config_cmd *cmd = NULL; struct net *net = sock_net(ctnl); struct nfnl_queue_net *q = nfnl_queue_pernet(net); int ret = 0; - if (nfqa[NFQA_CFG_CMD]) { - cmd = nla_data(nfqa[NFQA_CFG_CMD]); - - /* Obsolete commands without queue context */ - switch (cmd->command) { - case NFQNL_CFG_CMD_PF_BIND: return 0; - case NFQNL_CFG_CMD_PF_UNBIND: return 0; - } - } - rcu_read_lock(); queue = instance_lookup(q, queue_num); if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { @@ -1138,7 +1127,9 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, goto err_out_unlock; } - if (cmd != NULL) { + if (nfqa[NFQA_CFG_CMD]) { + struct nfqnl_msg_config_cmd *cmd = nla_data(nfqa[NFQA_CFG_CMD]); + switch (cmd->command) { case NFQNL_CFG_CMD_BIND: if (queue) { @@ -1161,7 +1152,8 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, break; case NFQNL_CFG_CMD_PF_BIND: case NFQNL_CFG_CMD_PF_UNBIND: - break; + /* Obsolete commands without queue context */ + goto err_out_unlock; default: ret = -ENOTSUPP; break; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling 2015-11-06 0:49 ` [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling Ken-ichirou MATSUZAWA @ 2015-11-08 22:14 ` Pablo Neira Ayuso 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA 0 siblings, 1 reply; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-11-08 22:14 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Fri, Nov 06, 2015 at 09:49:47AM +0900, Ken-ichirou MATSUZAWA wrote: > Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> > --- > net/netfilter/nfnetlink_queue.c | 18 +++++------------- > 1 file changed, 5 insertions(+), 13 deletions(-) > > diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c > index 7d81d28..f85a3d3 100644 > --- a/net/netfilter/nfnetlink_queue.c > +++ b/net/netfilter/nfnetlink_queue.c > @@ -1116,21 +1116,10 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, > struct nfgenmsg *nfmsg = nlmsg_data(nlh); > u_int16_t queue_num = ntohs(nfmsg->res_id); > struct nfqnl_instance *queue; > - struct nfqnl_msg_config_cmd *cmd = NULL; > struct net *net = sock_net(ctnl); > struct nfnl_queue_net *q = nfnl_queue_pernet(net); > int ret = 0; > > - if (nfqa[NFQA_CFG_CMD]) { > - cmd = nla_data(nfqa[NFQA_CFG_CMD]); > - > - /* Obsolete commands without queue context */ > - switch (cmd->command) { > - case NFQNL_CFG_CMD_PF_BIND: return 0; > - case NFQNL_CFG_CMD_PF_UNBIND: return 0; > - } > - } > - Thanks for following up on this Ken-ichirou. However, I suspect this is wrong: commit 0360ae412d09bc6f4864c801effcb20bfd84520e Author: Florian Westphal <fw@strlen.de> Date: Fri Nov 23 06:22:21 2012 +0000 netfilter: kill support for per-af queue backends The former behaviour before Florian's patch is that NFQNL_CFG_CMD_PF_BIND and NFQNL_CFG_CMD_PF_UNBIND are terminal. This code is there not to break backward compatibility, ie. old userspace that rely on these commands. After this patch, we inspect if there is an existing queue for this, which was not happening before. As I said, my concerns go in the direction of possible breakage of old code, we shouldn't break backward. Let me know, thanks. > rcu_read_lock(); > queue = instance_lookup(q, queue_num); > if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { ^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling 2015-11-08 22:14 ` Pablo Neira Ayuso @ 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA 2016-01-05 0:28 ` [PATCH nf-next 1/5] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity Ken-ichirou MATSUZAWA ` (4 more replies) 0 siblings, 5 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2016-01-05 0:24 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Sorry for being late often. On Sun, Nov 08, 2015 at 11:14:54PM +0100, Pablo Neira Ayuso wrote: > commit 0360ae412d09bc6f4864c801effcb20bfd84520e > Author: Florian Westphal <fw@strlen.de> > Date: Fri Nov 23 06:22:21 2012 +0000 > > netfilter: kill support for per-af queue backends > > The former behaviour before Florian's patch is that > NFQNL_CFG_CMD_PF_BIND and NFQNL_CFG_CMD_PF_UNBIND are terminal. > > This code is there not to break backward compatibility, ie. old > userspace that rely on these commands. > > After this patch, we inspect if there is an existing queue for this, > which was not happening before. I tried to follow your advice, would you see again? > As I said, my concerns go in the direction of possible breakage of old > code, we shouldn't break backward. I'm worry about appended patches 3/5 and 5/5 may not follow it. After these patches applied, userspace can not update instance's options with invalid command. is it acceptable? Thanks, ^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH nf-next 1/5] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA @ 2016-01-05 0:28 ` Ken-ichirou MATSUZAWA 2016-01-05 0:29 ` [PATCH nf-next 2/5] netfilter: nfnetlink_queue: not handle options after unbind Ken-ichirou MATSUZAWA ` (3 subsequent siblings) 4 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2016-01-05 0:28 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Check that dependencies are fulfilled before updating the queue instance, otherwise we can leave things in intermediate state on errors in nfqnl_recv_config(). Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 74 +++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 3d1f16c..a4ae731 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1113,6 +1113,7 @@ static int nfqnl_recv_config(struct net *net, struct sock *ctnl, struct nfqnl_instance *queue; struct nfqnl_msg_config_cmd *cmd = NULL; struct nfnl_queue_net *q = nfnl_queue_pernet(net); + __u32 flags = 0, mask = 0; int ret = 0; if (nfqa[NFQA_CFG_CMD]) { @@ -1125,6 +1126,29 @@ static int nfqnl_recv_config(struct net *net, struct sock *ctnl, } } + /* Check if we support these flags in first place, dependencies should + * be there too not to break atomicity. + */ + if (nfqa[NFQA_CFG_FLAGS]) { + if (!nfqa[NFQA_CFG_MASK]) { + /* A mask is needed to specify which flags are being + * changed. + */ + return -EINVAL; + } + + flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); + mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); + + if (flags >= NFQA_CFG_F_MAX) + return -EOPNOTSUPP; + +#if !IS_ENABLED(CONFIG_NETWORK_SECMARK) + if (flags & mask & NFQA_CFG_F_SECCTX) + return -EOPNOTSUPP; +#endif + } + rcu_read_lock(); queue = instance_lookup(q, queue_num); if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { @@ -1162,60 +1186,26 @@ static int nfqnl_recv_config(struct net *net, struct sock *ctnl, } } - if (nfqa[NFQA_CFG_PARAMS]) { - struct nfqnl_msg_config_params *params; + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - params = nla_data(nfqa[NFQA_CFG_PARAMS]); + if (nfqa[NFQA_CFG_PARAMS]) { + struct nfqnl_msg_config_params *params + = nla_data(nfqa[NFQA_CFG_PARAMS]); nfqnl_set_mode(queue, params->copy_mode, ntohl(params->copy_range)); } if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { - __be32 *queue_maxlen; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); + __be32 *queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); spin_lock_bh(&queue->lock); queue->queue_maxlen = ntohl(*queue_maxlen); spin_unlock_bh(&queue->lock); } if (nfqa[NFQA_CFG_FLAGS]) { - __u32 flags, mask; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - - if (!nfqa[NFQA_CFG_MASK]) { - /* A mask is needed to specify which flags are being - * changed. - */ - ret = -EINVAL; - goto err_out_unlock; - } - - flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); - mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); - - if (flags >= NFQA_CFG_F_MAX) { - ret = -EOPNOTSUPP; - goto err_out_unlock; - } -#if !IS_ENABLED(CONFIG_NETWORK_SECMARK) - if (flags & mask & NFQA_CFG_F_SECCTX) { - ret = -EOPNOTSUPP; - goto err_out_unlock; - } -#endif spin_lock_bh(&queue->lock); queue->flags &= ~mask; queue->flags |= flags & mask; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next 2/5] netfilter: nfnetlink_queue: not handle options after unbind 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA 2016-01-05 0:28 ` [PATCH nf-next 1/5] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity Ken-ichirou MATSUZAWA @ 2016-01-05 0:29 ` Ken-ichirou MATSUZAWA 2016-01-05 0:31 ` [PATCH nf-next 3/5] netfilter: nfnetlink_queue: just returns error for unknown command Ken-ichirou MATSUZAWA ` (2 subsequent siblings) 4 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2016-01-05 0:29 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch stops processing after destroying a queue instance. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index a4ae731..ca00a98 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1176,7 +1176,7 @@ static int nfqnl_recv_config(struct net *net, struct sock *ctnl, goto err_out_unlock; } instance_destroy(q, queue); - break; + goto err_out_unlock; case NFQNL_CFG_CMD_PF_BIND: case NFQNL_CFG_CMD_PF_UNBIND: break; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next 3/5] netfilter: nfnetlink_queue: just returns error for unknown command 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA 2016-01-05 0:28 ` [PATCH nf-next 1/5] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity Ken-ichirou MATSUZAWA 2016-01-05 0:29 ` [PATCH nf-next 2/5] netfilter: nfnetlink_queue: not handle options after unbind Ken-ichirou MATSUZAWA @ 2016-01-05 0:31 ` Ken-ichirou MATSUZAWA 2016-01-05 0:32 ` [PATCH nf-next 4/5] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA 2016-01-05 0:34 ` [PATCH nf-next 5/5] netfilter: nfnetlink_log: just returns error for unknown command Ken-ichirou MATSUZAWA 4 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2016-01-05 0:31 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch stops processing options for unknown command. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index ca00a98..c5cd50c 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1182,7 +1182,7 @@ static int nfqnl_recv_config(struct net *net, struct sock *ctnl, break; default: ret = -ENOTSUPP; - break; + goto err_out_unlock; } } -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next 4/5] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA ` (2 preceding siblings ...) 2016-01-05 0:31 ` [PATCH nf-next 3/5] netfilter: nfnetlink_queue: just returns error for unknown command Ken-ichirou MATSUZAWA @ 2016-01-05 0:32 ` Ken-ichirou MATSUZAWA 2016-01-05 0:34 ` [PATCH nf-next 5/5] netfilter: nfnetlink_log: just returns error for unknown command Ken-ichirou MATSUZAWA 4 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2016-01-05 0:32 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch enables to load nf_conntrack_netlink module if NFQA_CFG_F_CONNTRACK config flag is specified. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index c5cd50c..6168984 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1147,6 +1147,17 @@ static int nfqnl_recv_config(struct net *net, struct sock *ctnl, if (flags & mask & NFQA_CFG_F_SECCTX) return -EOPNOTSUPP; #endif + if ((flags & mask & NFQA_CFG_F_CONNTRACK) && + !rcu_access_pointer(nfnl_ct_hook)) { +#ifdef CONFIG_MODULES + nfnl_unlock(NFNL_SUBSYS_QUEUE); + request_module("ip_conntrack_netlink"); + nfnl_lock(NFNL_SUBSYS_QUEUE); + if (rcu_access_pointer(nfnl_ct_hook)) + return -EAGAIN; +#endif + return -EOPNOTSUPP; + } } rcu_read_lock(); -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next 5/5] netfilter: nfnetlink_log: just returns error for unknown command 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA ` (3 preceding siblings ...) 2016-01-05 0:32 ` [PATCH nf-next 4/5] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA @ 2016-01-05 0:34 ` Ken-ichirou MATSUZAWA 2016-01-05 11:03 ` Pablo Neira Ayuso 4 siblings, 1 reply; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2016-01-05 0:34 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch stops processing options for unknown command. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_log.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 6a57f10..643c2d3 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -888,7 +888,9 @@ static int nfulnl_recv_config(struct net *net, struct sock *ctnl, goto out_put; default: ret = -ENOTSUPP; - break; + if (inst) + goto out_put; + goto out; } } else if (!inst) { ret = -ENODEV; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH nf-next 5/5] netfilter: nfnetlink_log: just returns error for unknown command 2016-01-05 0:34 ` [PATCH nf-next 5/5] netfilter: nfnetlink_log: just returns error for unknown command Ken-ichirou MATSUZAWA @ 2016-01-05 11:03 ` Pablo Neira Ayuso 0 siblings, 0 replies; 32+ messages in thread From: Pablo Neira Ayuso @ 2016-01-05 11:03 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Tue, Jan 05, 2016 at 09:34:34AM +0900, Ken-ichirou MATSUZAWA wrote: > This patch stops processing options for unknown command. > > Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> > --- > net/netfilter/nfnetlink_log.c | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > > diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c > index 6a57f10..643c2d3 100644 > --- a/net/netfilter/nfnetlink_log.c > +++ b/net/netfilter/nfnetlink_log.c > @@ -888,7 +888,9 @@ static int nfulnl_recv_config(struct net *net, struct sock *ctnl, > goto out_put; > default: > ret = -ENOTSUPP; > - break; > + if (inst) > + goto out_put; > + goto out; We can simplify this to: goto out_put; since instance_put() already checks if inst is non-null. No need to resend, I'll fix this here. Thanks a lot for following up on this series Ken-ichirou :-). ^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH nf-next 2/3] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity 2015-11-06 0:46 ` Ken-ichirou MATSUZAWA 2015-11-06 0:49 ` [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling Ken-ichirou MATSUZAWA @ 2015-11-06 0:56 ` Ken-ichirou MATSUZAWA 2015-11-06 0:58 ` [PATCH nf-next 3/3] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA 2 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-11-06 0:56 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Check that dependencies are fulfilled before updating the queue instance, otherwise we can leave things in intermediate state on errors in nfqnl_recv_config(). This patch also fixes unknown and destroy command handling. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 74 +++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index f85a3d3..98c9edc 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1118,8 +1118,30 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, struct nfqnl_instance *queue; struct net *net = sock_net(ctnl); struct nfnl_queue_net *q = nfnl_queue_pernet(net); + __u32 flags, mask; int ret = 0; + /* Check if we support these flags in first place, dependencies should + * be there too not to break atomicity. + */ + if (nfqa[NFQA_CFG_FLAGS]) { + if (!nfqa[NFQA_CFG_MASK]) + /* A mask is needed to specify which flags are being + * changed. + */ + return -EINVAL; + + flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); + mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); + + if (flags >= NFQA_CFG_F_MAX) + return -EOPNOTSUPP; +#if !IS_ENABLED(CONFIG_NETWORK_SECMARK) + if (flags & mask & NFQA_CFG_F_SECCTX) + return -EOPNOTSUPP; +#endif + } + rcu_read_lock(); queue = instance_lookup(q, queue_num); if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { @@ -1149,71 +1171,39 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, goto err_out_unlock; } instance_destroy(q, queue); - break; + goto err_out_unlock; case NFQNL_CFG_CMD_PF_BIND: case NFQNL_CFG_CMD_PF_UNBIND: /* Obsolete commands without queue context */ goto err_out_unlock; default: ret = -ENOTSUPP; - break; + goto err_out_unlock; } } + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + if (nfqa[NFQA_CFG_PARAMS]) { - struct nfqnl_msg_config_params *params; + struct nfqnl_msg_config_params *params + = nla_data(nfqa[NFQA_CFG_PARAMS]); - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - params = nla_data(nfqa[NFQA_CFG_PARAMS]); nfqnl_set_mode(queue, params->copy_mode, ntohl(params->copy_range)); } if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { - __be32 *queue_maxlen; + __be32 *queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); spin_lock_bh(&queue->lock); queue->queue_maxlen = ntohl(*queue_maxlen); spin_unlock_bh(&queue->lock); } if (nfqa[NFQA_CFG_FLAGS]) { - __u32 flags, mask; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - - if (!nfqa[NFQA_CFG_MASK]) { - /* A mask is needed to specify which flags are being - * changed. - */ - ret = -EINVAL; - goto err_out_unlock; - } - - flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); - mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); - - if (flags >= NFQA_CFG_F_MAX) { - ret = -EOPNOTSUPP; - goto err_out_unlock; - } -#if !IS_ENABLED(CONFIG_NETWORK_SECMARK) - if (flags & mask & NFQA_CFG_F_SECCTX) { - ret = -EOPNOTSUPP; - goto err_out_unlock; - } -#endif spin_lock_bh(&queue->lock); queue->flags &= ~mask; queue->flags |= flags & mask; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next 3/3] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag 2015-11-06 0:46 ` Ken-ichirou MATSUZAWA 2015-11-06 0:49 ` [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling Ken-ichirou MATSUZAWA 2015-11-06 0:56 ` [PATCH nf-next 2/3] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity Ken-ichirou MATSUZAWA @ 2015-11-06 0:58 ` Ken-ichirou MATSUZAWA 2 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-11-06 0:58 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel This patch enables to load nf_conntrack_netlink module if NFQA_CFG_F_CONNTRACK config flag is specified. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nfnetlink_queue.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 98c9edc..d990c81 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1140,6 +1140,17 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, if (flags & mask & NFQA_CFG_F_SECCTX) return -EOPNOTSUPP; #endif + if ((flags & mask & NFQA_CFG_F_CONNTRACK) && + !rcu_access_pointer(nfnl_ct_hook)) { +#ifdef CONFIG_MODULES + nfnl_unlock(NFNL_SUBSYS_QUEUE); + request_module("ip_conntrack_netlink"); + nfnl_lock(NFNL_SUBSYS_QUEUE); + if (rcu_access_pointer(nfnl_ct_hook)) + return -EAGAIN; +#endif + return -EOPNOTSUPP; + } } rcu_read_lock(); -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next] netfilter: nf_conntrack_netlink: fix nf-nat module loaded checking 2015-10-07 4:20 ` Ken-ichirou MATSUZAWA 2015-10-07 4:23 ` [PATCHv3 nf-next] " Ken-ichirou MATSUZAWA 2015-10-07 4:25 ` [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module " Ken-ichirou MATSUZAWA @ 2015-10-07 4:27 ` Ken-ichirou MATSUZAWA 2015-10-07 4:30 ` [PATCH nf-next] netfilter: nf_conntrack_netlink: fix locks around helper module loading Ken-ichirou MATSUZAWA 3 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-07 4:27 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nf_conntrack_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 9f52729..dd13596 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1389,7 +1389,7 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, } nfnl_lock(NFNL_SUBSYS_CTNETLINK); rcu_read_lock(); - if (nfnetlink_parse_nat_setup_hook) + if (rcu_dereference(nfnetlink_parse_nat_setup_hook)) return -EAGAIN; #endif return -EOPNOTSUPP; -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH nf-next] netfilter: nf_conntrack_netlink: fix locks around helper module loading 2015-10-07 4:20 ` Ken-ichirou MATSUZAWA ` (2 preceding siblings ...) 2015-10-07 4:27 ` [PATCH nf-next] netfilter: nf_conntrack_netlink: fix nf-nat module loaded checking Ken-ichirou MATSUZAWA @ 2015-10-07 4:30 ` Ken-ichirou MATSUZAWA 3 siblings, 0 replies; 32+ messages in thread From: Ken-ichirou MATSUZAWA @ 2015-10-07 4:30 UTC (permalink / raw) To: Pablo Neira Ayuso; +Cc: netfilter-devel There are two paths for calling ctnetlink_change_helper which may call request_module() which requires nfnl lock, and may also call __nf_conntrack_helper_find() which needs rcu read lock. - nfqnl_cb[NFQNL_MSG_VERDICT].call_rcu / nfqnl_recv_verdict() - ctnl_cb[IPCTNL_MSG_CT_NEW].call / ctnetlink_new_conntrack() These uses different lock mechanism, it seems we need to adjust it. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> --- net/netfilter/nf_conntrack_netlink.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index dd13596..9121226 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1497,12 +1497,17 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[]) if (helper == NULL) { #ifdef CONFIG_MODULES spin_unlock_bh(&nf_conntrack_expect_lock); - + rcu_read_unlock(); + nfnl_unlock(NFNL_SUBSYS_CTNETLINK); if (request_module("nfct-helper-%s", helpname) < 0) { + nfnl_lock(NFNL_SUBSYS_CTNETLINK); + rcu_read_lock(); spin_lock_bh(&nf_conntrack_expect_lock); return -EOPNOTSUPP; } + nfnl_lock(NFNL_SUBSYS_CTNETLINK); + rcu_read_lock(); spin_lock_bh(&nf_conntrack_expect_lock); helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), nf_ct_protonum(ct)); @@ -1943,9 +1948,11 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, err = -EEXIST; ct = nf_ct_tuplehash_to_ctrack(h); if (!(nlh->nlmsg_flags & NLM_F_EXCL)) { + rcu_read_lock(); spin_lock_bh(&nf_conntrack_expect_lock); err = ctnetlink_change_conntrack(ct, cda); spin_unlock_bh(&nf_conntrack_expect_lock); + rcu_read_unlock(); if (err == 0) { nf_conntrack_eventmask_report((1 << IPCT_REPLY) | (1 << IPCT_ASSURED) | @@ -2287,7 +2294,9 @@ ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) return err; } if (cda[CTA_HELP]) { + nfnl_lock(NFNL_SUBSYS_CTNETLINK); err = ctnetlink_change_helper(ct, cda); + nfnl_unlock(NFNL_SUBSYS_CTNETLINK); if (err < 0) return err; } -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information 2015-10-05 2:44 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Ken-ichirou MATSUZAWA ` (3 preceding siblings ...) 2015-10-05 2:50 ` [PATCHv2 nf-next 4/4] netfilter: nfnetlink_log: allow to attach conntrack Ken-ichirou MATSUZAWA @ 2015-10-05 15:33 ` Pablo Neira Ayuso 4 siblings, 0 replies; 32+ messages in thread From: Pablo Neira Ayuso @ 2015-10-05 15:33 UTC (permalink / raw) To: Ken-ichirou MATSUZAWA; +Cc: netfilter-devel On Mon, Oct 05, 2015 at 11:44:54AM +0900, Ken-ichirou MATSUZAWA wrote: > Hi Pablo, > > I rebased on top of your patches, would you review these? > > You suggested it might be better to divide ct attaching Kconfig to > each of nfqueue and nflog, but I think it makes less sense now > because of your rework. is it right? Series applied, thanks for your patience. Please, follow up with fixes in case of problems. ^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2016-01-05 11:04 UTC | newest] Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-10-01 18:43 [PATCH 1/2 v3] netfilter: nfnetlink_queue: get rid of nfnetlink_queue_ct.c Pablo Neira Ayuso 2015-10-01 18:43 ` [PATCH 2/2 v3] netfilter: rename nfnetlink_queue_core.c to nfnetlink_queue.c Pablo Neira Ayuso 2015-10-05 2:44 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Ken-ichirou MATSUZAWA 2015-10-05 2:47 ` [PATCHv2 nf-next 1/4] netfilter: nfnetlink_queue: rename related to nfqueue attaching conntrack info Ken-ichirou MATSUZAWA 2015-10-05 2:48 ` [PATCHv2 nf-next 2/4] netfilter: Kconfig rename QUEUE_CT to GLUE_CT Ken-ichirou MATSUZAWA 2015-10-05 2:49 ` [PATCHv2 nf-next 3/4] netfilter: nf_conntrack_netlink: add const qualifier to nfnl_hook.get_ct Ken-ichirou MATSUZAWA 2015-10-05 2:50 ` [PATCHv2 nf-next 4/4] netfilter: nfnetlink_log: allow to attach conntrack Ken-ichirou MATSUZAWA 2015-10-05 15:23 ` Pablo Neira Ayuso 2015-10-06 2:10 ` [PATCHv2 nf-next 0/1] netfilter: nfnetlink_queue: check NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA 2015-10-06 2:12 ` [PATCHv2 nf-next 1/1] " Ken-ichirou MATSUZAWA 2015-10-06 10:07 ` Pablo Neira Ayuso 2015-10-07 4:20 ` Ken-ichirou MATSUZAWA 2015-10-07 4:23 ` [PATCHv3 nf-next] " Ken-ichirou MATSUZAWA 2015-10-07 4:25 ` [PATCH nf-next] netfilter: nfnetlink_log: autoload nf_conntrack_netlink module " Ken-ichirou MATSUZAWA 2015-10-12 17:13 ` Pablo Neira Ayuso 2015-10-12 20:10 ` Pablo Neira Ayuso 2015-10-16 17:05 ` Pablo Neira Ayuso 2015-11-06 0:46 ` Ken-ichirou MATSUZAWA 2015-11-06 0:49 ` [PATCH nf-next 1/3] netfilter: nfnetlink_queue: remove duplicated obsolete commands handling Ken-ichirou MATSUZAWA 2015-11-08 22:14 ` Pablo Neira Ayuso 2016-01-05 0:24 ` Ken-ichirou MATSUZAWA 2016-01-05 0:28 ` [PATCH nf-next 1/5] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity Ken-ichirou MATSUZAWA 2016-01-05 0:29 ` [PATCH nf-next 2/5] netfilter: nfnetlink_queue: not handle options after unbind Ken-ichirou MATSUZAWA 2016-01-05 0:31 ` [PATCH nf-next 3/5] netfilter: nfnetlink_queue: just returns error for unknown command Ken-ichirou MATSUZAWA 2016-01-05 0:32 ` [PATCH nf-next 4/5] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA 2016-01-05 0:34 ` [PATCH nf-next 5/5] netfilter: nfnetlink_log: just returns error for unknown command Ken-ichirou MATSUZAWA 2016-01-05 11:03 ` Pablo Neira Ayuso 2015-11-06 0:56 ` [PATCH nf-next 2/3] netfilter: nfnetlink_queue: validate dependencies to avoid breaking atomicity Ken-ichirou MATSUZAWA 2015-11-06 0:58 ` [PATCH nf-next 3/3] netfilter: nfnetlink_queue: autoload nf_conntrack_netlink module NFQA_CFG_F_CONNTRACK config flag Ken-ichirou MATSUZAWA 2015-10-07 4:27 ` [PATCH nf-next] netfilter: nf_conntrack_netlink: fix nf-nat module loaded checking Ken-ichirou MATSUZAWA 2015-10-07 4:30 ` [PATCH nf-next] netfilter: nf_conntrack_netlink: fix locks around helper module loading Ken-ichirou MATSUZAWA 2015-10-05 15:33 ` [PATCHv2 nf-next 0/4] netfilter: nfnetlink_log attach conntrack information Pablo Neira Ayuso
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.