cfg80211: make wdev_list accessible to drivers
[cascardo/linux.git] / net / ipv6 / datagram.c
index a73d701..ea9ee5c 100644 (file)
@@ -40,18 +40,114 @@ static bool ipv6_mapped_addr_any(const struct in6_addr *a)
        return ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0);
 }
 
+static void ip6_datagram_flow_key_init(struct flowi6 *fl6, struct sock *sk)
+{
+       struct inet_sock *inet = inet_sk(sk);
+       struct ipv6_pinfo *np = inet6_sk(sk);
+
+       memset(fl6, 0, sizeof(*fl6));
+       fl6->flowi6_proto = sk->sk_protocol;
+       fl6->daddr = sk->sk_v6_daddr;
+       fl6->saddr = np->saddr;
+       fl6->flowi6_oif = sk->sk_bound_dev_if;
+       fl6->flowi6_mark = sk->sk_mark;
+       fl6->fl6_dport = inet->inet_dport;
+       fl6->fl6_sport = inet->inet_sport;
+       fl6->flowlabel = np->flow_label;
+
+       if (!fl6->flowi6_oif)
+               fl6->flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
+
+       if (!fl6->flowi6_oif && ipv6_addr_is_multicast(&fl6->daddr))
+               fl6->flowi6_oif = np->mcast_oif;
+
+       security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
+}
+
+int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr)
+{
+       struct ip6_flowlabel *flowlabel = NULL;
+       struct in6_addr *final_p, final;
+       struct ipv6_txoptions *opt;
+       struct dst_entry *dst;
+       struct inet_sock *inet = inet_sk(sk);
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct flowi6 fl6;
+       int err = 0;
+
+       if (np->sndflow && (np->flow_label & IPV6_FLOWLABEL_MASK)) {
+               flowlabel = fl6_sock_lookup(sk, np->flow_label);
+               if (!flowlabel)
+                       return -EINVAL;
+       }
+       ip6_datagram_flow_key_init(&fl6, sk);
+
+       rcu_read_lock();
+       opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt);
+       final_p = fl6_update_dst(&fl6, opt, &final);
+       rcu_read_unlock();
+
+       dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
+       if (IS_ERR(dst)) {
+               err = PTR_ERR(dst);
+               goto out;
+       }
+
+       if (fix_sk_saddr) {
+               if (ipv6_addr_any(&np->saddr))
+                       np->saddr = fl6.saddr;
+
+               if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
+                       sk->sk_v6_rcv_saddr = fl6.saddr;
+                       inet->inet_rcv_saddr = LOOPBACK4_IPV6;
+                       if (sk->sk_prot->rehash)
+                               sk->sk_prot->rehash(sk);
+               }
+       }
+
+       ip6_dst_store(sk, dst,
+                     ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ?
+                     &sk->sk_v6_daddr : NULL,
+#ifdef CONFIG_IPV6_SUBTREES
+                     ipv6_addr_equal(&fl6.saddr, &np->saddr) ?
+                     &np->saddr :
+#endif
+                     NULL);
+
+out:
+       fl6_sock_release(flowlabel);
+       return err;
+}
+
+void ip6_datagram_release_cb(struct sock *sk)
+{
+       struct dst_entry *dst;
+
+       if (ipv6_addr_v4mapped(&sk->sk_v6_daddr))
+               return;
+
+       rcu_read_lock();
+       dst = __sk_dst_get(sk);
+       if (!dst || !dst->obsolete ||
+           dst->ops->check(dst, inet6_sk(sk)->dst_cookie)) {
+               rcu_read_unlock();
+               return;
+       }
+       rcu_read_unlock();
+
+       ip6_datagram_dst_update(sk, false);
+}
+EXPORT_SYMBOL_GPL(ip6_datagram_release_cb);
+
 static int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
        struct sockaddr_in6     *usin = (struct sockaddr_in6 *) uaddr;
        struct inet_sock        *inet = inet_sk(sk);
        struct ipv6_pinfo       *np = inet6_sk(sk);
