diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-11-19 09:19:39 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-11-19 16:39:06 -0500 |
commit | 2a94fe48f32ccf7321450a2cc07f2b724a444e5b (patch) | |
tree | e5a066d8f83d8822d448421019a4503f361295f9 /net/netlink | |
parent | 68eb55031da7c967d954e5f9415cd05f4abdb692 (diff) |
genetlink: make multicast groups const, prevent abuse
Register generic netlink multicast groups as an array with
the family and give them contiguous group IDs. Then instead
of passing the global group ID to the various functions that
send messages, pass the ID relative to the family - for most
families that's just 0 because the only have one group.
This avoids the list_head and ID in each group, adding a new
field for the mcast group ID offset to the family.
At the same time, this allows us to prevent abusing groups
again like the quota and dropmon code did, since we can now
check that a family only uses a group it owns.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/genetlink.c | 278 |
1 files changed, 166 insertions, 112 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 36e3a86cacf6..7dbc4f732c75 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c | |||
@@ -69,16 +69,20 @@ static struct list_head family_ht[GENL_FAM_TAB_SIZE]; | |||
69 | * abuses the API and thinks it can statically use group 1. | 69 | * abuses the API and thinks it can statically use group 1. |
70 | * That group will typically conflict with other groups that | 70 | * That group will typically conflict with other groups that |
71 | * any proper users use. | 71 | * any proper users use. |
72 | * Bit 16 is marked as used since it's used for generic netlink | ||
73 | * and the code no longer marks pre-reserved IDs as used. | ||
72 | * Bit 17 is marked as already used since the VFS quota code | 74 | * Bit 17 is marked as already used since the VFS quota code |
73 | * also abused this API and relied on family == group ID, we | 75 | * also abused this API and relied on family == group ID, we |
74 | * cater to that by giving it a static family and group ID. | 76 | * cater to that by giving it a static family and group ID. |
75 | */ | 77 | */ |
76 | static unsigned long mc_group_start = 0x3 | BIT(GENL_ID_VFS_DQUOT); | 78 | static unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) | |
79 | BIT(GENL_ID_VFS_DQUOT); | ||
77 | static unsigned long *mc_groups = &mc_group_start; | 80 | static unsigned long *mc_groups = &mc_group_start; |
78 | static unsigned long mc_groups_longs = 1; | 81 | static unsigned long mc_groups_longs = 1; |
79 | 82 | ||
80 | static int genl_ctrl_event(int event, struct genl_family *family, | 83 | static int genl_ctrl_event(int event, struct genl_family *family, |
81 | struct genl_multicast_group *grp); | 84 | const struct genl_multicast_group *grp, |
85 | int grp_id); | ||
82 | 86 | ||
83 | static inline unsigned int genl_family_hash(unsigned int id) | 87 | static inline unsigned int genl_family_hash(unsigned int id) |
84 | { | 88 | { |
@@ -144,66 +148,110 @@ static u16 genl_generate_id(void) | |||
144 | return 0; | 148 | return 0; |
145 | } | 149 | } |
146 | 150 | ||
147 | static struct genl_multicast_group notify_grp; | 151 | static int genl_allocate_reserve_groups(int n_groups, int *first_id) |
148 | |||
149 | /** | ||
150 | * genl_register_mc_group - register a multicast group | ||
151 | * | ||
152 | * Registers the specified multicast group and notifies userspace | ||
153 | * about the new group. | ||
154 | * | ||
155 | * Returns 0 on success or a negative error code. | ||
156 | * | ||
157 | * @family: The generic netlink family the group shall be registered for. | ||
158 | * @grp: The group to register, must have a name. | ||
159 | */ | ||
160 | int genl_register_mc_group(struct genl_family *family, | ||
161 | struct genl_multicast_group *grp) | ||
162 | { | 152 | { |
163 | int id; | ||
164 | unsigned long *new_groups; | 153 | unsigned long *new_groups; |
165 | int err = 0; | 154 | int start = 0; |
155 | int i; | ||
156 | int id; | ||
157 | bool fits; | ||
158 | |||
159 | do { | ||
160 | if (start == 0) | ||
161 | id = find_first_zero_bit(mc_groups, | ||
162 | mc_groups_longs * | ||
163 | BITS_PER_LONG); | ||
164 | else | ||
165 | id = find_next_zero_bit(mc_groups, | ||
166 | mc_groups_longs * BITS_PER_LONG, | ||
167 | start); | ||
168 | |||
169 | fits = true; | ||
170 | for (i = id; | ||
171 | i < min_t(int, id + n_groups, | ||
172 | mc_groups_longs * BITS_PER_LONG); | ||
173 | i++) { | ||
174 | if (test_bit(i, mc_groups)) { | ||
175 | start = i; | ||
176 | fits = false; | ||
177 | break; | ||
178 | } | ||
179 | } | ||
166 | 180 | ||
167 | BUG_ON(grp->name[0] == '\0'); | 181 | if (id >= mc_groups_longs * BITS_PER_LONG) { |
168 | BUG_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL); | 182 | unsigned long new_longs = mc_groups_longs + |
183 | BITS_TO_LONGS(n_groups); | ||
184 | size_t nlen = new_longs * sizeof(unsigned long); | ||
185 | |||
186 | if (mc_groups == &mc_group_start) { | ||
187 | new_groups = kzalloc(nlen, GFP_KERNEL); | ||
188 | if (!new_groups) | ||
189 | return -ENOMEM; | ||
190 | mc_groups = new_groups; | ||
191 | *mc_groups = mc_group_start; | ||
192 | } else { | ||
193 | new_groups = krealloc(mc_groups, nlen, | ||
194 | GFP_KERNEL); | ||
195 | if (!new_groups) | ||
196 | return -ENOMEM; | ||
197 | mc_groups = new_groups; | ||
198 | for (i = 0; i < BITS_TO_LONGS(n_groups); i++) | ||
199 | mc_groups[mc_groups_longs + i] = 0; | ||
200 | } | ||
201 | mc_groups_longs = new_longs; | ||
202 | } | ||
203 | } while (!fits); | ||
169 | 204 | ||
170 | genl_lock_all(); | 205 | for (i = id; i < id + n_groups; i++) |
206 | set_bit(i, mc_groups); | ||
207 | *first_id = id; | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static struct genl_family genl_ctrl; | ||
212 | |||
213 | static int genl_validate_assign_mc_groups(struct genl_family *family) | ||
214 | { | ||
215 | int first_id; | ||
216 | int n_groups = family->n_mcgrps; | ||
217 | int err, i; | ||
218 | bool groups_allocated = false; | ||
219 | |||
220 | if (!n_groups) | ||
221 | return 0; | ||
222 | |||
223 | for (i = 0; i < n_groups; i++) { | ||
224 | const struct genl_multicast_group *grp = &family->mcgrps[i]; | ||
225 | |||
226 | if (WARN_ON(grp->name[0] == '\0')) | ||
227 | return -EINVAL; | ||
228 | if (WARN_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL)) | ||
229 | return -EINVAL; | ||
230 | } | ||
171 | 231 | ||
172 | /* special-case our own group and hacks */ | 232 | /* special-case our own group and hacks */ |
173 | if (grp == ¬ify_grp) | 233 | if (family == &genl_ctrl) { |
174 | id = GENL_ID_CTRL; | 234 | first_id = GENL_ID_CTRL; |
175 | else if (strcmp(family->name, "NET_DM") == 0) | 235 | BUG_ON(n_groups != 1); |
176 | id = 1; | 236 | } else if (strcmp(family->name, "NET_DM") == 0) { |
177 | else if (strcmp(family->name, "VFS_DQUOT") == 0) | 237 | first_id = 1; |
178 | id = GENL_ID_VFS_DQUOT; | 238 | BUG_ON(n_groups != 1); |
179 | else | 239 | } else if (strcmp(family->name, "VFS_DQUOT") == 0) { |
180 | id = find_first_zero_bit(mc_groups, | 240 | first_id = GENL_ID_VFS_DQUOT; |
181 | mc_groups_longs * BITS_PER_LONG); | 241 | BUG_ON(n_groups != 1); |
182 | 242 | } else { | |
183 | 243 | groups_allocated = true; | |
184 | if (id >= mc_groups_longs * BITS_PER_LONG) { | 244 | err = genl_allocate_reserve_groups(n_groups, &first_id); |
185 | size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long); | 245 | if (err) |
186 | 246 | return err; | |
187 | if (mc_groups == &mc_group_start) { | ||
188 | new_groups = kzalloc(nlen, GFP_KERNEL); | ||
189 | if (!new_groups) { | ||
190 | err = -ENOMEM; | ||
191 | goto out; | ||
192 | } | ||
193 | mc_groups = new_groups; | ||
194 | *mc_groups = mc_group_start; | ||
195 | } else { | ||
196 | new_groups = krealloc(mc_groups, nlen, GFP_KERNEL); | ||
197 | if (!new_groups) { | ||
198 | err = -ENOMEM; | ||
199 | goto out; | ||
200 | } | ||
201 | mc_groups = new_groups; | ||
202 | mc_groups[mc_groups_longs] = 0; | ||
203 | } | ||
204 | mc_groups_longs++; | ||
205 | } | 247 | } |
206 | 248 | ||
249 | family->mcgrp_offset = first_id; | ||
250 | |||
251 | /* if still initializing, can't and don't need to to realloc bitmaps */ | ||
252 | if (!init_net.genl_sock) | ||
253 | return 0; | ||
254 | |||
207 | if (family->netnsok) { | 255 | if (family->netnsok) { |
208 | struct net *net; | 256 | struct net *net; |
209 | 257 | ||
@@ -219,9 +267,7 @@ int genl_register_mc_group(struct genl_family *family, | |||
219 | * number of _possible_ groups has been | 267 | * number of _possible_ groups has been |
220 | * increased on some sockets which is ok. | 268 | * increased on some sockets which is ok. |
221 | */ | 269 | */ |
222 | rcu_read_unlock(); | 270 | break; |
223 | netlink_table_ungrab(); | ||
224 | goto out; | ||
225 | } | 271 | } |
226 | } | 272 | } |
227 | rcu_read_unlock(); | 273 | rcu_read_unlock(); |
@@ -229,46 +275,39 @@ int genl_register_mc_group(struct genl_family *family, | |||
229 | } else { | 275 | } else { |
230 | err = netlink_change_ngroups(init_net.genl_sock, | 276 | err = netlink_change_ngroups(init_net.genl_sock, |
231 | mc_groups_longs * BITS_PER_LONG); | 277 | mc_groups_longs * BITS_PER_LONG); |
232 | if (err) | ||
233 | goto out; | ||
234 | } | 278 | } |
235 | 279 | ||
236 | grp->id = id; | 280 | if (groups_allocated && err) { |
237 | set_bit(id, mc_groups); | 281 | for (i = 0; i < family->n_mcgrps; i++) |
238 | list_add_tail(&grp->list, &family->mcast_groups); | 282 | clear_bit(family->mcgrp_offset + i, mc_groups); |
283 | } | ||
239 | 284 | ||
240 | genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family, grp); | ||
241 | out: | ||
242 | genl_unlock_all(); | ||
243 | return err; | 285 | return err; |
244 | } | 286 | } |
245 | EXPORT_SYMBOL(genl_register_mc_group); | ||
246 | 287 | ||
247 | static void __genl_unregister_mc_group(struct genl_family *family, | 288 | static void genl_unregister_mc_groups(struct genl_family *family) |
248 | struct genl_multicast_group *grp) | ||
249 | { | 289 | { |
250 | struct net *net; | 290 | struct net *net; |
291 | int i; | ||
251 | 292 | ||
252 | netlink_table_grab(); | 293 | netlink_table_grab(); |
253 | rcu_read_lock(); | 294 | rcu_read_lock(); |
254 | for_each_net_rcu(net) | 295 | for_each_net_rcu(net) { |
255 | __netlink_clear_multicast_users(net->genl_sock, grp->id); | 296 | for (i = 0; i < family->n_mcgrps; i++) |
297 | __netlink_clear_multicast_users( | ||
298 | net->genl_sock, family->mcgrp_offset + i); | ||
299 | } | ||
256 | rcu_read_unlock(); | 300 | rcu_read_unlock(); |
257 | netlink_table_ungrab(); | 301 | netlink_table_ungrab(); |
258 | 302 | ||
259 | if (grp->id != 1) | 303 | for (i = 0; i < family->n_mcgrps; i++) { |
260 | clear_bit(grp->id, mc_groups); | 304 | int grp_id = family->mcgrp_offset + i; |
261 | list_del(&grp->list); | ||
262 | genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family, grp); | ||
263 | grp->id = 0; | ||
264 | } | ||
265 | |||
266 | static void genl_unregister_mc_groups(struct genl_family *family) | ||
267 | { | ||
268 | struct genl_multicast_group *grp, *tmp; | ||
269 | 305 | ||
270 | list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list) | 306 | if (grp_id != 1) |
271 | __genl_unregister_mc_group(family, grp); | 307 | clear_bit(grp_id, mc_groups); |
308 | genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family, | ||
309 | &family->mcgrps[i], grp_id); | ||
310 | } | ||
272 | } | 311 | } |
273 | 312 | ||
274 | static int genl_validate_ops(struct genl_family *family) | 313 | static int genl_validate_ops(struct genl_family *family) |
@@ -314,7 +353,7 @@ static int genl_validate_ops(struct genl_family *family) | |||
314 | */ | 353 | */ |
315 | int __genl_register_family(struct genl_family *family) | 354 | int __genl_register_family(struct genl_family *family) |
316 | { | 355 | { |
317 | int err = -EINVAL; | 356 | int err = -EINVAL, i; |
318 | 357 | ||
319 | if (family->id && family->id < GENL_MIN_ID) | 358 | if (family->id && family->id < GENL_MIN_ID) |
320 | goto errout; | 359 | goto errout; |
@@ -326,8 +365,6 @@ int __genl_register_family(struct genl_family *family) | |||
326 | if (err) | 365 | if (err) |
327 | return err; | 366 | return err; |
328 | 367 | ||
329 | INIT_LIST_HEAD(&family->mcast_groups); | ||
330 | |||
331 | genl_lock_all(); | 368 | genl_lock_all(); |
332 | 369 | ||
333 | if (genl_family_find_byname(family->name)) { | 370 | if (genl_family_find_byname(family->name)) { |
@@ -359,10 +396,18 @@ int __genl_register_family(struct genl_family *family) | |||
359 | } else | 396 | } else |
360 | family->attrbuf = NULL; | 397 | family->attrbuf = NULL; |
361 | 398 | ||
399 | err = genl_validate_assign_mc_groups(family); | ||
400 | if (err) | ||
401 | goto errout_locked; | ||
402 | |||
362 | list_add_tail(&family->family_list, genl_family_chain(family->id)); | 403 | list_add_tail(&family->family_list, genl_family_chain(family->id)); |
363 | genl_unlock_all(); | 404 | genl_unlock_all(); |
364 | 405 | ||
365 | genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL); | 406 | /* send all events */ |
407 | genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0); | ||
408 | for (i = 0; i < family->n_mcgrps; i++) | ||
409 | genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family, | ||
410 | &family->mcgrps[i], family->mcgrp_offset + i); | ||
366 | 411 | ||
367 | return 0; | 412 | return 0; |
368 | 413 | ||
@@ -398,7 +443,7 @@ int genl_unregister_family(struct genl_family *family) | |||
398 | genl_unlock_all(); | 443 | genl_unlock_all(); |
399 | 444 | ||
400 | kfree(family->attrbuf); | 445 | kfree(family->attrbuf); |
401 | genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL); | 446 | genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); |
402 | return 0; | 447 | return 0; |
403 | } | 448 | } |
404 | 449 | ||
@@ -658,23 +703,26 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq, | |||
658 | nla_nest_end(skb, nla_ops); | 703 | nla_nest_end(skb, nla_ops); |
659 | } | 704 | } |
660 | 705 | ||
661 | if (!list_empty(&family->mcast_groups)) { | 706 | if (family->n_mcgrps) { |
662 | struct genl_multicast_group *grp; | ||
663 | struct nlattr *nla_grps; | 707 | struct nlattr *nla_grps; |
664 | int idx = 1; | 708 | int i; |
665 | 709 | ||
666 | nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); | 710 | nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); |
667 | if (nla_grps == NULL) | 711 | if (nla_grps == NULL) |
668 | goto nla_put_failure; | 712 | goto nla_put_failure; |
669 | 713 | ||
670 | list_for_each_entry(grp, &family->mcast_groups, list) { | 714 | for (i = 0; i < family->n_mcgrps; i++) { |
671 | struct nlattr *nest; | 715 | struct nlattr *nest; |
716 | const struct genl_multicast_group *grp; | ||
672 | 717 | ||
673 | nest = nla_nest_start(skb, idx++); | 718 | grp = &family->mcgrps[i]; |
719 | |||
720 | nest = nla_nest_start(skb, i + 1); | ||
674 | if (nest == NULL) | 721 | if (nest == NULL) |
675 | goto nla_put_failure; | 722 | goto nla_put_failure; |
676 | 723 | ||
677 | if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id) || | 724 | if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, |
725 | family->mcgrp_offset + i) || | ||
678 | nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, | 726 | nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, |
679 | grp->name)) | 727 | grp->name)) |
680 | goto nla_put_failure; | 728 | goto nla_put_failure; |
@@ -692,9 +740,9 @@ nla_put_failure: | |||
692 | } | 740 | } |
693 | 741 | ||
694 | static int ctrl_fill_mcgrp_info(struct genl_family *family, | 742 | static int ctrl_fill_mcgrp_info(struct genl_family *family, |
695 | struct genl_multicast_group *grp, u32 portid, | 743 | const struct genl_multicast_group *grp, |
696 | u32 seq, u32 flags, struct sk_buff *skb, | 744 | int grp_id, u32 portid, u32 seq, u32 flags, |
697 | u8 cmd) | 745 | struct sk_buff *skb, u8 cmd) |
698 | { | 746 | { |
699 | void *hdr; | 747 | void *hdr; |
700 | struct nlattr *nla_grps; | 748 | struct nlattr *nla_grps; |
@@ -716,7 +764,7 @@ static int ctrl_fill_mcgrp_info(struct genl_family *family, | |||
716 | if (nest == NULL) | 764 | if (nest == NULL) |
717 | goto nla_put_failure; | 765 | goto nla_put_failure; |
718 | 766 | ||
719 | if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id) || | 767 | if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp_id) || |
720 | nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, | 768 | nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, |
721 | grp->name)) | 769 | grp->name)) |
722 | goto nla_put_failure; | 770 | goto nla_put_failure; |
@@ -782,9 +830,10 @@ static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, | |||
782 | return skb; | 830 | return skb; |
783 | } | 831 | } |
784 | 832 | ||
785 | static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_family *family, | 833 | static struct sk_buff * |
786 | struct genl_multicast_group *grp, | 834 | ctrl_build_mcgrp_msg(struct genl_family *family, |
787 | u32 portid, int seq, u8 cmd) | 835 | const struct genl_multicast_group *grp, |
836 | int grp_id, u32 portid, int seq, u8 cmd) | ||
788 | { | 837 | { |
789 | struct sk_buff *skb; | 838 | struct sk_buff *skb; |
790 | int err; | 839 | int err; |
@@ -793,7 +842,8 @@ static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_family *family, | |||
793 | if (skb == NULL) | 842 | if (skb == NULL) |
794 | return ERR_PTR(-ENOBUFS); | 843 | return ERR_PTR(-ENOBUFS); |
795 | 844 | ||
796 | err = ctrl_fill_mcgrp_info(family, grp, portid, seq, 0, skb, cmd); | 845 | err = ctrl_fill_mcgrp_info(family, grp, grp_id, portid, |
846 | seq, 0, skb, cmd); | ||
797 | if (err < 0) { | 847 | if (err < 0) { |
798 | nlmsg_free(skb); | 848 | nlmsg_free(skb); |
799 | return ERR_PTR(err); | 849 | return ERR_PTR(err); |
@@ -856,7 +906,8 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) | |||
856 | } | 906 | } |
857 | 907 | ||
858 | static int genl_ctrl_event(int event, struct genl_family *family, | 908 | static int genl_ctrl_event(int event, struct genl_family *family, |
859 | struct genl_multicast_group *grp) | 909 | const struct genl_multicast_group *grp, |
910 | int grp_id) | ||
860 | { | 911 | { |
861 | struct sk_buff *msg; | 912 | struct sk_buff *msg; |
862 | 913 | ||
@@ -873,7 +924,7 @@ static int genl_ctrl_event(int event, struct genl_family *family, | |||
873 | case CTRL_CMD_NEWMCAST_GRP: | 924 | case CTRL_CMD_NEWMCAST_GRP: |
874 | case CTRL_CMD_DELMCAST_GRP: | 925 | case CTRL_CMD_DELMCAST_GRP: |
875 | BUG_ON(!grp); | 926 | BUG_ON(!grp); |
876 | msg = ctrl_build_mcgrp_msg(family, grp, 0, 0, event); | 927 | msg = ctrl_build_mcgrp_msg(family, grp, grp_id, 0, 0, event); |
877 | break; | 928 | break; |
878 | default: | 929 | default: |
879 | return -EINVAL; | 930 | return -EINVAL; |
@@ -884,11 +935,11 @@ static int genl_ctrl_event(int event, struct genl_family *family, | |||
884 | 935 | ||
885 | if (!family->netnsok) { | 936 | if (!family->netnsok) { |
886 | genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0, | 937 | genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0, |
887 | GENL_ID_CTRL, GFP_KERNEL); | 938 | 0, GFP_KERNEL); |
888 | } else { | 939 | } else { |
889 | rcu_read_lock(); | 940 | rcu_read_lock(); |
890 | genlmsg_multicast_allns(&genl_ctrl, msg, 0, | 941 | genlmsg_multicast_allns(&genl_ctrl, msg, 0, |
891 | GENL_ID_CTRL, GFP_ATOMIC); | 942 | 0, GFP_ATOMIC); |
892 | rcu_read_unlock(); | 943 | rcu_read_unlock(); |
893 | } | 944 | } |
894 | 945 | ||
@@ -904,8 +955,8 @@ static struct genl_ops genl_ctrl_ops[] = { | |||
904 | }, | 955 | }, |
905 | }; | 956 | }; |
906 | 957 | ||
907 | static struct genl_multicast_group notify_grp = { | 958 | static struct genl_multicast_group genl_ctrl_groups[] = { |
908 | .name = "notify", | 959 | { .name = "notify", }, |
909 | }; | 960 | }; |
910 | 961 | ||
911 | static int __net_init genl_pernet_init(struct net *net) | 962 | static int __net_init genl_pernet_init(struct net *net) |
@@ -945,7 +996,8 @@ static int __init genl_init(void) | |||
945 | for (i = 0; i < GENL_FAM_TAB_SIZE; i++) | 996 | for (i = 0; i < GENL_FAM_TAB_SIZE; i++) |
946 | INIT_LIST_HEAD(&family_ht[i]); | 997 | INIT_LIST_HEAD(&family_ht[i]); |
947 | 998 | ||
948 | err = genl_register_family_with_ops(&genl_ctrl, genl_ctrl_ops); | 999 | err = genl_register_family_with_ops_groups(&genl_ctrl, genl_ctrl_ops, |
1000 | genl_ctrl_groups); | ||
949 | if (err < 0) | 1001 | if (err < 0) |
950 | goto problem; | 1002 | goto problem; |
951 | 1003 | ||
@@ -953,10 +1005,6 @@ static int __init genl_init(void) | |||
953 | if (err) | 1005 | if (err) |
954 | goto problem; | 1006 | goto problem; |
955 | 1007 | ||
956 | err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); | ||
957 | if (err < 0) | ||
958 | goto problem; | ||
959 | |||
960 | return 0; | 1008 | return 0; |
961 | 1009 | ||
962 | problem: | 1010 | problem: |
@@ -997,6 +1045,9 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, | |||
997 | int genlmsg_multicast_allns(struct genl_family *family, struct sk_buff *skb, | 1045 | int genlmsg_multicast_allns(struct genl_family *family, struct sk_buff *skb, |
998 | u32 portid, unsigned int group, gfp_t flags) | 1046 | u32 portid, unsigned int group, gfp_t flags) |
999 | { | 1047 | { |
1048 | if (group >= family->n_mcgrps) | ||
1049 | return -EINVAL; | ||
1050 | group = family->mcgrp_offset + group; | ||
1000 | return genlmsg_mcast(skb, portid, group, flags); | 1051 | return genlmsg_mcast(skb, portid, group, flags); |
1001 | } | 1052 | } |
1002 | EXPORT_SYMBOL(genlmsg_multicast_allns); | 1053 | EXPORT_SYMBOL(genlmsg_multicast_allns); |
@@ -1011,6 +1062,9 @@ void genl_notify(struct genl_family *family, | |||
1011 | if (nlh) | 1062 | if (nlh) |
1012 | report = nlmsg_report(nlh); | 1063 | report = nlmsg_report(nlh); |
1013 | 1064 | ||
1065 | if (group >= family->n_mcgrps) | ||
1066 | return; | ||
1067 | group = family->mcgrp_offset + group; | ||
1014 | nlmsg_notify(sk, skb, portid, group, report, flags); | 1068 | nlmsg_notify(sk, skb, portid, group, report, flags); |
1015 | } | 1069 | } |
1016 | EXPORT_SYMBOL(genl_notify); | 1070 | EXPORT_SYMBOL(genl_notify); |