TCPCT part 1b: generate Responder Cookie secret
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / tcp.c
index 90b2e0649bfb76419ae0f6ee552a0f40c41bf43f..ba03ac80435a535dde4a9627f617333949d99e98 100644 (file)
 #include <linux/cache.h>
 #include <linux/err.h>
 #include <linux/crypto.h>
+#include <linux/time.h>
 
 #include <net/icmp.h>
 #include <net/tcp.h>
@@ -1183,7 +1184,9 @@ void tcp_cleanup_rbuf(struct sock *sk, int copied)
 #if TCP_DEBUG
        struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
 
-       WARN_ON(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
+       WARN(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq),
+            KERN_INFO "cleanup rbuf bug: copied %X seq %X rcvnxt %X\n",
+            tp->copied_seq, TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt);
 #endif
 
        if (inet_csk_ack_scheduled(sk)) {
@@ -1430,11 +1433,13 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                        /* Now that we have two receive queues this
                         * shouldn't happen.
                         */
-                       if (before(*seq, TCP_SKB_CB(skb)->seq)) {
-                               printk(KERN_INFO "recvmsg bug: copied %X "
-                                      "seq %X\n", *seq, TCP_SKB_CB(skb)->seq);
+                       if (WARN(before(*seq, TCP_SKB_CB(skb)->seq),
+                            KERN_INFO "recvmsg bug: copied %X "
+                                      "seq %X rcvnxt %X fl %X\n", *seq,
+                                      TCP_SKB_CB(skb)->seq, tp->rcv_nxt,
+                                      flags))
                                break;
-                       }
+
                        offset = *seq - TCP_SKB_CB(skb)->seq;
                        if (tcp_hdr(skb)->syn)
                                offset--;
@@ -1442,9 +1447,10 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                                goto found_ok_skb;
                        if (tcp_hdr(skb)->fin)
                                goto found_fin_ok;
-                       if (WARN_ON(!(flags & MSG_PEEK)))
-                               printk(KERN_INFO "recvmsg bug 2: copied %X "
-                                      "seq %X\n", *seq, TCP_SKB_CB(skb)->seq);
+                       WARN(!(flags & MSG_PEEK), KERN_INFO "recvmsg bug 2: "
+                                       "copied %X seq %X rcvnxt %X fl %X\n",
+                                       *seq, TCP_SKB_CB(skb)->seq,
+                                       tp->rcv_nxt, flags);
                }
 
                /* Well, if we have backlog, try to process it now yet. */
@@ -2037,7 +2043,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        __skb_queue_purge(&sk->sk_async_wait_queue);
 #endif
 
-       inet->dport = 0;
+       inet->inet_dport = 0;
 
        if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
                inet_reset_saddr(sk);
@@ -2054,6 +2060,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
        tp->snd_cwnd_cnt = 0;
        tp->bytes_acked = 0;
+       tp->window_clamp = 0;
        tcp_set_ca_state(sk, TCP_CA_Open);
        tcp_clear_retrans(tp);
        inet_csk_delack_init(sk);
@@ -2061,7 +2068,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));
        __sk_dst_reset(sk);
 
-       WARN_ON(inet->num && !icsk->icsk_bind_hash);
+       WARN_ON(inet->inet_num && !icsk->icsk_bind_hash);
 
        sk->sk_error_report(sk);
        return err;
@@ -2842,6 +2849,135 @@ EXPORT_SYMBOL(tcp_md5_hash_key);
 
 #endif
 
