diff options
-rw-r--r-- | crypto/crypto_user.c | 3 | ||||
-rw-r--r-- | drivers/infiniband/core/netlink.c | 5 | ||||
-rw-r--r-- | drivers/scsi/scsi_netlink.c | 2 | ||||
-rw-r--r-- | include/linux/netlink.h | 26 | ||||
-rw-r--r-- | include/net/netlink.h | 3 | ||||
-rw-r--r-- | include/uapi/linux/netlink.h | 32 | ||||
-rw-r--r-- | kernel/audit.c | 2 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 3 | ||||
-rw-r--r-- | net/core/sock_diag.c | 3 | ||||
-rw-r--r-- | net/decnet/netfilter/dn_rtmsg.c | 2 | ||||
-rw-r--r-- | net/hsr/hsr_netlink.c | 4 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_core.c | 2 | ||||
-rw-r--r-- | net/netfilter/nfnetlink.c | 22 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 71 | ||||
-rw-r--r-- | net/netlink/af_netlink.h | 1 | ||||
-rw-r--r-- | net/netlink/genetlink.c | 3 | ||||
-rw-r--r-- | net/xfrm/xfrm_user.c | 3 |
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 | ||
486 | static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 486 | static 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 | } |
147 | EXPORT_SYMBOL(ibnl_put_attr); | 147 | EXPORT_SYMBOL(ibnl_put_attr); |
148 | 148 | ||
149 | static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 149 | static 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 | ||
112 | next_msg: | 112 | next_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 | */ | ||
71 | struct 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 | |||
65 | extern void netlink_kernel_release(struct sock *sk); | 88 | extern void netlink_kernel_release(struct sock *sk); |
66 | extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); | 89 | extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); |
67 | extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); | 90 | extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); |
68 | extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group); | 91 | extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group); |
69 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); | 92 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, |
93 | const struct netlink_ext_ack *extack); | ||
70 | extern int netlink_has_listeners(struct sock *sk, unsigned int group); | 94 | extern int netlink_has_listeners(struct sock *sk, unsigned int group); |
71 | 95 | ||
72 | extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock); | 96 | extern 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 | ||
235 | int netlink_rcv_skb(struct sk_buff *skb, | 235 | int 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 *)); | ||
237 | int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid, | 238 | int 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 { | |||
101 | struct nlmsgerr { | 105 | struct 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 | */ | ||
128 | enum 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 | ||
119 | struct nl_pktinfo { | 151 | struct 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 | ||
4049 | static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 4049 | static 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 | ||
241 | static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 241 | static 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 | ||
101 | static inline void dnrmg_receive_user_skb(struct sk_buff *skb) | 101 | static 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 | ||
352 | invalid: | 352 | invalid: |
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 | ||
356 | nla_put_failure: | 356 | nla_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 | ||
434 | invalid: | 434 | invalid: |
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 | ||
438 | nla_put_failure: | 438 | nla_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, | |||
148 | EXPORT_SYMBOL_GPL(nfnetlink_unicast); | 148 | EXPORT_SYMBOL_GPL(nfnetlink_unicast); |
149 | 149 | ||
150 | /* Process one complete nfnetlink message. */ | 150 | /* Process one complete nfnetlink message. */ |
151 | static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 151 | static 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); |
288 | replay: | 289 | replay: |
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 | } |
2268 | EXPORT_SYMBOL(__netlink_dump_start); | 2284 | EXPORT_SYMBOL(__netlink_dump_start); |
2269 | 2285 | ||
2270 | void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) | 2286 | void 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 | } |
2306 | EXPORT_SYMBOL(netlink_ack); | 2357 | EXPORT_SYMBOL(netlink_ack); |
2307 | 2358 | ||
2308 | int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, | 2359 | int 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 | ||
2335 | ack: | 2388 | ack: |
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 | ||
2339 | skip: | 2392 | skip: |
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 | ||
608 | static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 608 | static 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 | ||
2451 | static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 2451 | static 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]; |