aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlink/af_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r--net/netlink/af_netlink.c71
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}
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);