+/**
+ * Each Responder maintains up to two secret values concurrently for
+ * efficient secret rollover.  Each secret value has 4 states:
+ *
+ * Generating.  (tcp_secret_generating != tcp_secret_primary)
+ *    Generates new Responder-Cookies, but not yet used for primary
+ *    verification.  This is a short-term state, typically lasting only
+ *    one round trip time (RTT).
+ *
+ * Primary.  (tcp_secret_generating == tcp_secret_primary)
+ *    Used both for generation and primary verification.
+ *
+ * Retiring.  (tcp_secret_retiring != tcp_secret_secondary)
+ *    Used for verification, until the first failure that can be
+ *    verified by the newer Generating secret.  At that time, this
+ *    cookie's state is changed to Secondary, and the Generating
+ *    cookie's state is changed to Primary.  This is a short-term state,
+ *    typically lasting only one round trip time (RTT).
+ *
+ * Secondary.  (tcp_secret_retiring == tcp_secret_secondary)
+ *    Used for secondary verification, after primary verification
+ *    failures.  This state lasts no more than twice the Maximum Segment
+ *    Lifetime (2MSL).  Then, the secret is discarded.
+ */
+struct tcp_cookie_secret {
+       /* The secret is divided into two parts.  The digest part is the
+        * equivalent of previously hashing a secret and saving the state,
+        * and serves as an initialization vector (IV).  The message part
+        * serves as the trailing secret.
+        */
+       u32                             secrets[COOKIE_WORKSPACE_WORDS];
+       unsigned long                   expires;
+};
+
+#define TCP_SECRET_1MSL (HZ * TCP_PAWS_MSL)
+#define TCP_SECRET_2MSL (HZ * TCP_PAWS_MSL * 2)
+#define TCP_SECRET_LIFE (HZ * 600)
+
+static struct tcp_cookie_secret tcp_secret_one;
+static struct tcp_cookie_secret tcp_secret_two;
+
+/* Essentially a circular list, without dynamic allocation. */
+static struct tcp_cookie_secret *tcp_secret_generating;
+static struct tcp_cookie_secret *tcp_secret_primary;
+static struct tcp_cookie_secret *tcp_secret_retiring;
+static struct tcp_cookie_secret *tcp_secret_secondary;
+
+static DEFINE_SPINLOCK(tcp_secret_locker);
+
+/* Select a pseudo-random word in the cookie workspace.
+ */
+static inline u32 tcp_cookie_work(const u32 *ws, const int n)
+{
+       return ws[COOKIE_DIGEST_WORDS + ((COOKIE_MESSAGE_WORDS-1) & ws[n])];
+}
+
+/* Fill bakery[COOKIE_WORKSPACE_WORDS] with generator, updating as needed.
+ * Called in softirq context.
+ * Returns: 0 for success.
+ */
+int tcp_cookie_generator(u32 *bakery)
+{
+       unsigned long jiffy = jiffies;
+
+       if (unlikely(time_after_eq(jiffy, tcp_secret_generating->expires))) {
+               spin_lock_bh(&tcp_secret_locker);
+               if (!time_after_eq(jiffy, tcp_secret_generating->expires)) {
+                       /* refreshed by another */
+                       memcpy(bakery,
+                              &tcp_secret_generating->secrets[0],
+                              COOKIE_WORKSPACE_WORDS);
+               } else {
+                       /* still needs refreshing */
+                       get_random_bytes(bakery, COOKIE_WORKSPACE_WORDS);
+
+                       /* The first time, paranoia assumes that the
+                        * randomization function isn't as strong.  But,
+                        * this secret initialization is delayed until
+                        * the last possible moment (packet arrival).
+                        * Although that time is observable, it is
+                        * unpredictably variable.  Mash in the most
+                        * volatile clock bits available, and expire the
+                        * secret extra quickly.
+                        */
+                       if (unlikely(tcp_secret_primary->expires ==
+                                    tcp_secret_secondary->expires)) {
+                               struct timespec tv;
+
+                               getnstimeofday(&tv);
+                               bakery[COOKIE_DIGEST_WORDS+0] ^=
+                                       (u32)tv.tv_nsec;
+
+                               tcp_secret_secondary->expires = jiffy
+                                       + TCP_SECRET_1MSL
+                                       + (0x0f & tcp_cookie_work(bakery, 0));
+                       } else {
+                               tcp_secret_secondary->expires = jiffy
+                                       + TCP_SECRET_LIFE
+                                       + (0xff & tcp_cookie_work(bakery, 1));
+                               tcp_secret_primary->expires = jiffy
+                                       + TCP_SECRET_2MSL
+                                       + (0x1f & tcp_cookie_work(bakery, 2));
+                       }
+                       memcpy(&tcp_secret_secondary->secrets[0],
+                              bakery, COOKIE_WORKSPACE_WORDS);
+
+                       rcu_assign_pointer(tcp_secret_generating,
+                                          tcp_secret_secondary);
+                       rcu_assign_pointer(tcp_secret_retiring,
+                                          tcp_secret_primary);
+                       /*
+                        * Neither call_rcu() nor synchronize_rcu() needed.
+                        * Retiring data is not freed.  It is replaced after
+                        * further (locked) pointer updates, and a quiet time
+                        * (minimum 1MSL, maximum LIFE - 2MSL).
+                        */
+               }
+               spin_unlock_bh(&tcp_secret_locker);
+       } else {
+               rcu_read_lock_bh();
+               memcpy(bakery,
+                      &rcu_dereference(tcp_secret_generating)->secrets[0],
+                      COOKIE_WORKSPACE_WORDS);
+               rcu_read_unlock_bh();
+       }
+       return 0;
+}
+EXPORT_SYMBOL(tcp_cookie_generator);
+
 void tcp_done(struct sock *sk)
 {
        if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
@@ -2876,6 +3012,7 @@ void __init tcp_init(void)
        struct sk_buff *skb = NULL;
        unsigned long nr_pages, limit;
        int order, i, max_share;
+       unsigned long jiffy = jiffies;
 
        BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb));
 
