diff options
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r-- | net/netlink/af_netlink.c | 71 |
1 files changed, 62 insertions, 9 deletions
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); |