TCPCT part 1b: generate Responder Cookie secret

Define (missing) hash message size for SHA1.

Define hashing size constants specific to TCP cookies.

Add new function: tcp_cookie_generator().

Maintain global secret values for tcp_cookie_generator().

This is a significantly revised implementation of earlier (15-year-old)
Photuris [RFC-2522] code for the KA9Q cooperative multitasking platform.

Linux RCU technique appears to be well-suited to this application, though
neither of the circular queue items are freed.

These functions will also be used in subsequent patches that implement
additional features.

Signed-off-by: William.Allen.Simpson@gmail.com
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
William Allen Simpson 2009-12-02 18:12:09 +00:00 committed by David S. Miller
parent e6b4d11367
commit da5c78c826
3 changed files with 149 additions and 0 deletions

View file

@ -2,6 +2,7 @@
#define __CRYPTOHASH_H
#define SHA_DIGEST_WORDS 5
#define SHA_MESSAGE_BYTES (512 /*bits*/ / 8)
#define SHA_WORKSPACE_WORDS 80
void sha_init(__u32 *buf);

View file

@ -1478,6 +1478,14 @@ struct tcp_request_sock_ops {
#endif
};
/* Using SHA1 for now, define some constants.
*/
#define COOKIE_DIGEST_WORDS (SHA_DIGEST_WORDS)
#define COOKIE_MESSAGE_WORDS (SHA_MESSAGE_BYTES / 4)
#define COOKIE_WORKSPACE_WORDS (COOKIE_DIGEST_WORDS + COOKIE_MESSAGE_WORDS)
extern int tcp_cookie_generator(u32 *bakery);
extern void tcp_v4_init(void);
extern void tcp_init(void);

View file

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