@@ -2898,11 +3035,10 @@ void __init tcp_init(void)
                                        (totalram_pages >= 128 * 1024) ?
                                        13 : 15,
                                        0,
-                                       &tcp_hashinfo.ehash_size,
                                        NULL,
+                                       &tcp_hashinfo.ehash_mask,
                                        thash_entries ? 0 : 512 * 1024);
-       tcp_hashinfo.ehash_size = 1 << tcp_hashinfo.ehash_size;
-       for (i = 0; i < tcp_hashinfo.ehash_size; i++) {
+       for (i = 0; i <= tcp_hashinfo.ehash_mask; i++) {
                INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].chain, i);
                INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].twchain, i);
        }
@@ -2911,7 +3047,7 @@ void __init tcp_init(void)
        tcp_hashinfo.bhash =
                alloc_large_system_hash("TCP bind",
                                        sizeof(struct inet_bind_hashbucket),
-                                       tcp_hashinfo.ehash_size,
+                                       tcp_hashinfo.ehash_mask + 1,
                                        (totalram_pages >= 128 * 1024) ?
                                        13 : 15,
                                        0,
@@ -2966,10 +3102,19 @@ void __init tcp_init(void)
        sysctl_tcp_rmem[2] = max(87380, max_share);
 
        printk(KERN_INFO "TCP: Hash tables configured "
-              "(established %d bind %d)\n",
-              tcp_hashinfo.ehash_size, tcp_hashinfo.bhash_size);
+              "(established %u bind %u)\n",
+              tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size);
 
        tcp_register_congestion_control(&tcp_reno);
+
+       memset(&tcp_secret_one.secrets[0], 0, sizeof(tcp_secret_one.secrets));
+       memset(&tcp_secret_two.secrets[0], 0, sizeof(tcp_secret_two.secrets));
+       tcp_secret_one.expires = jiffy; /* past due */
+       tcp_secret_two.expires = jiffy; /* past due */
+       tcp_secret_generating = &tcp_secret_one;
+       tcp_secret_primary = &tcp_secret_one;
+       tcp_secret_retiring = &tcp_secret_two;
+       tcp_secret_secondary = &tcp_secret_two;
 }
 
 EXPORT_SYMBOL(tcp_close);