diff options
author | William Allen Simpson <william.allen.simpson@gmail.com> | 2009-12-02 13:12:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-03 01:07:23 -0500 |
commit | da5c78c82629a167794436e4306b4cf1faddea90 (patch) | |
tree | c933fdc0583b592c01885890ebf770840555d28b /net | |
parent | e6b4d11367519bc71729c09d05a126b133c755be (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/tcp.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 7d4648f8b3d3..ba03ac80435a 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -264,6 +264,7 @@ | |||
264 | #include <linux/cache.h> | 264 | #include <linux/cache.h> |
265 | #include <linux/err.h> | 265 | #include <linux/err.h> |
266 | #include <linux/crypto.h> | 266 | #include <linux/crypto.h> |
267 | #include <linux/time.h> | ||
267 | 268 | ||
268 | #include <net/icmp.h> | 269 | #include <net/icmp.h> |
269 | #include <net/tcp.h> | 270 | #include <net/tcp.h> |
@@ -2848,6 +2849,135 @@ EXPORT_SYMBOL(tcp_md5_hash_key); | |||
2848 | 2849 | ||
2849 | #endif | 2850 | #endif |
2850 | 2851 | ||
2852 | /** | ||
2853 | * Each Responder maintains up to two secret values concurrently for | ||
2854 | * efficient secret rollover. Each secret value has 4 states: | ||
2855 | * | ||
2856 | * Generating. (tcp_secret_generating != tcp_secret_primary) | ||
2857 | * Generates new Responder-Cookies, but not yet used for primary | ||
2858 | * verification. This is a short-term state, typically lasting only | ||
2859 | * one round trip time (RTT). | ||
2860 | * | ||
2861 | * Primary. (tcp_secret_generating == tcp_secret_primary) | ||
2862 | * Used both for generation and primary verification. | ||
2863 | * | ||
2864 | * Retiring. (tcp_secret_retiring != tcp_secret_secondary) | ||
2865 | * Used for verification, until the first failure that can be | ||
2866 | * verified by the newer Generating secret. At that time, this | ||
2867 | * cookie's state is changed to Secondary, and the Generating | ||
2868 | * cookie's state is changed to Primary. This is a short-term state, | ||
2869 | * typically lasting only one round trip time (RTT). | ||
2870 | * | ||
2871 | * Secondary. (tcp_secret_retiring == tcp_secret_secondary) | ||
2872 | * Used for secondary verification, after primary verification | ||
2873 | * failures. This state lasts no more than twice the Maximum Segment | ||
2874 | * Lifetime (2MSL). Then, the secret is discarded. | ||
2875 | */ | ||
2876 | struct tcp_cookie_secret { | ||
2877 | /* The secret is divided into two parts. The digest part is the | ||
2878 | * equivalent of previously hashing a secret and saving the state, | ||
2879 | * and serves as an initialization vector (IV). The message part | ||
2880 | * serves as the trailing secret. | ||
2881 | */ | ||
2882 | u32 secrets[COOKIE_WORKSPACE_WORDS]; | ||
2883 | unsigned long expires; | ||
2884 | }; | ||
2885 | |||
2886 | #define TCP_SECRET_1MSL (HZ * TCP_PAWS_MSL) | ||
2887 | #define TCP_SECRET_2MSL (HZ * TCP_PAWS_MSL * 2) | ||
2888 | #define TCP_SECRET_LIFE (HZ * 600) | ||
2889 | |||
2890 | static struct tcp_cookie_secret tcp_secret_one; | ||
2891 | static struct tcp_cookie_secret tcp_secret_two; | ||
2892 | |||
2893 | /* Essentially a circular list, without dynamic allocation. */ | ||
2894 | static struct tcp_cookie_secret *tcp_secret_generating; | ||
2895 | static struct tcp_cookie_secret *tcp_secret_primary; | ||
2896 | static struct tcp_cookie_secret *tcp_secret_retiring; | ||
2897 | static struct tcp_cookie_secret *tcp_secret_secondary; | ||
2898 | |||
2899 | static DEFINE_SPINLOCK(tcp_secret_locker); | ||
2900 | |||
2901 | /* Select a pseudo-random word in the cookie workspace. | ||
2902 | */ | ||
2903 | static inline u32 tcp_cookie_work(const u32 *ws, const int n) | ||
2904 | { | ||
2905 | return ws[COOKIE_DIGEST_WORDS + ((COOKIE_MESSAGE_WORDS-1) & ws[n])]; | ||
2906 | } | ||
2907 | |||
2908 | /* Fill bakery[COOKIE_WORKSPACE_WORDS] with generator, updating as needed. | ||
2909 | * Called in softirq context. | ||
2910 | * Returns: 0 for success. | ||
2911 | */ | ||
2912 | int tcp_cookie_generator(u32 *bakery) | ||
2913 | { | ||
2914 | unsigned long jiffy = jiffies; | ||
2915 | |||
2916 | if (unlikely(time_after_eq(jiffy, tcp_secret_generating->expires))) { | ||
2917 | spin_lock_bh(&tcp_secret_locker); | ||
2918 | if (!time_after_eq(jiffy, tcp_secret_generating->expires)) { | ||
2919 | /* refreshed by another */ | ||
2920 | memcpy(bakery, | ||
2921 | &tcp_secret_generating->secrets[0], | ||
2922 | COOKIE_WORKSPACE_WORDS); | ||
2923 | } else { | ||
2924 | /* still needs refreshing */ | ||
2925 | get_random_bytes(bakery, COOKIE_WORKSPACE_WORDS); | ||
2926 | |||
2927 | /* The first time, paranoia assumes that the | ||
2928 | * randomization function isn't as strong. But, | ||
2929 | * this secret initialization is delayed until | ||
2930 | * the last possible moment (packet arrival). | ||
2931 | * Although that time is observable, it is | ||
2932 | * unpredictably variable. Mash in the most | ||
2933 | * volatile clock bits available, and expire the | ||
2934 | * secret extra quickly. | ||
2935 | */ | ||
2936 | if (unlikely(tcp_secret_primary->expires == | ||
2937 | tcp_secret_secondary->expires)) { | ||
2938 | struct timespec tv; | ||
2939 | |||
2940 | getnstimeofday(&tv); | ||
2941 | bakery[COOKIE_DIGEST_WORDS+0] ^= | ||
2942 | (u32)tv.tv_nsec; | ||
2943 | |||
2944 | tcp_secret_secondary->expires = jiffy | ||
2945 | + TCP_SECRET_1MSL | ||
2946 | + (0x0f & tcp_cookie_work(bakery, 0)); | ||
2947 | } else { | ||
2948 | tcp_secret_secondary->expires = jiffy | ||
2949 | + TCP_SECRET_LIFE | ||
2950 | + (0xff & tcp_cookie_work(bakery, 1)); | ||
2951 | tcp_secret_primary->expires = jiffy | ||
2952 | + TCP_SECRET_2MSL | ||
2953 | + (0x1f & tcp_cookie_work(bakery, 2)); | ||
2954 | } | ||
2955 | memcpy(&tcp_secret_secondary->secrets[0], | ||
2956 | bakery, COOKIE_WORKSPACE_WORDS); | ||
2957 | |||
2958 | rcu_assign_pointer(tcp_secret_generating, | ||
2959 | tcp_secret_secondary); | ||
2960 | rcu_assign_pointer(tcp_secret_retiring, | ||
2961 | tcp_secret_primary); | ||
2962 | /* | ||
2963 | * Neither call_rcu() nor synchronize_rcu() needed. | ||
2964 | * Retiring data is not freed. It is replaced after | ||
2965 | * further (locked) pointer updates, and a quiet time | ||
2966 | * (minimum 1MSL, maximum LIFE - 2MSL). | ||
2967 | */ | ||
2968 | } | ||
2969 | spin_unlock_bh(&tcp_secret_locker); | ||
2970 | } else { | ||
2971 | rcu_read_lock_bh(); | ||
2972 | memcpy(bakery, | ||
2973 | &rcu_dereference(tcp_secret_generating)->secrets[0], | ||
2974 | COOKIE_WORKSPACE_WORDS); | ||
2975 | rcu_read_unlock_bh(); | ||
2976 | } | ||
2977 | return 0; | ||
2978 | } | ||
2979 | EXPORT_SYMBOL(tcp_cookie_generator); | ||
2980 | |||
2851 | void tcp_done(struct sock *sk) | 2981 | void tcp_done(struct sock *sk) |
2852 | { | 2982 | { |
2853 | if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) | 2983 | if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) |
@@ -2882,6 +3012,7 @@ void __init tcp_init(void) | |||
2882 | struct sk_buff *skb = NULL; | 3012 | struct sk_buff *skb = NULL; |
2883 | unsigned long nr_pages, limit; | 3013 | unsigned long nr_pages, limit; |
2884 | int order, i, max_share; | 3014 | int order, i, max_share; |
3015 | unsigned long jiffy = jiffies; | ||
2885 | 3016 | ||
2886 | BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)); | 3017 | BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)); |
2887 | 3018 | ||
@@ -2975,6 +3106,15 @@ void __init tcp_init(void) | |||
2975 | tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size); | 3106 | tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size); |
2976 | 3107 | ||
2977 | tcp_register_congestion_control(&tcp_reno); | 3108 | tcp_register_congestion_control(&tcp_reno); |
3109 | |||
3110 | memset(&tcp_secret_one.secrets[0], 0, sizeof(tcp_secret_one.secrets)); | ||
3111 | memset(&tcp_secret_two.secrets[0], 0, sizeof(tcp_secret_two.secrets)); | ||
3112 | tcp_secret_one.expires = jiffy; /* past due */ | ||
3113 | tcp_secret_two.expires = jiffy; /* past due */ | ||
3114 | tcp_secret_generating = &tcp_secret_one; | ||
3115 | tcp_secret_primary = &tcp_secret_one; | ||
3116 | tcp_secret_retiring = &tcp_secret_two; | ||
3117 | tcp_secret_secondary = &tcp_secret_two; | ||
2978 | } | 3118 | } |
2979 | 3119 | ||
2980 | EXPORT_SYMBOL(tcp_close); | 3120 | EXPORT_SYMBOL(tcp_close); |