Commit d44dc741 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-genetlink-parse-attrs-for-dumpit-callback'



Jiri Pirko says:

====================
net: genetlink: parse attrs for dumpit() callback

In generic netlink, parsing attributes for doit() callback is already
implemented. They are available in info->attrs.

For dumpit() however, each user which is interested in attributes have to
parse it manually. Even though the attributes may be (depending on flag)
already validated (by parse function).

Make usage of attributes in dumpit() more convenient and prepare
info->attrs too.

Patchset also make the existing users of genl_family_attrbuf() converted
to use info->attrs and removes the helper.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 248d45f1 ee85da53
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -75,8 +75,6 @@ struct genl_family {
	struct module		*module;
};

struct nlattr **genl_family_attrbuf(const struct genl_family *family);

/**
 * struct genl_info - receiving information
 * @snd_seq: sending sequence number
@@ -127,6 +125,24 @@ enum genl_validate_flags {
	GENL_DONT_VALIDATE_DUMP_STRICT		= BIT(2),
};

/**
 * struct genl_info - info that is available during dumpit op call
 * @family: generic netlink family - for internal genl code usage
 * @ops: generic netlink ops - for internal genl code usage
 * @attrs: netlink attributes
 */
struct genl_dumpit_info {
	const struct genl_family *family;
	const struct genl_ops *ops;
	struct nlattr **attrs;
};

static inline const struct genl_dumpit_info *
genl_dumpit_info(struct netlink_callback *cb)
{
	return cb->data;
}

/**
 * struct genl_ops - generic netlink operations
 * @cmd: command identifier
+6 −32
Original line number Diff line number Diff line
@@ -3943,29 +3943,19 @@ static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb,
static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
					     struct netlink_callback *cb)
{
	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
	u64 ret_offset, start_offset, end_offset = 0;
	struct nlattr **attrs = info->attrs;
	struct devlink_region *region;
	struct nlattr *chunks_attr;
	const char *region_name;
	struct devlink *devlink;
	struct nlattr **attrs;
	bool dump = true;
	void *hdr;
	int err;

	start_offset = *((u64 *)&cb->args[0]);

	attrs = kmalloc_array(DEVLINK_ATTR_MAX + 1, sizeof(*attrs), GFP_KERNEL);
	if (!attrs)
		return -ENOMEM;

	err = nlmsg_parse_deprecated(cb->nlh,
				     GENL_HDRLEN + devlink_nl_family.hdrsize,
				     attrs, DEVLINK_ATTR_MAX,
				     devlink_nl_family.policy, cb->extack);
	if (err)
		goto out_free;

	mutex_lock(&devlink_mutex);
	devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
	if (IS_ERR(devlink)) {
@@ -4042,7 +4032,6 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
	genlmsg_end(skb, hdr);
	mutex_unlock(&devlink->lock);
	mutex_unlock(&devlink_mutex);
	kfree(attrs);

	return skb->len;

@@ -4052,8 +4041,6 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
	mutex_unlock(&devlink->lock);
out_dev:
	mutex_unlock(&devlink_mutex);
out_free:
	kfree(attrs);
	return err;
}

@@ -4995,21 +4982,10 @@ devlink_health_reporter_get_from_info(struct devlink *devlink,
static struct devlink_health_reporter *
devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
{
	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
	struct devlink_health_reporter *reporter;
	struct nlattr **attrs = info->attrs;
	struct devlink *devlink;
	struct nlattr **attrs;
	int err;

	attrs = kmalloc_array(DEVLINK_ATTR_MAX + 1, sizeof(*attrs), GFP_KERNEL);
	if (!attrs)
		return NULL;

	err = nlmsg_parse_deprecated(cb->nlh,
				     GENL_HDRLEN + devlink_nl_family.hdrsize,
				     attrs, DEVLINK_ATTR_MAX,
				     devlink_nl_family.policy, cb->extack);
	if (err)
		goto free;

	mutex_lock(&devlink_mutex);
	devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
@@ -5018,12 +4994,9 @@ devlink_health_reporter_get_from_cb(struct netlink_callback *cb)

	reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
	mutex_unlock(&devlink_mutex);
	kfree(attrs);
	return reporter;
unlock:
	mutex_unlock(&devlink_mutex);
free:
	kfree(attrs);
	return NULL;
}

@@ -6154,7 +6127,8 @@ static const struct genl_ops devlink_nl_ops[] = {
	},
	{
		.cmd = DEVLINK_CMD_REGION_READ,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.validate = GENL_DONT_VALIDATE_STRICT |
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		.dumpit = devlink_nl_cmd_region_read_dumpit,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+14 −25
Original line number Diff line number Diff line
@@ -236,21 +236,14 @@ nl802154_prepare_wpan_dev_dump(struct sk_buff *skb,
			       struct cfg802154_registered_device **rdev,
			       struct wpan_dev **wpan_dev)
{
	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
	int err;

	rtnl_lock();

	if (!cb->args[0]) {
		err = nlmsg_parse_deprecated(cb->nlh,
					     GENL_HDRLEN + nl802154_fam.hdrsize,
					     genl_family_attrbuf(&nl802154_fam),
					     nl802154_fam.maxattr,
					     nl802154_policy, NULL);
		if (err)
			goto out_unlock;

		*wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk),
							    genl_family_attrbuf(&nl802154_fam));
							    info->attrs);
		if (IS_ERR(*wpan_dev)) {
			err = PTR_ERR(*wpan_dev);
			goto out_unlock;
@@ -557,17 +550,8 @@ static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
					struct netlink_callback *cb,
					struct nl802154_dump_wpan_phy_state *state)
{
	struct nlattr **tb = genl_family_attrbuf(&nl802154_fam);
	int ret = nlmsg_parse_deprecated(cb->nlh,
					 GENL_HDRLEN + nl802154_fam.hdrsize,
					 tb, nl802154_fam.maxattr,
					 nl802154_policy, NULL);

	/* TODO check if we can handle error here,
	 * we have no backward compatibility
	 */
	if (ret)
		return 0;
	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
	struct nlattr **tb = info->attrs;

	if (tb[NL802154_ATTR_WPAN_PHY])
		state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
