Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3b37f11 authored by Aaron Conole's avatar Aaron Conole Committed by Pablo Neira Ayuso
Browse files

netfilter: replace list_head with single linked list



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: default avatarAaron Conole <aconole@bytheb.org>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 54f17bbc
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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];
+34 −29
Original line number Diff line number Diff line
@@ -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,
@@ -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;
@@ -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) &&
@@ -170,23 +178,20 @@ 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;
}

static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
			  struct sock *sk, struct sk_buff *skb,
+12 −5
Original line number Diff line number Diff line
@@ -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)
+1 −2
Original line number Diff line number Diff line
@@ -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 */

@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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
Loading