aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2017-04-12 08:34:04 -0400
committerDavid S. Miller <davem@davemloft.net>2017-04-13 13:58:20 -0400
commit2d4bc93368f5a0ddb57c8c885cdad9c9b7a10ed5 (patch)
tree9a2dc502e27712a7742ae9315543395a29487fc9
parentfb9eb899a6dc663e4a2deed9af2ac28f507d0ffb (diff)
netlink: extended ACK reporting
Add the base infrastructure and UAPI for netlink extended ACK reporting. All "manual" calls to netlink_ack() pass NULL for now and thus don't get extended ACK reporting. Big thanks goes to Pablo Neira Ayuso for not only bringing up the whole topic at netconf (again) but also coming up with the nlattr passing trick and various other ideas. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Reviewed-by: David Ahern <dsa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--crypto/crypto_user.c3
-rw-r--r--drivers/infiniband/core/netlink.c5
-rw-r--r--drivers/scsi/scsi_netlink.c2
-rw-r--r--include/linux/netlink.h26
-rw-r--r--include/net/netlink.h3
-rw-r--r--include/uapi/linux/netlink.h32
-rw-r--r--kernel/audit.c2
-rw-r--r--net/core/rtnetlink.c3
-rw-r--r--net/core/sock_diag.c3
-rw-r--r--net/decnet/netfilter/dn_rtmsg.c2
-rw-r--r--net/hsr/hsr_netlink.c4
-rw-r--r--net/netfilter/ipset/ip_set_core.c2
-rw-r--r--net/netfilter/nfnetlink.c22
-rw-r--r--net/netlink/af_netlink.c71
-rw-r--r--net/netlink/af_netlink.h1
-rw-r--r--net/netlink/genetlink.c3
-rw-r--r--net/xfrm/xfrm_user.c3
17 files changed, 153 insertions, 34 deletions
diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c
index a90404a0c5ff..4a44830741c1 100644
--- a/crypto/crypto_user.c
+++ b/crypto/crypto_user.c
@@ -483,7 +483,8 @@ static const struct crypto_link {
483 [CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = { .doit = crypto_del_rng }, 483 [CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = { .doit = crypto_del_rng },
484}; 484};
485 485
486static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 486static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
487 struct netlink_ext_ack *extack)
487{ 488{
488 struct nlattr *attrs[CRYPTOCFGA_MAX+1]; 489 struct nlattr *attrs[CRYPTOCFGA_MAX+1];
489 const struct crypto_link *link; 490 const struct crypto_link *link;
diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c
index 10469b0088b5..b784055423c8 100644
--- a/drivers/infiniband/core/netlink.c
+++ b/drivers/infiniband/core/netlink.c
@@ -146,7 +146,8 @@ nla_put_failure:
146} 146}
147EXPORT_SYMBOL(ibnl_put_attr); 147EXPORT_SYMBOL(ibnl_put_attr);
148 148
149static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 149static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
150 struct netlink_ext_ack *extack)
150{ 151{
151 struct ibnl_client *client; 152 struct ibnl_client *client;
152 int type = nlh->nlmsg_type; 153 int type = nlh->nlmsg_type;
@@ -209,7 +210,7 @@ static void ibnl_rcv_reply_skb(struct sk_buff *skb)
209 if (nlh->nlmsg_flags & NLM_F_REQUEST) 210 if (nlh->nlmsg_flags & NLM_F_REQUEST)
210 return; 211 return;
211 212
212 ibnl_rcv_msg(skb, nlh); 213 ibnl_rcv_msg(skb, nlh, NULL);
213 214
214 msglen = NLMSG_ALIGN(nlh->nlmsg_len); 215 msglen = NLMSG_ALIGN(nlh->nlmsg_len);
215 if (msglen > skb->len) 216 if (msglen > skb->len)
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c
index 109802f776ed..50e624fb8307 100644
--- a/drivers/scsi/scsi_netlink.c
+++ b/drivers/scsi/scsi_netlink.c
@@ -111,7 +111,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
111 111
112next_msg: 112next_msg:
113 if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) 113 if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
114 netlink_ack(skb, nlh, err); 114 netlink_ack(skb, nlh, err, NULL);
115 115
116 skb_pull(skb, rlen); 116 skb_pull(skb, rlen);
117 } 117 }
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index da14ab61f363..60e7137f840d 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -62,11 +62,35 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
62 return __netlink_kernel_create(net, unit, THIS_MODULE, cfg); 62 return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
63} 63}
64 64
65/**
66 * struct netlink_ext_ack - netlink extended ACK report struct
67 * @_msg: message string to report - don't access directly, use
68 * %NL_SET_ERR_MSG
69 * @bad_attr: attribute with error
70 */
71struct netlink_ext_ack {
72 const char *_msg;
73 const struct nlattr *bad_attr;
74};
75
76/* Always use this macro, this allows later putting the
77 * message into a separate section or such for things
78 * like translation or listing all possible messages.
79 * Currently string formatting is not supported (due
80 * to the lack of an output buffer.)
81 */
82#define NL_SET_ERR_MSG(extack, msg) do { \
83 static const char _msg[] = (msg); \
84 \
85 (extack)->_msg = _msg; \
86} while (0)
87
65extern void netlink_kernel_release(struct sock *sk); 88extern void netlink_kernel_release(struct sock *sk);
66extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); 89extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
67extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); 90extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
68extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group); 91extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group);
69extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); 92extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
93 const struct netlink_ext_ack *extack);
70extern int netlink_has_listeners(struct sock *sk, unsigned int group); 94extern int netlink_has_listeners(struct sock *sk, unsigned int group);
71 95
72extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock); 96extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
diff --git a/include/net/netlink.h b/include/net/netlink.h
index b239fcd33d80..a064ec3e2ee1 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -233,7 +233,8 @@ struct nl_info {
233}; 233};
234 234
235int netlink_rcv_skb(struct sk_buff *skb, 235int netlink_rcv_skb(struct sk_buff *skb,
236 int (*cb)(struct sk_buff *, struct nlmsghdr *)); 236 int (*cb)(struct sk_buff *, struct nlmsghdr *,
237 struct netlink_ext_ack *));
237int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid, 238int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid,
238 unsigned int group, int report, gfp_t flags); 239 unsigned int group, int report, gfp_t flags);
239 240
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index b2c9c26ea30f..7df88770e029 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -69,6 +69,10 @@ struct nlmsghdr {
69#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ 69#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
70#define NLM_F_APPEND 0x800 /* Add to end of list */ 70#define NLM_F_APPEND 0x800 /* Add to end of list */
71 71
72/* Flags for ACK message */
73#define NLM_F_CAPPED 0x100 /* request was capped */
74#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
75
72/* 76/*
73 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL 77 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
74 4.4BSD CHANGE NLM_F_REPLACE 78 4.4BSD CHANGE NLM_F_REPLACE
@@ -101,6 +105,33 @@ struct nlmsghdr {
101struct nlmsgerr { 105struct nlmsgerr {
102 int error; 106 int error;
103 struct nlmsghdr msg; 107 struct nlmsghdr msg;
108 /*
109 * followed by the message contents unless NETLINK_CAP_ACK was set
110 * or the ACK indicates success (error == 0)
111 * message length is aligned with NLMSG_ALIGN()
112 */
113 /*
114 * followed by TLVs defined in enum nlmsgerr_attrs
115 * if NETLINK_EXT_ACK was set
116 */
117};
118
119/**
120 * enum nlmsgerr_attrs - nlmsgerr attributes
121 * @NLMSGERR_ATTR_UNUSED: unused
122 * @NLMSGERR_ATTR_MSG: error message string (string)
123 * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
124 * message, counting from the beginning of the header (u32)
125 * @__NLMSGERR_ATTR_MAX: number of attributes
126 * @NLMSGERR_ATTR_MAX: highest attribute number
127 */
128enum nlmsgerr_attrs {
129 NLMSGERR_ATTR_UNUSED,
130 NLMSGERR_ATTR_MSG,
131 NLMSGERR_ATTR_OFFS,
132
133 __NLMSGERR_ATTR_MAX,
134 NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
104}; 135};
105 136
106#define NETLINK_ADD_MEMBERSHIP 1 137#define NETLINK_ADD_MEMBERSHIP 1
@@ -115,6 +146,7 @@ struct nlmsgerr {
115#define NETLINK_LISTEN_ALL_NSID 8 146#define NETLINK_LISTEN_ALL_NSID 8
116#define NETLINK_LIST_MEMBERSHIPS 9 147#define NETLINK_LIST_MEMBERSHIPS 9
117#define NETLINK_CAP_ACK 10 148#define NETLINK_CAP_ACK 10
149#define NETLINK_EXT_ACK 11
118 150
119struct nl_pktinfo { 151struct nl_pktinfo {
120 __u32 group; 152 __u32 group;
diff --git a/kernel/audit.c b/kernel/audit.c
index 2f4964cfde0b..d54bf5932374 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1402,7 +1402,7 @@ static void audit_receive_skb(struct sk_buff *skb)
1402 err = audit_receive_msg(skb, nlh); 1402 err = audit_receive_msg(skb, nlh);
1403 /* if err or if this message says it wants a response */ 1403 /* if err or if this message says it wants a response */
1404 if (err || (nlh->nlmsg_flags & NLM_F_ACK)) 1404 if (err || (nlh->nlmsg_flags & NLM_F_ACK))
1405 netlink_ack(skb, nlh, err); 1405 netlink_ack(skb, nlh, err, NULL);
1406 1406
1407 nlh = nlmsg_next(nlh, &len); 1407 nlh = nlmsg_next(nlh, &len);
1408 } 1408 }
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index c138b6b75e59..3cc4a627a537 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -4046,7 +4046,8 @@ out:
4046 4046
4047/* Process one rtnetlink message. */ 4047/* Process one rtnetlink message. */
4048 4048
4049static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 4049static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4050 struct netlink_ext_ack *extack)
4050{ 4051{
4051 struct net *net = sock_net(skb->sk); 4052 struct net *net = sock_net(skb->sk);
4052 rtnl_doit_func doit; 4053 rtnl_doit_func doit;
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index fb9d0e2fd148..217f4e3b82f6 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -238,7 +238,8 @@ static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
238 return err; 238 return err;
239} 239}
240 240
241static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 241static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
242 struct netlink_ext_ack *extack)
242{ 243{
243 int ret; 244 int ret;
244 245
diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c
index 85f2fdc360c2..c8bf5136a72b 100644
--- a/net/decnet/netfilter/dn_rtmsg.c
+++ b/net/decnet/netfilter/dn_rtmsg.c
@@ -96,7 +96,7 @@ static unsigned int dnrmg_hook(void *priv,
96} 96}
97 97
98 98
99#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) 99#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err), NULL); return; } while (0)
100 100
101static inline void dnrmg_receive_user_skb(struct sk_buff *skb) 101static inline void dnrmg_receive_user_skb(struct sk_buff *skb)
102{ 102{
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
index 1ab30e7d3f99..81dac16933fc 100644
--- a/net/hsr/hsr_netlink.c
+++ b/net/hsr/hsr_netlink.c
@@ -350,7 +350,7 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
350 return 0; 350 return 0;
351 351
352invalid: 352invalid:
353 netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL); 353 netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
354 return 0; 354 return 0;
355 355
356nla_put_failure: 356nla_put_failure:
@@ -432,7 +432,7 @@ static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
432 return 0; 432 return 0;
433 433
434invalid: 434invalid:
435 netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL); 435 netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
436 return 0; 436 return 0;
437 437
438nla_put_failure: 438nla_put_failure:
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index c296f9b606d4..26356bf8cebf 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -1305,7 +1305,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
1305 * manually :-( 1305 * manually :-(
1306 */ 1306 */
1307 if (nlh->nlmsg_flags & NLM_F_ACK) 1307 if (nlh->nlmsg_flags & NLM_F_ACK)
1308 netlink_ack(cb->skb, nlh, ret); 1308 netlink_ack(cb->skb, nlh, ret, NULL);
1309 return ret; 1309 return ret;
1310 } 1310 }
1311 } 1311 }
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 68eda920160e..181d3bb800e6 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -148,7 +148,8 @@ int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
148EXPORT_SYMBOL_GPL(nfnetlink_unicast); 148EXPORT_SYMBOL_GPL(nfnetlink_unicast);
149 149
150/* Process one complete nfnetlink message. */ 150/* Process one complete nfnetlink message. */
151static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 151static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
152 struct netlink_ext_ack *extack)
152{ 153{
153 struct net *net = sock_net(skb->sk); 154 struct net *net = sock_net(skb->sk);
154 const struct nfnl_callback *nc; 155 const struct nfnl_callback *nc;
@@ -261,7 +262,7 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
261 struct nfnl_err *nfnl_err, *next; 262 struct nfnl_err *nfnl_err, *next;
262 263
263 list_for_each_entry_safe(nfnl_err, next, err_list, head) { 264 list_for_each_entry_safe(nfnl_err, next, err_list, head) {
264 netlink_ack(skb, nfnl_err->nlh, nfnl_err->err); 265 netlink_ack(skb, nfnl_err->nlh, nfnl_err->err, NULL);
265 nfnl_err_del(nfnl_err); 266 nfnl_err_del(nfnl_err);
266 } 267 }
267} 268}
@@ -284,13 +285,13 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
284 int err; 285 int err;
285 286
286 if (subsys_id >= NFNL_SUBSYS_COUNT) 287 if (subsys_id >= NFNL_SUBSYS_COUNT)
287 return netlink_ack(skb, nlh, -EINVAL); 288 return netlink_ack(skb, nlh, -EINVAL, NULL);
288replay: 289replay:
289 status = 0; 290 status = 0;
290 291
291 skb = netlink_skb_clone(oskb, GFP_KERNEL); 292 skb = netlink_skb_clone(oskb, GFP_KERNEL);
292 if (!skb) 293 if (!skb)
293 return netlink_ack(oskb, nlh, -ENOMEM); 294 return netlink_ack(oskb, nlh, -ENOMEM, NULL);
294 295
295 nfnl_lock(subsys_id); 296 nfnl_lock(subsys_id);
296 ss = nfnl_dereference_protected(subsys_id); 297 ss = nfnl_dereference_protected(subsys_id);
@@ -304,20 +305,20 @@ replay:
304#endif 305#endif
305 { 306 {
306 nfnl_unlock(subsys_id); 307 nfnl_unlock(subsys_id);
307 netlink_ack(oskb, nlh, -EOPNOTSUPP); 308 netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
308 return kfree_skb(skb); 309 return kfree_skb(skb);
309 } 310 }
310 } 311 }
311 312
312 if (!ss->commit || !ss->abort) { 313 if (!ss->commit || !ss->abort) {
313 nfnl_unlock(subsys_id); 314 nfnl_unlock(subsys_id);
314 netlink_ack(oskb, nlh, -EOPNOTSUPP); 315 netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
315 return kfree_skb(skb); 316 return kfree_skb(skb);
316 } 317 }
317 318
318 if (genid && ss->valid_genid && !ss->valid_genid(net, genid)) { 319 if (genid && ss->valid_genid && !ss->valid_genid(net, genid)) {
319 nfnl_unlock(subsys_id); 320 nfnl_unlock(subsys_id);
320 netlink_ack(oskb, nlh, -ERESTART); 321 netlink_ack(oskb, nlh, -ERESTART, NULL);
321 return kfree_skb(skb); 322 return kfree_skb(skb);
322 } 323 }
323 324
@@ -407,7 +408,8 @@ ack:
407 * pointing to the batch header. 408 * pointing to the batch header.
408 */ 409 */
409 nfnl_err_reset(&err_list); 410 nfnl_err_reset(&err_list);
410 netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM); 411 netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM,
412 NULL);
411 status |= NFNL_BATCH_FAILURE; 413 status |= NFNL_BATCH_FAILURE;
412 goto done; 414 goto done;
413 } 415 }
@@ -467,7 +469,7 @@ static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh)
467 469
468 err = nla_parse(cda, NFNL_BATCH_MAX, attr, attrlen, nfnl_batch_policy); 470 err = nla_parse(cda, NFNL_BATCH_MAX, attr, attrlen, nfnl_batch_policy);
469 if (err < 0) { 471 if (err < 0) {
470 netlink_ack(skb, nlh, err); 472 netlink_ack(skb, nlh, err, NULL);
471 return; 473 return;
472 } 474 }
473 if (cda[NFNL_BATCH_GENID]) 475 if (cda[NFNL_BATCH_GENID])
@@ -493,7 +495,7 @@ static void nfnetlink_rcv(struct sk_buff *skb)
493 return; 495 return;
494 496
495 if (!netlink_net_capable(skb, CAP_NET_ADMIN)) { 497 if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
496 netlink_ack(skb, nlh, -EPERM); 498 netlink_ack(skb, nlh, -EPERM, NULL);
497 return; 499 return;
498 } 500 }
499 501
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index fc232441cf23..c1564768000e 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1652,6 +1652,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
1652 nlk->flags &= ~NETLINK_F_CAP_ACK; 1652 nlk->flags &= ~NETLINK_F_CAP_ACK;
1653 err = 0; 1653 err = 0;
1654 break; 1654 break;
1655 case NETLINK_EXT_ACK:
1656 if (val)
1657 nlk->flags |= NETLINK_F_EXT_ACK;
1658 else
1659 nlk->flags &= ~NETLINK_F_EXT_ACK;
1660 err = 0;
1661 break;
1655 default: 1662 default:
1656 err = -ENOPROTOOPT; 1663 err = -ENOPROTOOPT;
1657 } 1664 }
@@ -1736,6 +1743,15 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
1736 return -EFAULT; 1743 return -EFAULT;
1737 err = 0; 1744 err = 0;
1738 break; 1745 break;
1746 case NETLINK_EXT_ACK:
1747 if (len < sizeof(int))
1748 return -EINVAL;
1749 len = sizeof(int);
1750 val = nlk->flags & NETLINK_F_EXT_ACK ? 1 : 0;
1751 if (put_user(len, optlen) || put_user(val, optval))
1752 return -EFAULT;
1753 err = 0;
1754 break;
1739 default: 1755 default:
1740 err = -ENOPROTOOPT; 1756 err = -ENOPROTOOPT;
1741 } 1757 }
@@ -2267,21 +2283,40 @@ error_free:
2267} 2283}
2268EXPORT_SYMBOL(__netlink_dump_start); 2284EXPORT_SYMBOL(__netlink_dump_start);
2269 2285
2270void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) 2286void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
2287 const struct netlink_ext_ack *extack)
2271{ 2288{
2272 struct sk_buff *skb; 2289 struct sk_buff *skb;
2273 struct nlmsghdr *rep; 2290 struct nlmsghdr *rep;
2274 struct nlmsgerr *errmsg; 2291 struct nlmsgerr *errmsg;
2275 size_t payload = sizeof(*errmsg); 2292 size_t payload = sizeof(*errmsg);
2293 size_t tlvlen = 0;
2276 struct netlink_sock *nlk = nlk_sk(NETLINK_CB(in_skb).sk); 2294 struct netlink_sock *nlk = nlk_sk(NETLINK_CB(in_skb).sk);
2295 unsigned int flags = 0;
2277 2296
2278 /* Error messages get the original request appened, unless the user 2297 /* Error messages get the original request appened, unless the user
2279 * requests to cap the error message. 2298 * requests to cap the error message, and get extra error data if
2299 * requested.
2280 */ 2300 */
2281 if (!(nlk->flags & NETLINK_F_CAP_ACK) && err) 2301 if (err) {
2282 payload += nlmsg_len(nlh); 2302 if (!(nlk->flags & NETLINK_F_CAP_ACK))
2303 payload += nlmsg_len(nlh);
2304 else
2305 flags |= NLM_F_CAPPED;
2306 if (nlk->flags & NETLINK_F_EXT_ACK && extack) {
2307 if (extack->_msg)
2308 tlvlen += nla_total_size(strlen(extack->_msg) + 1);
2309 if (extack->bad_attr)
2310 tlvlen += nla_total_size(sizeof(u32));
2311 }
2312 } else {
2313 flags |= NLM_F_CAPPED;
2314 }
2283 2315
2284 skb = nlmsg_new(payload, GFP_KERNEL); 2316 if (tlvlen)
2317 flags |= NLM_F_ACK_TLVS;
2318
2319 skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
2285 if (!skb) { 2320 if (!skb) {
2286 struct sock *sk; 2321 struct sock *sk;
2287 2322
@@ -2297,17 +2332,35 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
2297 } 2332 }
2298 2333
2299 rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 2334 rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
2300 NLMSG_ERROR, payload, 0); 2335 NLMSG_ERROR, payload, flags);
2301 errmsg = nlmsg_data(rep); 2336 errmsg = nlmsg_data(rep);
2302 errmsg->error = err; 2337 errmsg->error = err;
2303 memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh)); 2338 memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
2339
2340 if (err && nlk->flags & NETLINK_F_EXT_ACK && extack) {
2341 if (extack->_msg)
2342 WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
2343 extack->_msg));
2344 if (extack->bad_attr &&
2345 !WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
2346 (u8 *)extack->bad_attr >= in_skb->data +
2347 in_skb->len))
2348 WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS,
2349 (u8 *)extack->bad_attr -
2350 in_skb->data));
2351 }
2352
2353 nlmsg_end(skb, rep);
2354
2304 netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT); 2355 netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);
2305} 2356}
2306EXPORT_SYMBOL(netlink_ack); 2357EXPORT_SYMBOL(netlink_ack);
2307 2358
2308int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, 2359int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
2309 struct nlmsghdr *)) 2360 struct nlmsghdr *,
2361 struct netlink_ext_ack *))
2310{ 2362{
2363 struct netlink_ext_ack extack = {};
2311 struct nlmsghdr *nlh; 2364 struct nlmsghdr *nlh;
2312 int err; 2365 int err;
2313 2366
@@ -2328,13 +2381,13 @@ int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
2328 if (nlh->nlmsg_type < NLMSG_MIN_TYPE) 2381 if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
2329 goto ack; 2382 goto ack;
2330 2383
2331 err = cb(skb, nlh); 2384 err = cb(skb, nlh, &extack);
2332 if (err == -EINTR) 2385 if (err == -EINTR)
2333 goto skip; 2386 goto skip;
2334 2387
2335ack: 2388ack:
2336 if (nlh->nlmsg_flags & NLM_F_ACK || err) 2389 if (nlh->nlmsg_flags & NLM_F_ACK || err)
2337 netlink_ack(skb, nlh, err); 2390 netlink_ack(skb, nlh, err, &extack);
2338 2391
2339skip: 2392skip:
2340 msglen = NLMSG_ALIGN(nlh->nlmsg_len); 2393 msglen = NLMSG_ALIGN(nlh->nlmsg_len);
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
index f792f8d7f982..3490f2430532 100644
--- a/net/netlink/af_netlink.h
+++ b/net/netlink/af_netlink.h
@@ -13,6 +13,7 @@
13#define NETLINK_F_RECV_NO_ENOBUFS 0x8 13#define NETLINK_F_RECV_NO_ENOBUFS 0x8
14#define NETLINK_F_LISTEN_ALL_NSID 0x10 14#define NETLINK_F_LISTEN_ALL_NSID 0x10
15#define NETLINK_F_CAP_ACK 0x20 15#define NETLINK_F_CAP_ACK 0x20
16#define NETLINK_F_EXT_ACK 0x40
16 17
17#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) 18#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
18#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long)) 19#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 92e0981f7404..57b2e3648bc0 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -605,7 +605,8 @@ out:
605 return err; 605 return err;
606} 606}
607 607
608static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 608static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
609 struct netlink_ext_ack *extack)
609{ 610{
610 const struct genl_family *family; 611 const struct genl_family *family;
611 int err; 612 int err;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 4f7e62ddc17e..e93d5c0471b2 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2448,7 +2448,8 @@ static const struct xfrm_link {
2448 [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 2448 [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
2449}; 2449};
2450 2450
2451static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 2451static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
2452 struct netlink_ext_ack *extack)
2452{ 2453{
2453 struct net *net = sock_net(skb->sk); 2454 struct net *net = sock_net(skb->sk);
2454 struct nlattr *attrs[XFRMA_MAX+1]; 2455 struct nlattr *attrs[XFRMA_MAX+1];