@@ -2203,7 +2187,8 @@ static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
static const struct genl_ops nl802154_ops[] = {
	{
		.cmd = NL802154_CMD_GET_WPAN_PHY,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.validate = GENL_DONT_VALIDATE_STRICT |
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		.doit = nl802154_get_wpan_phy,
		.dumpit = nl802154_dump_wpan_phy,
		.done = nl802154_dump_wpan_phy_done,
@@ -2343,7 +2328,8 @@ static const struct genl_ops nl802154_ops[] = {
	},
	{
		.cmd = NL802154_CMD_GET_SEC_KEY,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.validate = GENL_DONT_VALIDATE_STRICT |
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		/* TODO .doit by matching key id? */
		.dumpit = nl802154_dump_llsec_key,
		.flags = GENL_ADMIN_PERM,
@@ -2369,7 +2355,8 @@ static const struct genl_ops nl802154_ops[] = {
	/* TODO unique identifier must short+pan OR extended_addr */
	{
		.cmd = NL802154_CMD_GET_SEC_DEV,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.validate = GENL_DONT_VALIDATE_STRICT |
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		/* TODO .doit by matching extended_addr? */
		.dumpit = nl802154_dump_llsec_dev,
		.flags = GENL_ADMIN_PERM,
@@ -2395,7 +2382,8 @@ static const struct genl_ops nl802154_ops[] = {
	/* TODO remove complete devkey, put it as nested? */
	{
		.cmd = NL802154_CMD_GET_SEC_DEVKEY,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.validate = GENL_DONT_VALIDATE_STRICT |
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		/* TODO doit by matching ??? */
		.dumpit = nl802154_dump_llsec_devkey,
		.flags = GENL_ADMIN_PERM,
@@ -2420,7 +2408,8 @@ static const struct genl_ops nl802154_ops[] = {
	},
	{
		.cmd = NL802154_CMD_GET_SEC_LEVEL,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.validate = GENL_DONT_VALIDATE_STRICT |
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		/* TODO .doit by matching frame_type? */
		.dumpit = nl802154_dump_llsec_seclevel,
		.flags = GENL_ADMIN_PERM,
+176 −119
Original line number Diff line number Diff line
@@ -458,10 +458,58 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
}
EXPORT_SYMBOL(genlmsg_put);

static struct genl_dumpit_info *genl_dumpit_info_alloc(void)
{
	return kmalloc(sizeof(struct genl_dumpit_info), GFP_KERNEL);
}

static void genl_dumpit_info_free(const struct genl_dumpit_info *info)
{
	kfree(info);
}

static struct nlattr **
genl_family_rcv_msg_attrs_parse(const struct genl_family *family,
				struct nlmsghdr *nlh,
				struct netlink_ext_ack *extack,
				const struct genl_ops *ops,
				int hdrlen,
				enum genl_validate_flags no_strict_flag)
{
	enum netlink_validation validate = ops->validate & no_strict_flag ?
					   NL_VALIDATE_LIBERAL :
					   NL_VALIDATE_STRICT;
	struct nlattr **attrbuf;
	int err;

	if (family->maxattr && family->parallel_ops) {
		attrbuf = kmalloc_array(family->maxattr + 1,
					sizeof(struct nlattr *), GFP_KERNEL);
		if (!attrbuf)
			return ERR_PTR(-ENOMEM);
	} else {
		attrbuf = family->attrbuf;
	}

	err = __nlmsg_parse(nlh, hdrlen, attrbuf, family->maxattr,
			    family->policy, validate, extack);
	if (err && family->maxattr && family->parallel_ops) {
		kfree(attrbuf);
		return ERR_PTR(err);
	}
	return attrbuf;
}

static void genl_family_rcv_msg_attrs_free(const struct genl_family *family,
					   struct nlattr **attrbuf)
{
	if (family->maxattr && family->parallel_ops)
		kfree(attrbuf);
}

static int genl_lock_start(struct netlink_callback *cb)
{
	/* our ops are always const - netlink API doesn't propagate that */
	const struct genl_ops *ops = cb->data;
	const struct genl_ops *ops = genl_dumpit_info(cb)->ops;
	int rc = 0;

	if (ops->start) {
@@ -474,8 +522,7 @@ static int genl_lock_start(struct netlink_callback *cb)

static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
	/* our ops are always const - netlink API doesn't propagate that */
	const struct genl_ops *ops = cb->data;
	const struct genl_ops *ops = genl_dumpit_info(cb)->ops;
	int rc;

	genl_lock();
@@ -486,8 +533,8 @@ static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)

static int genl_lock_done(struct netlink_callback *cb)
{
	/* our ops are always const - netlink API doesn't propagate that */
	const struct genl_ops *ops = cb->data;
	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
	const struct genl_ops *ops = info->ops;
	int rc = 0;

	if (ops->done) {
@@ -495,120 +542,112 @@ static int genl_lock_done(struct netlink_callback *cb)
		rc = ops->done(cb);
		genl_unlock();
	}
	genl_family_rcv_msg_attrs_free(info->family, info->attrs);
	genl_dumpit_info_free(info);
	return rc;
}

static int genl_family_rcv_msg(const struct genl_family *family,
			       struct sk_buff *skb,
			       struct nlmsghdr *nlh,
			       struct netlink_ext_ack *extack)
static int genl_parallel_done(struct netlink_callback *cb)
{
	const struct genl_ops *ops;
	struct net *net = sock_net(skb->sk);
	struct genl_info info;
	struct genlmsghdr *hdr = nlmsg_data(nlh);
	struct nlattr **attrbuf;
	int hdrlen, err;

	/* this family doesn't exist in this netns */
	if (!family->netnsok && !net_eq(net, &init_net))
		return -ENOENT;

	hdrlen = GENL_HDRLEN + family->hdrsize;
	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
		return -EINVAL;

	ops = genl_get_cmd(hdr->cmd, family);
	if (ops == NULL)
		return -EOPNOTSUPP;

	if ((ops->flags & GENL_ADMIN_PERM) &&
	    !netlink_capable(skb, CAP_NET_ADMIN))
		return -EPERM;
	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
	const struct genl_ops *ops = info->ops;
	int rc = 0;

	if ((ops->flags & GENL_UNS_ADMIN_PERM) &&
	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
		return -EPERM;
	if (ops->done)
		rc = ops->done(cb);
	genl_family_rcv_msg_attrs_free(info->family, info->attrs);
	genl_dumpit_info_free(info);
	return rc;
}

	if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) {
		int rc;
static int genl_family_rcv_msg_dumpit(const struct genl_family *family,
				      struct sk_buff *skb,
				      struct nlmsghdr *nlh,
				      struct netlink_ext_ack *extack,
				      const struct genl_ops *ops,
				      int hdrlen, struct net *net)
{
	struct genl_dumpit_info *info;
	struct nlattr **attrs = NULL;
	int err;

		if (ops->dumpit == NULL)
	if (!ops->dumpit)
		return -EOPNOTSUPP;

		if (!(ops->validate & GENL_DONT_VALIDATE_DUMP)) {
			int hdrlen = GENL_HDRLEN + family->hdrsize;
	if (ops->validate & GENL_DONT_VALIDATE_DUMP)
		goto no_attrs;

	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
		return -EINVAL;

			if (family->maxattr) {
				unsigned int validate = NL_VALIDATE_STRICT;

				if (ops->validate &
				    GENL_DONT_VALIDATE_DUMP_STRICT)
					validate = NL_VALIDATE_LIBERAL;
				rc = __nla_validate(nlmsg_attrdata(nlh, hdrlen),
						    nlmsg_attrlen(nlh, hdrlen),
						    family->maxattr,
						    family->policy,
						    validate, extack);
				if (rc)
					return rc;
			}
	if (!family->maxattr)
		goto no_attrs;

	attrs = genl_family_rcv_msg_attrs_parse(family, nlh, extack,
						ops, hdrlen,
						GENL_DONT_VALIDATE_DUMP_STRICT);
	if (IS_ERR(attrs))
		return PTR_ERR(attrs);

no_attrs:
	/* Allocate dumpit info. It is going to be freed by done() callback. */
	info = genl_dumpit_info_alloc();
	if (!info) {
		genl_family_rcv_msg_attrs_free(family, attrs);
		return -ENOMEM;
	}

	info->family = family;
	info->ops = ops;
	info->attrs = attrs;

	if (!family->parallel_ops) {
		struct netlink_dump_control c = {
			.module = family->module,
				/* we have const, but the netlink API doesn't */
				.data = (void *)ops,
			.data = info,
			.start = genl_lock_start,
			.dump = genl_lock_dumpit,
			.done = genl_lock_done,
		};

		genl_unlock();
			rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
		err = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
		genl_lock();

	} else {
		struct netlink_dump_control c = {
			.module = family->module,
			.data = info,
			.start = ops->start,
			.dump = ops->dumpit,
				.done = ops->done,
			.done = genl_parallel_done,
		};

			rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
		err = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
	}

		return rc;
	return err;
}

	if (ops->doit == NULL)
		return -EOPNOTSUPP;

	if (family->maxattr && family->parallel_ops) {
		attrbuf = kmalloc_array(family->maxattr + 1,
					sizeof(struct nlattr *),
					GFP_KERNEL);
		if (attrbuf == NULL)
			return -ENOMEM;
	} else
		attrbuf = family->attrbuf;

	if (attrbuf) {
		enum netlink_validation validate = NL_VALIDATE_STRICT;
static int genl_family_rcv_msg_doit(const struct genl_family *family,
				    struct sk_buff *skb,
				    struct nlmsghdr *nlh,
				    struct netlink_ext_ack *extack,
				    const struct genl_ops *ops,
				    int hdrlen, struct net *net)
{
	struct nlattr **attrbuf;
	struct genl_info info;
	int err;

		if (ops->validate & GENL_DONT_VALIDATE_STRICT)
			validate = NL_VALIDATE_LIBERAL;
	if (!ops->doit)
		return -EOPNOTSUPP;

		err = __nlmsg_parse(nlh, hdrlen, attrbuf, family->maxattr,
				    family->policy, validate, extack);
		if (err < 0)
			goto out;
	}
	attrbuf = genl_family_rcv_msg_attrs_parse(family, nlh, extack,
						  ops, hdrlen,
						  GENL_DONT_VALIDATE_STRICT);
	if (IS_ERR(attrbuf))
		return PTR_ERR(attrbuf);

	info.snd_seq = nlh->nlmsg_seq;
	info.snd_portid = NETLINK_CB(skb).portid;
@@ -632,12 +671,49 @@ static int genl_family_rcv_msg(const struct genl_family *family,
		family->post_doit(ops, skb, &info);

out:
	if (family->parallel_ops)
		kfree(attrbuf);
	genl_family_rcv_msg_attrs_free(family, attrbuf);

	return err;
}

static int genl_family_rcv_msg(const struct genl_family *family,
			       struct sk_buff *skb,
			       struct nlmsghdr *nlh,
			       struct netlink_ext_ack *extack)
{
	const struct genl_ops *ops;
	struct net *net = sock_net(skb->sk);
	struct genlmsghdr *hdr = nlmsg_data(nlh);
	int hdrlen;

	/* this family doesn't exist in this netns */
	if (!family->netnsok && !net_eq(net, &init_net))
		return -ENOENT;

	hdrlen = GENL_HDRLEN + family->hdrsize;
	if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
		return -EINVAL;

	ops = genl_get_cmd(hdr->cmd, family);
	if (ops == NULL)
		return -EOPNOTSUPP;

	if ((ops->flags & GENL_ADMIN_PERM) &&
	    !netlink_capable(skb, CAP_NET_ADMIN))
		return -EPERM;

	if ((ops->flags & GENL_UNS_ADMIN_PERM) &&
	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

	if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP)
		return genl_family_rcv_msg_dumpit(family, skb, nlh, extack,
						  ops, hdrlen, net);
	else
		return genl_family_rcv_msg_doit(family, skb, nlh, extack,
						ops, hdrlen, net);
}

static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
			struct netlink_ext_ack *extack)
{
@@ -1088,25 +1164,6 @@ static int __init genl_init(void)

subsys_initcall(genl_init);

/**
 * genl_family_attrbuf - return family's attrbuf
 * @family: the family
 *
 * Return the family's attrbuf, while validating that it's
 * actually valid to access it.
 *
 * You cannot use this function with a family that has parallel_ops
 * and you can only use it within (pre/post) doit/dumpit callbacks.
 */
struct nlattr **genl_family_attrbuf(const struct genl_family *family)
{
	if (!WARN_ON(family->parallel_ops))
		lockdep_assert_held(&genl_mutex);

	return family->attrbuf;
}
EXPORT_SYMBOL(genl_family_attrbuf);

static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
			 gfp_t flags)
{
+5 −12
Original line number Diff line number Diff line
@@ -102,22 +102,14 @@ static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,

static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb)
{
	struct nlattr **attrbuf = genl_family_attrbuf(&nfc_genl_family);
	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
	struct nfc_dev *dev;
	int rc;
	u32 idx;

	rc = nlmsg_parse_deprecated(cb->nlh,
				    GENL_HDRLEN + nfc_genl_family.hdrsize,
				    attrbuf, nfc_genl_family.maxattr,
				    nfc_genl_policy, NULL);
	if (rc < 0)
		return ERR_PTR(rc);

	if (!attrbuf[NFC_ATTR_DEVICE_INDEX])
	if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
		return ERR_PTR(-EINVAL);

	idx = nla_get_u32(attrbuf[NFC_ATTR_DEVICE_INDEX]);
	idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);

	dev = nfc_get_device(idx);
	if (!dev)
@@ -1697,7 +1689,8 @@ static const struct genl_ops nfc_genl_ops[] = {
	},
	{
		.cmd = NFC_CMD_GET_TARGET,
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.validate = GENL_DONT_VALIDATE_STRICT |
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		.dumpit = nfc_genl_dump_targets,
		.done = nfc_genl_dump_targets_done,
	},
Loading