aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ipv6.h3
-rw-r--r--include/net/inet_timewait_sock.h3
-rw-r--r--include/net/sock.h4
-rw-r--r--include/net/tcp.h3
-rw-r--r--include/net/timewait_sock.h31
-rw-r--r--net/core/sock.c21
-rw-r--r--net/dccp/ipv4.c9
-rw-r--r--net/dccp/ipv6.c6
-rw-r--r--net/ipv4/inet_timewait_sock.c5
-rw-r--r--net/ipv4/tcp_ipv4.c71
-rw-r--r--net/ipv6/tcp_ipv6.c25
11 files changed, 118 insertions, 63 deletions
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 7d3908594fac..a0d04891fe12 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -360,7 +360,8 @@ struct tcp6_timewait_sock {
360 360
361static inline u16 inet6_tw_offset(const struct proto *prot) 361static inline u16 inet6_tw_offset(const struct proto *prot)
362{ 362{
363 return prot->twsk_obj_size - sizeof(struct inet6_timewait_sock); 363 return prot->twsk_prot->twsk_obj_size -
364 sizeof(struct inet6_timewait_sock);
364} 365}
365 366
366static inline struct inet6_timewait_sock *inet6_twsk(const struct sock *sk) 367static inline struct inet6_timewait_sock *inet6_twsk(const struct sock *sk)
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index ca240f856c46..e396a65473d7 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -26,6 +26,7 @@
26 26
27#include <net/sock.h> 27#include <net/sock.h>
28#include <net/tcp_states.h> 28#include <net/tcp_states.h>
29#include <net/timewait_sock.h>
29 30
30#include <asm/atomic.h> 31#include <asm/atomic.h>
31 32
@@ -200,7 +201,7 @@ static inline void inet_twsk_put(struct inet_timewait_sock *tw)
200 printk(KERN_DEBUG "%s timewait_sock %p released\n", 201 printk(KERN_DEBUG "%s timewait_sock %p released\n",
201 tw->tw_prot->name, tw); 202 tw->tw_prot->name, tw);
202#endif 203#endif
203 kmem_cache_free(tw->tw_prot->twsk_slab, tw); 204 kmem_cache_free(tw->tw_prot->twsk_prot->twsk_slab, tw);
204 module_put(owner); 205 module_put(owner);
205 } 206 }
206} 207}
diff --git a/include/net/sock.h b/include/net/sock.h
index 0fbae85c6d55..91d28957dc10 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -493,6 +493,7 @@ extern void sk_stream_kill_queues(struct sock *sk);
493extern int sk_wait_data(struct sock *sk, long *timeo); 493extern int sk_wait_data(struct sock *sk, long *timeo);
494 494
495struct request_sock_ops; 495struct request_sock_ops;
496struct timewait_sock_ops;
496 497
497/* Networking protocol blocks we attach to sockets. 498/* Networking protocol blocks we attach to sockets.
498 * socket layer -> transport layer interface 499 * socket layer -> transport layer interface
@@ -557,11 +558,10 @@ struct proto {
557 kmem_cache_t *slab; 558 kmem_cache_t *slab;
558 unsigned int obj_size; 559 unsigned int obj_size;
559 560
560 kmem_cache_t *twsk_slab;
561 unsigned int twsk_obj_size;
562 atomic_t *orphan_count; 561 atomic_t *orphan_count;
563 562
564 struct request_sock_ops *rsk_prot; 563 struct request_sock_ops *rsk_prot;
564 struct timewait_sock_ops *twsk_prot;
565 565
566 struct module *owner; 566 struct module *owner;
567 567
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 83b117a25c2a..176221cd0cce 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -287,6 +287,9 @@ extern int tcp_rcv_established(struct sock *sk,
287 287
288extern void tcp_rcv_space_adjust(struct sock *sk); 288extern void tcp_rcv_space_adjust(struct sock *sk);
289 289
290extern int tcp_twsk_unique(struct sock *sk,
291 struct sock *sktw, void *twp);
292
290static inline void tcp_dec_quickack_mode(struct sock *sk, 293static inline void tcp_dec_quickack_mode(struct sock *sk,
291 const unsigned int pkts) 294 const unsigned int pkts)
292{ 295{
diff --git a/include/net/timewait_sock.h b/include/net/timewait_sock.h
new file mode 100644
index 000000000000..2544281e1d5e
--- /dev/null
+++ b/include/net/timewait_sock.h
@@ -0,0 +1,31 @@
1/*
2 * NET Generic infrastructure for Network protocols.
3 *
4 * Authors: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11#ifndef _TIMEWAIT_SOCK_H
12#define _TIMEWAIT_SOCK_H
13
14#include <linux/slab.h>
15#include <net/sock.h>
16
17struct timewait_sock_ops {
18 kmem_cache_t *twsk_slab;
19 unsigned int twsk_obj_size;
20 int (*twsk_unique)(struct sock *sk,
21 struct sock *sktw, void *twp);
22};
23
24static inline int twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
25{
26 if (sk->sk_prot->twsk_prot->twsk_unique != NULL)
27 return sk->sk_prot->twsk_prot->twsk_unique(sk, sktw, twp);
28 return 0;
29}
30
31#endif /* _TIMEWAIT_SOCK_H */
diff --git a/net/core/sock.c b/net/core/sock.c
index 13cc3be4f056..6465b0e4c8cb 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1488,7 +1488,7 @@ int proto_register(struct proto *prot, int alloc_slab)
1488 } 1488 }
1489 } 1489 }
1490 1490
1491 if (prot->twsk_obj_size) { 1491 if (prot->twsk_prot != NULL) {
1492 static const char mask[] = "tw_sock_%s"; 1492 static const char mask[] = "tw_sock_%s";
1493 1493
1494 timewait_sock_slab_name = kmalloc(strlen(prot->name) + sizeof(mask) - 1, GFP_KERNEL); 1494 timewait_sock_slab_name = kmalloc(strlen(prot->name) + sizeof(mask) - 1, GFP_KERNEL);
@@ -1497,11 +1497,12 @@ int proto_register(struct proto *prot, int alloc_slab)
1497 goto out_free_request_sock_slab; 1497 goto out_free_request_sock_slab;
1498 1498
1499 sprintf(timewait_sock_slab_name, mask, prot->name); 1499 sprintf(timewait_sock_slab_name, mask, prot->name);
1500 prot->twsk_slab = kmem_cache_create(timewait_sock_slab_name, 1500 prot->twsk_prot->twsk_slab =
1501 prot->twsk_obj_size, 1501 kmem_cache_create(timewait_sock_slab_name,
1502 0, SLAB_HWCACHE_ALIGN, 1502 prot->twsk_prot->twsk_obj_size,
1503 NULL, NULL); 1503 0, SLAB_HWCACHE_ALIGN,
1504 if (prot->twsk_slab == NULL) 1504 NULL, NULL);
1505 if (prot->twsk_prot->twsk_slab == NULL)
1505 goto out_free_timewait_sock_slab_name; 1506 goto out_free_timewait_sock_slab_name;
1506 } 1507 }
1507 } 1508 }
@@ -1548,12 +1549,12 @@ void proto_unregister(struct proto *prot)
1548 prot->rsk_prot->slab = NULL; 1549 prot->rsk_prot->slab = NULL;
1549 } 1550 }
1550 1551
1551 if (prot->twsk_slab != NULL) { 1552 if (prot->twsk_prot != NULL && prot->twsk_prot->twsk_slab != NULL) {
1552 const char *name = kmem_cache_name(prot->twsk_slab); 1553 const char *name = kmem_cache_name(prot->twsk_prot->twsk_slab);
1553 1554
1554 kmem_cache_destroy(prot->twsk_slab); 1555 kmem_cache_destroy(prot->twsk_prot->twsk_slab);
1555 kfree(name); 1556 kfree(name);
1556 prot->twsk_slab = NULL; 1557 prot->twsk_prot->twsk_slab = NULL;
1557 } 1558 }
1558} 1559}
1559 1560
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index bc28d71905e2..e11cda0cb6b3 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -20,6 +20,7 @@
20#include <net/icmp.h> 20#include <net/icmp.h>
21#include <net/inet_hashtables.h> 21#include <net/inet_hashtables.h>
22#include <net/sock.h> 22#include <net/sock.h>
23#include <net/timewait_sock.h>
23#include <net/tcp_states.h> 24#include <net/tcp_states.h>
24#include <net/xfrm.h> 25#include <net/xfrm.h>
25 26
@@ -1309,6 +1310,10 @@ static struct request_sock_ops dccp_request_sock_ops = {
1309 .send_reset = dccp_v4_ctl_send_reset, 1310 .send_reset = dccp_v4_ctl_send_reset,
1310}; 1311};
1311 1312
1313static struct timewait_sock_ops dccp_timewait_sock_ops = {
1314 .twsk_obj_size = sizeof(struct inet_timewait_sock),
1315};
1316
1312struct proto dccp_prot = { 1317struct proto dccp_prot = {
1313 .name = "DCCP", 1318 .name = "DCCP",
1314 .owner = THIS_MODULE, 1319 .owner = THIS_MODULE,
@@ -1332,5 +1337,7 @@ struct proto dccp_prot = {
1332 .max_header = MAX_DCCP_HEADER, 1337 .max_header = MAX_DCCP_HEADER,
1333 .obj_size = sizeof(struct dccp_sock), 1338 .obj_size = sizeof(struct dccp_sock),
1334 .rsk_prot = &dccp_request_sock_ops, 1339 .rsk_prot = &dccp_request_sock_ops,
1335 .twsk_obj_size = sizeof(struct inet_timewait_sock), 1340 .twsk_prot = &dccp_timewait_sock_ops,
1336}; 1341};
1342
1343EXPORT_SYMBOL_GPL(dccp_prot);
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index a7d2aee5b3af..4d078f5b911b 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -652,6 +652,10 @@ static struct request_sock_ops dccp6_request_sock_ops = {
652 .send_reset = dccp_v6_ctl_send_reset, 652 .send_reset = dccp_v6_ctl_send_reset,
653}; 653};
654 654
655static struct timewait_sock_ops dccp6_timewait_sock_ops = {
656 .twsk_obj_size = sizeof(struct dccp6_timewait_sock),
657};
658
655static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) 659static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
656{ 660{
657 struct ipv6_pinfo *np = inet6_sk(sk); 661 struct ipv6_pinfo *np = inet6_sk(sk);
@@ -1359,7 +1363,7 @@ static struct proto dccp_v6_prot = {
1359 .max_header = MAX_DCCP_HEADER, 1363 .max_header = MAX_DCCP_HEADER,
1360 .obj_size = sizeof(struct dccp6_sock), 1364 .obj_size = sizeof(struct dccp6_sock),
1361 .rsk_prot = &dccp6_request_sock_ops, 1365 .rsk_prot = &dccp6_request_sock_ops,
1362 .twsk_obj_size = sizeof(struct dccp6_timewait_sock), 1366 .twsk_prot = &dccp6_timewait_sock_ops,
1363}; 1367};
1364 1368
1365static struct inet6_protocol dccp_v6_protocol = { 1369static struct inet6_protocol dccp_v6_protocol = {
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index a010e9a68811..417f126c749e 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -90,8 +90,9 @@ EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
90 90
91struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state) 91struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state)
92{ 92{
93 struct inet_timewait_sock *tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_slab, 93 struct inet_timewait_sock *tw =
94 SLAB_ATOMIC); 94 kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
95 SLAB_ATOMIC);
95 if (tw != NULL) { 96 if (tw != NULL) {
96 const struct inet_sock *inet = inet_sk(sk); 97 const struct inet_sock *inet = inet_sk(sk);
97 98
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 0b5ab04d3c5a..6728772a943a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -69,6 +69,7 @@
69#include <net/transp_v6.h> 69#include <net/transp_v6.h>
70#include <net/ipv6.h> 70#include <net/ipv6.h>
71#include <net/inet_common.h> 71#include <net/inet_common.h>
72#include <net/timewait_sock.h>
72#include <net/xfrm.h> 73#include <net/xfrm.h>
73 74
74#include <linux/inet.h> 75#include <linux/inet.h>
@@ -118,6 +119,39 @@ static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
118 skb->h.th->source); 119 skb->h.th->source);
119} 120}
120 121
122int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
123{
124 const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
125 struct tcp_sock *tp = tcp_sk(sk);
126
127 /* With PAWS, it is safe from the viewpoint
128 of data integrity. Even without PAWS it is safe provided sequence
129 spaces do not overlap i.e. at data rates <= 80Mbit/sec.
130
131 Actually, the idea is close to VJ's one, only timestamp cache is
132 held not per host, but per port pair and TW bucket is used as state
133 holder.
134
135 If TW bucket has been already destroyed we fall back to VJ's scheme
136 and use initial timestamp retrieved from peer table.
137 */
138 if (tcptw->tw_ts_recent_stamp &&
139 (twp == NULL || (sysctl_tcp_tw_reuse &&
140 xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
141 tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
142 if (tp->write_seq == 0)
143 tp->write_seq = 1;
144 tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
145 tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
146 sock_hold(sktw);
147 return 1;
148 }
149
150 return 0;
151}
152
153EXPORT_SYMBOL_GPL(tcp_twsk_unique);
154
121/* called with local bh disabled */ 155/* called with local bh disabled */
122static int __tcp_v4_check_established(struct sock *sk, __u16 lport, 156static int __tcp_v4_check_established(struct sock *sk, __u16 lport,
123 struct inet_timewait_sock **twp) 157 struct inet_timewait_sock **twp)
@@ -142,35 +176,9 @@ static int __tcp_v4_check_established(struct sock *sk, __u16 lport,
142 tw = inet_twsk(sk2); 176 tw = inet_twsk(sk2);
143 177
144 if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) { 178 if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
145 const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2); 179 if (twsk_unique(sk, sk2, twp))
146 struct tcp_sock *tp = tcp_sk(sk);
147
148 /* With PAWS, it is safe from the viewpoint
149 of data integrity. Even without PAWS it
150 is safe provided sequence spaces do not
151 overlap i.e. at data rates <= 80Mbit/sec.
152
153 Actually, the idea is close to VJ's one,
154 only timestamp cache is held not per host,
155 but per port pair and TW bucket is used
156 as state holder.
157
158 If TW bucket has been already destroyed we
159 fall back to VJ's scheme and use initial
160 timestamp retrieved from peer table.
161 */
162 if (tcptw->tw_ts_recent_stamp &&
163 (!twp || (sysctl_tcp_tw_reuse &&
164 xtime.tv_sec -
165 tcptw->tw_ts_recent_stamp > 1))) {
166 tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
167 if (tp->write_seq == 0)
168 tp->write_seq = 1;
169 tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
170 tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
171 sock_hold(sk2);
172 goto unique; 180 goto unique;
173 } else 181 else
174 goto not_unique; 182 goto not_unique;
175 } 183 }
176 } 184 }
@@ -869,6 +877,11 @@ struct request_sock_ops tcp_request_sock_ops = {
869 .send_reset = tcp_v4_send_reset, 877 .send_reset = tcp_v4_send_reset,
870}; 878};
871 879
880static struct timewait_sock_ops tcp_timewait_sock_ops = {
881 .twsk_obj_size = sizeof(struct tcp_timewait_sock),
882 .twsk_unique = tcp_twsk_unique,
883};
884
872int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) 885int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
873{ 886{
874 struct inet_request_sock *ireq; 887 struct inet_request_sock *ireq;
@@ -1979,7 +1992,7 @@ struct proto tcp_prot = {
1979 .sysctl_rmem = sysctl_tcp_rmem, 1992 .sysctl_rmem = sysctl_tcp_rmem,
1980 .max_header = MAX_TCP_HEADER, 1993 .max_header = MAX_TCP_HEADER,
1981 .obj_size = sizeof(struct tcp_sock), 1994 .obj_size = sizeof(struct tcp_sock),
1982 .twsk_obj_size = sizeof(struct tcp_timewait_sock), 1995 .twsk_prot = &tcp_timewait_sock_ops,
1983 .rsk_prot = &tcp_request_sock_ops, 1996 .rsk_prot = &tcp_request_sock_ops,
1984}; 1997};
1985 1998
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index e5c8a669e84e..514b57bb80b7 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -60,6 +60,7 @@
60#include <net/addrconf.h> 60#include <net/addrconf.h>
61#include <net/snmp.h> 61#include <net/snmp.h>
62#include <net/dsfield.h> 62#include <net/dsfield.h>
63#include <net/timewait_sock.h>
63 64
64#include <asm/uaccess.h> 65#include <asm/uaccess.h>
65 66
@@ -147,22 +148,9 @@ static int __tcp_v6_check_established(struct sock *sk, const __u16 lport,
147 ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) && 148 ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
148 ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) && 149 ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
149 sk2->sk_bound_dev_if == sk->sk_bound_dev_if) { 150 sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
150 const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2); 151 if (twsk_unique(sk, sk2, twp))
151 struct tcp_sock *tp = tcp_sk(sk);
152
153 if (tcptw->tw_ts_recent_stamp &&
154 (!twp ||
155 (sysctl_tcp_tw_reuse &&
156 xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
157 /* See comment in tcp_ipv4.c */
158 tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
159 if (!tp->write_seq)
160 tp->write_seq = 1;
161 tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
162 tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
163 sock_hold(sk2);
164 goto unique; 152 goto unique;
165 } else 153 else
166 goto not_unique; 154 goto not_unique;
167 } 155 }
168 } 156 }
@@ -711,6 +699,11 @@ static struct request_sock_ops tcp6_request_sock_ops = {
711 .send_reset = tcp_v6_send_reset 699 .send_reset = tcp_v6_send_reset
712}; 700};
713 701
702static struct timewait_sock_ops tcp6_timewait_sock_ops = {
703 .twsk_obj_size = sizeof(struct tcp6_timewait_sock),
704 .twsk_unique = tcp_twsk_unique,
705};
706
714static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) 707static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
715{ 708{
716 struct ipv6_pinfo *np = inet6_sk(sk); 709 struct ipv6_pinfo *np = inet6_sk(sk);
@@ -1752,7 +1745,7 @@ struct proto tcpv6_prot = {
1752 .sysctl_rmem = sysctl_tcp_rmem, 1745 .sysctl_rmem = sysctl_tcp_rmem,
1753 .max_header = MAX_TCP_HEADER, 1746 .max_header = MAX_TCP_HEADER,
1754 .obj_size = sizeof(struct tcp6_sock), 1747 .obj_size = sizeof(struct tcp6_sock),
1755 .twsk_obj_size = sizeof(struct tcp6_timewait_sock), 1748 .twsk_prot = &tcp6_timewait_sock_ops,
1756 .rsk_prot = &tcp6_request_sock_ops, 1749 .rsk_prot = &tcp6_request_sock_ops,
1757}; 1750};
1758 1751