Merge remote-tracking branches 'asoc/topic/sunxi', 'asoc/topic/symmetry', 'asoc/topic...
[sfrench/cifs-2.6.git] / net / netlink / af_netlink.c
index b93148e8e9fb2dc9a22cccf34d168e99b55042de..79cc1bf36e4af7d2c70575e56203a482ba2dca97 100644 (file)
@@ -128,7 +128,6 @@ static const char *const nlk_cb_mutex_key_strings[MAX_LINKS + 1] = {
 };
 
 static int netlink_dump(struct sock *sk);
-static void netlink_skb_destructor(struct sk_buff *skb);
 
 /* nl_table locking explained:
  * Lookup and traversal are protected with an RCU read-side lock. Insertion
@@ -254,6 +253,9 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb,
        struct sock *sk = skb->sk;
        int ret = -ENOMEM;
 
+       if (!net_eq(dev_net(dev), sock_net(sk)))
+               return 0;
+
        dev_hold(dev);
 
        if (is_vmalloc_addr(skb->head))
@@ -2136,7 +2138,7 @@ static int netlink_dump(struct sock *sk)
        struct sk_buff *skb = NULL;
        struct nlmsghdr *nlh;
        struct module *module;
-       int len, err = -ENOBUFS;
+       int err = -ENOBUFS;
        int alloc_min_size;
        int alloc_size;
 
@@ -2183,9 +2185,11 @@ static int netlink_dump(struct sock *sk)
        skb_reserve(skb, skb_tailroom(skb) - alloc_size);
        netlink_skb_set_owner_r(skb, sk);
 
-       len = cb->dump(skb, cb);
+       if (nlk->dump_done_errno > 0)
+               nlk->dump_done_errno = cb->dump(skb, cb);
 
-       if (len > 0) {
+       if (nlk->dump_done_errno > 0 ||
+           skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) {
                mutex_unlock(nlk->cb_mutex);
 
                if (sk_filter(sk, skb))
@@ -2195,13 +2199,15 @@ static int netlink_dump(struct sock *sk)
                return 0;
        }
 
-       nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI);
-       if (!nlh)
+       nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE,
+                              sizeof(nlk->dump_done_errno), NLM_F_MULTI);
+       if (WARN_ON(!nlh))
                goto errout_skb;
 
        nl_dump_check_consistent(cb, nlh);
 
-       memcpy(nlmsg_data(nlh), &len, sizeof(len));
+       memcpy(nlmsg_data(nlh), &nlk->dump_done_errno,
+              sizeof(nlk->dump_done_errno));
 
        if (sk_filter(sk, skb))
                kfree_skb(skb);
@@ -2273,6 +2279,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        }
 
        nlk->cb_running = true;
+       nlk->dump_done_errno = INT_MAX;
 
        mutex_unlock(nlk->cb_mutex);
 
@@ -2313,17 +2320,16 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
         * requests to cap the error message, and get extra error data if
         * requested.
         */
+       if (nlk_has_extack && extack && extack->_msg)
+               tlvlen += nla_total_size(strlen(extack->_msg) + 1);
+
        if (err) {
                if (!(nlk->flags & NETLINK_F_CAP_ACK))
                        payload += nlmsg_len(nlh);
                else
                        flags |= NLM_F_CAPPED;
-               if (nlk_has_extack && extack) {
-                       if (extack->_msg)
-                               tlvlen += nla_total_size(strlen(extack->_msg) + 1);
-                       if (extack->bad_attr)
-                               tlvlen += nla_total_size(sizeof(u32));
-               }
+               if (nlk_has_extack && extack && extack->bad_attr)
+                       tlvlen += nla_total_size(sizeof(u32));
        } else {
                flags |= NLM_F_CAPPED;
 
@@ -2336,16 +2342,8 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
 
        skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
        if (!skb) {
-               struct sock *sk;
-
-               sk = netlink_lookup(sock_net(in_skb->sk),
-                                   in_skb->sk->sk_protocol,
-                                   NETLINK_CB(in_skb).portid);
-               if (sk) {
-                       sk->sk_err = ENOBUFS;
-                       sk->sk_error_report(sk);
-                       sock_put(sk);
-               }
+               NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
+               NETLINK_CB(in_skb).sk->sk_error_report(NETLINK_CB(in_skb).sk);
                return;
        }
 
@@ -2356,10 +2354,11 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
        memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
 
        if (nlk_has_extack && extack) {
+               if (extack->_msg) {
+                       WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
+                                              extack->_msg));
+               }
                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 +