From 3d1c0bfef9d6ca43050e7a9077b46e0139ee1e1f Mon Sep 17 00:00:00 2001 From: Tom Goff Date: Mon, 3 Jun 2013 18:31:29 -0700 Subject: [PATCH 3/3] netfilter: nfnetlink_queue: Add netns support. Make nfnetlink_queue network namespace aware including a per-netns /proc/net/netfilter/nfnetlink_queue file. Signed-off-by: Tom Goff --- include/net/net_namespace.h | 6 ++ include/net/netfilter/nf_queue.h | 3 +- net/netfilter/nf_queue.c | 12 ++- net/netfilter/nfnetlink_queue_core.c | 137 ++++++++++++++++++----------------- 4 files changed, 88 insertions(+), 70 deletions(-) diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 96b2242..1af155b 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -22,6 +22,9 @@ #include #endif #include +#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE) +#include +#endif struct user_namespace; struct proc_dir_entry; @@ -106,6 +109,9 @@ struct net { #endif struct sock *nfnl; struct sock *nfnl_stash; +#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE) + struct netns_nfqnl nfqnl; +#endif #endif #ifdef CONFIG_WEXT_CORE struct sk_buff_head wext_nlevents; diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index fb1c0be..484453d 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -19,7 +19,8 @@ struct nf_queue_entry { /* Packet queuing */ struct nf_queue_handler { - int (*outfn)(struct nf_queue_entry *entry, + int (*outfn)(struct net *net, + struct nf_queue_entry *entry, unsigned int queuenum); }; diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index d812c12..0d69be9 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -81,6 +81,16 @@ static int __nf_queue(struct sk_buff *skb, #endif const struct nf_afinfo *afinfo; const struct nf_queue_handler *qh; + struct net *net; + + if (indev) + net = dev_net(indev); + else if (skb->sk) + net = sock_net(skb->sk); + else if (outdev) + net = dev_net(outdev); + else + return status; /* QUEUE == DROP if no one is waiting, to be safe. */ rcu_read_lock(); @@ -133,7 +143,7 @@ static int __nf_queue(struct sk_buff *skb, #endif skb_dst_force(skb); afinfo->saveroute(skb, entry); - status = qh->outfn(entry, queuenum); + status = qh->outfn(net, entry, queuenum); rcu_read_unlock(); diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 8f2d9bb..1e0a830 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -66,24 +66,19 @@ struct nfqnl_instance { typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); -static DEFINE_SPINLOCK(instances_lock); - -#define INSTANCE_BUCKETS 16 -static struct hlist_head instance_table[INSTANCE_BUCKETS] __read_mostly; - static inline u_int8_t instance_hashfn(u_int16_t queue_num) { - return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS; + return ((queue_num >> 8) | queue_num) % NFQNL_INSTANCE_BUCKETS; } static struct nfqnl_instance * -instance_lookup(u_int16_t queue_num) +instance_lookup(struct net *net, u_int16_t queue_num) { struct hlist_head *head; struct hlist_node *pos; struct nfqnl_instance *inst; - head = &instance_table[instance_hashfn(queue_num)]; + head = &net->nfqnl.instance_table[instance_hashfn(queue_num)]; hlist_for_each_entry_rcu(inst, pos, head, hlist) { if (inst->queue_num == queue_num) return inst; @@ -92,14 +87,14 @@ instance_lookup(u_int16_t queue_num) } static struct nfqnl_instance * -instance_create(u_int16_t queue_num, int portid) +instance_create(struct net *net, u_int16_t queue_num, int portid) { struct nfqnl_instance *inst; unsigned int h; int err; - spin_lock(&instances_lock); - if (instance_lookup(queue_num)) { + spin_lock(&net->nfqnl.instances_lock); + if (instance_lookup(net, queue_num)) { err = -EEXIST; goto out_unlock; } @@ -124,16 +119,16 @@ instance_create(u_int16_t queue_num, int portid) } h = instance_hashfn(queue_num); - hlist_add_head_rcu(&inst->hlist, &instance_table[h]); + hlist_add_head_rcu(&inst->hlist, &net->nfqnl.instance_table[h]); - spin_unlock(&instances_lock); + spin_unlock(&net->nfqnl.instances_lock); return inst; out_free: kfree(inst); out_unlock: - spin_unlock(&instances_lock); + spin_unlock(&net->nfqnl.instances_lock); return ERR_PTR(err); } @@ -159,11 +154,11 @@ __instance_destroy(struct nfqnl_instance *inst) } static void -instance_destroy(struct nfqnl_instance *inst) +instance_destroy(struct net *net, struct nfqnl_instance *inst) { - spin_lock(&instances_lock); + spin_lock(&net->nfqnl.instances_lock); __instance_destroy(inst); - spin_unlock(&instances_lock); + spin_unlock(&net->nfqnl.instances_lock); } static inline void @@ -417,7 +412,8 @@ nla_put_failure: } static int -nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) +nfqnl_enqueue_packet(struct net *net, struct nf_queue_entry *entry, + unsigned int queuenum) { struct sk_buff *nskb; struct nfqnl_instance *queue; @@ -426,7 +422,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) int failopen = 0; /* rcu_read_lock()ed by nf_hook_slow() */ - queue = instance_lookup(queuenum); + queue = instance_lookup(net, queuenum); if (!queue) { err = -ESRCH; goto err_out; @@ -463,7 +459,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) *packet_id_ptr = htonl(entry->id); /* nfnetlink_unicast will either free the nskb or add it to a socket */ - err = nfnetlink_unicast(nskb, &init_net, queue->peer_portid, MSG_DONTWAIT); + err = nfnetlink_unicast(nskb, net, queue->peer_portid, MSG_DONTWAIT); if (err < 0) { queue->queue_user_dropped++; goto err_out_unlock; @@ -576,16 +572,16 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) /* drop all packets with either indev or outdev == ifindex from all queue * instances */ static void -nfqnl_dev_drop(int ifindex) +nfqnl_dev_drop(struct net *net, int ifindex) { int i; rcu_read_lock(); - for (i = 0; i < INSTANCE_BUCKETS; i++) { + for (i = 0; i < NFQNL_INSTANCE_BUCKETS; i++) { struct hlist_node *tmp; struct nfqnl_instance *inst; - struct hlist_head *head = &instance_table[i]; + struct hlist_head *head = &net->nfqnl.instance_table[i]; hlist_for_each_entry_rcu(inst, tmp, head, hlist) nfqnl_flush(inst, dev_cmp, ifindex); @@ -602,12 +598,9 @@ nfqnl_rcv_dev_event(struct notifier_block *this, { struct net_device *dev = ptr; - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - /* Drop any packets associated with the downed device */ if (event == NETDEV_DOWN) - nfqnl_dev_drop(dev->ifindex); + nfqnl_dev_drop(dev_net(dev), dev->ifindex); return NOTIFY_DONE; } @@ -625,19 +618,19 @@ nfqnl_rcv_nl_event(struct notifier_block *this, int i; /* destroy all instances for this portid */ - spin_lock(&instances_lock); - for (i = 0; i < INSTANCE_BUCKETS; i++) { + spin_lock(&n->net->nfqnl.instances_lock); + for (i = 0; i < NFQNL_INSTANCE_BUCKETS; i++) { struct hlist_node *tmp, *t2; struct nfqnl_instance *inst; - struct hlist_head *head = &instance_table[i]; + struct hlist_head *head = + &n->net->nfqnl.instance_table[i]; hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { - if ((n->net == &init_net) && - (n->portid == inst->peer_portid)) + if (n->portid == inst->peer_portid) __instance_destroy(inst); } } - spin_unlock(&instances_lock); + spin_unlock(&n->net->nfqnl.instances_lock); } return NOTIFY_DONE; } @@ -658,11 +651,13 @@ static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { [NFQA_MARK] = { .type = NLA_U32 }, }; -static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlportid) +static struct nfqnl_instance *verdict_instance_lookup(struct net *net, + u16 queue_num, + int nlportid) { struct nfqnl_instance *queue; - queue = instance_lookup(queue_num); + queue = instance_lookup(net, queue_num); if (!queue) return ERR_PTR(-ENODEV); @@ -706,7 +701,8 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, LIST_HEAD(batch_list); u16 queue_num = ntohs(nfmsg->res_id); - queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).portid); + queue = verdict_instance_lookup(sock_net(ctnl), queue_num, + NETLINK_CB(skb).portid); if (IS_ERR(queue)) return PTR_ERR(queue); @@ -751,13 +747,14 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, struct nfqnl_instance *queue; unsigned int verdict; struct nf_queue_entry *entry; + struct net *net = sock_net(ctnl); enum ip_conntrack_info uninitialized_var(ctinfo); struct nf_conn *ct = NULL; - queue = instance_lookup(queue_num); + queue = instance_lookup(net, queue_num); if (!queue) - queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).portid); + queue = verdict_instance_lookup(net, queue_num, NETLINK_CB(skb).portid); if (IS_ERR(queue)) return PTR_ERR(queue); @@ -822,6 +819,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, struct nfqnl_instance *queue; struct nfqnl_msg_config_cmd *cmd = NULL; int ret = 0; + struct net *net = sock_net(ctnl); if (nfqa[NFQA_CFG_CMD]) { cmd = nla_data(nfqa[NFQA_CFG_CMD]); @@ -834,7 +832,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, } rcu_read_lock(); - queue = instance_lookup(queue_num); + queue = instance_lookup(net, queue_num); if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { ret = -EPERM; goto err_out_unlock; @@ -847,7 +845,8 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ret = -EBUSY; goto err_out_unlock; } - queue = instance_create(queue_num, NETLINK_CB(skb).portid); + queue = instance_create(net, queue_num, + NETLINK_CB(skb).portid); if (IS_ERR(queue)) { ret = PTR_ERR(queue); goto err_out_unlock; @@ -858,7 +857,7 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ret = -ENODEV; goto err_out_unlock; } - instance_destroy(queue); + instance_destroy(net, queue); break; case NFQNL_CFG_CMD_PF_BIND: case NFQNL_CFG_CMD_PF_UNBIND: @@ -952,65 +951,64 @@ static const struct nfnetlink_subsystem nfqnl_subsys = { #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) +static struct hlist_node *get_first(struct net *net, struct iter_state *st) { - struct iter_state *st = seq->private; - if (!st) return NULL; - for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { - if (!hlist_empty(&instance_table[st->bucket])) - return instance_table[st->bucket].first; + for (st->bucket = 0; st->bucket < NFQNL_INSTANCE_BUCKETS; st->bucket++) { + if (!hlist_empty(&net->nfqnl.instance_table[st->bucket])) + return net->nfqnl.instance_table[st->bucket].first; } return NULL; } -static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) +static struct hlist_node *get_next(struct net *net, struct iter_state *st, + struct hlist_node *h) { - struct iter_state *st = seq->private; - h = h->next; while (!h) { - if (++st->bucket >= INSTANCE_BUCKETS) + if (++st->bucket >= NFQNL_INSTANCE_BUCKETS) return NULL; - h = instance_table[st->bucket].first; + h = net->nfqnl.instance_table[st->bucket].first; } return h; } -static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) +static struct hlist_node *get_idx(struct net *net, struct iter_state *st, + loff_t pos) { struct hlist_node *head; - head = get_first(seq); + head = get_first(net, st); if (head) - while (pos && (head = get_next(seq, head))) + while (pos && (head = get_next(net, st, head))) pos--; return pos ? NULL : head; } static void *seq_start(struct seq_file *seq, loff_t *pos) - __acquires(instances_lock) { - spin_lock(&instances_lock); - return get_idx(seq, *pos); + struct net *net = seq_file_net(seq); + spin_lock(&net->nfqnl.instances_lock); + return get_idx(net, seq->private, *pos); } static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; - return get_next(s, v); + return get_next(seq_file_net(s), s->private, v); } static void seq_stop(struct seq_file *s, void *v) - __releases(instances_lock) { - spin_unlock(&instances_lock); + struct net *net = seq_file_net(s); + spin_unlock(&net->nfqnl.instances_lock); } static int seq_show(struct seq_file *s, void *v) @@ -1034,8 +1032,8 @@ static const struct seq_operations nfqnl_seq_ops = { static int nfqnl_open(struct inode *inode, struct file *file) { - return seq_open_private(file, &nfqnl_seq_ops, - sizeof(struct iter_state)); + return seq_open_net(inode, file, &nfqnl_seq_ops, + sizeof(struct iter_state)); } static const struct file_operations nfqnl_file_ops = { @@ -1043,13 +1041,19 @@ static const struct file_operations nfqnl_file_ops = { .open = nfqnl_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release_private, + .release = seq_release_net, }; #endif /* PROC_FS */ static int __net_init nfnetlink_queue_net_init(struct net *net) { + int i; + + spin_lock_init(&net->nfqnl.instances_lock); + for (i = 0; i < NFQNL_INSTANCE_BUCKETS; i++) + INIT_HLIST_HEAD(&net->nfqnl.instance_table[i]); + #ifdef CONFIG_PROC_FS if (!proc_create("nfnetlink_queue", 0440, net->proc_net_netfilter, &nfqnl_file_ops)) @@ -1073,7 +1077,7 @@ static struct pernet_operations nfnetlink_queue_net_ops = { static int __init nfnetlink_queue_init(void) { - int i, status; + int status; status = register_pernet_subsys(&nfnetlink_queue_net_ops); if (status) { @@ -1082,9 +1086,6 @@ static int __init nfnetlink_queue_init(void) return status; } - for (i = 0; i < INSTANCE_BUCKETS; i++) - INIT_HLIST_HEAD(&instance_table[i]); - netlink_register_notifier(&nfqnl_rtnl_notifier); status = nfnetlink_subsys_register(&nfqnl_subsys); if (status < 0) { -- 1.8.1.2