diff options
| -rw-r--r-- | include/linux/genetlink.h | 13 | ||||
| -rw-r--r-- | include/net/genetlink.h | 22 | ||||
| -rw-r--r-- | net/netlink/genetlink.c | 235 |
3 files changed, 263 insertions, 7 deletions
diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h index f7a93770e1be..7da02c93002b 100644 --- a/include/linux/genetlink.h +++ b/include/linux/genetlink.h | |||
| @@ -39,6 +39,9 @@ enum { | |||
| 39 | CTRL_CMD_NEWOPS, | 39 | CTRL_CMD_NEWOPS, |
| 40 | CTRL_CMD_DELOPS, | 40 | CTRL_CMD_DELOPS, |
| 41 | CTRL_CMD_GETOPS, | 41 | CTRL_CMD_GETOPS, |
| 42 | CTRL_CMD_NEWMCAST_GRP, | ||
| 43 | CTRL_CMD_DELMCAST_GRP, | ||
| 44 | CTRL_CMD_GETMCAST_GRP, /* unused */ | ||
| 42 | __CTRL_CMD_MAX, | 45 | __CTRL_CMD_MAX, |
| 43 | }; | 46 | }; |
| 44 | 47 | ||
| @@ -52,6 +55,7 @@ enum { | |||
| 52 | CTRL_ATTR_HDRSIZE, | 55 | CTRL_ATTR_HDRSIZE, |
| 53 | CTRL_ATTR_MAXATTR, | 56 | CTRL_ATTR_MAXATTR, |
| 54 | CTRL_ATTR_OPS, | 57 | CTRL_ATTR_OPS, |
| 58 | CTRL_ATTR_MCAST_GROUPS, | ||
| 55 | __CTRL_ATTR_MAX, | 59 | __CTRL_ATTR_MAX, |
| 56 | }; | 60 | }; |
| 57 | 61 | ||
| @@ -66,4 +70,13 @@ enum { | |||
| 66 | 70 | ||
| 67 | #define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) | 71 | #define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) |
| 68 | 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) | ||
| 81 | |||
| 69 | #endif /* __LINUX_GENERIC_NETLINK_H */ | 82 | #endif /* __LINUX_GENERIC_NETLINK_H */ |
diff --git a/include/net/genetlink.h b/include/net/genetlink.h index b6eaca122db8..decdda546829 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h | |||
| @@ -5,6 +5,22 @@ | |||
| 5 | #include <net/netlink.h> | 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 | * struct genl_family - generic netlink family | 24 | * struct genl_family - generic netlink family |
| 9 | * @id: protocol family idenfitier | 25 | * @id: protocol family idenfitier |
| 10 | * @hdrsize: length of user specific header in bytes | 26 | * @hdrsize: length of user specific header in bytes |
| @@ -14,6 +30,7 @@ | |||
| 14 | * @attrbuf: buffer to store parsed attributes | 30 | * @attrbuf: buffer to store parsed attributes |
| 15 | * @ops_list: list of all assigned operations | 31 | * @ops_list: list of all assigned operations |
| 16 | * @family_list: family list | 32 | * @family_list: family list |
| 33 | * @mcast_groups: multicast groups list | ||
| 17 | */ | 34 | */ |
| 18 | struct genl_family | 35 | struct genl_family |
| 19 | { | 36 | { |
| @@ -25,6 +42,7 @@ struct genl_family | |||
| 25 | struct nlattr ** attrbuf; /* private */ | 42 | struct nlattr ** attrbuf; /* private */ |
| 26 | struct list_head ops_list; /* private */ | 43 | struct list_head ops_list; /* private */ |
| 27 | struct list_head family_list; /* private */ | 44 | struct list_head family_list; /* private */ |
| 45 | struct list_head mcast_groups; /* private */ | ||
| 28 | }; | 46 | }; |
| 29 | 47 | ||
| 30 | /** | 48 | /** |
| @@ -73,6 +91,10 @@ extern int genl_register_family(struct genl_family *family); | |||
| 73 | extern int genl_unregister_family(struct genl_family *family); | 91 | extern int genl_unregister_family(struct genl_family *family); |
| 74 | extern int genl_register_ops(struct genl_family *, struct genl_ops *ops); | 92 | extern int genl_register_ops(struct genl_family *, struct genl_ops *ops); |
| 75 | extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops); | 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 | extern struct sock *genl_sock; | 99 | extern struct sock *genl_sock; |
| 78 | 100 | ||
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: |
