|
@@ -264,6 +264,7 @@
|
|
|
#include <linux/cache.h>
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/crypto.h>
|
|
|
+#include <linux/time.h>
|
|
|
|
|
|
#include <net/icmp.h>
|
|
|
#include <net/tcp.h>
|
|
@@ -2848,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)
|
|
@@ -2882,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));
|
|
|
|
|
@@ -2975,6 +3106,15 @@ void __init tcp_init(void)
|
|
|
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);
|