Commit 50a896cf authored by Johannes Berg's avatar Johannes Berg Committed by David S. Miller
Browse files

genetlink: properly support per-op policy dumping



Add support for per-op policy dumping. The data is pretty much
as before, except that now the assumption that the policy with
index 0 is "the" policy no longer holds - you now need to look
at the new CTRL_ATTR_OP_POLICY attribute which is a nested attr
(indexed by op) containing attributes for do and dump policies.

When a single op is requested, the CTRL_ATTR_OP_POLICY will be
added in the same way, since do and dump policies may differ.

v2:
 - conditionally advertise per-command policies only if there
   actually is a policy being used for the do/dump and it's
   present at all

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent aa85ee5f
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ enum {
	CTRL_ATTR_OPS,
	CTRL_ATTR_MCAST_GROUPS,
	CTRL_ATTR_POLICY,
	CTRL_ATTR_OP_POLICY,
	__CTRL_ATTR_MAX,
};

@@ -85,6 +86,15 @@ enum {
	__CTRL_ATTR_MCAST_GRP_MAX,
};

enum {
	CTRL_ATTR_POLICY_UNSPEC,
	CTRL_ATTR_POLICY_DO,
	CTRL_ATTR_POLICY_DUMP,

	__CTRL_ATTR_POLICY_DUMP_MAX,
	CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1
};

#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)


+92 −10
Original line number Diff line number Diff line
@@ -1112,7 +1112,10 @@ static int genl_ctrl_event(int event, const struct genl_family *family,

struct ctrl_dump_policy_ctx {
	struct netlink_policy_dump_state *state;
	const struct genl_family *rt;
	unsigned int opidx;
	u16 fam_id;
	u8 policies:1;
};

static const struct nla_policy ctrl_policy_policy[] = {
@@ -1127,6 +1130,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb)
	struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx;
	struct nlattr **tb = info->attrs;
	const struct genl_family *rt;
	struct genl_ops op;
	int err, i;

	BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));

@@ -1147,11 +1152,23 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb)
	if (!rt)
		return -ENOENT;

	if (!rt->policy)
		return -ENODATA;
	ctx->rt = rt;

	for (i = 0; i < genl_get_cmd_cnt(rt); i++) {
		genl_get_cmd_by_index(i, rt, &op);

		if (op.policy) {
			err = netlink_policy_dump_add_policy(&ctx->state,
							     op.policy,
							     op.maxattr);
			if (err)
				return err;
		}
	}

	return netlink_policy_dump_add_policy(&ctx->state, rt->policy,
					      rt->maxattr);
	if (!ctx->state)
		return -ENODATA;
	return 0;
}

static void *ctrl_dumppolicy_prep(struct sk_buff *skb,
@@ -1172,12 +1189,78 @@ static void *ctrl_dumppolicy_prep(struct sk_buff *skb,
	return hdr;
}

static int ctrl_dumppolicy_put_op(struct sk_buff *skb,
				  struct netlink_callback *cb,
			          struct genl_ops *op)
{
	struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx;
	struct nlattr *nest_pol, *nest_op;
	void *hdr;
	int idx;

	/* skip if we have nothing to show */
	if (!op->policy)
		return 0;
	if (!op->doit &&
	    (!op->dumpit || op->validate & GENL_DONT_VALIDATE_DUMP))
		return 0;

	hdr = ctrl_dumppolicy_prep(skb, cb);
	if (!hdr)
		return -ENOBUFS;

	nest_pol = nla_nest_start(skb, CTRL_ATTR_OP_POLICY);
	if (!nest_pol)
		goto err;

	nest_op = nla_nest_start(skb, op->cmd);
	if (!nest_op)
		goto err;

	/* for now both do/dump are always the same */
	idx = netlink_policy_dump_get_policy_idx(ctx->state,
						 op->policy,
						 op->maxattr);

	if (op->doit && nla_put_u32(skb, CTRL_ATTR_POLICY_DO, idx))
		goto err;

	if (op->dumpit && !(op->validate & GENL_DONT_VALIDATE_DUMP) &&
	    nla_put_u32(skb, CTRL_ATTR_POLICY_DUMP, idx))
		goto err;

	nla_nest_end(skb, nest_op);
	nla_nest_end(skb, nest_pol);
	genlmsg_end(skb, hdr);

	return 0;
err:
	genlmsg_cancel(skb, hdr);
	return -ENOBUFS;
}

static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx;
	void *hdr;

	if (!ctx->policies) {
		while (ctx->opidx < genl_get_cmd_cnt(ctx->rt)) {
			struct genl_ops op;

			genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op);

			if (ctrl_dumppolicy_put_op(skb, cb, &op))
				return skb->len;

			ctx->opidx++;
		}

		/* completed with the per-op policy index list */
		ctx->policies = true;
	}

	while (netlink_policy_dump_loop(ctx->state)) {
		void *hdr;
		struct nlattr *nest;

		hdr = ctrl_dumppolicy_prep(skb, cb);
@@ -1194,13 +1277,12 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
		nla_nest_end(skb, nest);

		genlmsg_end(skb, hdr);
		continue;
	}

	return skb->len;

nla_put_failure:
	genlmsg_cancel(skb, hdr);
		break;
	}

	return skb->len;
}