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:
parent
e6b4d11367
commit
da5c78c826
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
140
net/ipv4/tcp.c
140
net/ipv4/tcp.c
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue