|
@@ -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;
|
|
|
}
|
|
|
|