diff options
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r-- | net/ipv4/tcp.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c05e8edaf544..dadef867a3bb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -258,6 +258,7 @@ | |||
258 | #include <linux/bootmem.h> | 258 | #include <linux/bootmem.h> |
259 | #include <linux/cache.h> | 259 | #include <linux/cache.h> |
260 | #include <linux/err.h> | 260 | #include <linux/err.h> |
261 | #include <linux/crypto.h> | ||
261 | 262 | ||
262 | #include <net/icmp.h> | 263 | #include <net/icmp.h> |
263 | #include <net/tcp.h> | 264 | #include <net/tcp.h> |
@@ -1942,6 +1943,13 @@ static int do_tcp_setsockopt(struct sock *sk, int level, | |||
1942 | } | 1943 | } |
1943 | break; | 1944 | break; |
1944 | 1945 | ||
1946 | #ifdef CONFIG_TCP_MD5SIG | ||
1947 | case TCP_MD5SIG: | ||
1948 | /* Read the IP->Key mappings from userspace */ | ||
1949 | err = tp->af_specific->md5_parse(sk, optval, optlen); | ||
1950 | break; | ||
1951 | #endif | ||
1952 | |||
1945 | default: | 1953 | default: |
1946 | err = -ENOPROTOOPT; | 1954 | err = -ENOPROTOOPT; |
1947 | break; | 1955 | break; |
@@ -2231,6 +2239,135 @@ out: | |||
2231 | } | 2239 | } |
2232 | EXPORT_SYMBOL(tcp_tso_segment); | 2240 | EXPORT_SYMBOL(tcp_tso_segment); |
2233 | 2241 | ||
2242 | #ifdef CONFIG_TCP_MD5SIG | ||
2243 | static unsigned long tcp_md5sig_users; | ||
2244 | static struct tcp_md5sig_pool **tcp_md5sig_pool; | ||
2245 | static DEFINE_SPINLOCK(tcp_md5sig_pool_lock); | ||
2246 | |||
2247 | static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool **pool) | ||
2248 | { | ||
2249 | int cpu; | ||
2250 | for_each_possible_cpu(cpu) { | ||
2251 | struct tcp_md5sig_pool *p = *per_cpu_ptr(pool, cpu); | ||
2252 | if (p) { | ||
2253 | if (p->md5_desc.tfm) | ||
2254 | crypto_free_hash(p->md5_desc.tfm); | ||
2255 | kfree(p); | ||
2256 | p = NULL; | ||
2257 | } | ||
2258 | } | ||
2259 | free_percpu(pool); | ||
2260 | } | ||
2261 | |||
2262 | void tcp_free_md5sig_pool(void) | ||
2263 | { | ||
2264 | struct tcp_md5sig_pool **pool = NULL; | ||
2265 | |||
2266 | spin_lock(&tcp_md5sig_pool_lock); | ||
2267 | if (--tcp_md5sig_users == 0) { | ||
2268 | pool = tcp_md5sig_pool; | ||
2269 | tcp_md5sig_pool = NULL; | ||
2270 | } | ||
2271 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2272 | if (pool) | ||
2273 | __tcp_free_md5sig_pool(pool); | ||
2274 | } | ||
2275 | |||
2276 | EXPORT_SYMBOL(tcp_free_md5sig_pool); | ||
2277 | |||
2278 | struct tcp_md5sig_pool **__tcp_alloc_md5sig_pool(void) | ||
2279 | { | ||
2280 | int cpu; | ||
2281 | struct tcp_md5sig_pool **pool; | ||
2282 | |||
2283 | pool = alloc_percpu(struct tcp_md5sig_pool *); | ||
2284 | if (!pool) | ||
2285 | return NULL; | ||
2286 | |||
2287 | for_each_possible_cpu(cpu) { | ||
2288 | struct tcp_md5sig_pool *p; | ||
2289 | struct crypto_hash *hash; | ||
2290 | |||
2291 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
2292 | if (!p) | ||
2293 | goto out_free; | ||
2294 | *per_cpu_ptr(pool, cpu) = p; | ||
2295 | |||
2296 | hash = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); | ||
2297 | if (!hash || IS_ERR(hash)) | ||
2298 | goto out_free; | ||
2299 | |||
2300 | p->md5_desc.tfm = hash; | ||
2301 | } | ||
2302 | return pool; | ||
2303 | out_free: | ||
2304 | __tcp_free_md5sig_pool(pool); | ||
2305 | return NULL; | ||
2306 | } | ||
2307 | |||
2308 | struct tcp_md5sig_pool **tcp_alloc_md5sig_pool(void) | ||
2309 | { | ||
2310 | struct tcp_md5sig_pool **pool; | ||
2311 | int alloc = 0; | ||
2312 | |||
2313 | retry: | ||
2314 | spin_lock(&tcp_md5sig_pool_lock); | ||
2315 | pool = tcp_md5sig_pool; | ||
2316 | if (tcp_md5sig_users++ == 0) { | ||
2317 | alloc = 1; | ||
2318 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2319 | } else if (!pool) { | ||
2320 | tcp_md5sig_users--; | ||
2321 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2322 | cpu_relax(); | ||
2323 | goto retry; | ||
2324 | } else | ||
2325 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2326 | |||
2327 | if (alloc) { | ||
2328 | /* we cannot hold spinlock here because this may sleep. */ | ||
2329 | struct tcp_md5sig_pool **p = __tcp_alloc_md5sig_pool(); | ||
2330 | spin_lock(&tcp_md5sig_pool_lock); | ||
2331 | if (!p) { | ||
2332 | tcp_md5sig_users--; | ||
2333 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2334 | return NULL; | ||
2335 | } | ||
2336 | pool = tcp_md5sig_pool; | ||
2337 | if (pool) { | ||
2338 | /* oops, it has already been assigned. */ | ||
2339 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2340 | __tcp_free_md5sig_pool(p); | ||
2341 | } else { | ||
2342 | tcp_md5sig_pool = pool = p; | ||
2343 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2344 | } | ||
2345 | } | ||
2346 | return pool; | ||
2347 | } | ||
2348 | |||
2349 | EXPORT_SYMBOL(tcp_alloc_md5sig_pool); | ||
2350 | |||
2351 | struct tcp_md5sig_pool *__tcp_get_md5sig_pool(int cpu) | ||
2352 | { | ||
2353 | struct tcp_md5sig_pool **p; | ||
2354 | spin_lock(&tcp_md5sig_pool_lock); | ||
2355 | p = tcp_md5sig_pool; | ||
2356 | if (p) | ||
2357 | tcp_md5sig_users++; | ||
2358 | spin_unlock(&tcp_md5sig_pool_lock); | ||
2359 | return (p ? *per_cpu_ptr(p, cpu) : NULL); | ||
2360 | } | ||
2361 | |||
2362 | EXPORT_SYMBOL(__tcp_get_md5sig_pool); | ||
2363 | |||
2364 | void __tcp_put_md5sig_pool(void) { | ||
2365 | __tcp_free_md5sig_pool(tcp_md5sig_pool); | ||
2366 | } | ||
2367 | |||
2368 | EXPORT_SYMBOL(__tcp_put_md5sig_pool); | ||
2369 | #endif | ||
2370 | |||
2234 | extern void __skb_cb_too_small_for_tcp(int, int); | 2371 | extern void __skb_cb_too_small_for_tcp(int, int); |
2235 | extern struct tcp_congestion_ops tcp_reno; | 2372 | extern struct tcp_congestion_ops tcp_reno; |
2236 | 2373 | ||