summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2017-04-13 20:58:23 +0300
committerDavid S. Miller <davem@davemloft.net>2017-04-13 20:58:23 +0300
commitce07183282975026716107d36fd3f5f93de76668 (patch)
tree035cf3626e36e74c27846e297c09596273dd0c9e /include
parentfb9eb899a6dc663e4a2deed9af2ac28f507d0ffb (diff)
parentfe52145f91fe81b994e4622f6b9c3a0f22643363 (diff)
downloadlinux-ce07183282975026716107d36fd3f5f93de76668.tar.xz
Merge branch 'netlink_ext_ACK'
Johannes Berg says: ==================== netlink extended ACK reporting Changes since v4: * use __NLMSGERR_ATTR_MAX instead of NUM_NLMSGERR_ATTRS Changes since v3: * Add NLM_F_CAPPED and NLM_F_ACK_TLVS flags, to allow entirely stateless parsing of the ACK messages by looking at the new flags. Need to check NLM_F_ACK_TLVS first, since capping can be done in kernels before this patchset without setting the flag. * Remove "missing_attr" functionality - this can obviously be added back rather easily, but I'd rather have more discussion about the nesting problem there. * Improve documentation of NLMSGERR_ATTR_OFFS * Improve message structure documentation, documenting that the request message is always capped for success cases * fix nlmsg_len of the outer message by calling nlmsg_end() * fix memcpy() of the request in success cases, going back to the original code that I'd changed before due to the payload adjustments that I reverted when introducing tlvlen Changes since v2: * add NUM_NLMSGERR_ATTRS, NLMSGERR_ATTR_MAX * fix cookie length to 20 (sha-1 length) * move struct members for cookie to patch 3 where they should be * another cleanup suggested by David Ahern Changes since v1: * credit Pablo and Jamal * incorporate suggestion from David Ahern * fix compilation in decnet ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'include')
-rw-r--r--include/linux/netlink.h33
-rw-r--r--include/net/genetlink.h20
-rw-r--r--include/net/netlink.h36
-rw-r--r--include/net/rtnetlink.h3
-rw-r--r--include/uapi/linux/netlink.h36
5 files changed, 112 insertions, 16 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index da14ab61f363..8d2a8924705c 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -62,11 +62,42 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
}
+/* this can be increased when necessary - don't expose to userland */
+#define NETLINK_MAX_COOKIE_LEN 20
+
+/**
+ * struct netlink_ext_ack - netlink extended ACK report struct
+ * @_msg: message string to report - don't access directly, use
+ * %NL_SET_ERR_MSG
+ * @bad_attr: attribute with error
+ * @cookie: cookie data to return to userspace (for success)
+ * @cookie_len: actual cookie data length
+ */
+struct netlink_ext_ack {
+ const char *_msg;
+ const struct nlattr *bad_attr;
+ u8 cookie[NETLINK_MAX_COOKIE_LEN];
+ u8 cookie_len;
+};
+
+/* Always use this macro, this allows later putting the
+ * message into a separate section or such for things
+ * like translation or listing all possible messages.
+ * Currently string formatting is not supported (due
+ * to the lack of an output buffer.)
+ */
+#define NL_SET_ERR_MSG(extack, msg) do { \
+ static const char _msg[] = (msg); \
+ \
+ (extack)->_msg = _msg; \
+} while (0)
+
extern void netlink_kernel_release(struct sock *sk);
extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group);
-extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
+extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
+ const struct netlink_ext_ack *extack);
extern int netlink_has_listeners(struct sock *sk, unsigned int group);
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index a34275be3600..68b88192b00c 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -84,6 +84,7 @@ struct nlattr **genl_family_attrbuf(const struct genl_family *family);
* @attrs: netlink attributes
* @_net: network namespace
* @user_ptr: user pointers
+ * @extack: extended ACK report struct
*/
struct genl_info {
u32 snd_seq;
@@ -94,6 +95,7 @@ struct genl_info {
struct nlattr ** attrs;
possible_net_t _net;
void * user_ptr[2];
+ struct netlink_ext_ack *extack;
};
static inline struct net *genl_info_net(struct genl_info *info)
@@ -106,6 +108,16 @@ static inline void genl_info_net_set(struct genl_info *info, struct net *net)
write_pnet(&info->_net, net);
}
+#define GENL_SET_ERR_MSG(info, msg) NL_SET_ERR_MSG((info)->extack, msg)
+
+static inline int genl_err_attr(struct genl_info *info, int err,
+ struct nlattr *attr)
+{
+ info->extack->bad_attr = attr;
+
+ return err;
+}
+
/**
* struct genl_ops - generic netlink operations
* @cmd: command identifier
@@ -162,14 +174,16 @@ genlmsg_nlhdr(void *user_hdr, const struct genl_family *family)
* @tb: destination array with maxtype+1 elements
* @maxtype: maximum attribute type to be expected
* @policy: validation policy
- * */
+ * @extack: extended ACK report struct
+ */
static inline int genlmsg_parse(const struct nlmsghdr *nlh,
const struct genl_family *family,
struct nlattr *tb[], int maxtype,
- const struct nla_policy *policy)
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
{
return nlmsg_parse(nlh, family->hdrsize + GENL_HDRLEN, tb, maxtype,
- policy);
+ policy, extack);
}
/**
diff --git a/include/net/netlink.h b/include/net/netlink.h
index b239fcd33d80..01709172b3d3 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -233,14 +233,17 @@ struct nl_info {
};
int netlink_rcv_skb(struct sk_buff *skb,
- int (*cb)(struct sk_buff *, struct nlmsghdr *));
+ int (*cb)(struct sk_buff *, struct nlmsghdr *,
+ struct netlink_ext_ack *));
int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid,
unsigned int group, int report, gfp_t flags);
int nla_validate(const struct nlattr *head, int len, int maxtype,
- const struct nla_policy *policy);
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack);
int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head,
- int len, const struct nla_policy *policy);
+ int len, const struct nla_policy *policy,
+ struct netlink_ext_ack *extack);
int nla_policy_len(const struct nla_policy *, int);
struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype);
size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize);
@@ -374,18 +377,20 @@ nlmsg_next(const struct nlmsghdr *nlh, int *remaining)
* @tb: destination array with maxtype+1 elements
* @maxtype: maximum attribute type to be expected
* @policy: validation policy
+ * @extack: extended ACK report struct
*
* See nla_parse()
*/
static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen,
struct nlattr *tb[], int maxtype,
- const struct nla_policy *policy)
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
{
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
return -EINVAL;
return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
- nlmsg_attrlen(nlh, hdrlen), policy);
+ nlmsg_attrlen(nlh, hdrlen), policy, extack);
}
/**
@@ -409,16 +414,19 @@ static inline struct nlattr *nlmsg_find_attr(const struct nlmsghdr *nlh,
* @hdrlen: length of familiy specific header
* @maxtype: maximum attribute type to be expected
* @policy: validation policy
+ * @extack: extended ACK report struct
*/
static inline int nlmsg_validate(const struct nlmsghdr *nlh,
int hdrlen, int maxtype,
- const struct nla_policy *policy)
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
{
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
return -EINVAL;
return nla_validate(nlmsg_attrdata(nlh, hdrlen),
- nlmsg_attrlen(nlh, hdrlen), maxtype, policy);
+ nlmsg_attrlen(nlh, hdrlen), maxtype, policy,
+ extack);
}
/**
@@ -739,14 +747,17 @@ nla_find_nested(const struct nlattr *nla, int attrtype)
* @maxtype: maximum attribute type to be expected
* @nla: attribute containing the nested attributes
* @policy: validation policy
+ * @extack: extended ACK report struct
*
* See nla_parse()
*/
static inline int nla_parse_nested(struct nlattr *tb[], int maxtype,
const struct nlattr *nla,
- const struct nla_policy *policy)
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
{
- return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
+ return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy,
+ extack);
}
/**
@@ -1252,6 +1263,7 @@ static inline void nla_nest_cancel(struct sk_buff *skb, struct nlattr *start)
* @start: container attribute
* @maxtype: maximum attribute type to be expected
* @policy: validation policy
+ * @extack: extended ACK report struct
*
* Validates all attributes in the nested attribute stream against the
* specified policy. Attributes with a type exceeding maxtype will be
@@ -1260,9 +1272,11 @@ static inline void nla_nest_cancel(struct sk_buff *skb, struct nlattr *start)
* Returns 0 on success or a negative error code.
*/
static inline int nla_validate_nested(const struct nlattr *start, int maxtype,
- const struct nla_policy *policy)
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
{
- return nla_validate(nla_data(start), nla_len(start), maxtype, policy);
+ return nla_validate(nla_data(start), nla_len(start), maxtype, policy,
+ extack);
}
/**
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h
index 106de5f7bf06..c07b941fce89 100644
--- a/include/net/rtnetlink.h
+++ b/include/net/rtnetlink.h
@@ -158,7 +158,8 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname,
int rtnl_delete_link(struct net_device *dev);
int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm);
-int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len);
+int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len,
+ struct netlink_ext_ack *exterr);
#define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index b2c9c26ea30f..f86127a46cfc 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -69,6 +69,10 @@ struct nlmsghdr {
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
#define NLM_F_APPEND 0x800 /* Add to end of list */
+/* Flags for ACK message */
+#define NLM_F_CAPPED 0x100 /* request was capped */
+#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
+
/*
4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
4.4BSD CHANGE NLM_F_REPLACE
@@ -101,6 +105,37 @@ struct nlmsghdr {
struct nlmsgerr {
int error;
struct nlmsghdr msg;
+ /*
+ * followed by the message contents unless NETLINK_CAP_ACK was set
+ * or the ACK indicates success (error == 0)
+ * message length is aligned with NLMSG_ALIGN()
+ */
+ /*
+ * followed by TLVs defined in enum nlmsgerr_attrs
+ * if NETLINK_EXT_ACK was set
+ */
+};
+
+/**
+ * enum nlmsgerr_attrs - nlmsgerr attributes
+ * @NLMSGERR_ATTR_UNUSED: unused
+ * @NLMSGERR_ATTR_MSG: error message string (string)
+ * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
+ * message, counting from the beginning of the header (u32)
+ * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
+ * be used - in the success case - to identify a created
+ * object or operation or similar (binary)
+ * @__NLMSGERR_ATTR_MAX: number of attributes
+ * @NLMSGERR_ATTR_MAX: highest attribute number
+ */
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
};
#define NETLINK_ADD_MEMBERSHIP 1
@@ -115,6 +150,7 @@ struct nlmsgerr {
#define NETLINK_LISTEN_ALL_NSID 8
#define NETLINK_LIST_MEMBERSHIPS 9
#define NETLINK_CAP_ACK 10
+#define NETLINK_EXT_ACK 11
struct nl_pktinfo {
__u32 group;