diff options
author | William Allen Simpson <william.allen.simpson@gmail.com> | 2009-12-02 13:19:30 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-03 01:07:25 -0500 |
commit | e56fb50f2b7958b931c8a2fc0966061b3f3c8f3a (patch) | |
tree | 392f548e99f5d098286cea54fb9e18ac8c31e672 /net/ipv4 | |
parent | 435cf559f02ea3a3159eb316f97dc88bdebe9432 (diff) |
TCPCT part 1e: implement socket option TCP_COOKIE_TRANSACTIONS
Provide per socket control of the TCP cookie option and SYN/SYNACK data.
This is a straightforward re-implementation of an earlier (year-old)
patch that no longer applies cleanly, with permission of the original
author (Adam Langley):
http://thread.gmane.org/gmane.linux.network/102586
The principle difference is using a TCP option to carry the cookie nonce,
instead of a user configured offset in the data.
Allocations have been rearranged to avoid requiring GFP_ATOMIC.
Requires:
net: TCP_MSS_DEFAULT, TCP_MSS_DESIRED
TCPCT part 1c: sysctl_tcp_cookie_size, socket option TCP_COOKIE_TRANSACTIONS
TCPCT part 1d: define TCP cookie option, extend existing struct's
Signed-off-by: William.Allen.Simpson@gmail.com
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/tcp.c | 133 |
1 files changed, 131 insertions, 2 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ba03ac80435a..c8666b70cde0 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -2085,8 +2085,9 @@ static int do_tcp_setsockopt(struct sock *sk, int level, | |||
2085 | int val; | 2085 | int val; |
2086 | int err = 0; | 2086 | int err = 0; |
2087 | 2087 | ||
2088 | /* This is a string value all the others are int's */ | 2088 | /* These are data/string values, all the others are ints */ |
2089 | if (optname == TCP_CONGESTION) { | 2089 | switch (optname) { |
2090 | case TCP_CONGESTION: { | ||
2090 | char name[TCP_CA_NAME_MAX]; | 2091 | char name[TCP_CA_NAME_MAX]; |
2091 | 2092 | ||
2092 | if (optlen < 1) | 2093 | if (optlen < 1) |
@@ -2103,6 +2104,93 @@ static int do_tcp_setsockopt(struct sock *sk, int level, | |||
2103 | release_sock(sk); | 2104 | release_sock(sk); |
2104 | return err; | 2105 | return err; |
2105 | } | 2106 | } |
2107 | case TCP_COOKIE_TRANSACTIONS: { | ||
2108 | struct tcp_cookie_transactions ctd; | ||
2109 | struct tcp_cookie_values *cvp = NULL; | ||
2110 | |||
2111 | if (sizeof(ctd) > optlen) | ||
2112 | return -EINVAL; | ||
2113 | if (copy_from_user(&ctd, optval, sizeof(ctd))) | ||
2114 | return -EFAULT; | ||
2115 | |||
2116 | if (ctd.tcpct_used > sizeof(ctd.tcpct_value) || | ||
2117 | ctd.tcpct_s_data_desired > TCP_MSS_DESIRED) | ||
2118 | return -EINVAL; | ||
2119 | |||
2120 | if (ctd.tcpct_cookie_desired == 0) { | ||
2121 | /* default to global value */ | ||
2122 | } else if ((0x1 & ctd.tcpct_cookie_desired) || | ||
2123 | ctd.tcpct_cookie_desired > TCP_COOKIE_MAX || | ||
2124 | ctd.tcpct_cookie_desired < TCP_COOKIE_MIN) { | ||
2125 | return -EINVAL; | ||
2126 | } | ||
2127 | |||
2128 | if (TCP_COOKIE_OUT_NEVER & ctd.tcpct_flags) { | ||
2129 | /* Supercedes all other values */ | ||
2130 | lock_sock(sk); | ||
2131 | if (tp->cookie_values != NULL) { | ||
2132 | kref_put(&tp->cookie_values->kref, | ||
2133 | tcp_cookie_values_release); | ||
2134 | tp->cookie_values = NULL; | ||
2135 | } | ||
2136 | tp->rx_opt.cookie_in_always = 0; /* false */ | ||
2137 | tp->rx_opt.cookie_out_never = 1; /* true */ | ||
2138 | release_sock(sk); | ||
2139 | return err; | ||
2140 | } | ||
2141 | |||
2142 | /* Allocate ancillary memory before locking. | ||
2143 | */ | ||
2144 | if (ctd.tcpct_used > 0 || | ||
2145 | (tp->cookie_values == NULL && | ||
2146 | (sysctl_tcp_cookie_size > 0 || | ||
2147 | ctd.tcpct_cookie_desired > 0 || | ||
2148 | ctd.tcpct_s_data_desired > 0))) { | ||
2149 | cvp = kzalloc(sizeof(*cvp) + ctd.tcpct_used, | ||
2150 | GFP_KERNEL); | ||
2151 | if (cvp == NULL) | ||
2152 | return -ENOMEM; | ||
2153 | } | ||
2154 | lock_sock(sk); | ||
2155 | tp->rx_opt.cookie_in_always = | ||
2156 | (TCP_COOKIE_IN_ALWAYS & ctd.tcpct_flags); | ||
2157 | tp->rx_opt.cookie_out_never = 0; /* false */ | ||
2158 | |||
2159 | if (tp->cookie_values != NULL) { | ||
2160 | if (cvp != NULL) { | ||
2161 | /* Changed values are recorded by a changed | ||
2162 | * pointer, ensuring the cookie will differ, | ||
2163 | * without separately hashing each value later. | ||
2164 | */ | ||
2165 | kref_put(&tp->cookie_values->kref, | ||
2166 | tcp_cookie_values_release); | ||
2167 | kref_init(&cvp->kref); | ||
2168 | tp->cookie_values = cvp; | ||
2169 | } else { | ||
2170 | cvp = tp->cookie_values; | ||
2171 | } | ||
2172 | } | ||
2173 | if (cvp != NULL) { | ||
2174 | cvp->cookie_desired = ctd.tcpct_cookie_desired; | ||
2175 | |||
2176 | if (ctd.tcpct_used > 0) { | ||
2177 | memcpy(cvp->s_data_payload, ctd.tcpct_value, | ||
2178 | ctd.tcpct_used); | ||
2179 | cvp->s_data_desired = ctd.tcpct_used; | ||
2180 | cvp->s_data_constant = 1; /* true */ | ||
2181 | } else { | ||
2182 | /* No constant payload data. */ | ||
2183 | cvp->s_data_desired = ctd.tcpct_s_data_desired; | ||
2184 | cvp->s_data_constant = 0; /* false */ | ||
2185 | } | ||
2186 | } | ||
2187 | release_sock(sk); | ||
2188 | return err; | ||
2189 | } | ||
2190 | default: | ||
2191 | /* fallthru */ | ||
2192 | break; | ||
2193 | }; | ||
2106 | 2194 | ||
2107 | if (optlen < sizeof(int)) | 2195 | if (optlen < sizeof(int)) |
2108 | return -EINVAL; | 2196 | return -EINVAL; |
@@ -2427,6 +2515,47 @@ static int do_tcp_getsockopt(struct sock *sk, int level, | |||
2427 | if (copy_to_user(optval, icsk->icsk_ca_ops->name, len)) | 2515 | if (copy_to_user(optval, icsk->icsk_ca_ops->name, len)) |
2428 | return -EFAULT; | 2516 | return -EFAULT; |
2429 | return 0; | 2517 | return 0; |
2518 | |||
2519 | case TCP_COOKIE_TRANSACTIONS: { | ||
2520 | struct tcp_cookie_transactions ctd; | ||
2521 | struct tcp_cookie_values *cvp = tp->cookie_values; | ||
2522 | |||
2523 | if (get_user(len, optlen)) | ||
2524 | return -EFAULT; | ||
2525 | if (len < sizeof(ctd)) | ||
2526 | return -EINVAL; | ||
2527 | |||
2528 | memset(&ctd, 0, sizeof(ctd)); | ||
2529 | ctd.tcpct_flags = (tp->rx_opt.cookie_in_always ? | ||
2530 | TCP_COOKIE_IN_ALWAYS : 0) | ||
2531 | | (tp->rx_opt.cookie_out_never ? | ||
2532 | TCP_COOKIE_OUT_NEVER : 0); | ||
2533 | |||
2534 | if (cvp != NULL) { | ||
2535 | ctd.tcpct_flags |= (cvp->s_data_in ? | ||
2536 | TCP_S_DATA_IN : 0) | ||
2537 | | (cvp->s_data_out ? | ||
2538 | TCP_S_DATA_OUT : 0); | ||
2539 | |||
2540 | ctd.tcpct_cookie_desired = cvp->cookie_desired; | ||
2541 | ctd.tcpct_s_data_desired = cvp->s_data_desired; | ||
2542 | |||
2543 | /* Cookie(s) saved, return as nonce */ | ||
2544 | if (sizeof(ctd.tcpct_value) < cvp->cookie_pair_size) { | ||
2545 | /* impossible? */ | ||
2546 | return -EINVAL; | ||
2547 | } | ||
2548 | memcpy(&ctd.tcpct_value[0], &cvp->cookie_pair[0], | ||
2549 | cvp->cookie_pair_size); | ||
2550 | ctd.tcpct_used = cvp->cookie_pair_size; | ||
2551 | } | ||
2552 | |||
2553 | if (put_user(sizeof(ctd), optlen)) | ||
2554 | return -EFAULT; | ||
2555 | if (copy_to_user(optval, &ctd, sizeof(ctd))) | ||
2556 | return -EFAULT; | ||
2557 | return 0; | ||
2558 | } | ||
2430 | default: | 2559 | default: |
2431 | return -ENOPROTOOPT; | 2560 | return -ENOPROTOOPT; |
2432 | } | 2561 | } |