aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp.c
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2006-11-14 22:07:45 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-12-03 00:22:39 -0500
commitcfb6eeb4c860592edd123fdea908d23c6ad1c7dc (patch)
tree361c073622faa540ef6602ef1b0a6e8c0a17fc60 /net/ipv4/tcp.c
parentbf6bce71eae386dbc37f93af7e5ad173450d9945 (diff)
[TCP]: MD5 Signature Option (RFC2385) support.
Based on implementation by Rick Payne. Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r--net/ipv4/tcp.c137
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}
2232EXPORT_SYMBOL(tcp_tso_segment); 2240EXPORT_SYMBOL(tcp_tso_segment);
2233 2241
2242#ifdef CONFIG_TCP_MD5SIG
2243static unsigned long tcp_md5sig_users;
2244static struct tcp_md5sig_pool **tcp_md5sig_pool;
2245static DEFINE_SPINLOCK(tcp_md5sig_pool_lock);
2246
2247static 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
2262void 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
2276EXPORT_SYMBOL(tcp_free_md5sig_pool);
2277
2278struct 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;
2303out_free:
2304 __tcp_free_md5sig_pool(pool);
2305 return NULL;
2306}
2307
2308struct tcp_md5sig_pool **tcp_alloc_md5sig_pool(void)
2309{
2310 struct tcp_md5sig_pool **pool;
2311 int alloc = 0;
2312
2313retry:
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
2349EXPORT_SYMBOL(tcp_alloc_md5sig_pool);
2350
2351struct 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
2362EXPORT_SYMBOL(__tcp_get_md5sig_pool);
2363
2364void __tcp_put_md5sig_pool(void) {
2365 __tcp_free_md5sig_pool(tcp_md5sig_pool);
2366}
2367
2368EXPORT_SYMBOL(__tcp_put_md5sig_pool);
2369#endif
2370
2234extern void __skb_cb_too_small_for_tcp(int, int); 2371extern void __skb_cb_too_small_for_tcp(int, int);
2235extern struct tcp_congestion_ops tcp_reno; 2372extern struct tcp_congestion_ops tcp_reno;
2236 2373