ipv6/udp: use sticky pktinfo egress ifindex on connect()
[pandora-kernel.git] / net / ipv6 / datagram.c
index e248069..20437cb 100644 (file)
@@ -38,7 +38,7 @@ static inline int ipv6_mapped_addr_any(const struct in6_addr *a)
        return (ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0));
 }
 
-int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+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);
@@ -54,7 +54,7 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        if (usin->sin6_family == AF_INET) {
                if (__ipv6_only_sock(sk))
                        return -EAFNOSUPPORT;
-               err = ip4_datagram_connect(sk, uaddr, addr_len);
+               err = __ip4_datagram_connect(sk, uaddr, addr_len);
                goto ipv4_connected;
        }
 
@@ -97,9 +97,9 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                sin.sin_addr.s_addr = daddr->s6_addr32[3];
                sin.sin_port = usin->sin6_port;
 
-               err = ip4_datagram_connect(sk,
-                                          (struct sockaddr*) &sin,
-                                          sizeof(sin));
+               err = __ip4_datagram_connect(sk,
+                                            (struct sockaddr *) &sin,
+                                            sizeof(sin));
 
 ipv4_connected:
                if (err)
@@ -161,13 +161,18 @@ ipv4_connected:
        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));
 
-       opt = flowlabel ? flowlabel->opt : np->opt;
+       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, true);
        err = 0;
@@ -203,6 +208,16 @@ out:
        return err;
 }
 
+int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+       int res;
+
+       lock_sock(sk);
+       res = __ip6_datagram_connect(sk, uaddr, addr_len);
+       release_sock(sk);
+       return res;
+}
+
 void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
                     __be16 port, u32 info, u8 *payload)
 {
@@ -315,7 +330,7 @@ void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu)
 /*
  *     Handle MSG_ERRQUEUE
  */
-int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
+int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sock_exterr_skb *serr;
@@ -366,15 +381,15 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
                        ipv6_addr_set_v4mapped(*(__be32 *)(nh + serr->addr_offset),
                                               &sin->sin6_addr);
                }
+               *addr_len = sizeof(*sin);
        }
 
        memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
        sin = &errhdr.offender;
-       sin->sin6_family = AF_UNSPEC;
+       memset(sin, 0, sizeof(*sin));
+
        if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
                sin->sin6_family = AF_INET6;
-               sin->sin6_flowinfo = 0;
-               sin->sin6_scope_id = 0;
                if (skb->protocol == htons(ETH_P_IPV6)) {
                        ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr);
                        if (np->rxopt.all)
@@ -382,11 +397,9 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
                        if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
                                sin->sin6_scope_id = IP6CB(skb)->iif;
                } else {
-                       struct inet_sock *inet = inet_sk(sk);
-
                        ipv6_addr_set_v4mapped(ip_hdr(skb)->saddr,
                                               &sin->sin6_addr);
-                       if (inet->cmsg_flags)
+                       if (inet_sk(sk)->cmsg_flags)
                                ip_cmsg_recv(msg, skb);
                }
        }
@@ -418,7 +431,8 @@ out:
 /*
  *     Handle IPV6_RECVPATHMTU
  */
-int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len)
+int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len,
+                    int *addr_len)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sk_buff *skb;
@@ -452,6 +466,7 @@ int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len)
                sin->sin6_port = 0;
                sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id;
                ipv6_addr_copy(&sin->sin6_addr, &mtu_info.ip6m_addr.sin6_addr);
+               *addr_len = sizeof(*sin);
        }
 
        put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info);