Commit 186e26eb authored by David S. Miller's avatar David S. Miller

Merge branch 'genetlink-per-op-policy-export'

Johannes Berg says:

====================
genetlink per-op policy export

Here's a respin, now including Jakub's patch last so that it will
do the right thing from the start.

The first patch remains the same, of course; the others have mostly
some rebasing going on, except for the actual export patch (patch 4)
which is adjusted per Jakub's review comments about exporting the
policy only if it's actually used for do/dump.

To see that, the dump for "nlctrl" (i.e. the generic netlink control)
is instructive, because the ops are this:

        {
                .cmd            = CTRL_CMD_GETFAMILY,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .policy         = ctrl_policy_family,
                .maxattr        = ARRAY_SIZE(ctrl_policy_family) - 1,
                .doit           = ctrl_getfamily,
                .dumpit         = ctrl_dumpfamily,
        },
        {
                .cmd            = CTRL_CMD_GETPOLICY,
                .policy         = ctrl_policy_policy,
                .maxattr        = ARRAY_SIZE(ctrl_policy_policy) - 1,
                .start          = ctrl_dumppolicy_start,
                .dumpit         = ctrl_dumppolicy,
                .done           = ctrl_dumppolicy_done,
        },

So we exercise both "don't have doit" and "GENL_DONT_VALIDATE_DUMP"
parts, and get (with the current genl patch):

$ genl ctrl policy name nlctrl
	ID: 0x10  op 3 policies: do=0
	ID: 0x10  op 10 policies: dump=1
	ID: 0x10  policy[0]:attr[1]: type=U16 range:[0,65535]
	ID: 0x10  policy[0]:attr[2]: type=NUL_STRING max len:15
	ID: 0x10  policy[1]:attr[1]: type=U16 range:[0,65535]
	ID: 0x10  policy[1]:attr[2]: type=NUL_STRING max len:15
	ID: 0x10  policy[1]:attr[10]: type=U32 range:[0,4294967295]
