Skip to content

Commit 44f3625

Browse files
jmberg-intelkuba-moo
authored andcommitted
netlink: export policy in extended ACK
Add a new attribute NLMSGERR_ATTR_POLICY to the extended ACK to advertise the policy, e.g. if an attribute was out of range, you'll know the range that's permissible. Add new NL_SET_ERR_MSG_ATTR_POL() and NL_SET_ERR_MSG_ATTR_POL() macros to set this, since realistically it's only useful to do this when the bad attribute (offset) is also returned. Use it in lib/nlattr.c which practically does all the policy validation. v2: - add and use netlink_policy_dump_attr_size_estimate() v3: - remove redundant break v4: - really remove redundant break ... sorry Reviewed-by: Jakub Kicinski <[email protected]> Signed-off-by: Johannes Berg <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent d2681e9 commit 44f3625

File tree

6 files changed

+110
-27
lines changed

6 files changed

+110
-27
lines changed

include/linux/netlink.h

+20-10
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,14 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
6868
* @_msg: message string to report - don't access directly, use
6969
* %NL_SET_ERR_MSG
7070
* @bad_attr: attribute with error
71+
* @policy: policy for a bad attribute
7172
* @cookie: cookie data to return to userspace (for success)
7273
* @cookie_len: actual cookie data length
7374
*/
7475
struct netlink_ext_ack {
7576
const char *_msg;
7677
const struct nlattr *bad_attr;
78+
const struct nla_policy *policy;
7779
u8 cookie[NETLINK_MAX_COOKIE_LEN];
7880
u8 cookie_len;
7981
};
@@ -95,21 +97,29 @@ struct netlink_ext_ack {
9597
#define NL_SET_ERR_MSG_MOD(extack, msg) \
9698
NL_SET_ERR_MSG((extack), KBUILD_MODNAME ": " msg)
9799

98-
#define NL_SET_BAD_ATTR(extack, attr) do { \
99-
if ((extack)) \
100+
#define NL_SET_BAD_ATTR_POLICY(extack, attr, pol) do { \
101+
if ((extack)) { \
100102
(extack)->bad_attr = (attr); \
103+
(extack)->policy = (pol); \
104+
} \
101105
} while (0)
102106

103-
#define NL_SET_ERR_MSG_ATTR(extack, attr, msg) do { \
104-
static const char __msg[] = msg; \
105-
struct netlink_ext_ack *__extack = (extack); \
106-
\
107-
if (__extack) { \
108-
__extack->_msg = __msg; \
109-
__extack->bad_attr = (attr); \
110-
} \
107+
#define NL_SET_BAD_ATTR(extack, attr) NL_SET_BAD_ATTR_POLICY(extack, attr, NULL)
108+
109+
#define NL_SET_ERR_MSG_ATTR_POL(extack, attr, pol, msg) do { \
110+
static const char __msg[] = msg; \
111+
struct netlink_ext_ack *__extack = (extack); \
112+
\
113+
if (__extack) { \
114+
__extack->_msg = __msg; \
115+
__extack->bad_attr = (attr); \
116+
__extack->policy = (pol); \
117+
} \
111118
} while (0)
112119

120+
#define NL_SET_ERR_MSG_ATTR(extack, attr, msg) \
121+
NL_SET_ERR_MSG_ATTR_POL(extack, attr, NULL, msg)
122+
113123
static inline void nl_set_extack_cookie_u64(struct netlink_ext_ack *extack,
114124
u64 cookie)
115125
{

include/net/netlink.h

+4
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,10 @@ int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
19571957
bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state);
19581958
int netlink_policy_dump_write(struct sk_buff *skb,
19591959
struct netlink_policy_dump_state *state);
1960+
int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt);
1961+
int netlink_policy_dump_write_attr(struct sk_buff *skb,
1962+
const struct nla_policy *pt,
1963+
int nestattr);
19601964
void netlink_policy_dump_free(struct netlink_policy_dump_state *state);
19611965

19621966
#endif

