net: ipv6: fix TCP early demux
authorEric Dumazet <edumazet@google.com>
Mon, 6 Aug 2012 05:09:33 +0000 (05:09 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 6 Aug 2012 20:33:21 +0000 (13:33 -0700)
IPv6 needs a cookie in dst_check() call.

We need to add rx_dst_cookie and provide a family independent
sk_rx_dst_set(sk, skb) method to properly support IPv6 TCP early demux.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/ipv6.h
include/net/inet_connection_sock.h
include/net/inet_sock.h
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_minisocks.c
net/ipv6/tcp_ipv6.c

index 379e433e15e0fc97fc3f376b610f12ff77f694ae..879db26ec4013297fb76f16143b87ec9db52b5e4 100644 (file)
@@ -369,6 +369,7 @@ struct ipv6_pinfo {
        __u8                    rcv_tclass;
 
        __u32                   dst_cookie;
+       __u32                   rx_dst_cookie;
 
        struct ipv6_mc_socklist __rcu *ipv6_mc_list;
        struct ipv6_ac_socklist *ipv6_ac_list;
index 5ee66f517b4f6c34734be9d5f6552439802a5a38..ba1d3615acbb7bc481e7b676d897140cea0213b6 100644 (file)
@@ -39,6 +39,7 @@ struct inet_connection_sock_af_ops {
        int         (*queue_xmit)(struct sk_buff *skb, struct flowi *fl);
        void        (*send_check)(struct sock *sk, struct sk_buff *skb);
        int         (*rebuild_header)(struct sock *sk);
+       void        (*sk_rx_dst_set)(struct sock *sk, const struct sk_buff *skb);
        int         (*conn_request)(struct sock *sk, struct sk_buff *skb);
        struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb,
                                      struct request_sock *req,
index 83b567fe194163b74f734cedaf1dffff28e914a6..613cfa4016728300da88a8fc6f1f664de99da5cc 100644 (file)
@@ -249,13 +249,4 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk)
        return flags;
 }
 
-static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
-{
-       struct dst_entry *dst = skb_dst(skb);
-
-       dst_hold(dst);
-       sk->sk_rx_dst = dst;
-       inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
-}
-
 #endif /* _INET_SOCK_H */
index 2fd2bc9e3c644d650282ea8c66a41fab9683167f..85308b90df80a844119a8cc773783f78aab5de04 100644 (file)
@@ -5392,6 +5392,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
+       if (unlikely(sk->sk_rx_dst == NULL))
+               inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb);
        /*
         *      Header prediction.
         *      The code loosely follows the one in the famous
@@ -5605,7 +5607,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
        tcp_set_state(sk, TCP_ESTABLISHED);
 
        if (skb != NULL) {
-               inet_sk_rx_dst_set(sk, skb);
+               icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
                security_inet_conn_established(sk, skb);
        }
 
index 42b2a6a73092a9a75bd96239af003269575328f5..272241f16fcb219dd44accae8ec0cc47eb25ab47 100644 (file)
@@ -1627,9 +1627,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
                                sk->sk_rx_dst = NULL;
                        }
                }
-               if (unlikely(sk->sk_rx_dst == NULL))
-                       inet_sk_rx_dst_set(sk, skb);
-
                if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
                        rsk = sk;
                        goto reset;
@@ -1872,10 +1869,20 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = {
        .twsk_destructor= tcp_twsk_destructor,
 };
 
+static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+{
+       struct dst_entry *dst = skb_dst(skb);
+
+       dst_hold(dst);
+       sk->sk_rx_dst = dst;
+       inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
+}
+
 const struct inet_connection_sock_af_ops ipv4_specific = {
        .queue_xmit        = ip_queue_xmit,
        .send_check        = tcp_v4_send_check,
        .rebuild_header    = inet_sk_rebuild_header,
+       .sk_rx_dst_set     = inet_sk_rx_dst_set,
        .conn_request      = tcp_v4_conn_request,
        .syn_recv_sock     = tcp_v4_syn_recv_sock,
        .net_header_len    = sizeof(struct iphdr),
index 232a90c3ec8695dfa740fe141ba113074f99a289..d9c9dcef2de303592832867af979dcfb380b6132 100644 (file)
@@ -387,7 +387,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
                struct tcp_sock *oldtp = tcp_sk(sk);
                struct tcp_cookie_values *oldcvp = oldtp->cookie_values;
 
-               inet_sk_rx_dst_set(newsk, skb);
+               newicsk->icsk_af_ops->sk_rx_dst_set(newsk, skb);
 
                /* TCP Cookie Transactions require space for the cookie pair,
                 * as it differs for each connection.  There is no need to
index c66b90f71c9b9610b5dec49c038b80dffd836058..5a439e9a4c0171fc23ed49421bc6708ef8df05d8 100644 (file)
@@ -1447,7 +1447,17 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
                opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC));
 
        if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
+               struct dst_entry *dst = sk->sk_rx_dst;
+
                sock_rps_save_rxhash(sk, skb);
+               if (dst) {
+                       if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
+                           dst->ops->check(dst, np->rx_dst_cookie) == NULL) {
+                               dst_release(dst);
+                               sk->sk_rx_dst = NULL;
+                       }
+               }
+
                if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len))
                        goto reset;
                if (opt_skb)
@@ -1705,9 +1715,9 @@ static void tcp_v6_early_demux(struct sk_buff *skb)
                        struct dst_entry *dst = sk->sk_rx_dst;
                        struct inet_sock *icsk = inet_sk(sk);
                        if (dst)
-                               dst = dst_check(dst, 0);
+                               dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
                        if (dst &&
-                           icsk->rx_dst_ifindex == inet6_iif(skb))
+                           icsk->rx_dst_ifindex == skb->skb_iif)
                                skb_dst_set_noref(skb, dst);
                }
        }
@@ -1719,10 +1729,23 @@ static struct timewait_sock_ops tcp6_timewait_sock_ops = {
        .twsk_destructor= tcp_twsk_destructor,
 };
 
+static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+{
+       struct dst_entry *dst = skb_dst(skb);
+       const struct rt6_info *rt = (const struct rt6_info *)dst;
+
+       dst_hold(dst);
+       sk->sk_rx_dst = dst;
+       inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
+       if (rt->rt6i_node)
+               inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum;
+}
+
 static const struct inet_connection_sock_af_ops ipv6_specific = {
        .queue_xmit        = inet6_csk_xmit,
        .send_check        = tcp_v6_send_check,
        .rebuild_header    = inet6_sk_rebuild_header,
+       .sk_rx_dst_set     = inet6_sk_rx_dst_set,
        .conn_request      = tcp_v6_conn_request,
        .syn_recv_sock     = tcp_v6_syn_recv_sock,
        .net_header_len    = sizeof(struct ipv6hdr),