-       struct in6_addr *daddr, *final_p, final;
-       struct dst_entry        *dst;
-       struct flowi6           fl6;
-       struct ip6_flowlabel    *flowlabel = NULL;
-       struct ipv6_txoptions   *opt;
+       struct in6_addr         *daddr;
        int                     addr_type;
        int                     err;
+       __be32                  fl6_flowlabel = 0;
 
        if (usin->sin6_family == AF_INET) {
                if (__ipv6_only_sock(sk))
@@ -66,15 +162,8 @@ static int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int a
        if (usin->sin6_family != AF_INET6)
                return -EAFNOSUPPORT;
 
-       memset(&fl6, 0, sizeof(fl6));
-       if (np->sndflow) {
-               fl6.flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
-               if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
-                       flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
-                       if (!flowlabel)
-                               return -EINVAL;
-               }
-       }
+       if (np->sndflow)
+               fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
 
        addr_type = ipv6_addr_type(&usin->sin6_addr);
 
@@ -145,7 +234,7 @@ ipv4_connected:
        }
 
        sk->sk_v6_daddr = *daddr;
-       np->flow_label = fl6.flowlabel;
+       np->flow_label = fl6_flowlabel;
 
        inet->inet_dport = usin->sin6_port;
 
@@ -154,59 +243,13 @@ ipv4_connected:
         *      destination cache for it.
         */
 
-       fl6.flowi6_proto = sk->sk_protocol;
-       fl6.daddr = sk->sk_v6_daddr;
-       fl6.saddr = np->saddr;
-       fl6.flowi6_oif = sk->sk_bound_dev_if;
-       fl6.flowi6_mark = sk->sk_mark;
-       fl6.fl6_dport = inet->inet_dport;
-       fl6.fl6_sport = inet->inet_sport;
-
-       if (!fl6.flowi6_oif)
-               fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
-
-       if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST))
-               fl6.flowi6_oif = np->mcast_oif;
-
-       security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
-
-       rcu_read_lock();
-       opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt);
-       final_p = fl6_update_dst(&fl6, opt, &final);
-       rcu_read_unlock();
-
-       dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
-       err = 0;
-       if (IS_ERR(dst)) {
-               err = PTR_ERR(dst);
+       err = ip6_datagram_dst_update(sk, true);
+       if (err)
                goto out;
-       }
-
-       /* source address lookup done in ip6_dst_lookup */
-
-       if (ipv6_addr_any(&np->saddr))
-               np->saddr = fl6.saddr;
-
-       if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
-               sk->sk_v6_rcv_saddr = fl6.saddr;
-               inet->inet_rcv_saddr = LOOPBACK4_IPV6;
-               if (sk->sk_prot->rehash)
-                       sk->sk_prot->rehash(sk);
-       }
-
-       ip6_dst_store(sk, dst,
-                     ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ?
-                     &sk->sk_v6_daddr : NULL,
-#ifdef CONFIG_IPV6_SUBTREES
-                     ipv6_addr_equal(&fl6.saddr, &np->saddr) ?
-                     &np->saddr :
-#endif
-                     NULL);
 
        sk->sk_state = TCP_ESTABLISHED;
        sk_set_txhash(sk);
 out:
-       fl6_sock_release(flowlabel);
        return err;
 }
 
@@ -407,9 +450,10 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
                copied = len;
        }
        err = skb_copy_datagram_msg(skb, 0, msg, copied);
-       if (err)
-               goto out_free_skb;
-
+       if (unlikely(err)) {
+               kfree_skb(skb);
+               return err;
+       }
        sock_recv_timestamp(msg, sk, skb);
 
        serr = SKB_EXT_ERR(skb);
@@ -466,8 +510,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
        msg->msg_flags |= MSG_ERRQUEUE;
        err = copied;
 
-out_free_skb:
-       kfree_skb(skb);
+       consume_skb(skb);
 out:
        return err;
 }