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
3 changed files with 149 additions and 0 deletions
|
@ -2,6 +2,7 @@
|
||||||
#define __CRYPTOHASH_H
|
#define __CRYPTOHASH_H
|
||||||
|
|
||||||
#define SHA_DIGEST_WORDS 5
|
#define SHA_DIGEST_WORDS 5
|
||||||
|
#define SHA_MESSAGE_BYTES (512 /*bits*/ / 8)
|
||||||
#define SHA_WORKSPACE_WORDS 80
|
#define SHA_WORKSPACE_WORDS 80
|
||||||
|
|
||||||
void sha_init(__u32 *buf);
|
void sha_init(__u32 *buf);
|
||||||
|
|
|
@ -1478,6 +1478,14 @@ struct tcp_request_sock_ops {
|
||||||
#endif
|
#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_v4_init(void);
|
||||||
extern void tcp_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/cache.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/crypto.h>
|
#include <linux/crypto.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
|
||||||
#include <net/icmp.h>
|
#include <net/icmp.h>
|
||||||
#include <net/tcp.h>
|
#include <net/tcp.h>
|
||||||
|
@ -2848,6 +2849,135 @@ EXPORT_SYMBOL(tcp_md5_hash_key);
|
||||||
|
|
||||||
#endif
|
#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)
|
void tcp_done(struct sock *sk)
|
||||||
{
|
{
|
||||||
if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
|
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;
|
struct sk_buff *skb = NULL;
|
||||||
unsigned long nr_pages, limit;
|
unsigned long nr_pages, limit;
|
||||||
int order, i, max_share;
|
int order, i, max_share;
|
||||||
|
unsigned long jiffy = jiffies;
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb));
|
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_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size);
|
||||||
|
|
||||||
tcp_register_congestion_control(&tcp_reno);
|
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);
|
EXPORT_SYMBOL(tcp_close);
|
||||||
|
|
Loading…
Reference in a new issue