|
@@ -62,7 +62,9 @@
|
|
struct rtnl_link {
|
|
struct rtnl_link {
|
|
rtnl_doit_func doit;
|
|
rtnl_doit_func doit;
|
|
rtnl_dumpit_func dumpit;
|
|
rtnl_dumpit_func dumpit;
|
|
|
|
+ struct module *owner;
|
|
unsigned int flags;
|
|
unsigned int flags;
|
|
|
|
+ struct rcu_head rcu;
|
|
};
|
|
};
|
|
|
|
|
|
static DEFINE_MUTEX(rtnl_mutex);
|
|
static DEFINE_MUTEX(rtnl_mutex);
|
|
@@ -127,8 +129,7 @@ bool lockdep_rtnl_is_held(void)
|
|
EXPORT_SYMBOL(lockdep_rtnl_is_held);
|
|
EXPORT_SYMBOL(lockdep_rtnl_is_held);
|
|
#endif /* #ifdef CONFIG_PROVE_LOCKING */
|
|
#endif /* #ifdef CONFIG_PROVE_LOCKING */
|
|
|
|
|
|
-static struct rtnl_link __rcu *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
|
|
|
|
-static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1];
|
|
|
|
|
|
+static struct rtnl_link __rcu **rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
|
|
|
|
|
|
static inline int rtm_msgindex(int msgtype)
|
|
static inline int rtm_msgindex(int msgtype)
|
|
{
|
|
{
|
|
@@ -144,70 +145,125 @@ static inline int rtm_msgindex(int msgtype)
|
|
return msgindex;
|
|
return msgindex;
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * __rtnl_register - Register a rtnetlink message type
|
|
|
|
- * @protocol: Protocol family or PF_UNSPEC
|
|
|
|
- * @msgtype: rtnetlink message type
|
|
|
|
- * @doit: Function pointer called for each request message
|
|
|
|
- * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
|
|
|
|
- * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
|
|
|
|
- *
|
|
|
|
- * Registers the specified function pointers (at least one of them has
|
|
|
|
- * to be non-NULL) to be called whenever a request message for the
|
|
|
|
- * specified protocol family and message type is received.
|
|
|
|
- *
|
|
|
|
- * The special protocol family PF_UNSPEC may be used to define fallback
|
|
|
|
- * function pointers for the case when no entry for the specific protocol
|
|
|
|
- * family exists.
|
|
|
|
- *
|
|
|
|
- * Returns 0 on success or a negative error code.
|
|
|
|
- */
|
|
|
|
-int __rtnl_register(int protocol, int msgtype,
|
|
|
|
- rtnl_doit_func doit, rtnl_dumpit_func dumpit,
|
|
|
|
- unsigned int flags)
|
|
|
|
|
|
+static struct rtnl_link *rtnl_get_link(int protocol, int msgtype)
|
|
|
|
+{
|
|
|
|
+ struct rtnl_link **tab;
|
|
|
|
+
|
|
|
|
+ if (protocol >= ARRAY_SIZE(rtnl_msg_handlers))
|
|
|
|
+ protocol = PF_UNSPEC;
|
|
|
|
+
|
|
|
|
+ tab = rcu_dereference_rtnl(rtnl_msg_handlers[protocol]);
|
|
|
|
+ if (!tab)
|
|
|
|
+ tab = rcu_dereference_rtnl(rtnl_msg_handlers[PF_UNSPEC]);
|
|
|
|
+
|
|
|
|
+ return tab[msgtype];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int rtnl_register_internal(struct module *owner,
|
|
|
|
+ int protocol, int msgtype,
|
|
|
|
+ rtnl_doit_func doit, rtnl_dumpit_func dumpit,
|
|
|
|
+ unsigned int flags)
|
|
{
|
|
{
|
|
- struct rtnl_link *tab;
|
|
|
|
|
|
+ struct rtnl_link **tab, *link, *old;
|
|
int msgindex;
|
|
int msgindex;
|
|
|
|
+ int ret = -ENOBUFS;
|
|
|
|
|
|
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
|
|
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
|
|
msgindex = rtm_msgindex(msgtype);
|
|
msgindex = rtm_msgindex(msgtype);
|
|
|
|
|
|
- tab = rcu_dereference_raw(rtnl_msg_handlers[protocol]);
|
|
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ tab = rtnl_msg_handlers[protocol];
|
|
if (tab == NULL) {
|
|
if (tab == NULL) {
|
|
- tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL);
|
|
|
|
- if (tab == NULL)
|
|
|
|
- return -ENOBUFS;
|
|
|
|
|
|
+ tab = kcalloc(RTM_NR_MSGTYPES, sizeof(void *), GFP_KERNEL);
|
|
|
|
+ if (!tab)
|
|
|
|
+ goto unlock;
|
|
|
|
|
|
|
|
+ /* ensures we see the 0 stores */
|
|
rcu_assign_pointer(rtnl_msg_handlers[protocol], tab);
|
|
rcu_assign_pointer(rtnl_msg_handlers[protocol], tab);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ old = rtnl_dereference(tab[msgindex]);
|
|
|
|
+ if (old) {
|
|
|
|
+ link = kmemdup(old, sizeof(*old), GFP_KERNEL);
|
|
|
|
+ if (!link)
|
|
|
|
+ goto unlock;
|
|
|
|
+ } else {
|
|
|
|
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
|
|
|
|
+ if (!link)
|
|
|
|
+ goto unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ WARN_ON(link->owner && link->owner != owner);
|
|
|
|
+ link->owner = owner;
|
|
|
|
+
|
|
|
|
+ WARN_ON(doit && link->doit && link->doit != doit);
|
|
if (doit)
|
|
if (doit)
|
|
- tab[msgindex].doit = doit;
|
|
|
|
|
|
+ link->doit = doit;
|
|
|
|
+ WARN_ON(dumpit && link->dumpit && link->dumpit != dumpit);
|
|
if (dumpit)
|
|
if (dumpit)
|
|
- tab[msgindex].dumpit = dumpit;
|
|
|
|
- tab[msgindex].flags |= flags;
|
|
|
|
|
|
+ link->dumpit = dumpit;
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ link->flags |= flags;
|
|
|
|
+
|
|
|
|
+ /* publish protocol:msgtype */
|
|
|
|
+ rcu_assign_pointer(tab[msgindex], link);
|
|
|
|
+ ret = 0;
|
|
|
|
+ if (old)
|
|
|
|
+ kfree_rcu(old, rcu);
|
|
|
|
+unlock:
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL_GPL(__rtnl_register);
|
|
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * rtnl_register_module - Register a rtnetlink message type
|
|
|
|
+ *
|
|
|
|
+ * @owner: module registering the hook (THIS_MODULE)
|
|
|
|
+ * @protocol: Protocol family or PF_UNSPEC
|
|
|
|
+ * @msgtype: rtnetlink message type
|
|
|
|
+ * @doit: Function pointer called for each request message
|
|
|
|
+ * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
|
|
|
|
+ * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
|
|
|
|
+ *
|
|
|
|
+ * Like rtnl_register, but for use by removable modules.
|
|
|
|
+ */
|
|
|
|
+int rtnl_register_module(struct module *owner,
|
|
|
|
+ int protocol, int msgtype,
|
|
|
|
+ rtnl_doit_func doit, rtnl_dumpit_func dumpit,
|
|
|
|
+ unsigned int flags)
|
|
|
|
+{
|
|
|
|
+ return rtnl_register_internal(owner, protocol, msgtype,
|
|
|
|
+ doit, dumpit, flags);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(rtnl_register_module);
|
|
|
|
|
|
/**
|
|
/**
|
|
* rtnl_register - Register a rtnetlink message type
|
|
* rtnl_register - Register a rtnetlink message type
|
|
|
|
+ * @protocol: Protocol family or PF_UNSPEC
|
|
|
|
+ * @msgtype: rtnetlink message type
|
|
|
|
+ * @doit: Function pointer called for each request message
|
|
|
|
+ * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
|
|
|
|
+ * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
|
|
|
|
+ *
|
|
|
|
+ * Registers the specified function pointers (at least one of them has
|
|
|
|
+ * to be non-NULL) to be called whenever a request message for the
|
|
|
|
+ * specified protocol family and message type is received.
|
|
*
|
|
*
|
|
- * Identical to __rtnl_register() but panics on failure. This is useful
|
|
|
|
- * as failure of this function is very unlikely, it can only happen due
|
|
|
|
- * to lack of memory when allocating the chain to store all message
|
|
|
|
- * handlers for a protocol. Meant for use in init functions where lack
|
|
|
|
- * of memory implies no sense in continuing.
|
|
|
|
|
|
+ * The special protocol family PF_UNSPEC may be used to define fallback
|
|
|
|
+ * function pointers for the case when no entry for the specific protocol
|
|
|
|
+ * family exists.
|
|
*/
|
|
*/
|
|
void rtnl_register(int protocol, int msgtype,
|
|
void rtnl_register(int protocol, int msgtype,
|
|
rtnl_doit_func doit, rtnl_dumpit_func dumpit,
|
|
rtnl_doit_func doit, rtnl_dumpit_func dumpit,
|
|
unsigned int flags)
|
|
unsigned int flags)
|
|
{
|
|
{
|
|
- if (__rtnl_register(protocol, msgtype, doit, dumpit, flags) < 0)
|
|
|
|
- panic("Unable to register rtnetlink message handler, "
|
|
|
|
- "protocol = %d, message type = %d\n",
|
|
|
|
- protocol, msgtype);
|
|
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ err = rtnl_register_internal(NULL, protocol, msgtype, doit, dumpit,
|
|
|
|
+ flags);
|
|
|
|
+ if (err)
|
|
|
|
+ pr_err("Unable to register rtnetlink message handler, "
|
|
|
|
+ "protocol = %d, message type = %d\n", protocol, msgtype);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(rtnl_register);
|
|
EXPORT_SYMBOL_GPL(rtnl_register);
|
|
|
|
|
|
@@ -220,24 +276,25 @@ EXPORT_SYMBOL_GPL(rtnl_register);
|
|
*/
|
|
*/
|
|
int rtnl_unregister(int protocol, int msgtype)
|
|
int rtnl_unregister(int protocol, int msgtype)
|
|
{
|
|
{
|
|
- struct rtnl_link *handlers;
|
|
|
|
|
|
+ struct rtnl_link **tab, *link;
|
|
int msgindex;
|
|
int msgindex;
|
|
|
|
|
|
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
|
|
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
|
|
msgindex = rtm_msgindex(msgtype);
|
|
msgindex = rtm_msgindex(msgtype);
|
|
|
|
|
|
rtnl_lock();
|
|
rtnl_lock();
|
|
- handlers = rtnl_dereference(rtnl_msg_handlers[protocol]);
|
|
|
|
- if (!handlers) {
|
|
|
|
|
|
+ tab = rtnl_dereference(rtnl_msg_handlers[protocol]);
|
|
|
|
+ if (!tab) {
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
return -ENOENT;
|
|
return -ENOENT;
|
|
}
|
|
}
|
|
|
|
|
|
- handlers[msgindex].doit = NULL;
|
|
|
|
- handlers[msgindex].dumpit = NULL;
|
|
|
|
- handlers[msgindex].flags = 0;
|
|
|
|
|
|
+ link = tab[msgindex];
|
|
|
|
+ rcu_assign_pointer(tab[msgindex], NULL);
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
+ kfree_rcu(link, rcu);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(rtnl_unregister);
|
|
EXPORT_SYMBOL_GPL(rtnl_unregister);
|
|
@@ -251,20 +308,27 @@ EXPORT_SYMBOL_GPL(rtnl_unregister);
|
|
*/
|
|
*/
|
|
void rtnl_unregister_all(int protocol)
|
|
void rtnl_unregister_all(int protocol)
|
|
{
|
|
{
|
|
- struct rtnl_link *handlers;
|
|
|
|
|
|
+ struct rtnl_link **tab, *link;
|
|
|
|
+ int msgindex;
|
|
|
|
|
|
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
|
|
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
|
|
|
|
|
|
rtnl_lock();
|
|
rtnl_lock();
|
|
- handlers = rtnl_dereference(rtnl_msg_handlers[protocol]);
|
|
|
|
|
|
+ tab = rtnl_msg_handlers[protocol];
|
|
RCU_INIT_POINTER(rtnl_msg_handlers[protocol], NULL);
|
|
RCU_INIT_POINTER(rtnl_msg_handlers[protocol], NULL);
|
|
|
|
+ for (msgindex = 0; msgindex < RTM_NR_MSGTYPES; msgindex++) {
|
|
|
|
+ link = tab[msgindex];
|
|
|
|
+ if (!link)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ rcu_assign_pointer(tab[msgindex], NULL);
|
|
|
|
+ kfree_rcu(link, rcu);
|
|
|
|
+ }
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
|
|
|
|
synchronize_net();
|
|
synchronize_net();
|
|
|
|
|
|
- while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1)
|
|
|
|
- schedule();
|
|
|
|
- kfree(handlers);
|
|
|
|
|
|
+ kfree(tab);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(rtnl_unregister_all);
|
|
EXPORT_SYMBOL_GPL(rtnl_unregister_all);
|
|
|
|
|
|
@@ -2973,18 +3037,26 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
|
|
s_idx = 1;
|
|
s_idx = 1;
|
|
|
|
|
|
for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) {
|
|
for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) {
|
|
|
|
+ struct rtnl_link **tab;
|
|
int type = cb->nlh->nlmsg_type-RTM_BASE;
|
|
int type = cb->nlh->nlmsg_type-RTM_BASE;
|
|
- struct rtnl_link *handlers;
|
|
|
|
|
|
+ struct rtnl_link *link;
|
|
rtnl_dumpit_func dumpit;
|
|
rtnl_dumpit_func dumpit;
|
|
|
|
|
|
if (idx < s_idx || idx == PF_PACKET)
|
|
if (idx < s_idx || idx == PF_PACKET)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- handlers = rtnl_dereference(rtnl_msg_handlers[idx]);
|
|
|
|
- if (!handlers)
|
|
|
|
|
|
+ if (type < 0 || type >= RTM_NR_MSGTYPES)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- dumpit = READ_ONCE(handlers[type].dumpit);
|
|
|
|
|
|
+ tab = rcu_dereference_rtnl(rtnl_msg_handlers[idx]);
|
|
|
|
+ if (!tab)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ link = tab[type];
|
|
|
|
+ if (!link)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ dumpit = link->dumpit;
|
|
if (!dumpit)
|
|
if (!dumpit)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
@@ -4314,7 +4386,8 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
struct netlink_ext_ack *extack)
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
{
|
|
struct net *net = sock_net(skb->sk);
|
|
struct net *net = sock_net(skb->sk);
|
|
- struct rtnl_link *handlers;
|
|
|
|
|
|
+ struct rtnl_link *link;
|
|
|
|
+ struct module *owner;
|
|
int err = -EOPNOTSUPP;
|
|
int err = -EOPNOTSUPP;
|
|
rtnl_doit_func doit;
|
|
rtnl_doit_func doit;
|
|
unsigned int flags;
|
|
unsigned int flags;
|
|
@@ -4338,79 +4411,85 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
|
|
if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
|
|
return -EPERM;
|
|
return -EPERM;
|
|
|
|
|
|
- if (family >= ARRAY_SIZE(rtnl_msg_handlers))
|
|
|
|
- family = PF_UNSPEC;
|
|
|
|
-
|
|
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
- handlers = rcu_dereference(rtnl_msg_handlers[family]);
|
|
|
|
- if (!handlers) {
|
|
|
|
- family = PF_UNSPEC;
|
|
|
|
- handlers = rcu_dereference(rtnl_msg_handlers[family]);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
|
|
if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
|
|
struct sock *rtnl;
|
|
struct sock *rtnl;
|
|
rtnl_dumpit_func dumpit;
|
|
rtnl_dumpit_func dumpit;
|
|
u16 min_dump_alloc = 0;
|
|
u16 min_dump_alloc = 0;
|
|
|
|
|
|
- dumpit = READ_ONCE(handlers[type].dumpit);
|
|
|
|
- if (!dumpit) {
|
|
|
|
|
|
+ link = rtnl_get_link(family, type);
|
|
|
|
+ if (!link || !link->dumpit) {
|
|
family = PF_UNSPEC;
|
|
family = PF_UNSPEC;
|
|
- handlers = rcu_dereference(rtnl_msg_handlers[PF_UNSPEC]);
|
|
|
|
- if (!handlers)
|
|
|
|
- goto err_unlock;
|
|
|
|
-
|
|
|
|
- dumpit = READ_ONCE(handlers[type].dumpit);
|
|
|
|
- if (!dumpit)
|
|
|
|
|
|
+ link = rtnl_get_link(family, type);
|
|
|
|
+ if (!link || !link->dumpit)
|
|
goto err_unlock;
|
|
goto err_unlock;
|
|
}
|
|
}
|
|
-
|
|
|
|
- refcount_inc(&rtnl_msg_handlers_ref[family]);
|
|
|
|
|
|
+ owner = link->owner;
|
|
|
|
+ dumpit = link->dumpit;
|
|
|
|
|
|
if (type == RTM_GETLINK - RTM_BASE)
|
|
if (type == RTM_GETLINK - RTM_BASE)
|
|
min_dump_alloc = rtnl_calcit(skb, nlh);
|
|
min_dump_alloc = rtnl_calcit(skb, nlh);
|
|
|
|
|
|
|
|
+ err = 0;
|
|
|
|
+ /* need to do this before rcu_read_unlock() */
|
|
|
|
+ if (!try_module_get(owner))
|
|
|
|
+ err = -EPROTONOSUPPORT;
|
|
|
|
+
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
|
|
|
|
rtnl = net->rtnl;
|
|
rtnl = net->rtnl;
|
|
- {
|
|
|
|
|
|
+ if (err == 0) {
|
|
struct netlink_dump_control c = {
|
|
struct netlink_dump_control c = {
|
|
.dump = dumpit,
|
|
.dump = dumpit,
|
|
.min_dump_alloc = min_dump_alloc,
|
|
.min_dump_alloc = min_dump_alloc,
|
|
|
|
+ .module = owner,
|
|
};
|
|
};
|
|
err = netlink_dump_start(rtnl, skb, nlh, &c);
|
|
err = netlink_dump_start(rtnl, skb, nlh, &c);
|
|
|
|
+ /* netlink_dump_start() will keep a reference on
|
|
|
|
+ * module if dump is still in progress.
|
|
|
|
+ */
|
|
|
|
+ module_put(owner);
|
|
}
|
|
}
|
|
- refcount_dec(&rtnl_msg_handlers_ref[family]);
|
|
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
- doit = READ_ONCE(handlers[type].doit);
|
|
|
|
- if (!doit) {
|
|
|
|
|
|
+ link = rtnl_get_link(family, type);
|
|
|
|
+ if (!link || !link->doit) {
|
|
family = PF_UNSPEC;
|
|
family = PF_UNSPEC;
|
|
- handlers = rcu_dereference(rtnl_msg_handlers[family]);
|
|
|
|
|
|
+ link = rtnl_get_link(PF_UNSPEC, type);
|
|
|
|
+ if (!link || !link->doit)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ owner = link->owner;
|
|
|
|
+ if (!try_module_get(owner)) {
|
|
|
|
+ err = -EPROTONOSUPPORT;
|
|
|
|
+ goto out_unlock;
|
|
}
|
|
}
|
|
|
|
|
|
- flags = READ_ONCE(handlers[type].flags);
|
|
|
|
|
|
+ flags = link->flags;
|
|
if (flags & RTNL_FLAG_DOIT_UNLOCKED) {
|
|
if (flags & RTNL_FLAG_DOIT_UNLOCKED) {
|
|
- refcount_inc(&rtnl_msg_handlers_ref[family]);
|
|
|
|
- doit = READ_ONCE(handlers[type].doit);
|
|
|
|
|
|
+ doit = link->doit;
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
if (doit)
|
|
if (doit)
|
|
err = doit(skb, nlh, extack);
|
|
err = doit(skb, nlh, extack);
|
|
- refcount_dec(&rtnl_msg_handlers_ref[family]);
|
|
|
|
|
|
+ module_put(owner);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
-
|
|
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
|
|
|
|
rtnl_lock();
|
|
rtnl_lock();
|
|
- handlers = rtnl_dereference(rtnl_msg_handlers[family]);
|
|
|
|
- if (handlers) {
|
|
|
|
- doit = READ_ONCE(handlers[type].doit);
|
|
|
|
- if (doit)
|
|
|
|
- err = doit(skb, nlh, extack);
|
|
|
|
- }
|
|
|
|
|
|
+ link = rtnl_get_link(family, type);
|
|
|
|
+ if (link && link->doit)
|
|
|
|
+ err = link->doit(skb, nlh, extack);
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
|
|
+
|
|
|
|
+ module_put(owner);
|
|
|
|
+
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+out_unlock:
|
|
|
|
+ rcu_read_unlock();
|
|
return err;
|
|
return err;
|
|
|
|
|
|
err_unlock:
|
|
err_unlock:
|
|
@@ -4498,11 +4577,6 @@ static struct pernet_operations rtnetlink_net_ops = {
|
|
|
|
|
|
void __init rtnetlink_init(void)
|
|
void __init rtnetlink_init(void)
|
|
{
|
|
{
|
|
- int i;
|
|
|
|
-
|
|
|
|
- for (i = 0; i < ARRAY_SIZE(rtnl_msg_handlers_ref); i++)
|
|
|
|
- refcount_set(&rtnl_msg_handlers_ref[i], 1);
|
|
|
|
-
|
|
|
|
if (register_pernet_subsys(&rtnetlink_net_ops))
|
|
if (register_pernet_subsys(&rtnetlink_net_ops))
|
|
panic("rtnetlink_init: cannot initialize rtnetlink\n");
|
|
panic("rtnetlink_init: cannot initialize rtnetlink\n");
|
|
|
|
|