Commit 2dbba6f773d1e1e4c78f03b0dbf19790d9017693
Committed by
David S. Miller
1 parent
84659eb529
Exists in
master
and in
39 other branches
[GENETLINK]: Dynamic multicast groups.
Introduce API to dynamically register and unregister multicast groups. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Acked-by: Patrick McHardy <kaber@trash.net> Acked-by: Jamal Hadi Salim <hadi@cyberus.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 3 changed files with 263 additions and 7 deletions Side-by-side Diff
include/linux/genetlink.h
... | ... | @@ -39,6 +39,9 @@ |
39 | 39 | CTRL_CMD_NEWOPS, |
40 | 40 | CTRL_CMD_DELOPS, |
41 | 41 | CTRL_CMD_GETOPS, |
42 | + CTRL_CMD_NEWMCAST_GRP, | |
43 | + CTRL_CMD_DELMCAST_GRP, | |
44 | + CTRL_CMD_GETMCAST_GRP, /* unused */ | |
42 | 45 | __CTRL_CMD_MAX, |
43 | 46 | }; |
44 | 47 | |
... | ... | @@ -52,6 +55,7 @@ |
52 | 55 | CTRL_ATTR_HDRSIZE, |
53 | 56 | CTRL_ATTR_MAXATTR, |
54 | 57 | CTRL_ATTR_OPS, |
58 | + CTRL_ATTR_MCAST_GROUPS, | |
55 | 59 | __CTRL_ATTR_MAX, |
56 | 60 | }; |
57 | 61 | |
... | ... | @@ -65,6 +69,15 @@ |
65 | 69 | }; |
66 | 70 | |
67 | 71 | #define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) |
72 | + | |
73 | +enum { | |
74 | + CTRL_ATTR_MCAST_GRP_UNSPEC, | |
75 | + CTRL_ATTR_MCAST_GRP_NAME, | |
76 | + CTRL_ATTR_MCAST_GRP_ID, | |
77 | + __CTRL_ATTR_MCAST_GRP_MAX, | |
78 | +}; | |
79 | + | |
80 | +#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) | |
68 | 81 | |
69 | 82 | #endif /* __LINUX_GENERIC_NETLINK_H */ |
include/net/genetlink.h
... | ... | @@ -5,6 +5,22 @@ |
5 | 5 | #include <net/netlink.h> |
6 | 6 | |
7 | 7 | /** |
8 | + * struct genl_multicast_group - generic netlink multicast group | |
9 | + * @name: name of the multicast group, names are per-family | |
10 | + * @id: multicast group ID, assigned by the core, to use with | |
11 | + * genlmsg_multicast(). | |
12 | + * @list: list entry for linking | |
13 | + * @family: pointer to family, need not be set before registering | |
14 | + */ | |
15 | +struct genl_multicast_group | |
16 | +{ | |
17 | + struct genl_family *family; /* private */ | |
18 | + struct list_head list; /* private */ | |
19 | + char name[GENL_NAMSIZ]; | |
20 | + u32 id; | |
21 | +}; | |
22 | + | |
23 | +/** | |
8 | 24 | * struct genl_family - generic netlink family |
9 | 25 | * @id: protocol family idenfitier |
10 | 26 | * @hdrsize: length of user specific header in bytes |
... | ... | @@ -14,6 +30,7 @@ |
14 | 30 | * @attrbuf: buffer to store parsed attributes |
15 | 31 | * @ops_list: list of all assigned operations |
16 | 32 | * @family_list: family list |
33 | + * @mcast_groups: multicast groups list | |
17 | 34 | */ |
18 | 35 | struct genl_family |
19 | 36 | { |
... | ... | @@ -25,6 +42,7 @@ |
25 | 42 | struct nlattr ** attrbuf; /* private */ |
26 | 43 | struct list_head ops_list; /* private */ |
27 | 44 | struct list_head family_list; /* private */ |
45 | + struct list_head mcast_groups; /* private */ | |
28 | 46 | }; |
29 | 47 | |
30 | 48 | /** |
... | ... | @@ -73,6 +91,10 @@ |
73 | 91 | extern int genl_unregister_family(struct genl_family *family); |
74 | 92 | extern int genl_register_ops(struct genl_family *, struct genl_ops *ops); |
75 | 93 | extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops); |
94 | +extern int genl_register_mc_group(struct genl_family *family, | |
95 | + struct genl_multicast_group *grp); | |
96 | +extern void genl_unregister_mc_group(struct genl_family *family, | |
97 | + struct genl_multicast_group *grp); | |
76 | 98 | |
77 | 99 | extern struct sock *genl_sock; |
78 | 100 |
net/netlink/genetlink.c
... | ... | @@ -3,6 +3,7 @@ |
3 | 3 | * |
4 | 4 | * Authors: Jamal Hadi Salim |
5 | 5 | * Thomas Graf <tgraf@suug.ch> |
6 | + * Johannes Berg <johannes@sipsolutions.net> | |
6 | 7 | */ |
7 | 8 | |
8 | 9 | #include <linux/module.h> |
... | ... | @@ -13,6 +14,7 @@ |
13 | 14 | #include <linux/string.h> |
14 | 15 | #include <linux/skbuff.h> |
15 | 16 | #include <linux/mutex.h> |
17 | +#include <linux/bitmap.h> | |
16 | 18 | #include <net/sock.h> |
17 | 19 | #include <net/genetlink.h> |
18 | 20 | |
... | ... | @@ -42,6 +44,16 @@ |
42 | 44 | #define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) |
43 | 45 | |
44 | 46 | static struct list_head family_ht[GENL_FAM_TAB_SIZE]; |
47 | +/* | |
48 | + * Bitmap of multicast groups that are currently in use. | |
49 | + * | |
50 | + * To avoid an allocation at boot of just one unsigned long, | |
51 | + * declare it global instead. | |
52 | + * Bit 0 is marked as already used since group 0 is invalid. | |
53 | + */ | |
54 | +static unsigned long mc_group_start = 0x1; | |
55 | +static unsigned long *mc_groups = &mc_group_start; | |
56 | +static unsigned long mc_groups_longs = 1; | |
45 | 57 | |
46 | 58 | static int genl_ctrl_event(int event, void *data); |
47 | 59 | |
48 | 60 | |
... | ... | @@ -116,7 +128,115 @@ |
116 | 128 | return id_gen_idx; |
117 | 129 | } |
118 | 130 | |
131 | +static struct genl_multicast_group notify_grp; | |
132 | + | |
119 | 133 | /** |
134 | + * genl_register_mc_group - register a multicast group | |
135 | + * | |
136 | + * Registers the specified multicast group and notifies userspace | |
137 | + * about the new group. | |
138 | + * | |
139 | + * Returns 0 on success or a negative error code. | |
140 | + * | |
141 | + * @family: The generic netlink family the group shall be registered for. | |
142 | + * @grp: The group to register, must have a name. | |
143 | + */ | |
144 | +int genl_register_mc_group(struct genl_family *family, | |
145 | + struct genl_multicast_group *grp) | |
146 | +{ | |
147 | + int id; | |
148 | + unsigned long *new_groups; | |
149 | + int err; | |
150 | + | |
151 | + BUG_ON(grp->name[0] == '\0'); | |
152 | + | |
153 | + genl_lock(); | |
154 | + | |
155 | + /* special-case our own group */ | |
156 | + if (grp == ¬ify_grp) | |
157 | + id = GENL_ID_CTRL; | |
158 | + else | |
159 | + id = find_first_zero_bit(mc_groups, | |
160 | + mc_groups_longs * BITS_PER_LONG); | |
161 | + | |
162 | + | |
163 | + if (id >= mc_groups_longs * BITS_PER_LONG) { | |
164 | + size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long); | |
165 | + | |
166 | + if (mc_groups == &mc_group_start) { | |
167 | + new_groups = kzalloc(nlen, GFP_KERNEL); | |
168 | + if (!new_groups) { | |
169 | + err = -ENOMEM; | |
170 | + goto out; | |
171 | + } | |
172 | + mc_groups = new_groups; | |
173 | + *mc_groups = mc_group_start; | |
174 | + } else { | |
175 | + new_groups = krealloc(mc_groups, nlen, GFP_KERNEL); | |
176 | + if (!new_groups) { | |
177 | + err = -ENOMEM; | |
178 | + goto out; | |
179 | + } | |
180 | + mc_groups = new_groups; | |
181 | + mc_groups[mc_groups_longs] = 0; | |
182 | + } | |
183 | + mc_groups_longs++; | |
184 | + } | |
185 | + | |
186 | + err = netlink_change_ngroups(genl_sock, | |
187 | + sizeof(unsigned long) * NETLINK_GENERIC); | |
188 | + if (err) | |
189 | + goto out; | |
190 | + | |
191 | + grp->id = id; | |
192 | + set_bit(id, mc_groups); | |
193 | + list_add_tail(&grp->list, &family->mcast_groups); | |
194 | + grp->family = family; | |
195 | + | |
196 | + genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp); | |
197 | + out: | |
198 | + genl_unlock(); | |
199 | + return 0; | |
200 | +} | |
201 | +EXPORT_SYMBOL(genl_register_mc_group); | |
202 | + | |
203 | +/** | |
204 | + * genl_unregister_mc_group - unregister a multicast group | |
205 | + * | |
206 | + * Unregisters the specified multicast group and notifies userspace | |
207 | + * about it. All current listeners on the group are removed. | |
208 | + * | |
209 | + * Note: It is not necessary to unregister all multicast groups before | |
210 | + * unregistering the family, unregistering the family will cause | |
211 | + * all assigned multicast groups to be unregistered automatically. | |
212 | + * | |
213 | + * @family: Generic netlink family the group belongs to. | |
214 | + * @grp: The group to unregister, must have been registered successfully | |
215 | + * previously. | |
216 | + */ | |
217 | +void genl_unregister_mc_group(struct genl_family *family, | |
218 | + struct genl_multicast_group *grp) | |
219 | +{ | |
220 | + BUG_ON(grp->family != family); | |
221 | + genl_lock(); | |
222 | + netlink_clear_multicast_users(genl_sock, grp->id); | |
223 | + clear_bit(grp->id, mc_groups); | |
224 | + list_del(&grp->list); | |
225 | + genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); | |
226 | + grp->id = 0; | |
227 | + grp->family = NULL; | |
228 | + genl_unlock(); | |
229 | +} | |
230 | + | |
231 | +static void genl_unregister_mc_groups(struct genl_family *family) | |
232 | +{ | |
233 | + struct genl_multicast_group *grp, *tmp; | |
234 | + | |
235 | + list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list) | |
236 | + genl_unregister_mc_group(family, grp); | |
237 | +} | |
238 | + | |
239 | +/** | |
120 | 240 | * genl_register_ops - register generic netlink operations |
121 | 241 | * @family: generic netlink family |
122 | 242 | * @ops: operations to be registered |
... | ... | @@ -216,6 +336,7 @@ |
216 | 336 | goto errout; |
217 | 337 | |
218 | 338 | INIT_LIST_HEAD(&family->ops_list); |
339 | + INIT_LIST_HEAD(&family->mcast_groups); | |
219 | 340 | |
220 | 341 | genl_lock(); |
221 | 342 | |
... | ... | @@ -275,6 +396,8 @@ |
275 | 396 | { |
276 | 397 | struct genl_family *rc; |
277 | 398 | |
399 | + genl_unregister_mc_groups(family); | |
400 | + | |
278 | 401 | genl_lock(); |
279 | 402 | |
280 | 403 | list_for_each_entry(rc, genl_family_chain(family->id), family_list) { |
281 | 404 | |
... | ... | @@ -410,12 +533,73 @@ |
410 | 533 | nla_nest_end(skb, nla_ops); |
411 | 534 | } |
412 | 535 | |
536 | + if (!list_empty(&family->mcast_groups)) { | |
537 | + struct genl_multicast_group *grp; | |
538 | + struct nlattr *nla_grps; | |
539 | + int idx = 1; | |
540 | + | |
541 | + nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); | |
542 | + if (nla_grps == NULL) | |
543 | + goto nla_put_failure; | |
544 | + | |
545 | + list_for_each_entry(grp, &family->mcast_groups, list) { | |
546 | + struct nlattr *nest; | |
547 | + | |
548 | + nest = nla_nest_start(skb, idx++); | |
549 | + if (nest == NULL) | |
550 | + goto nla_put_failure; | |
551 | + | |
552 | + NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id); | |
553 | + NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME, | |
554 | + grp->name); | |
555 | + | |
556 | + nla_nest_end(skb, nest); | |
557 | + } | |
558 | + nla_nest_end(skb, nla_grps); | |
559 | + } | |
560 | + | |
413 | 561 | return genlmsg_end(skb, hdr); |
414 | 562 | |
415 | 563 | nla_put_failure: |
416 | 564 | return genlmsg_cancel(skb, hdr); |
417 | 565 | } |
418 | 566 | |
567 | +static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 pid, | |
568 | + u32 seq, u32 flags, struct sk_buff *skb, | |
569 | + u8 cmd) | |
570 | +{ | |
571 | + void *hdr; | |
572 | + struct nlattr *nla_grps; | |
573 | + struct nlattr *nest; | |
574 | + | |
575 | + hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd); | |
576 | + if (hdr == NULL) | |
577 | + return -1; | |
578 | + | |
579 | + NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, grp->family->name); | |
580 | + NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, grp->family->id); | |
581 | + | |
582 | + nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); | |
583 | + if (nla_grps == NULL) | |
584 | + goto nla_put_failure; | |
585 | + | |
586 | + nest = nla_nest_start(skb, 1); | |
587 | + if (nest == NULL) | |
588 | + goto nla_put_failure; | |
589 | + | |
590 | + NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id); | |
591 | + NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME, | |
592 | + grp->name); | |
593 | + | |
594 | + nla_nest_end(skb, nest); | |
595 | + nla_nest_end(skb, nla_grps); | |
596 | + | |
597 | + return genlmsg_end(skb, hdr); | |
598 | + | |
599 | +nla_put_failure: | |
600 | + return genlmsg_cancel(skb, hdr); | |
601 | +} | |
602 | + | |
419 | 603 | static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) |
420 | 604 | { |
421 | 605 | |
... | ... | @@ -453,8 +637,8 @@ |
453 | 637 | return skb->len; |
454 | 638 | } |
455 | 639 | |
456 | -static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid, | |
457 | - int seq, u8 cmd) | |
640 | +static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, | |
641 | + u32 pid, int seq, u8 cmd) | |
458 | 642 | { |
459 | 643 | struct sk_buff *skb; |
460 | 644 | int err; |
... | ... | @@ -472,6 +656,25 @@ |
472 | 656 | return skb; |
473 | 657 | } |
474 | 658 | |
659 | +static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp, | |
660 | + u32 pid, int seq, u8 cmd) | |
661 | +{ | |
662 | + struct sk_buff *skb; | |
663 | + int err; | |
664 | + | |
665 | + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
666 | + if (skb == NULL) | |
667 | + return ERR_PTR(-ENOBUFS); | |
668 | + | |
669 | + err = ctrl_fill_mcgrp_info(grp, pid, seq, 0, skb, cmd); | |
670 | + if (err < 0) { | |
671 | + nlmsg_free(skb); | |
672 | + return ERR_PTR(err); | |
673 | + } | |
674 | + | |
675 | + return skb; | |
676 | +} | |
677 | + | |
475 | 678 | static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { |
476 | 679 | [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, |
477 | 680 | [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, |
... | ... | @@ -501,8 +704,8 @@ |
501 | 704 | goto errout; |
502 | 705 | } |
503 | 706 | |
504 | - msg = ctrl_build_msg(res, info->snd_pid, info->snd_seq, | |
505 | - CTRL_CMD_NEWFAMILY); | |
707 | + msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, | |
708 | + CTRL_CMD_NEWFAMILY); | |
506 | 709 | if (IS_ERR(msg)) { |
507 | 710 | err = PTR_ERR(msg); |
508 | 711 | goto errout; |
509 | 712 | |
... | ... | @@ -523,12 +726,20 @@ |
523 | 726 | switch (event) { |
524 | 727 | case CTRL_CMD_NEWFAMILY: |
525 | 728 | case CTRL_CMD_DELFAMILY: |
526 | - msg = ctrl_build_msg(data, 0, 0, event); | |
729 | + msg = ctrl_build_family_msg(data, 0, 0, event); | |
527 | 730 | if (IS_ERR(msg)) |
528 | 731 | return PTR_ERR(msg); |
529 | 732 | |
530 | 733 | genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); |
531 | 734 | break; |
735 | + case CTRL_CMD_NEWMCAST_GRP: | |
736 | + case CTRL_CMD_DELMCAST_GRP: | |
737 | + msg = ctrl_build_mcgrp_msg(data, 0, 0, event); | |
738 | + if (IS_ERR(msg)) | |
739 | + return PTR_ERR(msg); | |
740 | + | |
741 | + genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); | |
742 | + break; | |
532 | 743 | } |
533 | 744 | |
534 | 745 | return 0; |
... | ... | @@ -541,6 +752,10 @@ |
541 | 752 | .policy = ctrl_policy, |
542 | 753 | }; |
543 | 754 | |
755 | +static struct genl_multicast_group notify_grp = { | |
756 | + .name = "notify", | |
757 | +}; | |
758 | + | |
544 | 759 | static int __init genl_init(void) |
545 | 760 | { |
546 | 761 | int i, err; |
547 | 762 | |
... | ... | @@ -557,10 +772,16 @@ |
557 | 772 | goto errout_register; |
558 | 773 | |
559 | 774 | netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); |
560 | - genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID, | |
561 | - genl_rcv, NULL, THIS_MODULE); | |
775 | + | |
776 | + /* we'll bump the group number right afterwards */ | |
777 | + genl_sock = netlink_kernel_create(NETLINK_GENERIC, 0, genl_rcv, | |
778 | + NULL, THIS_MODULE); | |
562 | 779 | if (genl_sock == NULL) |
563 | 780 | panic("GENL: Cannot initialize generic netlink\n"); |
781 | + | |
782 | + err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); | |
783 | + if (err < 0) | |
784 | + goto errout_register; | |
564 | 785 | |
565 | 786 | return 0; |
566 | 787 |