Skip to content

Commit

Permalink
netfilter: replace list_head with single linked list
Browse files Browse the repository at this point in the history
The netfilter hook list never uses the prev pointer, and so can be trimmed to
be a simple singly-linked list.

In addition to having a more light weight structure for hook traversal,
struct net becomes 5568 bytes (down from 6400) and struct net_device becomes
2176 bytes (down from 2240).

Signed-off-by: Aaron Conole <aconole@bytheb.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
apconole authored and ummakynes committed Sep 25, 2016
1 parent 54f17bb commit e3b37f1
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 116 deletions.
2 changes: 1 addition & 1 deletion include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -1783,7 +1783,7 @@ struct net_device {
#endif
struct netdev_queue __rcu *ingress_queue;
#ifdef CONFIG_NETFILTER_INGRESS
struct list_head nf_hooks_ingress;
struct nf_hook_entry __rcu *nf_hooks_ingress;
#endif

unsigned char broadcast[MAX_ADDR_LEN];
Expand Down
63 changes: 34 additions & 29 deletions include/linux/netfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,34 @@ struct nf_hook_state {
struct net_device *out;
struct sock *sk;
struct net *net;
struct list_head *hook_list;
struct nf_hook_entry __rcu *hook_entries;
int (*okfn)(struct net *, struct sock *, struct sk_buff *);
};

typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);
struct nf_hook_ops {
struct list_head list;

/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
void *priv;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};

struct nf_hook_entry {
struct nf_hook_entry __rcu *next;
struct nf_hook_ops ops;
const struct nf_hook_ops *orig_ops;
};

static inline void nf_hook_state_init(struct nf_hook_state *p,
struct list_head *hook_list,
struct nf_hook_entry *hook_entry,
unsigned int hook,
int thresh, u_int8_t pf,
struct net_device *indev,
Expand All @@ -76,26 +98,11 @@ static inline void nf_hook_state_init(struct nf_hook_state *p,
p->out = outdev;
p->sk = sk;
p->net = net;
p->hook_list = hook_list;
RCU_INIT_POINTER(p->hook_entries, hook_entry);
p->okfn = okfn;
}

typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);

struct nf_hook_ops {
struct list_head list;

/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
void *priv;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};

struct nf_sockopt_ops {
struct list_head list;
Expand Down Expand Up @@ -161,7 +168,8 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
int (*okfn)(struct net *, struct sock *, struct sk_buff *),
int thresh)
{
struct list_head *hook_list;
struct nf_hook_entry *hook_head;
int ret = 1;

#ifdef HAVE_JUMP_LABEL
if (__builtin_constant_p(pf) &&
Expand All @@ -170,22 +178,19 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
return 1;
#endif

hook_list = &net->nf.hooks[pf][hook];

if (!list_empty(hook_list)) {
rcu_read_lock();
hook_head = rcu_dereference(net->nf.hooks[pf][hook]);
if (hook_head) {
struct nf_hook_state state;
int ret;

/* We may already have this, but read-locks nest anyway */
rcu_read_lock();
nf_hook_state_init(&state, hook_list, hook, thresh,
nf_hook_state_init(&state, hook_head, hook, thresh,
pf, indev, outdev, sk, net, okfn);

ret = nf_hook_slow(skb, &state);
rcu_read_unlock();
return ret;
}
return 1;
rcu_read_unlock();

return ret;
}

static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
Expand Down
17 changes: 12 additions & 5 deletions include/linux/netfilter_ingress.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,30 @@ static inline bool nf_hook_ingress_active(const struct sk_buff *skb)
if (!static_key_false(&nf_hooks_needed[NFPROTO_NETDEV][NF_NETDEV_INGRESS]))
return false;
#endif
return !list_empty(&skb->dev->nf_hooks_ingress);
return rcu_access_pointer(skb->dev->nf_hooks_ingress);
}

/* caller must hold rcu_read_lock */
static inline int nf_hook_ingress(struct sk_buff *skb)
{
struct nf_hook_entry *e = rcu_dereference(skb->dev->nf_hooks_ingress);
struct nf_hook_state state;

nf_hook_state_init(&state, &skb->dev->nf_hooks_ingress,
NF_NETDEV_INGRESS, INT_MIN, NFPROTO_NETDEV,
skb->dev, NULL, NULL, dev_net(skb->dev), NULL);
/* Must recheck the ingress hook head, in the event it became NULL
* after the check in nf_hook_ingress_active evaluated to true.
*/
if (unlikely(!e))
return 0;

nf_hook_state_init(&state, e, NF_NETDEV_INGRESS, INT_MIN,
NFPROTO_NETDEV, skb->dev, NULL, NULL,
dev_net(skb->dev), NULL);
return nf_hook_slow(skb, &state);
}

static inline void nf_hook_ingress_init(struct net_device *dev)
{
INIT_LIST_HEAD(&dev->nf_hooks_ingress);
RCU_INIT_POINTER(dev->nf_hooks_ingress, NULL);
}
#else /* CONFIG_NETFILTER_INGRESS */
static inline int nf_hook_ingress_active(struct sk_buff *skb)
Expand Down
3 changes: 1 addition & 2 deletions include/net/netfilter/nf_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ struct nf_queue_entry {
struct sk_buff *skb;
unsigned int id;

struct nf_hook_ops *elem;
struct nf_hook_state state;
u16 size; /* sizeof(entry) + saved route keys */

Expand All @@ -25,7 +24,7 @@ struct nf_queue_handler {
int (*outfn)(struct nf_queue_entry *entry,
unsigned int queuenum);
void (*nf_hook_drop)(struct net *net,
struct nf_hook_ops *ops);
const struct nf_hook_entry *hooks);
};

void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh);
Expand Down
2 changes: 1 addition & 1 deletion include/net/netns/netfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ struct netns_nf {
#ifdef CONFIG_SYSCTL
struct ctl_table_header *nf_log_dir_header;
#endif
struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
struct nf_hook_entry __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
};
#endif
19 changes: 6 additions & 13 deletions net/bridge/br_netfilter_hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1002,28 +1002,21 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net,
int (*okfn)(struct net *, struct sock *,
struct sk_buff *))
{
struct nf_hook_ops *elem;
struct nf_hook_entry *elem;
struct nf_hook_state state;
struct list_head *head;
int ret;

head = &net->nf.hooks[NFPROTO_BRIDGE][hook];
elem = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]);

list_for_each_entry_rcu(elem, head, list) {
struct nf_hook_ops *next;
while (elem && (elem->ops.priority <= NF_BR_PRI_BRNF))
elem = rcu_dereference(elem->next);

next = list_entry_rcu(list_next_rcu(&elem->list),
struct nf_hook_ops, list);
if (next->priority <= NF_BR_PRI_BRNF)
continue;
}

if (&elem->list == head)
if (!elem)
return okfn(net, sk, skb);

/* We may already have this, but read-locks nest anyway */
rcu_read_lock();
nf_hook_state_init(&state, head, hook, NF_BR_PRI_BRNF + 1,
nf_hook_state_init(&state, elem, hook, NF_BR_PRI_BRNF + 1,
NFPROTO_BRIDGE, indev, outdev, sk, net, okfn);

ret = nf_hook_slow(skb, &state);
Expand Down
Loading

0 comments on commit e3b37f1

Please sign in to comment.