include/uapi/linux/netlink.h

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ struct nlmsgerr {
129129
* @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
130130
* be used - in the success case - to identify a created
131131
* object or operation or similar (binary)
132+
* @NLMSGERR_ATTR_POLICY: policy for a rejected attribute
132133
* @__NLMSGERR_ATTR_MAX: number of attributes
133134
* @NLMSGERR_ATTR_MAX: highest attribute number
134135
*/
@@ -137,6 +138,7 @@ enum nlmsgerr_attrs {
137138
NLMSGERR_ATTR_MSG,
138139
NLMSGERR_ATTR_OFFS,
139140
NLMSGERR_ATTR_COOKIE,
141+
NLMSGERR_ATTR_POLICY,
140142

141143
__NLMSGERR_ATTR_MAX,
142144
NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1

lib/nlattr.c

+18-17
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
9696
continue;
9797

9898
if (nla_len(entry) < NLA_HDRLEN) {
99-
NL_SET_ERR_MSG_ATTR(extack, entry,
100-
"Array element too short");
99+
NL_SET_ERR_MSG_ATTR_POL(extack, entry, policy,
100+
"Array element too short");
101101
return -ERANGE;
102102
}
103103

@@ -195,8 +195,8 @@ static int nla_validate_range_unsigned(const struct nla_policy *pt,
195195
pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",
196196
current->comm, pt->type);
197197
if (validate & NL_VALIDATE_STRICT_ATTRS) {
198-
NL_SET_ERR_MSG_ATTR(extack, nla,
199-
"invalid attribute length");
198+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
199+
"invalid attribute length");
200200
return -EINVAL;
201201
}
202202

@@ -208,11 +208,11 @@ static int nla_validate_range_unsigned(const struct nla_policy *pt,
208208
bool binary = pt->type == NLA_BINARY;
209209

210210
if (binary)
211-
NL_SET_ERR_MSG_ATTR(extack, nla,
212-
"binary attribute size out of range");
211+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
212+
"binary attribute size out of range");
213213
else
214-
NL_SET_ERR_MSG_ATTR(extack, nla,
215-
"integer out of range");
214+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
215+
"integer out of range");
216216

217217
return -ERANGE;
218218
}
@@ -291,8 +291,8 @@ static int nla_validate_int_range_signed(const struct nla_policy *pt,
291291
nla_get_range_signed(pt, &range);
292292

293293
if (value < range.min || value > range.max) {
294-
NL_SET_ERR_MSG_ATTR(extack, nla,
295-
"integer out of range");
294+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
295+
"integer out of range");
296296
return -ERANGE;
297297
}
298298

@@ -377,23 +377,23 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
377377
pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",
378378
current->comm, type);
379379
if (validate & NL_VALIDATE_STRICT_ATTRS) {
380-
NL_SET_ERR_MSG_ATTR(extack, nla,
381-
"invalid attribute length");
380+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
381+
"invalid attribute length");
382382
return -EINVAL;
383383
}
384384
}
385385

386386
if (validate & NL_VALIDATE_NESTED) {
387387
if ((pt->type == NLA_NESTED || pt->type == NLA_NESTED_ARRAY) &&
388388
!(nla->nla_type & NLA_F_NESTED)) {
389-
NL_SET_ERR_MSG_ATTR(extack, nla,
390-
"NLA_F_NESTED is missing");
389+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
390+
"NLA_F_NESTED is missing");
391391
return -EINVAL;
392392
}
393393
if (pt->type != NLA_NESTED && pt->type != NLA_NESTED_ARRAY &&
394394
pt->type != NLA_UNSPEC && (nla->nla_type & NLA_F_NESTED)) {
395-
NL_SET_ERR_MSG_ATTR(extack, nla,
396-
"NLA_F_NESTED not expected");
395+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
396+
"NLA_F_NESTED not expected");
397397
return -EINVAL;
398398
}
399399
}
@@ -550,7 +550,8 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
550550

551551
return 0;
552552
out_err:
553-
NL_SET_ERR_MSG_ATTR(extack, nla, "Attribute failed policy validation");
553+
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
554+
"Attribute failed policy validation");
554555
return err;
555556
}
556557

