Skip to content

Commit 6b75e3e

Browse files
Eric Dumazetkaber
authored andcommitted
netfilter: nfnetlink: add RCU in nfnetlink_rcv_msg()
Goal of this patch is to permit nfnetlink providers not mandate nfnl_mutex being held while nfnetlink_rcv_msg() calls them. If struct nfnl_callback contains a non NULL call_rcu(), then nfnetlink_rcv_msg() will use it instead of call() field, holding rcu_read_lock instead of nfnl_mutex Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> CC: Florian Westphal <fw@strlen.de> CC: Eric Leblond <eric@regit.org> Signed-off-by: Patrick McHardy <kaber@trash.net>
1 parent 131ad62 commit 6b75e3e

2 files changed

Lines changed: 33 additions & 10 deletions

File tree

include/linux/netfilter/nfnetlink.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ struct nfnl_callback {
6060
int (*call)(struct sock *nl, struct sk_buff *skb,
6161
const struct nlmsghdr *nlh,
6262
const struct nlattr * const cda[]);
63+
int (*call_rcu)(struct sock *nl, struct sk_buff *skb,
64+
const struct nlmsghdr *nlh,
65+
const struct nlattr * const cda[]);
6366
const struct nla_policy *policy; /* netlink attribute policy */
6467
const u_int16_t attr_count; /* number of nlattr's */
6568
};

net/netfilter/nfnetlink.c

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
3737

3838
static char __initdata nfversion[] = "0.30";
3939

40-
static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
40+
static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
4141
static DEFINE_MUTEX(nfnl_mutex);
4242

4343
void nfnl_lock(void)
@@ -59,7 +59,7 @@ int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
5959
nfnl_unlock();
6060
return -EBUSY;
6161
}
62-
subsys_table[n->subsys_id] = n;
62+
rcu_assign_pointer(subsys_table[n->subsys_id], n);
6363
nfnl_unlock();
6464

6565
return 0;
@@ -71,7 +71,7 @@ int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
7171
nfnl_lock();
7272
subsys_table[n->subsys_id] = NULL;
7373
nfnl_unlock();
74-
74+
synchronize_rcu();
7575
return 0;
7676
}
7777
EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
@@ -83,7 +83,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t
8383
if (subsys_id >= NFNL_SUBSYS_COUNT)
8484
return NULL;
8585

86-
return subsys_table[subsys_id];
86+
return rcu_dereference(subsys_table[subsys_id]);
8787
}
8888

8989
static inline const struct nfnl_callback *
@@ -139,21 +139,27 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
139139

140140
type = nlh->nlmsg_type;
141141
replay:
142+
rcu_read_lock();
142143
ss = nfnetlink_get_subsys(type);
143144
if (!ss) {
144145
#ifdef CONFIG_MODULES
145-
nfnl_unlock();
146+
rcu_read_unlock();
146147
request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
147-
nfnl_lock();
148+
rcu_read_lock();
148149
ss = nfnetlink_get_subsys(type);
149150
if (!ss)
150151
#endif
152+
{
153+
rcu_read_unlock();
151154
return -EINVAL;
155+
}
152156
}
153157

154158
nc = nfnetlink_find_client(type, ss);
155-
if (!nc)
159+
if (!nc) {
160+
rcu_read_unlock();
156161
return -EINVAL;
162+
}
157163

158164
{
159165
int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
@@ -167,7 +173,23 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
167173
if (err < 0)
168174
return err;
169175

170-
err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda);
176+
if (nc->call_rcu) {
177+
err = nc->call_rcu(net->nfnl, skb, nlh,
178+
(const struct nlattr **)cda);
179+
rcu_read_unlock();
180+
} else {
181+
rcu_read_unlock();
182+
nfnl_lock();
183+
if (rcu_dereference_protected(
184+
subsys_table[NFNL_SUBSYS_ID(type)],
185+
lockdep_is_held(&nfnl_mutex)) != ss ||
186+
nfnetlink_find_client(type, ss) != nc)
187+
err = -EAGAIN;
188+
else
189+
err = nc->call(net->nfnl, skb, nlh,
190+
(const struct nlattr **)cda);
191+
nfnl_unlock();
192+
}
171193
if (err == -EAGAIN)
172194
goto replay;
173195
return err;
@@ -176,9 +198,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
176198

177199
static void nfnetlink_rcv(struct sk_buff *skb)
178200
{
179-
nfnl_lock();
180201
netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
181-
nfnl_unlock();
182202
}
183203

184204
static int __net_init nfnetlink_net_init(struct net *net)

0 commit comments

Comments
 (0)