diff options
Diffstat (limited to 'net/netlink/genetlink.c')
| -rw-r--r-- | net/netlink/genetlink.c | 552 | 
1 files changed, 270 insertions, 282 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 0c741cec4d0..76393f2f4b2 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -65,12 +65,27 @@ static struct list_head family_ht[GENL_FAM_TAB_SIZE];   * To avoid an allocation at boot of just one unsigned long,   * declare it global instead.   * Bit 0 is marked as already used since group 0 is invalid. + * Bit 1 is marked as already used since the drop-monitor code + * abuses the API and thinks it can statically use group 1. + * That group will typically conflict with other groups that + * any proper users use. + * Bit 16 is marked as used since it's used for generic netlink + * and the code no longer marks pre-reserved IDs as used. + * Bit 17 is marked as already used since the VFS quota code + * also abused this API and relied on family == group ID, we + * cater to that by giving it a static family and group ID. + * Bit 18 is marked as already used since the PMCRAID driver + * did the same thing as the VFS quota code (maybe copied?)   */ -static unsigned long mc_group_start = 0x1; +static unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) | +				      BIT(GENL_ID_VFS_DQUOT) | +				      BIT(GENL_ID_PMCRAID);  static unsigned long *mc_groups = &mc_group_start;  static unsigned long mc_groups_longs = 1; -static int genl_ctrl_event(int event, void *data); +static int genl_ctrl_event(int event, struct genl_family *family, +			   const struct genl_multicast_group *grp, +			   int grp_id);  static inline unsigned int genl_family_hash(unsigned int id)  { @@ -106,13 +121,13 @@ static struct genl_family *genl_family_find_byname(char *name)  	return NULL;  } -static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) +static const struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)  { -	struct genl_ops *ops; +	int i; -	list_for_each_entry(ops, &family->ops_list, ops_list) -		if (ops->cmd == cmd) -			return ops; +	for (i = 0; i < family->n_ops; i++) +		if (family->ops[i].cmd == cmd) +			return &family->ops[i];  	return NULL;  } @@ -126,7 +141,9 @@ static u16 genl_generate_id(void)  	int i;  	for (i = 0; i <= GENL_MAX_ID - GENL_MIN_ID; i++) { -		if (!genl_family_find_byid(id_gen_idx)) +		if (id_gen_idx != GENL_ID_VFS_DQUOT && +		    id_gen_idx != GENL_ID_PMCRAID && +		    !genl_family_find_byid(id_gen_idx))  			return id_gen_idx;  		if (++id_gen_idx > GENL_MAX_ID)  			id_gen_idx = GENL_MIN_ID; @@ -135,62 +152,113 @@ static u16 genl_generate_id(void)  	return 0;  } -static struct genl_multicast_group notify_grp; - -/** - * genl_register_mc_group - register a multicast group - * - * Registers the specified multicast group and notifies userspace - * about the new group. - * - * Returns 0 on success or a negative error code. - * - * @family: The generic netlink family the group shall be registered for. - * @grp: The group to register, must have a name. - */ -int genl_register_mc_group(struct genl_family *family, -			   struct genl_multicast_group *grp) +static int genl_allocate_reserve_groups(int n_groups, int *first_id)  { -	int id;  	unsigned long *new_groups; -	int err = 0; +	int start = 0; +	int i; +	int id; +	bool fits; + +	do { +		if (start == 0) +			id = find_first_zero_bit(mc_groups, +						 mc_groups_longs * +						 BITS_PER_LONG); +		else +			id = find_next_zero_bit(mc_groups, +						mc_groups_longs * BITS_PER_LONG, +						start); + +		fits = true; +		for (i = id; +		     i < min_t(int, id + n_groups, +			       mc_groups_longs * BITS_PER_LONG); +		     i++) { +			if (test_bit(i, mc_groups)) { +				start = i; +				fits = false; +				break; +			} +		} -	BUG_ON(grp->name[0] == '\0'); -	BUG_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL); +		if (id >= mc_groups_longs * BITS_PER_LONG) { +			unsigned long new_longs = mc_groups_longs + +						  BITS_TO_LONGS(n_groups); +			size_t nlen = new_longs * sizeof(unsigned long); + +			if (mc_groups == &mc_group_start) { +				new_groups = kzalloc(nlen, GFP_KERNEL); +				if (!new_groups) +					return -ENOMEM; +				mc_groups = new_groups; +				*mc_groups = mc_group_start; +			} else { +				new_groups = krealloc(mc_groups, nlen, +						      GFP_KERNEL); +				if (!new_groups) +					return -ENOMEM; +				mc_groups = new_groups; +				for (i = 0; i < BITS_TO_LONGS(n_groups); i++) +					mc_groups[mc_groups_longs + i] = 0; +			} +			mc_groups_longs = new_longs; +		} +	} while (!fits); -	genl_lock_all(); +	for (i = id; i < id + n_groups; i++) +		set_bit(i, mc_groups); +	*first_id = id; +	return 0; +} -	/* special-case our own group */ -	if (grp == ¬ify_grp) -		id = GENL_ID_CTRL; -	else -		id = find_first_zero_bit(mc_groups, -					 mc_groups_longs * BITS_PER_LONG); +static struct genl_family genl_ctrl; +static int genl_validate_assign_mc_groups(struct genl_family *family) +{ +	int first_id; +	int n_groups = family->n_mcgrps; +	int err = 0, i; +	bool groups_allocated = false; -	if (id >= mc_groups_longs * BITS_PER_LONG) { -		size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long); +	if (!n_groups) +		return 0; -		if (mc_groups == &mc_group_start) { -			new_groups = kzalloc(nlen, GFP_KERNEL); -			if (!new_groups) { -				err = -ENOMEM; -				goto out; -			} -			mc_groups = new_groups; -			*mc_groups = mc_group_start; -		} else { -			new_groups = krealloc(mc_groups, nlen, GFP_KERNEL); -			if (!new_groups) { -				err = -ENOMEM; -				goto out; -			} -			mc_groups = new_groups; -			mc_groups[mc_groups_longs] = 0; -		} -		mc_groups_longs++; +	for (i = 0; i < n_groups; i++) { +		const struct genl_multicast_group *grp = &family->mcgrps[i]; + +		if (WARN_ON(grp->name[0] == '\0')) +			return -EINVAL; +		if (WARN_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL)) +			return -EINVAL;  	} +	/* special-case our own group and hacks */ +	if (family == &genl_ctrl) { +		first_id = GENL_ID_CTRL; +		BUG_ON(n_groups != 1); +	} else if (strcmp(family->name, "NET_DM") == 0) { +		first_id = 1; +		BUG_ON(n_groups != 1); +	} else if (family->id == GENL_ID_VFS_DQUOT) { +		first_id = GENL_ID_VFS_DQUOT; +		BUG_ON(n_groups != 1); +	} else if (family->id == GENL_ID_PMCRAID) { +		first_id = GENL_ID_PMCRAID; +		BUG_ON(n_groups != 1); +	} else { +		groups_allocated = true; +		err = genl_allocate_reserve_groups(n_groups, &first_id); +		if (err) +			return err; +	} + +	family->mcgrp_offset = first_id; + +	/* if still initializing, can't and don't need to to realloc bitmaps */ +	if (!init_net.genl_sock) +		return 0; +  	if (family->netnsok) {  		struct net *net; @@ -206,9 +274,7 @@ int genl_register_mc_group(struct genl_family *family,  				 * number of _possible_ groups has been  				 * increased on some sockets which is ok.  				 */ -				rcu_read_unlock(); -				netlink_table_ungrab(); -				goto out; +				break;  			}  		}  		rcu_read_unlock(); @@ -216,152 +282,63 @@ int genl_register_mc_group(struct genl_family *family,  	} else {  		err = netlink_change_ngroups(init_net.genl_sock,  					     mc_groups_longs * BITS_PER_LONG); -		if (err) -			goto out;  	} -	grp->id = id; -	set_bit(id, mc_groups); -	list_add_tail(&grp->list, &family->mcast_groups); -	grp->family = family; +	if (groups_allocated && err) { +		for (i = 0; i < family->n_mcgrps; i++) +			clear_bit(family->mcgrp_offset + i, mc_groups); +	} -	genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp); - out: -	genl_unlock_all();  	return err;  } -EXPORT_SYMBOL(genl_register_mc_group); -static void __genl_unregister_mc_group(struct genl_family *family, -				       struct genl_multicast_group *grp) +static void genl_unregister_mc_groups(struct genl_family *family)  {  	struct net *net; -	BUG_ON(grp->family != family); +	int i;  	netlink_table_grab();  	rcu_read_lock(); -	for_each_net_rcu(net) -		__netlink_clear_multicast_users(net->genl_sock, grp->id); +	for_each_net_rcu(net) { +		for (i = 0; i < family->n_mcgrps; i++) +			__netlink_clear_multicast_users( +				net->genl_sock, family->mcgrp_offset + i); +	}  	rcu_read_unlock();  	netlink_table_ungrab(); -	clear_bit(grp->id, mc_groups); -	list_del(&grp->list); -	genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); -	grp->id = 0; -	grp->family = NULL; -} +	for (i = 0; i < family->n_mcgrps; i++) { +		int grp_id = family->mcgrp_offset + i; -/** - * genl_unregister_mc_group - unregister a multicast group - * - * Unregisters the specified multicast group and notifies userspace - * about it. All current listeners on the group are removed. - * - * Note: It is not necessary to unregister all multicast groups before - *       unregistering the family, unregistering the family will cause - *       all assigned multicast groups to be unregistered automatically. - * - * @family: Generic netlink family the group belongs to. - * @grp: The group to unregister, must have been registered successfully - *	 previously. - */ -void genl_unregister_mc_group(struct genl_family *family, -			      struct genl_multicast_group *grp) -{ -	genl_lock_all(); -	__genl_unregister_mc_group(family, grp); -	genl_unlock_all(); -} -EXPORT_SYMBOL(genl_unregister_mc_group); - -static void genl_unregister_mc_groups(struct genl_family *family) -{ -	struct genl_multicast_group *grp, *tmp; - -	list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list) -		__genl_unregister_mc_group(family, grp); +		if (grp_id != 1) +			clear_bit(grp_id, mc_groups); +		genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family, +				&family->mcgrps[i], grp_id); +	}  } -/** - * genl_register_ops - register generic netlink operations - * @family: generic netlink family - * @ops: operations to be registered - * - * Registers the specified operations and assigns them to the specified - * family. Either a doit or dumpit callback must be specified or the - * operation will fail. Only one operation structure per command - * identifier may be registered. - * - * See include/net/genetlink.h for more documenation on the operations - * structure. - * - * Returns 0 on success or a negative error code. - */ -int genl_register_ops(struct genl_family *family, struct genl_ops *ops) +static int genl_validate_ops(const struct genl_family *family)  { -	int err = -EINVAL; +	const struct genl_ops *ops = family->ops; +	unsigned int n_ops = family->n_ops; +	int i, j; -	if (ops->dumpit == NULL && ops->doit == NULL) -		goto errout; - -	if (genl_get_cmd(ops->cmd, family)) { -		err = -EEXIST; -		goto errout; -	} - -	if (ops->dumpit) -		ops->flags |= GENL_CMD_CAP_DUMP; -	if (ops->doit) -		ops->flags |= GENL_CMD_CAP_DO; -	if (ops->policy) -		ops->flags |= GENL_CMD_CAP_HASPOL; - -	genl_lock_all(); -	list_add_tail(&ops->ops_list, &family->ops_list); -	genl_unlock_all(); - -	genl_ctrl_event(CTRL_CMD_NEWOPS, ops); -	err = 0; -errout: -	return err; -} -EXPORT_SYMBOL(genl_register_ops); +	if (WARN_ON(n_ops && !ops)) +		return -EINVAL; -/** - * genl_unregister_ops - unregister generic netlink operations - * @family: generic netlink family - * @ops: operations to be unregistered - * - * Unregisters the specified operations and unassigns them from the - * specified family. The operation blocks until the current message - * processing has finished and doesn't start again until the - * unregister process has finished. - * - * Note: It is not necessary to unregister all operations before - *       unregistering the family, unregistering the family will cause - *       all assigned operations to be unregistered automatically. - * - * Returns 0 on success or a negative error code. - */ -int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops) -{ -	struct genl_ops *rc; +	if (!n_ops) +		return 0; -	genl_lock_all(); -	list_for_each_entry(rc, &family->ops_list, ops_list) { -		if (rc == ops) { -			list_del(&ops->ops_list); -			genl_unlock_all(); -			genl_ctrl_event(CTRL_CMD_DELOPS, ops); -			return 0; -		} +	for (i = 0; i < n_ops; i++) { +		if (ops[i].dumpit == NULL && ops[i].doit == NULL) +			return -EINVAL; +		for (j = i + 1; j < n_ops; j++) +			if (ops[i].cmd == ops[j].cmd) +				return -EINVAL;  	} -	genl_unlock_all(); -	return -ENOENT; +	return 0;  } -EXPORT_SYMBOL(genl_unregister_ops);  /**   * __genl_register_family - register a generic netlink family @@ -372,11 +349,14 @@ EXPORT_SYMBOL(genl_unregister_ops);   * The family id may equal GENL_ID_GENERATE causing an unique id to   * be automatically generated and assigned.   * + * The family's ops array must already be assigned, you can use the + * genl_register_family_with_ops() helper function. + *   * Return 0 on success or a negative error code.   */  int __genl_register_family(struct genl_family *family)  { -	int err = -EINVAL; +	int err = -EINVAL, i;  	if (family->id && family->id < GENL_MIN_ID)  		goto errout; @@ -384,8 +364,9 @@ int __genl_register_family(struct genl_family *family)  	if (family->id > GENL_MAX_ID)  		goto errout; -	INIT_LIST_HEAD(&family->ops_list); -	INIT_LIST_HEAD(&family->mcast_groups); +	err = genl_validate_ops(family); +	if (err) +		return err;  	genl_lock_all(); @@ -418,10 +399,18 @@ int __genl_register_family(struct genl_family *family)  	} else  		family->attrbuf = NULL; +	err = genl_validate_assign_mc_groups(family); +	if (err) +		goto errout_locked; +  	list_add_tail(&family->family_list, genl_family_chain(family->id));  	genl_unlock_all(); -	genl_ctrl_event(CTRL_CMD_NEWFAMILY, family); +	/* send all events */ +	genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0); +	for (i = 0; i < family->n_mcgrps; i++) +		genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family, +				&family->mcgrps[i], family->mcgrp_offset + i);  	return 0; @@ -433,52 +422,6 @@ errout:  EXPORT_SYMBOL(__genl_register_family);  /** - * __genl_register_family_with_ops - register a generic netlink family - * @family: generic netlink family - * @ops: operations to be registered - * @n_ops: number of elements to register - * - * Registers the specified family and operations from the specified table. - * Only one family may be registered with the same family name or identifier. - * - * The family id may equal GENL_ID_GENERATE causing an unique id to - * be automatically generated and assigned. - * - * Either a doit or dumpit callback must be specified for every registered - * operation or the function will fail. Only one operation structure per - * command identifier may be registered. - * - * See include/net/genetlink.h for more documenation on the operations - * structure. - * - * This is equivalent to calling genl_register_family() followed by - * genl_register_ops() for every operation entry in the table taking - * care to unregister the family on error path. - * - * Return 0 on success or a negative error code. - */ -int __genl_register_family_with_ops(struct genl_family *family, -	struct genl_ops *ops, size_t n_ops) -{ -	int err, i; - -	err = __genl_register_family(family); -	if (err) -		return err; - -	for (i = 0; i < n_ops; ++i, ++ops) { -		err = genl_register_ops(family, ops); -		if (err) -			goto err_out; -	} -	return 0; -err_out: -	genl_unregister_family(family); -	return err; -} -EXPORT_SYMBOL(__genl_register_family_with_ops); - -/**   * genl_unregister_family - unregister generic netlink family   * @family: generic netlink family   * @@ -499,11 +442,11 @@ int genl_unregister_family(struct genl_family *family)  			continue;  		list_del(&rc->family_list); -		INIT_LIST_HEAD(&family->ops_list); +		family->n_ops = 0;  		genl_unlock_all();  		kfree(family->attrbuf); -		genl_ctrl_event(CTRL_CMD_DELFAMILY, family); +		genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0);  		return 0;  	} @@ -514,6 +457,26 @@ int genl_unregister_family(struct genl_family *family)  EXPORT_SYMBOL(genl_unregister_family);  /** + * genlmsg_new_unicast - Allocate generic netlink message for unicast + * @payload: size of the message payload + * @info: information on destination + * @flags: the type of memory to allocate + * + * Allocates a new sk_buff large enough to cover the specified payload + * plus required Netlink headers. Will check receiving socket for + * memory mapped i/o capability and use it if enabled. Will fall back + * to non-mapped skb if message size exceeds the frame size of the ring. + */ +struct sk_buff *genlmsg_new_unicast(size_t payload, struct genl_info *info, +				    gfp_t flags) +{ +	size_t len = nlmsg_total_size(genlmsg_total_size(payload)); + +	return netlink_alloc_skb(info->dst_sk, len, info->snd_portid, flags); +} +EXPORT_SYMBOL_GPL(genlmsg_new_unicast); + +/**   * genlmsg_put - Add generic netlink header to netlink message   * @skb: socket buffer holding the message   * @portid: netlink portid the message is addressed to @@ -546,7 +509,8 @@ EXPORT_SYMBOL(genlmsg_put);  static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)  { -	struct genl_ops *ops = cb->data; +	/* our ops are always const - netlink API doesn't propagate that */ +	const struct genl_ops *ops = cb->data;  	int rc;  	genl_lock(); @@ -557,7 +521,8 @@ static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)  static int genl_lock_done(struct netlink_callback *cb)  { -	struct genl_ops *ops = cb->data; +	/* our ops are always const - netlink API doesn't propagate that */ +	const struct genl_ops *ops = cb->data;  	int rc = 0;  	if (ops->done) { @@ -572,7 +537,7 @@ static int genl_family_rcv_msg(struct genl_family *family,  			       struct sk_buff *skb,  			       struct nlmsghdr *nlh)  { -	struct genl_ops *ops; +	const struct genl_ops *ops;  	struct net *net = sock_net(skb->sk);  	struct genl_info info;  	struct genlmsghdr *hdr = nlmsg_data(nlh); @@ -592,7 +557,7 @@ static int genl_family_rcv_msg(struct genl_family *family,  		return -EOPNOTSUPP;  	if ((ops->flags & GENL_ADMIN_PERM) && -	    !capable(CAP_NET_ADMIN)) +	    !netlink_capable(skb, CAP_NET_ADMIN))  		return -EPERM;  	if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { @@ -604,7 +569,8 @@ static int genl_family_rcv_msg(struct genl_family *family,  		if (!family->parallel_ops) {  			struct netlink_dump_control c = {  				.module = family->module, -				.data = ops, +				/* we have const, but the netlink API doesn't */ +				.data = (void *)ops,  				.dump = genl_lock_dumpit,  				.done = genl_lock_done,  			}; @@ -650,6 +616,7 @@ static int genl_family_rcv_msg(struct genl_family *family,  	info.genlhdr = nlmsg_data(nlh);  	info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;  	info.attrs = attrbuf; +	info.dst_sk = skb->sk;  	genl_info_net_set(&info, net);  	memset(&info.user_ptr, 0, sizeof(info.user_ptr)); @@ -726,24 +693,32 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq,  	    nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr))  		goto nla_put_failure; -	if (!list_empty(&family->ops_list)) { +	if (family->n_ops) {  		struct nlattr *nla_ops; -		struct genl_ops *ops; -		int idx = 1; +		int i;  		nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);  		if (nla_ops == NULL)  			goto nla_put_failure; -		list_for_each_entry(ops, &family->ops_list, ops_list) { +		for (i = 0; i < family->n_ops; i++) {  			struct nlattr *nest; +			const struct genl_ops *ops = &family->ops[i]; +			u32 op_flags = ops->flags; -			nest = nla_nest_start(skb, idx++); +			if (ops->dumpit) +				op_flags |= GENL_CMD_CAP_DUMP; +			if (ops->doit) +				op_flags |= GENL_CMD_CAP_DO; +			if (ops->policy) +				op_flags |= GENL_CMD_CAP_HASPOL; + +			nest = nla_nest_start(skb, i + 1);  			if (nest == NULL)  				goto nla_put_failure;  			if (nla_put_u32(skb, CTRL_ATTR_OP_ID, ops->cmd) || -			    nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, ops->flags)) +			    nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags))  				goto nla_put_failure;  			nla_nest_end(skb, nest); @@ -752,23 +727,26 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq,  		nla_nest_end(skb, nla_ops);  	} -	if (!list_empty(&family->mcast_groups)) { -		struct genl_multicast_group *grp; +	if (family->n_mcgrps) {  		struct nlattr *nla_grps; -		int idx = 1; +		int i;  		nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);  		if (nla_grps == NULL)  			goto nla_put_failure; -		list_for_each_entry(grp, &family->mcast_groups, list) { +		for (i = 0; i < family->n_mcgrps; i++) {  			struct nlattr *nest; +			const struct genl_multicast_group *grp; + +			grp = &family->mcgrps[i]; -			nest = nla_nest_start(skb, idx++); +			nest = nla_nest_start(skb, i + 1);  			if (nest == NULL)  				goto nla_put_failure; -			if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id) || +			if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, +					family->mcgrp_offset + i) ||  			    nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME,  					   grp->name))  				goto nla_put_failure; @@ -785,9 +763,10 @@ nla_put_failure:  	return -EMSGSIZE;  } -static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 portid, -				u32 seq, u32 flags, struct sk_buff *skb, -				u8 cmd) +static int ctrl_fill_mcgrp_info(struct genl_family *family, +				const struct genl_multicast_group *grp, +				int grp_id, u32 portid, u32 seq, u32 flags, +				struct sk_buff *skb, u8 cmd)  {  	void *hdr;  	struct nlattr *nla_grps; @@ -797,8 +776,8 @@ static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 portid,  	if (hdr == NULL)  		return -1; -	if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, grp->family->name) || -	    nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, grp->family->id)) +	if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || +	    nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id))  		goto nla_put_failure;  	nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); @@ -809,7 +788,7 @@ static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 portid,  	if (nest == NULL)  		goto nla_put_failure; -	if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id) || +	if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp_id) ||  	    nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME,  			   grp->name))  		goto nla_put_failure; @@ -875,8 +854,10 @@ static struct sk_buff *ctrl_build_family_msg(struct genl_family *family,  	return skb;  } -static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp, -					    u32 portid, int seq, u8 cmd) +static struct sk_buff * +ctrl_build_mcgrp_msg(struct genl_family *family, +		     const struct genl_multicast_group *grp, +		     int grp_id, u32 portid, int seq, u8 cmd)  {  	struct sk_buff *skb;  	int err; @@ -885,7 +866,8 @@ static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp,  	if (skb == NULL)  		return ERR_PTR(-ENOBUFS); -	err = ctrl_fill_mcgrp_info(grp, portid, seq, 0, skb, cmd); +	err = ctrl_fill_mcgrp_info(family, grp, grp_id, portid, +				   seq, 0, skb, cmd);  	if (err < 0) {  		nlmsg_free(skb);  		return ERR_PTR(err); @@ -947,11 +929,11 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)  	return genlmsg_reply(msg, info);  } -static int genl_ctrl_event(int event, void *data) +static int genl_ctrl_event(int event, struct genl_family *family, +			   const struct genl_multicast_group *grp, +			   int grp_id)  {  	struct sk_buff *msg; -	struct genl_family *family; -	struct genl_multicast_group *grp;  	/* genl is still initialising */  	if (!init_net.genl_sock) @@ -960,14 +942,13 @@ static int genl_ctrl_event(int event, void *data)  	switch (event) {  	case CTRL_CMD_NEWFAMILY:  	case CTRL_CMD_DELFAMILY: -		family = data; +		WARN_ON(grp);  		msg = ctrl_build_family_msg(family, 0, 0, event);  		break;  	case CTRL_CMD_NEWMCAST_GRP:  	case CTRL_CMD_DELMCAST_GRP: -		grp = data; -		family = grp->family; -		msg = ctrl_build_mcgrp_msg(data, 0, 0, event); +		BUG_ON(!grp); +		msg = ctrl_build_mcgrp_msg(family, grp, grp_id, 0, 0, event);  		break;  	default:  		return -EINVAL; @@ -977,26 +958,29 @@ static int genl_ctrl_event(int event, void *data)  		return PTR_ERR(msg);  	if (!family->netnsok) { -		genlmsg_multicast_netns(&init_net, msg, 0, -					GENL_ID_CTRL, GFP_KERNEL); +		genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0, +					0, GFP_KERNEL);  	} else {  		rcu_read_lock(); -		genlmsg_multicast_allns(msg, 0, GENL_ID_CTRL, GFP_ATOMIC); +		genlmsg_multicast_allns(&genl_ctrl, msg, 0, +					0, GFP_ATOMIC);  		rcu_read_unlock();  	}  	return 0;  } -static struct genl_ops genl_ctrl_ops = { -	.cmd		= CTRL_CMD_GETFAMILY, -	.doit		= ctrl_getfamily, -	.dumpit		= ctrl_dumpfamily, -	.policy		= ctrl_policy, +static struct genl_ops genl_ctrl_ops[] = { +	{ +		.cmd		= CTRL_CMD_GETFAMILY, +		.doit		= ctrl_getfamily, +		.dumpit		= ctrl_dumpfamily, +		.policy		= ctrl_policy, +	},  }; -static struct genl_multicast_group notify_grp = { -	.name		= "notify", +static struct genl_multicast_group genl_ctrl_groups[] = { +	{ .name = "notify", },  };  static int __net_init genl_pernet_init(struct net *net) @@ -1036,7 +1020,8 @@ static int __init genl_init(void)  	for (i = 0; i < GENL_FAM_TAB_SIZE; i++)  		INIT_LIST_HEAD(&family_ht[i]); -	err = genl_register_family_with_ops(&genl_ctrl, &genl_ctrl_ops, 1); +	err = genl_register_family_with_ops_groups(&genl_ctrl, genl_ctrl_ops, +						   genl_ctrl_groups);  	if (err < 0)  		goto problem; @@ -1044,10 +1029,6 @@ static int __init genl_init(void)  	if (err)  		goto problem; -	err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); -	if (err < 0) -		goto problem; -  	return 0;  problem: @@ -1085,14 +1066,18 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,  	return err;  } -int genlmsg_multicast_allns(struct sk_buff *skb, u32 portid, unsigned int group, -			    gfp_t flags) +int genlmsg_multicast_allns(struct genl_family *family, struct sk_buff *skb, +			    u32 portid, unsigned int group, gfp_t flags)  { +	if (WARN_ON_ONCE(group >= family->n_mcgrps)) +		return -EINVAL; +	group = family->mcgrp_offset + group;  	return genlmsg_mcast(skb, portid, group, flags);  }  EXPORT_SYMBOL(genlmsg_multicast_allns); -void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group, +void genl_notify(struct genl_family *family, +		 struct sk_buff *skb, struct net *net, u32 portid, u32 group,  		 struct nlmsghdr *nlh, gfp_t flags)  {  	struct sock *sk = net->genl_sock; @@ -1101,6 +1086,9 @@ void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group,  	if (nlh)  		report = nlmsg_report(nlh); +	if (WARN_ON_ONCE(group >= family->n_mcgrps)) +		return; +	group = family->mcgrp_offset + group;  	nlmsg_notify(sk, skb, portid, group, report, flags);  }  EXPORT_SYMBOL(genl_notify);  | 
