summaryrefslogtreecommitdiff
path: root/net/netlink
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2020-10-08 13:45:17 +0300
committerJakub Kicinski <kuba@kernel.org>2020-10-10 06:22:32 +0300
commit44f3625bc61653ea3bde9960298faf2f5518fda5 (patch)
treee36d5d8e5795a8992618cc2f756c5bc38d977434 /net/netlink
parentd2681e93b0ab7afe01d07f8c96f14afaccdcea4c (diff)
downloadlinux-44f3625bc61653ea3bde9960298faf2f5518fda5.tar.xz
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 <kuba@kernel.org> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/netlink')
-rw-r--r--net/netlink/af_netlink.c5
-rw-r--r--net/netlink/policy.c61
2 files changed, 66 insertions, 0 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index df675a8e1918..daca50d6bb12 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2420,6 +2420,8 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
tlvlen += nla_total_size(sizeof(u32));
if (nlk_has_extack && extack && extack->cookie_len)
tlvlen += nla_total_size(extack->cookie_len);
+ if (err && nlk_has_extack && extack && extack->policy)
+ tlvlen += netlink_policy_dump_attr_size_estimate(extack->policy);
if (tlvlen)
flags |= NLM_F_ACK_TLVS;
@@ -2452,6 +2454,9 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
if (extack->cookie_len)
WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE,
extack->cookie_len, extack->cookie));
+ if (extack->policy)
+ netlink_policy_dump_write_attr(skb, extack->policy,
+ NLMSGERR_ATTR_POLICY);
}
nlmsg_end(skb, rep);
diff --git a/net/netlink/policy.c b/net/netlink/policy.c
index 4383436759e2..8d7c900e27f4 100644
--- a/net/netlink/policy.c
+++ b/net/netlink/policy.c
@@ -196,12 +196,54 @@ bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
return !netlink_policy_dump_finished(state);
}
+int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
+{
+ /* nested + type */
+ int common = 2 * nla_attr_size(sizeof(u32));
+
+ switch (pt->type) {
+ case NLA_UNSPEC:
+ case NLA_REJECT:
+ /* these actually don't need any space */
+ return 0;
+ case NLA_NESTED:
+ case NLA_NESTED_ARRAY:
+ /* common, policy idx, policy maxattr */
+ return common + 2 * nla_attr_size(sizeof(u32));
+ case NLA_U8:
+ case NLA_U16:
+ case NLA_U32:
+ case NLA_U64:
+ case NLA_MSECS:
+ case NLA_S8:
+ case NLA_S16:
+ case NLA_S32:
+ case NLA_S64:
+ /* maximum is common, u64 min/max with padding */
+ return common +
+ 2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64)));
+ case NLA_BITFIELD32:
+ return common + nla_attr_size(sizeof(u32));
+ case NLA_STRING:
+ case NLA_NUL_STRING:
+ case NLA_BINARY:
+ /* maximum is common, u32 min-length/max-length */
+ return common + 2 * nla_attr_size(sizeof(u32));
+ case NLA_FLAG:
+ return common;
+ }
+
+ /* this should then cause a warning later */
+ return 0;
+}
+
static int
__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
struct sk_buff *skb,
const struct nla_policy *pt,
int nestattr)
{
+ int estimate = netlink_policy_dump_attr_size_estimate(pt);
enum netlink_attribute_type type;
struct nlattr *attr;
@@ -334,6 +376,8 @@ __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
goto nla_put_failure;
nla_nest_end(skb, attr);
+ WARN_ON(attr->nla_len > estimate);
+
return 0;
nla_put_failure:
nla_nest_cancel(skb, attr);
@@ -341,6 +385,23 @@ nla_put_failure:
}
/**
+ * netlink_policy_dump_write_attr - write a given attribute policy
+ * @skb: the message skb to write to
+ * @pt: the attribute's policy
+ * @nestattr: the nested attribute ID to use
+ *
+ * Returns: 0 on success, an error code otherwise; -%ENODATA is
+ * special, indicating that there's no policy data and
+ * the attribute is generally rejected.
+ */
+int netlink_policy_dump_write_attr(struct sk_buff *skb,
+ const struct nla_policy *pt,
+ int nestattr)
+{
+ return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr);
+}
+
+/**
* netlink_policy_dump_write - write current policy dump attributes
* @skb: the message skb to write to
* @state: the policy dump state