|
@@ -18,8 +18,6 @@
|
|
#include <net/sock.h>
|
|
#include <net/sock.h>
|
|
#include <net/genetlink.h>
|
|
#include <net/genetlink.h>
|
|
|
|
|
|
-struct sock *genl_sock = NULL;
|
|
|
|
-
|
|
|
|
static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
|
|
static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
|
|
|
|
|
|
static inline void genl_lock(void)
|
|
static inline void genl_lock(void)
|
|
@@ -175,10 +173,31 @@ int genl_register_mc_group(struct genl_family *family,
|
|
mc_groups_longs++;
|
|
mc_groups_longs++;
|
|
}
|
|
}
|
|
|
|
|
|
- err = netlink_change_ngroups(genl_sock,
|
|
|
|
- mc_groups_longs * BITS_PER_LONG);
|
|
|
|
- if (err)
|
|
|
|
- goto out;
|
|
|
|
|
|
+ if (family->netnsok) {
|
|
|
|
+ struct net *net;
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ for_each_net_rcu(net) {
|
|
|
|
+ err = netlink_change_ngroups(net->genl_sock,
|
|
|
|
+ mc_groups_longs * BITS_PER_LONG);
|
|
|
|
+ if (err) {
|
|
|
|
+ /*
|
|
|
|
+ * No need to roll back, can only fail if
|
|
|
|
+ * memory allocation fails and then the
|
|
|
|
+ * number of _possible_ groups has been
|
|
|
|
+ * increased on some sockets which is ok.
|
|
|
|
+ */
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ } else {
|
|
|
|
+ err = netlink_change_ngroups(init_net.genl_sock,
|
|
|
|
+ mc_groups_longs * BITS_PER_LONG);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
|
|
grp->id = id;
|
|
grp->id = id;
|
|
set_bit(id, mc_groups);
|
|
set_bit(id, mc_groups);
|
|
@@ -195,8 +214,14 @@ EXPORT_SYMBOL(genl_register_mc_group);
|
|
static void __genl_unregister_mc_group(struct genl_family *family,
|
|
static void __genl_unregister_mc_group(struct genl_family *family,
|
|
struct genl_multicast_group *grp)
|
|
struct genl_multicast_group *grp)
|
|
{
|
|
{
|
|
|
|
+ struct net *net;
|
|
BUG_ON(grp->family != family);
|
|
BUG_ON(grp->family != family);
|
|
- netlink_clear_multicast_users(genl_sock, grp->id);
|
|
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ for_each_net_rcu(net)
|
|
|
|
+ netlink_clear_multicast_users(net->genl_sock, grp->id);
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+
|
|
clear_bit(grp->id, mc_groups);
|
|
clear_bit(grp->id, mc_groups);
|
|
list_del(&grp->list);
|
|
list_del(&grp->list);
|
|
genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp);
|
|
genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp);
|
|
@@ -467,6 +492,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
{
|
|
{
|
|
struct genl_ops *ops;
|
|
struct genl_ops *ops;
|
|
struct genl_family *family;
|
|
struct genl_family *family;
|
|
|
|
+ struct net *net = sock_net(skb->sk);
|
|
struct genl_info info;
|
|
struct genl_info info;
|
|
struct genlmsghdr *hdr = nlmsg_data(nlh);
|
|
struct genlmsghdr *hdr = nlmsg_data(nlh);
|
|
int hdrlen, err;
|
|
int hdrlen, err;
|
|
@@ -475,6 +501,10 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
if (family == NULL)
|
|
if (family == NULL)
|
|
return -ENOENT;
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
+ /* this family doesn't exist in this netns */
|
|
|
|
+ if (!family->netnsok && !net_eq(net, &init_net))
|
|
|
|
+ return -ENOENT;
|
|
|
|
+
|
|
hdrlen = GENL_HDRLEN + family->hdrsize;
|
|
hdrlen = GENL_HDRLEN + family->hdrsize;
|
|
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
|
|
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -492,7 +522,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
genl_unlock();
|
|
genl_unlock();
|
|
- err = netlink_dump_start(genl_sock, skb, nlh,
|
|
|
|
|
|
+ err = netlink_dump_start(net->genl_sock, skb, nlh,
|
|
ops->dumpit, ops->done);
|
|
ops->dumpit, ops->done);
|
|
genl_lock();
|
|
genl_lock();
|
|
return err;
|
|
return err;
|
|
@@ -514,6 +544,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
info.genlhdr = nlmsg_data(nlh);
|
|
info.genlhdr = nlmsg_data(nlh);
|
|
info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
|
|
info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
|
|
info.attrs = family->attrbuf;
|
|
info.attrs = family->attrbuf;
|
|
|
|
+ genl_info_net_set(&info, net);
|
|
|
|
|
|
return ops->doit(skb, &info);
|
|
return ops->doit(skb, &info);
|
|
}
|
|
}
|
|
@@ -534,6 +565,7 @@ static struct genl_family genl_ctrl = {
|
|
.name = "nlctrl",
|
|
.name = "nlctrl",
|
|
.version = 0x2,
|
|
.version = 0x2,
|
|
.maxattr = CTRL_ATTR_MAX,
|
|
.maxattr = CTRL_ATTR_MAX,
|
|
|
|
+ .netnsok = true,
|
|
};
|
|
};
|
|
|
|
|
|
static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq,
|
|
static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq,
|
|
@@ -650,6 +682,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
|
|
int i, n = 0;
|
|
int i, n = 0;
|
|
struct genl_family *rt;
|
|
struct genl_family *rt;
|
|
|
|
+ struct net *net = sock_net(skb->sk);
|
|
int chains_to_skip = cb->args[0];
|
|
int chains_to_skip = cb->args[0];
|
|
int fams_to_skip = cb->args[1];
|
|
int fams_to_skip = cb->args[1];
|
|
|
|
|
|
@@ -658,6 +691,8 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
|
|
continue;
|
|
continue;
|
|
n = 0;
|
|
n = 0;
|
|
list_for_each_entry(rt, genl_family_chain(i), family_list) {
|
|
list_for_each_entry(rt, genl_family_chain(i), family_list) {
|
|
|
|
+ if (!rt->netnsok && !net_eq(net, &init_net))
|
|
|
|
+ continue;
|
|
if (++n < fams_to_skip)
|
|
if (++n < fams_to_skip)
|
|
continue;
|
|
continue;
|
|
if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid,
|
|
if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid,
|
|
@@ -729,6 +764,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
|
|
if (info->attrs[CTRL_ATTR_FAMILY_ID]) {
|
|
if (info->attrs[CTRL_ATTR_FAMILY_ID]) {
|
|
u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]);
|
|
u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]);
|
|
res = genl_family_find_byid(id);
|
|
res = genl_family_find_byid(id);
|
|
|
|
+ err = -ENOENT;
|
|
}
|
|
}
|
|
|
|
|
|
if (info->attrs[CTRL_ATTR_FAMILY_NAME]) {
|
|
if (info->attrs[CTRL_ATTR_FAMILY_NAME]) {
|
|
@@ -736,49 +772,61 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
|
|
name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]);
|
|
name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]);
|
|
res = genl_family_find_byname(name);
|
|
res = genl_family_find_byname(name);
|
|
|
|
+ err = -ENOENT;
|
|
}
|
|
}
|
|
|
|
|
|
- if (res == NULL) {
|
|
|
|
- err = -ENOENT;
|
|
|
|
- goto errout;
|
|
|
|
|
|
+ if (res == NULL)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) {
|
|
|
|
+ /* family doesn't exist here */
|
|
|
|
+ return -ENOENT;
|
|
}
|
|
}
|
|
|
|
|
|
msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq,
|
|
msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq,
|
|
CTRL_CMD_NEWFAMILY);
|
|
CTRL_CMD_NEWFAMILY);
|
|
- if (IS_ERR(msg)) {
|
|
|
|
- err = PTR_ERR(msg);
|
|
|
|
- goto errout;
|
|
|
|
- }
|
|
|
|
|
|
+ if (IS_ERR(msg))
|
|
|
|
+ return PTR_ERR(msg);
|
|
|
|
|
|
- err = genlmsg_reply(msg, info);
|
|
|
|
-errout:
|
|
|
|
- return err;
|
|
|
|
|
|
+ return genlmsg_reply(msg, info);
|
|
}
|
|
}
|
|
|
|
|
|
static int genl_ctrl_event(int event, void *data)
|
|
static int genl_ctrl_event(int event, void *data)
|
|
{
|
|
{
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
|
|
+ struct genl_family *family;
|
|
|
|
+ struct genl_multicast_group *grp;
|
|
|
|
|
|
- if (genl_sock == NULL)
|
|
|
|
|
|
+ /* genl is still initialising */
|
|
|
|
+ if (!init_net.genl_sock)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
switch (event) {
|
|
switch (event) {
|
|
case CTRL_CMD_NEWFAMILY:
|
|
case CTRL_CMD_NEWFAMILY:
|
|
case CTRL_CMD_DELFAMILY:
|
|
case CTRL_CMD_DELFAMILY:
|
|
- msg = ctrl_build_family_msg(data, 0, 0, event);
|
|
|
|
- if (IS_ERR(msg))
|
|
|
|
- return PTR_ERR(msg);
|
|
|
|
-
|
|
|
|
- genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL);
|
|
|
|
|
|
+ family = data;
|
|
|
|
+ msg = ctrl_build_family_msg(family, 0, 0, event);
|
|
break;
|
|
break;
|
|
case CTRL_CMD_NEWMCAST_GRP:
|
|
case CTRL_CMD_NEWMCAST_GRP:
|
|
case CTRL_CMD_DELMCAST_GRP:
|
|
case CTRL_CMD_DELMCAST_GRP:
|
|
|
|
+ grp = data;
|
|
|
|
+ family = grp->family;
|
|
msg = ctrl_build_mcgrp_msg(data, 0, 0, event);
|
|
msg = ctrl_build_mcgrp_msg(data, 0, 0, event);
|
|
- if (IS_ERR(msg))
|
|
|
|
- return PTR_ERR(msg);
|
|
|
|
-
|
|
|
|
- genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL);
|
|
|
|
break;
|
|
break;
|
|
|
|
+ default:
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (IS_ERR(msg))
|
|
|
|
+ return PTR_ERR(msg);
|
|
|
|
+
|
|
|
|
+ if (!family->netnsok) {
|
|
|
|
+ genlmsg_multicast_netns(&init_net, msg, 0,
|
|
|
|
+ GENL_ID_CTRL, GFP_KERNEL);
|
|
|
|
+ } else {
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ genlmsg_multicast_allns(msg, 0, GENL_ID_CTRL, GFP_ATOMIC);
|
|
|
|
+ rcu_read_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -795,6 +843,33 @@ static struct genl_multicast_group notify_grp = {
|
|
.name = "notify",
|
|
.name = "notify",
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static int __net_init genl_pernet_init(struct net *net)
|
|
|
|
+{
|
|
|
|
+ /* we'll bump the group number right afterwards */
|
|
|
|
+ net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0,
|
|
|
|
+ genl_rcv, &genl_mutex,
|
|
|
|
+ THIS_MODULE);
|
|
|
|
+
|
|
|
|
+ if (!net->genl_sock && net_eq(net, &init_net))
|
|
|
|
+ panic("GENL: Cannot initialize generic netlink\n");
|
|
|
|
+
|
|
|
|
+ if (!net->genl_sock)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void __net_exit genl_pernet_exit(struct net *net)
|
|
|
|
+{
|
|
|
|
+ netlink_kernel_release(net->genl_sock);
|
|
|
|
+ net->genl_sock = NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct pernet_operations genl_pernet_ops = {
|
|
|
|
+ .init = genl_pernet_init,
|
|
|
|
+ .exit = genl_pernet_exit,
|
|
|
|
+};
|
|
|
|
+
|
|
static int __init genl_init(void)
|
|
static int __init genl_init(void)
|
|
{
|
|
{
|
|
int i, err;
|
|
int i, err;
|
|
@@ -804,36 +879,67 @@ static int __init genl_init(void)
|
|
|
|
|
|
err = genl_register_family(&genl_ctrl);
|
|
err = genl_register_family(&genl_ctrl);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
- goto errout;
|
|
|
|
|
|
+ goto problem;
|
|
|
|
|
|
err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops);
|
|
err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
- goto errout_register;
|
|
|
|
|
|
+ goto problem;
|
|
|
|
|
|
netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV);
|
|
netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV);
|
|
|
|
|
|
- /* we'll bump the group number right afterwards */
|
|
|
|
- genl_sock = netlink_kernel_create(&init_net, NETLINK_GENERIC, 0,
|
|
|
|
- genl_rcv, &genl_mutex, THIS_MODULE);
|
|
|
|
- if (genl_sock == NULL)
|
|
|
|
- panic("GENL: Cannot initialize generic netlink\n");
|
|
|
|
|
|
+ err = register_pernet_subsys(&genl_pernet_ops);
|
|
|
|
+ if (err)
|
|
|
|
+ goto problem;
|
|
|
|
|
|
err = genl_register_mc_group(&genl_ctrl, ¬ify_grp);
|
|
err = genl_register_mc_group(&genl_ctrl, ¬ify_grp);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
- goto errout_register;
|
|
|
|
|
|
+ goto problem;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
-errout_register:
|
|
|
|
- genl_unregister_family(&genl_ctrl);
|
|
|
|
-errout:
|
|
|
|
|
|
+problem:
|
|
panic("GENL: Cannot register controller: %d\n", err);
|
|
panic("GENL: Cannot register controller: %d\n", err);
|
|
}
|
|
}
|
|
|
|
|
|
subsys_initcall(genl_init);
|
|
subsys_initcall(genl_init);
|
|
|
|
|
|
-EXPORT_SYMBOL(genl_sock);
|
|
|
|
EXPORT_SYMBOL(genl_register_ops);
|
|
EXPORT_SYMBOL(genl_register_ops);
|
|
EXPORT_SYMBOL(genl_unregister_ops);
|
|
EXPORT_SYMBOL(genl_unregister_ops);
|
|
EXPORT_SYMBOL(genl_register_family);
|
|
EXPORT_SYMBOL(genl_register_family);
|
|
EXPORT_SYMBOL(genl_unregister_family);
|
|
EXPORT_SYMBOL(genl_unregister_family);
|
|
|
|
+
|
|
|
|
+static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group,
|
|
|
|
+ gfp_t flags)
|
|
|
|
+{
|
|
|
|
+ struct sk_buff *tmp;
|
|
|
|
+ struct net *net, *prev = NULL;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ for_each_net_rcu(net) {
|
|
|
|
+ if (prev) {
|
|
|
|
+ tmp = skb_clone(skb, flags);
|
|
|
|
+ if (!tmp) {
|
|
|
|
+ err = -ENOMEM;
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+ err = nlmsg_multicast(prev->genl_sock, tmp,
|
|
|
|
+ pid, group, flags);
|
|
|
|
+ if (err)
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ prev = net;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nlmsg_multicast(prev->genl_sock, skb, pid, group, flags);
|
|
|
|
+ error:
|
|
|
|
+ kfree_skb(skb);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int genlmsg_multicast_allns(struct sk_buff *skb, u32 pid, unsigned int group,
|
|
|
|
+ gfp_t flags)
|
|
|
|
+{
|
|
|
|
+ return genlmsg_mcast(skb, pid, group, flags);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(genlmsg_multicast_allns);
|