net: dropreason: add SKB_DROP_REASON_DUP_FRAG
[linux-block.git] / net / ipv6 / ipv6_sockglue.c
index 2d2f4dd9e5dfa8278f5dbad0bfd5a2e16a77406d..9ce51680290b11abc334ce9dcb238d02223ec75d 100644 (file)
@@ -419,15 +419,18 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                rtnl_lock();
        sockopt_lock_sock(sk);
 
+       /* Another thread has converted the socket into IPv4 with
+        * IPV6_ADDRFORM concurrently.
+        */
+       if (unlikely(sk->sk_family != AF_INET6))
+               goto unlock;
+
        switch (optname) {
 
        case IPV6_ADDRFORM:
                if (optlen < sizeof(int))
                        goto e_inval;
                if (val == PF_INET) {
-                       struct ipv6_txoptions *opt;
-                       struct sk_buff *pktopt;
-
                        if (sk->sk_type == SOCK_RAW)
                                break;
 
@@ -458,7 +461,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                                break;
                        }
 
-                       fl6_free_socklist(sk);
                        __ipv6_sock_mc_close(sk);
                        __ipv6_sock_ac_close(sk);
 
@@ -475,9 +477,10 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                                sock_prot_inuse_add(net, sk->sk_prot, -1);
                                sock_prot_inuse_add(net, &tcp_prot, 1);
 
-                               /* Paired with READ_ONCE(sk->sk_prot) in net/ipv6/af_inet6.c */
+                               /* Paired with READ_ONCE(sk->sk_prot) in inet6_stream_ops */
                                WRITE_ONCE(sk->sk_prot, &tcp_prot);
-                               icsk->icsk_af_ops = &ipv4_specific;
+                               /* Paired with READ_ONCE() in tcp_(get|set)sockopt() */
+                               WRITE_ONCE(icsk->icsk_af_ops, &ipv4_specific);
                                sk->sk_socket->ops = &inet_stream_ops;
                                sk->sk_family = PF_INET;
                                tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
@@ -490,19 +493,19 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                                sock_prot_inuse_add(net, sk->sk_prot, -1);
                                sock_prot_inuse_add(net, prot, 1);
 
-                               /* Paired with READ_ONCE(sk->sk_prot) in net/ipv6/af_inet6.c */
+                               /* Paired with READ_ONCE(sk->sk_prot) in inet6_dgram_ops */
                                WRITE_ONCE(sk->sk_prot, prot);
                                sk->sk_socket->ops = &inet_dgram_ops;
                                sk->sk_family = PF_INET;
                        }
-                       opt = xchg((__force struct ipv6_txoptions **)&np->opt,
-                                  NULL);
-                       if (opt) {
-                               atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
-                               txopt_put(opt);
-                       }
-                       pktopt = xchg(&np->pktoptions, NULL);
-                       kfree_skb(pktopt);
+
+                       /* Disable all options not to allocate memory anymore,
+                        * but there is still a race.  See the lockless path
+                        * in udpv6_sendmsg() and ipv6_local_rxpmtu().
+                        */
+                       np->rxopt.all = 0;
+
+                       inet6_cleanup_sock(sk);
 
                        /*
                         * ... and add it to the refcnt debug socks count
@@ -994,6 +997,7 @@ done:
                break;
        }
 
+unlock:
        sockopt_release_sock(sk);
        if (needs_rtnl)
                rtnl_unlock();
@@ -1001,10 +1005,8 @@ done:
        return retv;
 
 e_inval:
-       sockopt_release_sock(sk);
-       if (needs_rtnl)
-               rtnl_unlock();
-       return -EINVAL;
+       retv = -EINVAL;
+       goto unlock;
 }
 
 int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,