====================
Reviewed-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 678cdd49 e992a6ed
...@@ -1937,9 +1937,12 @@ void nla_get_range_signed(const struct nla_policy *pt, ...@@ -1937,9 +1937,12 @@ void nla_get_range_signed(const struct nla_policy *pt,
struct netlink_policy_dump_state; struct netlink_policy_dump_state;
int netlink_policy_dump_start(const struct nla_policy *policy, int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
unsigned int maxtype, const struct nla_policy *policy,
struct netlink_policy_dump_state **state); unsigned int maxtype);
int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
const struct nla_policy *policy,
unsigned int maxtype);
bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state); bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state);
int netlink_policy_dump_write(struct sk_buff *skb, int netlink_policy_dump_write(struct sk_buff *skb,
struct netlink_policy_dump_state *state); struct netlink_policy_dump_state *state);
......
...@@ -64,6 +64,8 @@ enum { ...@@ -64,6 +64,8 @@ enum {
CTRL_ATTR_OPS, CTRL_ATTR_OPS,
CTRL_ATTR_MCAST_GROUPS, CTRL_ATTR_MCAST_GROUPS,
CTRL_ATTR_POLICY, CTRL_ATTR_POLICY,
CTRL_ATTR_OP_POLICY,
CTRL_ATTR_OP,
__CTRL_ATTR_MAX, __CTRL_ATTR_MAX,
}; };
...@@ -85,6 +87,15 @@ enum { ...@@ -85,6 +87,15 @@ enum {
__CTRL_ATTR_MCAST_GRP_MAX, __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) #define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
......
...@@ -123,7 +123,7 @@ static void genl_op_from_full(const struct genl_family *family, ...@@ -123,7 +123,7 @@ static void genl_op_from_full(const struct genl_family *family,
op->policy = family->policy; op->policy = family->policy;
} }
static int genl_get_cmd_full(u8 cmd, const struct genl_family *family, static int genl_get_cmd_full(u32 cmd, const struct genl_family *family,
struct genl_ops *op) struct genl_ops *op)
{ {
int i; int i;
...@@ -152,7 +152,7 @@ static void genl_op_from_small(const struct genl_family *family, ...@@ -152,7 +152,7 @@ static void genl_op_from_small(const struct genl_family *family,
op->policy = family->policy; op->policy = family->policy;
} }
static int genl_get_cmd_small(u8 cmd, const struct genl_family *family, static int genl_get_cmd_small(u32 cmd, const struct genl_family *family,
struct genl_ops *op) struct genl_ops *op)
{ {
int i; int i;
...@@ -166,7 +166,7 @@ static int genl_get_cmd_small(u8 cmd, const struct genl_family *family, ...@@ -166,7 +166,7 @@ static int genl_get_cmd_small(u8 cmd, const struct genl_family *family,
return -ENOENT; return -ENOENT;
} }
static int genl_get_cmd(u8 cmd, const struct genl_family *family, static int genl_get_cmd(u32 cmd, const struct genl_family *family,
struct genl_ops *op) struct genl_ops *op)
{ {
if (!genl_get_cmd_full(cmd, family, op)) if (!genl_get_cmd_full(cmd, family, op))
...@@ -1112,13 +1112,19 @@ static int genl_ctrl_event(int event, const struct genl_family *family, ...@@ -1112,13 +1112,19 @@ static int genl_ctrl_event(int event, const struct genl_family *family,
struct ctrl_dump_policy_ctx { struct ctrl_dump_policy_ctx {
struct netlink_policy_dump_state *state; struct netlink_policy_dump_state *state;
const struct genl_family *rt;
unsigned int opidx;
u32 op;
u16 fam_id; u16 fam_id;
u8 policies:1,
single_op:1;
}; };
static const struct nla_policy ctrl_policy_policy[] = { static const struct nla_policy ctrl_policy_policy[] = {
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING,
.len = GENL_NAMSIZ - 1 }, .len = GENL_NAMSIZ - 1 },
[CTRL_ATTR_OP] = { .type = NLA_U32 },
}; };
static int ctrl_dumppolicy_start(struct netlink_callback *cb) static int ctrl_dumppolicy_start(struct netlink_callback *cb)
...@@ -1127,6 +1133,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) ...@@ -1127,6 +1133,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb)
struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx;
struct nlattr **tb = info->attrs; struct nlattr **tb = info->attrs;
const struct genl_family *rt; const struct genl_family *rt;
struct genl_ops op;
int err, i;
BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
...@@ -1147,29 +1155,149 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) ...@@ -1147,29 +1155,149 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb)
if (!rt) if (!rt)
return -ENOENT; return -ENOENT;
if (!rt->policy) ctx->rt = rt;
if (tb[CTRL_ATTR_OP]) {
ctx->single_op = true;
ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]);
err = genl_get_cmd(ctx->op, rt, &op);
if (err) {
NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]);
return err;
}
if (!op.policy)
return -ENODATA;
return netlink_policy_dump_add_policy(&ctx->state, op.policy,
op.maxattr);
}
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;
}
}
if (!ctx->state)
return -ENODATA; return -ENODATA;
return 0;
}
return netlink_policy_dump_start(rt->policy, rt->maxattr, &ctx->state); static void *ctrl_dumppolicy_prep(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx;
void *hdr;
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, &genl_ctrl,
NLM_F_MULTI, CTRL_CMD_GETPOLICY);
if (!hdr)
return NULL;
if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, ctx->fam_id))
return NULL;
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) static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 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;
if (ctx->single_op) {
int err;
err = genl_get_cmd(ctx->op, ctx->rt, &op);
if (WARN_ON(err))
return skb->len;
/* break out of the loop after this one */
ctx->opidx = genl_get_cmd_cnt(ctx->rt);
} else {
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)) { while (netlink_policy_dump_loop(ctx->state)) {
void *hdr;
struct nlattr *nest; struct nlattr *nest;
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, hdr = ctrl_dumppolicy_prep(skb, cb);
cb->nlh->nlmsg_seq, &genl_ctrl,
NLM_F_MULTI, CTRL_CMD_GETPOLICY);
if (!hdr) if (!hdr)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, ctx->fam_id))
goto nla_put_failure;
nest = nla_nest_start(skb, CTRL_ATTR_POLICY); nest = nla_nest_start(skb, CTRL_ATTR_POLICY);
if (!nest) if (!nest)
goto nla_put_failure; goto nla_put_failure;
...@@ -1180,14 +1308,13 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1180,14 +1308,13 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
genlmsg_end(skb, hdr); genlmsg_end(skb, hdr);
continue;
nla_put_failure:
genlmsg_cancel(skb, hdr);
break;
} }
return skb->len; return skb->len;
nla_put_failure:
genlmsg_cancel(skb, hdr);
return skb->len;
} }
static int ctrl_dumppolicy_done(struct netlink_callback *cb) static int ctrl_dumppolicy_done(struct netlink_callback *cb)
......
...@@ -35,7 +35,8 @@ static int add_policy(struct netlink_policy_dump_state **statep, ...@@ -35,7 +35,8 @@ static int add_policy(struct netlink_policy_dump_state **statep,
return 0; return 0;
for (i = 0; i < state->n_alloc; i++) { for (i = 0; i < state->n_alloc; i++) {
if (state->policies[i].policy == policy) if (state->policies[i].policy == policy &&
state->policies[i].maxtype == maxtype)
return 0; return 0;
if (!state->policies[i].policy) { if (!state->policies[i].policy) {
...@@ -62,42 +63,85 @@ static int add_policy(struct netlink_policy_dump_state **statep, ...@@ -62,42 +63,85 @@ static int add_policy(struct netlink_policy_dump_state **statep,
return 0; return 0;
} }
static unsigned int get_policy_idx(struct netlink_policy_dump_state *state, /**
const struct nla_policy *policy) * netlink_policy_dump_get_policy_idx - retrieve policy index
* @state: the policy dump state
* @policy: the policy to find
* @maxtype: the policy's maxattr
*
* Returns: the index of the given policy in the dump state
*
* Call this to find a policy index when you've added multiple and e.g.
* need to tell userspace which command has which policy (by index).
*
* Note: this will WARN and return 0 if the policy isn't found, which
* means it wasn't added in the first place, which would be an
* internal consistency bug.
*/
int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
const struct nla_policy *policy,
unsigned int maxtype)
{ {
unsigned int i; unsigned int i;
if (WARN_ON(!policy || !maxtype))
return 0;
for (i = 0; i < state->n_alloc; i++) { for (i = 0; i < state->n_alloc; i++) {
if (state->policies[i].policy == policy) if (state->policies[i].policy == policy &&
state->policies[i].maxtype == maxtype)
return i; return i;
} }
WARN_ON_ONCE(1); WARN_ON(1);
return -1; return 0;
} }
int netlink_policy_dump_start(const struct nla_policy *policy, static struct netlink_policy_dump_state *alloc_state(void)
unsigned int maxtype,
struct netlink_policy_dump_state **statep)
{ {
struct netlink_policy_dump_state *state; struct netlink_policy_dump_state *state;
state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
GFP_KERNEL);
if (!state)
return ERR_PTR(-ENOMEM);
state->n_alloc = INITIAL_POLICIES_ALLOC;
return state;
}
/**
* netlink_policy_dump_add_policy - add a policy to the dump
* @pstate: state to add to, may be reallocated, must be %NULL the first time
* @policy: the new policy to add to the dump
* @maxtype: the new policy's max attr type
*
* Returns: 0 on success, a negative error code otherwise.
*
* Call this to allocate a policy dump state, and to add policies to it. This
* should be called from the dump start() callback.
*
* Note: on failures, any previously allocated state is freed.
*/
int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
const struct nla_policy *policy,
unsigned int maxtype)
{
struct netlink_policy_dump_state *state = *pstate;
unsigned int policy_idx; unsigned int policy_idx;
int err; int err;
if (*statep) if (!state) {
return 0; state = alloc_state();
if (IS_ERR(state))
return PTR_ERR(state);
}
/* /*
* walk the policies and nested ones first, and build * walk the policies and nested ones first, and build
* a linear list of them. * a linear list of them.
*/ */
state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
GFP_KERNEL);
if (!state)
return -ENOMEM;
state->n_alloc = INITIAL_POLICIES_ALLOC;
err = add_policy(&state, policy, maxtype); err = add_policy(&state, policy, maxtype);
if (err) if (err)
return err; return err;
...@@ -128,8 +172,7 @@ int netlink_policy_dump_start(const struct nla_policy *policy, ...@@ -128,8 +172,7 @@ int netlink_policy_dump_start(const struct nla_policy *policy,
} }
} }
*statep = state; *pstate = state;
return 0; return 0;
} }
...@@ -140,11 +183,26 @@ netlink_policy_dump_finished(struct netlink_policy_dump_state *state) ...@@ -140,11 +183,26 @@ netlink_policy_dump_finished(struct netlink_policy_dump_state *state)
!state->policies[state->policy_idx].policy; !state->policies[state->policy_idx].policy;
} }
/**
* netlink_policy_dump_loop - dumping loop indicator
* @state: the policy dump state
*
* Returns: %true if the dump continues, %false otherwise
*
* Note: this frees the dump state when finishing
*/
bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state) bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
{ {
return !netlink_policy_dump_finished(state); return !netlink_policy_dump_finished(state);
} }
/**
* netlink_policy_dump_write - write current policy dump attributes
* @skb: the message skb to write to
* @state: the policy dump state
*
* Returns: 0 on success, an error code otherwise
*/
int netlink_policy_dump_write(struct sk_buff *skb, int netlink_policy_dump_write(struct sk_buff *skb,
struct netlink_policy_dump_state *state) struct netlink_policy_dump_state *state)
{ {
...@@ -182,7 +240,9 @@ int netlink_policy_dump_write(struct sk_buff *skb, ...@@ -182,7 +240,9 @@ int netlink_policy_dump_write(struct sk_buff *skb,
type = NL_ATTR_TYPE_NESTED_ARRAY; type = NL_ATTR_TYPE_NESTED_ARRAY;
if (pt->nested_policy && pt->len && if (pt->nested_policy && pt->len &&
(nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX, (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
get_policy_idx(state, pt->nested_policy)) || netlink_policy_dump_get_policy_idx(state,
pt->nested_policy,
pt->len)) ||
nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
pt->len))) pt->len)))
goto nla_put_failure; goto nla_put_failure;
...@@ -305,6 +365,12 @@ int netlink_policy_dump_write(struct sk_buff *skb, ...@@ -305,6 +365,12 @@ int netlink_policy_dump_write(struct sk_buff *skb,
return -ENOBUFS; return -ENOBUFS;
} }
/**
* netlink_policy_dump_free - free policy dump state
* @state: the policy dump state to free
*
* Call this from the done() method to ensure dump state is freed.
*/
void netlink_policy_dump_free(struct netlink_policy_dump_state *state) void netlink_policy_dump_free(struct netlink_policy_dump_state *state)
{ {
kfree(state); kfree(state);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment