tcp: constify tcp_make_synack() socket argument
authorEric Dumazet <edumazet@google.com>
Fri, 25 Sep 2015 14:39:19 +0000 (07:39 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 25 Sep 2015 20:00:38 +0000 (13:00 -0700)
listener socket is not locked when tcp_make_synack() is called.

We better make sure no field is written.

There is one exception : Since SYNACK packets are attached to the listener
at this moment (or SYN_RECV child in case of Fast Open),
sock_wmalloc() needs to update sk->sk_wmem_alloc, but this is done using
atomic operations so this is safe.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/tcp.h
net/ipv4/tcp_output.c

index 45bc3c63c3fdb24e6b81553de90504b88d66be33..19f23590baa0c2e9f296e57393de1eba2f0beb40 100644 (file)
@@ -461,7 +461,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
 int tcp_connect(struct sock *sk);
-struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
                                struct request_sock *req,
                                struct tcp_fastopen_cookie *foc);
 int tcp_disconnect(struct sock *sk, int flags);
index ba6194152d39b4c05027edb68dea0af1cdeb3e7f..9eb67a8933f14cb73d007b00af133577fd26d498 100644 (file)
@@ -2944,20 +2944,25 @@ int tcp_send_synack(struct sock *sk)
  * Allocate one skb and build a SYNACK packet.
  * @dst is consumed : Caller should not use it again.
  */
-struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
                                struct request_sock *req,
                                struct tcp_fastopen_cookie *foc)
 {
-       struct tcp_out_options opts;
        struct inet_request_sock *ireq = inet_rsk(req);
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct tcphdr *th;
-       struct sk_buff *skb;
+       const struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *md5 = NULL;
+       struct tcp_out_options opts;
+       struct sk_buff *skb;
        int tcp_header_size;
+       struct tcphdr *th;
+       u16 user_mss;
        int mss;
 
-       skb = sock_wmalloc(sk, MAX_TCP_HEADER, 1, GFP_ATOMIC);
+       /* sk is a const pointer, because we want to express multiple cpus
+        * might call us concurrently.
+        * sock_wmalloc() will change sk->sk_wmem_alloc in an atomic way.
+        */
+       skb = sock_wmalloc((struct sock *)sk, MAX_TCP_HEADER, 1, GFP_ATOMIC);
        if (unlikely(!skb)) {
                dst_release(dst);
                return NULL;
@@ -2968,8 +2973,9 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        skb_dst_set(skb, dst);
 
        mss = dst_metric_advmss(dst);
-       if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
-               mss = tp->rx_opt.user_mss;
+       user_mss = READ_ONCE(tp->rx_opt.user_mss);
+       if (user_mss && user_mss < mss)
+               mss = user_mss;
 
        memset(&opts, 0, sizeof(opts));
 #ifdef CONFIG_SYN_COOKIES
@@ -3009,7 +3015,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 
        /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
        th->window = htons(min(req->rcv_wnd, 65535U));
-       tcp_options_write((__be32 *)(th + 1), tp, &opts);
+       tcp_options_write((__be32 *)(th + 1), NULL, &opts);
        th->doff = (tcp_header_size >> 2);
        TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_OUTSEGS);