summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2017-12-02 15:44:06 -0500
committerDavid S. Miller <davem@davemloft.net>2017-12-04 11:32:31 -0500
commite4202511480da5f8e6870d8f6ecbb821aeaa8caf (patch)
treee5fadf5d76149f543fc003154f844b2ca19579bd
parentaddf9b90de22f7aaad0db39bccb5d51ac47dd4e1 (diff)
rtnetlink: get reference on module before invoking handlers
Add yet another rtnl_register function. It will be used by modules that can be removed. The passed module struct is used to prevent module unload while a netlink dump is in progress or when a DOIT_UNLOCKED doit callback is called. Cc: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/rtnetlink.h2
-rw-r--r--net/core/rtnetlink.c113
2 files changed, 80 insertions, 35 deletions
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h
index ead018744ff5..e326b3f9eb5f 100644
--- a/include/net/rtnetlink.h
+++ b/include/net/rtnetlink.h
@@ -17,6 +17,8 @@ int __rtnl_register(int protocol, int msgtype,
17 rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); 17 rtnl_doit_func, rtnl_dumpit_func, unsigned int flags);
18void rtnl_register(int protocol, int msgtype, 18void rtnl_register(int protocol, int msgtype,
19 rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); 19 rtnl_doit_func, rtnl_dumpit_func, unsigned int flags);
20int rtnl_register_module(struct module *owner, int protocol, int msgtype,
21 rtnl_doit_func, rtnl_dumpit_func, unsigned int flags);
20int rtnl_unregister(int protocol, int msgtype); 22int rtnl_unregister(int protocol, int msgtype);
21void rtnl_unregister_all(int protocol); 23void rtnl_unregister_all(int protocol);
22 24
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index ff292d3f2c41..de6390365c90 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -62,6 +62,7 @@
62struct rtnl_link { 62struct rtnl_link {
63 rtnl_doit_func doit; 63 rtnl_doit_func doit;
64 rtnl_dumpit_func dumpit; 64 rtnl_dumpit_func dumpit;
65 struct module *owner;
65 unsigned int flags; 66 unsigned int flags;
66 struct rcu_head rcu; 67 struct rcu_head rcu;
67}; 68};
@@ -129,7 +130,6 @@ EXPORT_SYMBOL(lockdep_rtnl_is_held);
129#endif /* #ifdef CONFIG_PROVE_LOCKING */ 130#endif /* #ifdef CONFIG_PROVE_LOCKING */
130 131
131static struct rtnl_link __rcu **rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; 132static struct rtnl_link __rcu **rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
132static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1];
133 133
134static inline int rtm_msgindex(int msgtype) 134static inline int rtm_msgindex(int msgtype)
135{ 135{
@@ -159,27 +159,10 @@ static struct rtnl_link *rtnl_get_link(int protocol, int msgtype)
159 return tab[msgtype]; 159 return tab[msgtype];
160} 160}
161 161
162/** 162static int rtnl_register_internal(struct module *owner,
163 * __rtnl_register - Register a rtnetlink message type 163 int protocol, int msgtype,
164 * @protocol: Protocol family or PF_UNSPEC 164 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
165 * @msgtype: rtnetlink message type 165 unsigned int flags)
166 * @doit: Function pointer called for each request message
167 * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
168 * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
169 *
170 * Registers the specified function pointers (at least one of them has
171 * to be non-NULL) to be called whenever a request message for the
172 * specified protocol family and message type is received.
173 *
174 * The special protocol family PF_UNSPEC may be used to define fallback
175 * function pointers for the case when no entry for the specific protocol
176 * family exists.
177 *
178 * Returns 0 on success or a negative error code.
179 */
180int __rtnl_register(int protocol, int msgtype,
181 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
182 unsigned int flags)
183{ 166{
184 struct rtnl_link **tab, *link, *old; 167 struct rtnl_link **tab, *link, *old;
185 int msgindex; 168 int msgindex;
@@ -210,6 +193,9 @@ int __rtnl_register(int protocol, int msgtype,
210 goto unlock; 193 goto unlock;
211 } 194 }
212 195
196 WARN_ON(link->owner && link->owner != owner);
197 link->owner = owner;
198
213 WARN_ON(doit && link->doit && link->doit != doit); 199 WARN_ON(doit && link->doit && link->doit != doit);
214 if (doit) 200 if (doit)
215 link->doit = doit; 201 link->doit = doit;
@@ -228,6 +214,54 @@ unlock:
228 rtnl_unlock(); 214 rtnl_unlock();
229 return ret; 215 return ret;
230} 216}
217
218/**
219 * rtnl_register_module - Register a rtnetlink message type
220 *
221 * @owner: module registering the hook (THIS_MODULE)
222 * @protocol: Protocol family or PF_UNSPEC
223 * @msgtype: rtnetlink message type
224 * @doit: Function pointer called for each request message
225 * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
226 * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
227 *
228 * Like rtnl_register, but for use by removable modules.
229 */
230int rtnl_register_module(struct module *owner,
231 int protocol, int msgtype,
232 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
233 unsigned int flags)
234{
235 return rtnl_register_internal(owner, protocol, msgtype,
236 doit, dumpit, flags);
237}
238EXPORT_SYMBOL_GPL(rtnl_register_module);
239
240/**
241 * __rtnl_register - Register a rtnetlink message type
242 * @protocol: Protocol family or PF_UNSPEC
243 * @msgtype: rtnetlink message type
244 * @doit: Function pointer called for each request message
245 * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
246 * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
247 *
248 * Registers the specified function pointers (at least one of them has
249 * to be non-NULL) to be called whenever a request message for the
250 * specified protocol family and message type is received.
251 *
252 * The special protocol family PF_UNSPEC may be used to define fallback
253 * function pointers for the case when no entry for the specific protocol
254 * family exists.
255 *
256 * Returns 0 on success or a negative error code.
257 */
258int __rtnl_register(int protocol, int msgtype,
259 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
260 unsigned int flags)
261{
262 return rtnl_register_internal(NULL, protocol, msgtype,
263 doit, dumpit, flags);
264}
231EXPORT_SYMBOL_GPL(__rtnl_register); 265EXPORT_SYMBOL_GPL(__rtnl_register);
232 266
233/** 267/**
@@ -311,8 +345,6 @@ void rtnl_unregister_all(int protocol)
311 345
312 synchronize_net(); 346 synchronize_net();
313 347
314 while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1)
315 schedule();
316 kfree(tab); 348 kfree(tab);
317} 349}
318EXPORT_SYMBOL_GPL(rtnl_unregister_all); 350EXPORT_SYMBOL_GPL(rtnl_unregister_all);
@@ -4372,6 +4404,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4372{ 4404{
4373 struct net *net = sock_net(skb->sk); 4405 struct net *net = sock_net(skb->sk);
4374 struct rtnl_link *link; 4406 struct rtnl_link *link;
4407 struct module *owner;
4375 int err = -EOPNOTSUPP; 4408 int err = -EOPNOTSUPP;
4376 rtnl_doit_func doit; 4409 rtnl_doit_func doit;
4377 unsigned int flags; 4410 unsigned int flags;
@@ -4408,24 +4441,32 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4408 if (!link || !link->dumpit) 4441 if (!link || !link->dumpit)
4409 goto err_unlock; 4442 goto err_unlock;
4410 } 4443 }
4444 owner = link->owner;
4411 dumpit = link->dumpit; 4445 dumpit = link->dumpit;
4412 4446
4413 refcount_inc(&rtnl_msg_handlers_ref[family]);
4414
4415 if (type == RTM_GETLINK - RTM_BASE) 4447 if (type == RTM_GETLINK - RTM_BASE)
4416 min_dump_alloc = rtnl_calcit(skb, nlh); 4448 min_dump_alloc = rtnl_calcit(skb, nlh);
4417 4449
4450 err = 0;
4451 /* need to do this before rcu_read_unlock() */
4452 if (!try_module_get(owner))
4453 err = -EPROTONOSUPPORT;
4454
4418 rcu_read_unlock(); 4455 rcu_read_unlock();
4419 4456
4420 rtnl = net->rtnl; 4457 rtnl = net->rtnl;
4421 { 4458 if (err == 0) {
4422 struct netlink_dump_control c = { 4459 struct netlink_dump_control c = {
4423 .dump = dumpit, 4460 .dump = dumpit,
4424 .min_dump_alloc = min_dump_alloc, 4461 .min_dump_alloc = min_dump_alloc,
4462 .module = owner,
4425 }; 4463 };
4426 err = netlink_dump_start(rtnl, skb, nlh, &c); 4464 err = netlink_dump_start(rtnl, skb, nlh, &c);
4465 /* netlink_dump_start() will keep a reference on
4466 * module if dump is still in progress.
4467 */
4468 module_put(owner);
4427 } 4469 }
4428 refcount_dec(&rtnl_msg_handlers_ref[family]);
4429 return err; 4470 return err;
4430 } 4471 }
4431 4472
@@ -4437,14 +4478,19 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4437 goto out_unlock; 4478 goto out_unlock;
4438 } 4479 }
4439 4480
4481 owner = link->owner;
4482 if (!try_module_get(owner)) {
4483 err = -EPROTONOSUPPORT;
4484 goto out_unlock;
4485 }
4486
4440 flags = link->flags; 4487 flags = link->flags;
4441 if (flags & RTNL_FLAG_DOIT_UNLOCKED) { 4488 if (flags & RTNL_FLAG_DOIT_UNLOCKED) {
4442 refcount_inc(&rtnl_msg_handlers_ref[family]);
4443 doit = link->doit; 4489 doit = link->doit;
4444 rcu_read_unlock(); 4490 rcu_read_unlock();
4445 if (doit) 4491 if (doit)
4446 err = doit(skb, nlh, extack); 4492 err = doit(skb, nlh, extack);
4447 refcount_dec(&rtnl_msg_handlers_ref[family]); 4493 module_put(owner);
4448 return err; 4494 return err;
4449 } 4495 }
4450 rcu_read_unlock(); 4496 rcu_read_unlock();
@@ -4455,6 +4501,8 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4455 err = link->doit(skb, nlh, extack); 4501 err = link->doit(skb, nlh, extack);
4456 rtnl_unlock(); 4502 rtnl_unlock();
4457 4503
4504 module_put(owner);
4505
4458 return err; 4506 return err;
4459 4507
4460out_unlock: 4508out_unlock:
@@ -4546,11 +4594,6 @@ static struct pernet_operations rtnetlink_net_ops = {
4546 4594
4547void __init rtnetlink_init(void) 4595void __init rtnetlink_init(void)
4548{ 4596{
4549 int i;
4550
4551 for (i = 0; i < ARRAY_SIZE(rtnl_msg_handlers_ref); i++)
4552 refcount_set(&rtnl_msg_handlers_ref[i], 1);
4553
4554 if (register_pernet_subsys(&rtnetlink_net_ops)) 4597 if (register_pernet_subsys(&rtnetlink_net_ops))
4555 panic("rtnetlink_init: cannot initialize rtnetlink\n"); 4598 panic("rtnetlink_init: cannot initialize rtnetlink\n");
4556 4599