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 | |
| 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>
| -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 | } |
