@@ -62,17 +62,24 @@ 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
* @missing_attr: number of missing attr (or 0)
+ * @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;
u16 missing_attr;
+ u8 cookie[NETLINK_MAX_COOKIE_LEN];
+ u8 cookie_len;
};
/* Always use this macro, this allows later putting the
@@ -118,6 +118,9 @@ struct nlmsgerr {
* @NLMSGERR_ATTR_OFFS: error offset in the original message (u32)
* @NLMSGERR_ATTR_ATTR: top-level attribute that caused the error
* (or is missing, u16)
+ * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
+ * be used - in the success case - to identify a created
+ * object or operation or similar (binary)
* @NUM_NLMSGERR_ATTRS: number of attributes
* @NLMSGERR_ATTR_MAX: highest attribute number
*/
@@ -126,6 +129,7 @@ enum nlmsgerr_attrs {
NLMSGERR_ATTR_MSG,
NLMSGERR_ATTR_OFFS,
NLMSGERR_ATTR_ATTR,
+ NLMSGERR_ATTR_COOKIE,
NUM_NLMSGERR_ATTRS,
NLMSGERR_ATTR_MAX = NUM_NLMSGERR_ATTRS - 1
@@ -2311,6 +2311,9 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
if (extack->missing_attr)
acksize += nla_total_size(sizeof(u16));
}
+ } else if (nlk->flags & NETLINK_F_EXT_ACK) {
+ if (extack && extack->cookie_len)
+ acksize += nla_total_size(extack->cookie_len);
}
skb = nlmsg_new(acksize, GFP_KERNEL);
@@ -2336,20 +2339,27 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
!(nlk->flags & NETLINK_F_CAP_ACK) ? nlh->nlmsg_len
: sizeof(*nlh));
- if (err && nlk->flags & NETLINK_F_EXT_ACK && extack) {
- if (extack->_msg)
- WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
- extack->_msg));
- if (extack->bad_attr &&
- !WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
- (u8 *)extack->bad_attr >= in_skb->data +
- in_skb->len))
- WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS,
- (u8 *)extack->bad_attr -
- in_skb->data));
- if (extack->missing_attr)
- WARN_ON(nla_put_u16(skb, NLMSGERR_ATTR_ATTR,
- extack->missing_attr));
+ if (nlk->flags & NETLINK_F_EXT_ACK && extack) {
+ if (err) {
+ if (extack->_msg)
+ WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
+ extack->_msg));
+ if (extack->bad_attr &&
+ !WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
+ (u8 *)extack->bad_attr >= in_skb->data +
+ in_skb->len))
+ WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS,
+ (u8 *)extack->bad_attr -
+ in_skb->data));
+ if (extack->missing_attr)
+ WARN_ON(nla_put_u16(skb, NLMSGERR_ATTR_ATTR,
+ extack->missing_attr));
+ } else {
+ if (extack->cookie_len)
+ WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE,
+ extack->cookie_len,
+ extack->cookie));
+ }
}
netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);