diff options
Diffstat (limited to 'net/netlink/genetlink.c')
-rw-r--r-- | net/netlink/genetlink.c | 225 |
1 files changed, 163 insertions, 62 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index eed4c6a8afc0..d07ecda0a92d 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c | |||
@@ -18,8 +18,6 @@ | |||
18 | #include <net/sock.h> | 18 | #include <net/sock.h> |
19 | #include <net/genetlink.h> | 19 | #include <net/genetlink.h> |
20 | 20 | ||
21 | struct sock *genl_sock = NULL; | ||
22 | |||
23 | static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ | 21 | static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ |
24 | 22 | ||
25 | static inline void genl_lock(void) | 23 | static inline void genl_lock(void) |
@@ -99,25 +97,17 @@ static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) | |||
99 | */ | 97 | */ |
100 | static inline u16 genl_generate_id(void) | 98 | static inline u16 genl_generate_id(void) |
101 | { | 99 | { |
102 | static u16 id_gen_idx; | 100 | static u16 id_gen_idx = GENL_MIN_ID; |
103 | int overflowed = 0; | 101 | int i; |
104 | 102 | ||
105 | do { | 103 | for (i = 0; i <= GENL_MAX_ID - GENL_MIN_ID; i++) { |
106 | if (id_gen_idx == 0) | 104 | if (!genl_family_find_byid(id_gen_idx)) |
105 | return id_gen_idx; | ||
106 | if (++id_gen_idx > GENL_MAX_ID) | ||
107 | id_gen_idx = GENL_MIN_ID; | 107 | id_gen_idx = GENL_MIN_ID; |
108 | } | ||
108 | 109 | ||
109 | if (++id_gen_idx > GENL_MAX_ID) { | 110 | return 0; |
110 | if (!overflowed) { | ||
111 | overflowed = 1; | ||
112 | id_gen_idx = 0; | ||
113 | continue; | ||
114 | } else | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | } while (genl_family_find_byid(id_gen_idx)); | ||
119 | |||
120 | return id_gen_idx; | ||
121 | } | 111 | } |
122 | 112 | ||
123 | static struct genl_multicast_group notify_grp; | 113 | static struct genl_multicast_group notify_grp; |
@@ -138,7 +128,7 @@ int genl_register_mc_group(struct genl_family *family, | |||
138 | { | 128 | { |
139 | int id; | 129 | int id; |
140 | unsigned long *new_groups; | 130 | unsigned long *new_groups; |
141 | int err; | 131 | int err = 0; |
142 | 132 | ||
143 | BUG_ON(grp->name[0] == '\0'); | 133 | BUG_ON(grp->name[0] == '\0'); |
144 | 134 | ||
@@ -175,10 +165,34 @@ int genl_register_mc_group(struct genl_family *family, | |||
175 | mc_groups_longs++; | 165 | mc_groups_longs++; |
176 | } | 166 | } |
177 | 167 | ||
178 | err = netlink_change_ngroups(genl_sock, | 168 | if (family->netnsok) { |
179 | mc_groups_longs * BITS_PER_LONG); | 169 | struct net *net; |
180 | if (err) | 170 | |
181 | goto out; | 171 | netlink_table_grab(); |
172 | rcu_read_lock(); | ||
173 | for_each_net_rcu(net) { | ||
174 | err = __netlink_change_ngroups(net->genl_sock, | ||
175 | mc_groups_longs * BITS_PER_LONG); | ||
176 | if (err) { | ||
177 | /* | ||
178 | * No need to roll back, can only fail if | ||
179 | * memory allocation fails and then the | ||
180 | * number of _possible_ groups has been | ||
181 | * increased on some sockets which is ok. | ||
182 | */ | ||
183 | rcu_read_unlock(); | ||
184 | netlink_table_ungrab(); | ||
185 | goto out; | ||
186 | } | ||
187 | } | ||
188 | rcu_read_unlock(); | ||
189 | netlink_table_ungrab(); | ||
190 | } else { | ||
191 | err = netlink_change_ngroups(init_net.genl_sock, | ||
192 | mc_groups_longs * BITS_PER_LONG); | ||
193 | if (err) | ||
194 | goto out; | ||
195 | } | ||
182 | 196 | ||
183 | grp->id = id; | 197 | grp->id = id; |
184 | set_bit(id, mc_groups); | 198 | set_bit(id, mc_groups); |
@@ -195,8 +209,16 @@ EXPORT_SYMBOL(genl_register_mc_group); | |||
195 | static void __genl_unregister_mc_group(struct genl_family *family, | 209 | static void __genl_unregister_mc_group(struct genl_family *family, |
196 | struct genl_multicast_group *grp) | 210 | struct genl_multicast_group *grp) |
197 | { | 211 | { |
212 | struct net *net; | ||
198 | BUG_ON(grp->family != family); | 213 | BUG_ON(grp->family != family); |
199 | netlink_clear_multicast_users(genl_sock, grp->id); | 214 | |
215 | netlink_table_grab(); | ||
216 | rcu_read_lock(); | ||
217 | for_each_net_rcu(net) | ||
218 | __netlink_clear_multicast_users(net->genl_sock, grp->id); | ||
219 | rcu_read_unlock(); | ||
220 | netlink_table_ungrab(); | ||
221 | |||
200 | clear_bit(grp->id, mc_groups); | 222 | clear_bit(grp->id, mc_groups); |
201 | list_del(&grp->list); | 223 | list_del(&grp->list); |
202 | genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); | 224 | genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); |
@@ -344,11 +366,6 @@ int genl_register_family(struct genl_family *family) | |||
344 | goto errout_locked; | 366 | goto errout_locked; |
345 | } | 367 | } |
346 | 368 | ||
347 | if (genl_family_find_byid(family->id)) { | ||
348 | err = -EEXIST; | ||
349 | goto errout_locked; | ||
350 | } | ||
351 | |||
352 | if (family->id == GENL_ID_GENERATE) { | 369 | if (family->id == GENL_ID_GENERATE) { |
353 | u16 newid = genl_generate_id(); | 370 | u16 newid = genl_generate_id(); |
354 | 371 | ||
@@ -358,6 +375,9 @@ int genl_register_family(struct genl_family *family) | |||
358 | } | 375 | } |
359 | 376 | ||
360 | family->id = newid; | 377 | family->id = newid; |
378 | } else if (genl_family_find_byid(family->id)) { | ||
379 | err = -EEXIST; | ||
380 | goto errout_locked; | ||
361 | } | 381 | } |
362 | 382 | ||
363 | if (family->maxattr) { | 383 | if (family->maxattr) { |
@@ -467,6 +487,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
467 | { | 487 | { |
468 | struct genl_ops *ops; | 488 | struct genl_ops *ops; |
469 | struct genl_family *family; | 489 | struct genl_family *family; |
490 | struct net *net = sock_net(skb->sk); | ||
470 | struct genl_info info; | 491 | struct genl_info info; |
471 | struct genlmsghdr *hdr = nlmsg_data(nlh); | 492 | struct genlmsghdr *hdr = nlmsg_data(nlh); |
472 | int hdrlen, err; | 493 | int hdrlen, err; |
@@ -475,6 +496,10 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
475 | if (family == NULL) | 496 | if (family == NULL) |
476 | return -ENOENT; | 497 | return -ENOENT; |
477 | 498 | ||
499 | /* this family doesn't exist in this netns */ | ||
500 | if (!family->netnsok && !net_eq(net, &init_net)) | ||
501 | return -ENOENT; | ||
502 | |||
478 | hdrlen = GENL_HDRLEN + family->hdrsize; | 503 | hdrlen = GENL_HDRLEN + family->hdrsize; |
479 | if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) | 504 | if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) |
480 | return -EINVAL; | 505 | return -EINVAL; |
@@ -492,7 +517,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
492 | return -EOPNOTSUPP; | 517 | return -EOPNOTSUPP; |
493 | 518 | ||
494 | genl_unlock(); | 519 | genl_unlock(); |
495 | err = netlink_dump_start(genl_sock, skb, nlh, | 520 | err = netlink_dump_start(net->genl_sock, skb, nlh, |
496 | ops->dumpit, ops->done); | 521 | ops->dumpit, ops->done); |
497 | genl_lock(); | 522 | genl_lock(); |
498 | return err; | 523 | return err; |
@@ -514,6 +539,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
514 | info.genlhdr = nlmsg_data(nlh); | 539 | info.genlhdr = nlmsg_data(nlh); |
515 | info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; | 540 | info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; |
516 | info.attrs = family->attrbuf; | 541 | info.attrs = family->attrbuf; |
542 | genl_info_net_set(&info, net); | ||
517 | 543 | ||
518 | return ops->doit(skb, &info); | 544 | return ops->doit(skb, &info); |
519 | } | 545 | } |
@@ -534,6 +560,7 @@ static struct genl_family genl_ctrl = { | |||
534 | .name = "nlctrl", | 560 | .name = "nlctrl", |
535 | .version = 0x2, | 561 | .version = 0x2, |
536 | .maxattr = CTRL_ATTR_MAX, | 562 | .maxattr = CTRL_ATTR_MAX, |
563 | .netnsok = true, | ||
537 | }; | 564 | }; |
538 | 565 | ||
539 | static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, | 566 | static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, |
@@ -650,6 +677,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) | |||
650 | 677 | ||
651 | int i, n = 0; | 678 | int i, n = 0; |
652 | struct genl_family *rt; | 679 | struct genl_family *rt; |
680 | struct net *net = sock_net(skb->sk); | ||
653 | int chains_to_skip = cb->args[0]; | 681 | int chains_to_skip = cb->args[0]; |
654 | int fams_to_skip = cb->args[1]; | 682 | int fams_to_skip = cb->args[1]; |
655 | 683 | ||
@@ -658,6 +686,8 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) | |||
658 | continue; | 686 | continue; |
659 | n = 0; | 687 | n = 0; |
660 | list_for_each_entry(rt, genl_family_chain(i), family_list) { | 688 | list_for_each_entry(rt, genl_family_chain(i), family_list) { |
689 | if (!rt->netnsok && !net_eq(net, &init_net)) | ||
690 | continue; | ||
661 | if (++n < fams_to_skip) | 691 | if (++n < fams_to_skip) |
662 | continue; | 692 | continue; |
663 | if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid, | 693 | if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid, |
@@ -729,6 +759,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) | |||
729 | if (info->attrs[CTRL_ATTR_FAMILY_ID]) { | 759 | if (info->attrs[CTRL_ATTR_FAMILY_ID]) { |
730 | u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); | 760 | u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); |
731 | res = genl_family_find_byid(id); | 761 | res = genl_family_find_byid(id); |
762 | err = -ENOENT; | ||
732 | } | 763 | } |
733 | 764 | ||
734 | if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { | 765 | if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { |
@@ -736,49 +767,61 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) | |||
736 | 767 | ||
737 | name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); | 768 | name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); |
738 | res = genl_family_find_byname(name); | 769 | res = genl_family_find_byname(name); |
770 | err = -ENOENT; | ||
739 | } | 771 | } |
740 | 772 | ||
741 | if (res == NULL) { | 773 | if (res == NULL) |
742 | err = -ENOENT; | 774 | return err; |
743 | goto errout; | 775 | |
776 | if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) { | ||
777 | /* family doesn't exist here */ | ||
778 | return -ENOENT; | ||
744 | } | 779 | } |
745 | 780 | ||
746 | msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, | 781 | msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, |
747 | CTRL_CMD_NEWFAMILY); | 782 | CTRL_CMD_NEWFAMILY); |
748 | if (IS_ERR(msg)) { | 783 | if (IS_ERR(msg)) |
749 | err = PTR_ERR(msg); | 784 | return PTR_ERR(msg); |
750 | goto errout; | ||
751 | } | ||
752 | 785 | ||
753 | err = genlmsg_reply(msg, info); | 786 | return genlmsg_reply(msg, info); |
754 | errout: | ||
755 | return err; | ||
756 | } | 787 | } |
757 | 788 | ||
758 | static int genl_ctrl_event(int event, void *data) | 789 | static int genl_ctrl_event(int event, void *data) |
759 | { | 790 | { |
760 | struct sk_buff *msg; | 791 | struct sk_buff *msg; |
792 | struct genl_family *family; | ||
793 | struct genl_multicast_group *grp; | ||
761 | 794 | ||
762 | if (genl_sock == NULL) | 795 | /* genl is still initialising */ |
796 | if (!init_net.genl_sock) | ||
763 | return 0; | 797 | return 0; |
764 | 798 | ||
765 | switch (event) { | 799 | switch (event) { |
766 | case CTRL_CMD_NEWFAMILY: | 800 | case CTRL_CMD_NEWFAMILY: |
767 | case CTRL_CMD_DELFAMILY: | 801 | case CTRL_CMD_DELFAMILY: |
768 | msg = ctrl_build_family_msg(data, 0, 0, event); | 802 | family = data; |
769 | if (IS_ERR(msg)) | 803 | msg = ctrl_build_family_msg(family, 0, 0, event); |
770 | return PTR_ERR(msg); | ||
771 | |||
772 | genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); | ||
773 | break; | 804 | break; |
774 | case CTRL_CMD_NEWMCAST_GRP: | 805 | case CTRL_CMD_NEWMCAST_GRP: |
775 | case CTRL_CMD_DELMCAST_GRP: | 806 | case CTRL_CMD_DELMCAST_GRP: |
807 | grp = data; | ||
808 | family = grp->family; | ||
776 | msg = ctrl_build_mcgrp_msg(data, 0, 0, event); | 809 | msg = ctrl_build_mcgrp_msg(data, 0, 0, event); |
777 | if (IS_ERR(msg)) | ||
778 | return PTR_ERR(msg); | ||
779 | |||
780 | genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); | ||
781 | break; | 810 | break; |
811 | default: | ||
812 | return -EINVAL; | ||
813 | } | ||
814 | |||
815 | if (IS_ERR(msg)) | ||
816 | return PTR_ERR(msg); | ||
817 | |||
818 | if (!family->netnsok) { | ||
819 | genlmsg_multicast_netns(&init_net, msg, 0, | ||
820 | GENL_ID_CTRL, GFP_KERNEL); | ||
821 | } else { | ||
822 | rcu_read_lock(); | ||
823 | genlmsg_multicast_allns(msg, 0, GENL_ID_CTRL, GFP_ATOMIC); | ||
824 | rcu_read_unlock(); | ||
782 | } | 825 | } |
783 | 826 | ||
784 | return 0; | 827 | return 0; |
@@ -795,6 +838,33 @@ static struct genl_multicast_group notify_grp = { | |||
795 | .name = "notify", | 838 | .name = "notify", |
796 | }; | 839 | }; |
797 | 840 | ||
841 | static int __net_init genl_pernet_init(struct net *net) | ||
842 | { | ||
843 | /* we'll bump the group number right afterwards */ | ||
844 | net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0, | ||
845 | genl_rcv, &genl_mutex, | ||
846 | THIS_MODULE); | ||
847 | |||
848 | if (!net->genl_sock && net_eq(net, &init_net)) | ||
849 | panic("GENL: Cannot initialize generic netlink\n"); | ||
850 | |||
851 | if (!net->genl_sock) | ||
852 | return -ENOMEM; | ||
853 | |||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | static void __net_exit genl_pernet_exit(struct net *net) | ||
858 | { | ||
859 | netlink_kernel_release(net->genl_sock); | ||
860 | net->genl_sock = NULL; | ||
861 | } | ||
862 | |||
863 | static struct pernet_operations genl_pernet_ops = { | ||
864 | .init = genl_pernet_init, | ||
865 | .exit = genl_pernet_exit, | ||
866 | }; | ||
867 | |||
798 | static int __init genl_init(void) | 868 | static int __init genl_init(void) |
799 | { | 869 | { |
800 | int i, err; | 870 | int i, err; |
@@ -804,36 +874,67 @@ static int __init genl_init(void) | |||
804 | 874 | ||
805 | err = genl_register_family(&genl_ctrl); | 875 | err = genl_register_family(&genl_ctrl); |
806 | if (err < 0) | 876 | if (err < 0) |
807 | goto errout; | 877 | goto problem; |
808 | 878 | ||
809 | err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops); | 879 | err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops); |
810 | if (err < 0) | 880 | if (err < 0) |
811 | goto errout_register; | 881 | goto problem; |
812 | 882 | ||
813 | netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); | 883 | netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); |
814 | 884 | ||
815 | /* we'll bump the group number right afterwards */ | 885 | err = register_pernet_subsys(&genl_pernet_ops); |
816 | genl_sock = netlink_kernel_create(&init_net, NETLINK_GENERIC, 0, | 886 | if (err) |
817 | genl_rcv, &genl_mutex, THIS_MODULE); | 887 | goto problem; |
818 | if (genl_sock == NULL) | ||
819 | panic("GENL: Cannot initialize generic netlink\n"); | ||
820 | 888 | ||
821 | err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); | 889 | err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); |
822 | if (err < 0) | 890 | if (err < 0) |
823 | goto errout_register; | 891 | goto problem; |
824 | 892 | ||
825 | return 0; | 893 | return 0; |
826 | 894 | ||
827 | errout_register: | 895 | problem: |
828 | genl_unregister_family(&genl_ctrl); | ||
829 | errout: | ||
830 | panic("GENL: Cannot register controller: %d\n", err); | 896 | panic("GENL: Cannot register controller: %d\n", err); |
831 | } | 897 | } |
832 | 898 | ||
833 | subsys_initcall(genl_init); | 899 | subsys_initcall(genl_init); |
834 | 900 | ||
835 | EXPORT_SYMBOL(genl_sock); | ||
836 | EXPORT_SYMBOL(genl_register_ops); | 901 | EXPORT_SYMBOL(genl_register_ops); |
837 | EXPORT_SYMBOL(genl_unregister_ops); | 902 | EXPORT_SYMBOL(genl_unregister_ops); |
838 | EXPORT_SYMBOL(genl_register_family); | 903 | EXPORT_SYMBOL(genl_register_family); |
839 | EXPORT_SYMBOL(genl_unregister_family); | 904 | EXPORT_SYMBOL(genl_unregister_family); |
905 | |||
906 | static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group, | ||
907 | gfp_t flags) | ||
908 | { | ||
909 | struct sk_buff *tmp; | ||
910 | struct net *net, *prev = NULL; | ||
911 | int err; | ||
912 | |||
913 | for_each_net_rcu(net) { | ||
914 | if (prev) { | ||
915 | tmp = skb_clone(skb, flags); | ||
916 | if (!tmp) { | ||
917 | err = -ENOMEM; | ||
918 | goto error; | ||
919 | } | ||
920 | err = nlmsg_multicast(prev->genl_sock, tmp, | ||
921 | pid, group, flags); | ||
922 | if (err) | ||
923 | goto error; | ||
924 | } | ||
925 | |||
926 | prev = net; | ||
927 | } | ||
928 | |||
929 | return nlmsg_multicast(prev->genl_sock, skb, pid, group, flags); | ||
930 | error: | ||
931 | kfree_skb(skb); | ||
932 | return err; | ||
933 | } | ||
934 | |||
935 | int genlmsg_multicast_allns(struct sk_buff *skb, u32 pid, unsigned int group, | ||
936 | gfp_t flags) | ||
937 | { | ||
938 | return genlmsg_mcast(skb, pid, group, flags); | ||
939 | } | ||
940 | EXPORT_SYMBOL(genlmsg_multicast_allns); | ||