diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-07-18 18:47:52 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-07-18 18:47:52 -0400 |
commit | 2dbba6f773d1e1e4c78f03b0dbf19790d9017693 (patch) | |
tree | 19535af46e79a0af756275f751d7b0080b72001d /net | |
parent | 84659eb529b33572bb3f8c94e0978bd5d084bc7e (diff) |
[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>
Diffstat (limited to 'net')
-rw-r--r-- | net/netlink/genetlink.c | 235 |
1 files changed, 228 insertions, 7 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index b9ab62f938d0..e146531faf1d 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Authors: Jamal Hadi Salim | 4 | * Authors: Jamal Hadi Salim |
5 | * Thomas Graf <tgraf@suug.ch> | 5 | * Thomas Graf <tgraf@suug.ch> |
6 | * Johannes Berg <johannes@sipsolutions.net> | ||
6 | */ | 7 | */ |
7 | 8 | ||
8 | #include <linux/module.h> | 9 | #include <linux/module.h> |
@@ -13,6 +14,7 @@ | |||
13 | #include <linux/string.h> | 14 | #include <linux/string.h> |
14 | #include <linux/skbuff.h> | 15 | #include <linux/skbuff.h> |
15 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
17 | #include <linux/bitmap.h> | ||
16 | #include <net/sock.h> | 18 | #include <net/sock.h> |
17 | #include <net/genetlink.h> | 19 | #include <net/genetlink.h> |
18 | 20 | ||
@@ -42,6 +44,16 @@ static void genl_unlock(void) | |||
42 | #define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) | 44 | #define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) |
43 | 45 | ||
44 | static struct list_head family_ht[GENL_FAM_TAB_SIZE]; | 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 | static int genl_ctrl_event(int event, void *data); | 58 | static int genl_ctrl_event(int event, void *data); |
47 | 59 | ||
@@ -116,6 +128,114 @@ static inline u16 genl_generate_id(void) | |||
116 | return id_gen_idx; | 128 | return id_gen_idx; |
117 | } | 129 | } |
118 | 130 | ||
131 | static struct genl_multicast_group notify_grp; | ||
132 | |||
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 | |||
119 | /** | 239 | /** |
120 | * genl_register_ops - register generic netlink operations | 240 | * genl_register_ops - register generic netlink operations |
121 | * @family: generic netlink family | 241 | * @family: generic netlink family |
@@ -216,6 +336,7 @@ int genl_register_family(struct genl_family *family) | |||
216 | goto errout; | 336 | goto errout; |
217 | 337 | ||
218 | INIT_LIST_HEAD(&family->ops_list); | 338 | INIT_LIST_HEAD(&family->ops_list); |
339 | INIT_LIST_HEAD(&family->mcast_groups); | ||
219 | 340 | ||
220 | genl_lock(); | 341 | genl_lock(); |
221 | 342 | ||
@@ -275,6 +396,8 @@ int genl_unregister_family(struct genl_family *family) | |||
275 | { | 396 | { |
276 | struct genl_family *rc; | 397 | struct genl_family *rc; |
277 | 398 | ||
399 | genl_unregister_mc_groups(family); | ||
400 | |||
278 | genl_lock(); | 401 | genl_lock(); |
279 | 402 | ||
280 | list_for_each_entry(rc, genl_family_chain(family->id), family_list) { | 403 | list_for_each_entry(rc, genl_family_chain(family->id), family_list) { |
@@ -410,6 +533,67 @@ static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, | |||
410 | nla_nest_end(skb, nla_ops); | 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 | |||
561 | return genlmsg_end(skb, hdr); | ||
562 | |||
563 | nla_put_failure: | ||
564 | return genlmsg_cancel(skb, hdr); | ||
565 | } | ||
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 | |||
413 | return genlmsg_end(skb, hdr); | 597 | return genlmsg_end(skb, hdr); |
414 | 598 | ||
415 | nla_put_failure: | 599 | nla_put_failure: |
@@ -453,8 +637,8 @@ errout: | |||
453 | return skb->len; | 637 | return skb->len; |
454 | } | 638 | } |
455 | 639 | ||
456 | static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid, | 640 | static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, |
457 | int seq, u8 cmd) | 641 | u32 pid, int seq, u8 cmd) |
458 | { | 642 | { |
459 | struct sk_buff *skb; | 643 | struct sk_buff *skb; |
460 | int err; | 644 | int err; |
@@ -472,6 +656,25 @@ static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid, | |||
472 | return skb; | 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 | static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { | 678 | static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { |
476 | [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, | 679 | [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, |
477 | [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, | 680 | [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, |
@@ -501,8 +704,8 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) | |||
501 | goto errout; | 704 | goto errout; |
502 | } | 705 | } |
503 | 706 | ||
504 | msg = ctrl_build_msg(res, info->snd_pid, info->snd_seq, | 707 | msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, |
505 | CTRL_CMD_NEWFAMILY); | 708 | CTRL_CMD_NEWFAMILY); |
506 | if (IS_ERR(msg)) { | 709 | if (IS_ERR(msg)) { |
507 | err = PTR_ERR(msg); | 710 | err = PTR_ERR(msg); |
508 | goto errout; | 711 | goto errout; |
@@ -523,7 +726,15 @@ static int genl_ctrl_event(int event, void *data) | |||
523 | switch (event) { | 726 | switch (event) { |
524 | case CTRL_CMD_NEWFAMILY: | 727 | case CTRL_CMD_NEWFAMILY: |
525 | case CTRL_CMD_DELFAMILY: | 728 | case CTRL_CMD_DELFAMILY: |
526 | msg = ctrl_build_msg(data, 0, 0, event); | 729 | msg = ctrl_build_family_msg(data, 0, 0, event); |
730 | if (IS_ERR(msg)) | ||
731 | return PTR_ERR(msg); | ||
732 | |||
733 | genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); | ||
734 | break; | ||
735 | case CTRL_CMD_NEWMCAST_GRP: | ||
736 | case CTRL_CMD_DELMCAST_GRP: | ||
737 | msg = ctrl_build_mcgrp_msg(data, 0, 0, event); | ||
527 | if (IS_ERR(msg)) | 738 | if (IS_ERR(msg)) |
528 | return PTR_ERR(msg); | 739 | return PTR_ERR(msg); |
529 | 740 | ||
@@ -541,6 +752,10 @@ static struct genl_ops genl_ctrl_ops = { | |||
541 | .policy = ctrl_policy, | 752 | .policy = ctrl_policy, |
542 | }; | 753 | }; |
543 | 754 | ||
755 | static struct genl_multicast_group notify_grp = { | ||
756 | .name = "notify", | ||
757 | }; | ||
758 | |||
544 | static int __init genl_init(void) | 759 | static int __init genl_init(void) |
545 | { | 760 | { |
546 | int i, err; | 761 | int i, err; |
@@ -557,11 +772,17 @@ static int __init genl_init(void) | |||
557 | goto errout_register; | 772 | goto errout_register; |
558 | 773 | ||
559 | netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); | 774 | netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); |
560 | genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID, | 775 | |
561 | genl_rcv, NULL, THIS_MODULE); | 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 | if (genl_sock == NULL) | 779 | if (genl_sock == NULL) |
563 | panic("GENL: Cannot initialize generic netlink\n"); | 780 | panic("GENL: Cannot initialize generic netlink\n"); |
564 | 781 | ||
782 | err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); | ||
783 | if (err < 0) | ||
784 | goto errout_register; | ||
785 | |||
565 | return 0; | 786 | return 0; |
566 | 787 | ||
567 | errout_register: | 788 | errout_register: |