net/netlink/af_netlink.c

+5
Original file line numberDiff line numberDiff line change
@@ -2420,6 +2420,8 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
24202420
tlvlen += nla_total_size(sizeof(u32));
24212421
if (nlk_has_extack && extack && extack->cookie_len)
24222422
tlvlen += nla_total_size(extack->cookie_len);
2423+
if (err && nlk_has_extack && extack && extack->policy)
2424+
tlvlen += netlink_policy_dump_attr_size_estimate(extack->policy);
24232425

24242426
if (tlvlen)
24252427
flags |= NLM_F_ACK_TLVS;
@@ -2452,6 +2454,9 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
24522454
if (extack->cookie_len)
24532455
WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE,
24542456
extack->cookie_len, extack->cookie));
2457+
if (extack->policy)
2458+
netlink_policy_dump_write_attr(skb, extack->policy,
2459+
NLMSGERR_ATTR_POLICY);
24552460
}
24562461

24572462
nlmsg_end(skb, rep);

net/netlink/policy.c

+61
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,54 @@ bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
196196
return !netlink_policy_dump_finished(state);
197197
}
198198

199+
int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
200+
{
201+
/* nested + type */
202+
int common = 2 * nla_attr_size(sizeof(u32));
203+
204+
switch (pt->type) {
205+
case NLA_UNSPEC:
206+
case NLA_REJECT:
207+
/* these actually don't need any space */
208+
return 0;
209+
case NLA_NESTED:
210+
case NLA_NESTED_ARRAY:
211+
/* common, policy idx, policy maxattr */
212+
return common + 2 * nla_attr_size(sizeof(u32));
213+
case NLA_U8:
214+
case NLA_U16:
215+
case NLA_U32:
216+
case NLA_U64:
217+
case NLA_MSECS:
218+
case NLA_S8:
219+
case NLA_S16:
220+
case NLA_S32:
221+
case NLA_S64:
222+
/* maximum is common, u64 min/max with padding */
223+
return common +
224+
2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64)));
225+
case NLA_BITFIELD32:
226+
return common + nla_attr_size(sizeof(u32));
227+
case NLA_STRING:
228+
case NLA_NUL_STRING:
229+
case NLA_BINARY:
230+
/* maximum is common, u32 min-length/max-length */
231+
return common + 2 * nla_attr_size(sizeof(u32));
232+
case NLA_FLAG:
233+
return common;
234+
}
235+
236+
/* this should then cause a warning later */
237+
return 0;
238+
}
239+
199240
static int
200241
__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
201242
struct sk_buff *skb,
202243
const struct nla_policy *pt,
203244
int nestattr)
204245
{
246+
int estimate = netlink_policy_dump_attr_size_estimate(pt);
205247
enum netlink_attribute_type type;
206248
struct nlattr *attr;
207249

@@ -334,12 +376,31 @@ __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
334376
goto nla_put_failure;
335377

336378
nla_nest_end(skb, attr);
379+
WARN_ON(attr->nla_len > estimate);
380+
337381
return 0;
338382
nla_put_failure:
339383
nla_nest_cancel(skb, attr);
340384
return -ENOBUFS;
341385
}
342386

387+
/**
388+
* netlink_policy_dump_write_attr - write a given attribute policy
389+
* @skb: the message skb to write to
390+
* @pt: the attribute's policy
391+
* @nestattr: the nested attribute ID to use
392+
*
393+
* Returns: 0 on success, an error code otherwise; -%ENODATA is
394+
* special, indicating that there's no policy data and
395+
* the attribute is generally rejected.
396+
*/
397+
int netlink_policy_dump_write_attr(struct sk_buff *skb,
398+
const struct nla_policy *pt,
399+
int nestattr)
400+
{
401+
return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr);
402+
}
403+
343404
/**
344405
* netlink_policy_dump_write - write current policy dump attributes
345406
* @skb: the message skb to write to

0 commit comments

Comments
 (0)