aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2008-04-10 06:12:40 -0400
committerDavid S. Miller <davem@davemloft.net>2008-04-10 06:12:40 -0400
commit4dfc2817025965a2fc78a18c50f540736a6b5c24 (patch)
treef8f2f76e660d9d5c7a0f39ed8a79cb6d6d181206
parent15be75cdb5db442d0e33d37b20832b88f3ccd383 (diff)
[Syncookies]: Add support for TCP options via timestamps.
Allow the use of SACK and window scaling when syncookies are used and the client supports tcp timestamps. Options are encoded into the timestamp sent in the syn-ack and restored from the timestamp echo when the ack is received. Based on earlier work by Glenn Griffin. This patch avoids increasing the size of structs by encoding TCP options into the least significant bits of the timestamp and by not using any 'timestamp offset'. The downside is that the timestamp sent in the packet after the synack will increase by several seconds. changes since v1: don't duplicate timestamp echo decoding function, put it into ipv4/syncookie.c and have ipv6/syncookies.c use it. Feedback from Glenn Griffin: fix line indented with spaces, kill redundant if () Reviewed-by: Hagen Paul Pfeifer <hagen@jauu.net> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/request_sock.h2
-rw-r--r--include/net/tcp.h4
-rw-r--r--net/ipv4/syncookies.c89
-rw-r--r--net/ipv4/tcp_ipv4.c5
-rw-r--r--net/ipv4/tcp_output.c6
-rw-r--r--net/ipv6/syncookies.c20
-rw-r--r--net/ipv6/tcp_ipv6.c5
7 files changed, 114 insertions, 17 deletions
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index 0369f98e9f3a..b220b5f624de 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -45,7 +45,7 @@ struct request_sock {
45 struct request_sock *dl_next; /* Must be first member! */ 45 struct request_sock *dl_next; /* Must be first member! */
46 u16 mss; 46 u16 mss;
47 u8 retrans; 47 u8 retrans;
48 u8 __pad; 48 u8 cookie_ts; /* syncookie: encode tcpopts in timestamp */
49 /* The following two fields can be easily recomputed I think -AK */ 49 /* The following two fields can be easily recomputed I think -AK */
50 u32 window_clamp; /* window clamp at creation time */ 50 u32 window_clamp; /* window clamp at creation time */
51 u32 rcv_wnd; /* rcv_wnd offered first time */ 51 u32 rcv_wnd; /* rcv_wnd offered first time */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 723b36851dde..7b41bb962b9c 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -442,6 +442,9 @@ extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
442extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, 442extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
443 __u16 *mss); 443 __u16 *mss);
444 444
445extern __u32 cookie_init_timestamp(struct request_sock *req);
446extern void cookie_check_timestamp(struct tcp_options_received *tcp_opt);
447
445/* From net/ipv6/syncookies.c */ 448/* From net/ipv6/syncookies.c */
446extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb); 449extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb);
447extern __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, 450extern __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb,
@@ -956,6 +959,7 @@ static inline void tcp_openreq_init(struct request_sock *req,
956 struct inet_request_sock *ireq = inet_rsk(req); 959 struct inet_request_sock *ireq = inet_rsk(req);
957 960
958 req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ 961 req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */
962 req->cookie_ts = 0;
959 tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; 963 tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;
960 req->mss = rx_opt->mss_clamp; 964 req->mss = rx_opt->mss_clamp;
961 req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; 965 req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0;
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index abc752d45cf7..73ba98921d64 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -19,6 +19,10 @@
19#include <linux/kernel.h> 19#include <linux/kernel.h>
20#include <net/tcp.h> 20#include <net/tcp.h>
21 21
22/* Timestamps: lowest 9 bits store TCP options */
23#define TSBITS 9
24#define TSMASK (((__u32)1 << TSBITS) - 1)
25
22extern int sysctl_tcp_syncookies; 26extern int sysctl_tcp_syncookies;
23 27
24__u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; 28__u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
@@ -51,6 +55,39 @@ static u32 cookie_hash(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport,
51 return tmp[17]; 55 return tmp[17];
52} 56}
53 57
58
59/*
60 * when syncookies are in effect and tcp timestamps are enabled we encode
61 * tcp options in the lowest 9 bits of the timestamp value that will be
62 * sent in the syn-ack.
63 * Since subsequent timestamps use the normal tcp_time_stamp value, we
64 * must make sure that the resulting initial timestamp is <= tcp_time_stamp.
65 */
66__u32 cookie_init_timestamp(struct request_sock *req)
67{
68 struct inet_request_sock *ireq;
69 u32 ts, ts_now = tcp_time_stamp;
70 u32 options = 0;
71
72 ireq = inet_rsk(req);
73 if (ireq->wscale_ok) {
74 options = ireq->snd_wscale;
75 options |= ireq->rcv_wscale << 4;
76 }
77 options |= ireq->sack_ok << 8;
78
79 ts = ts_now & ~TSMASK;
80 ts |= options;
81 if (ts > ts_now) {
82 ts >>= TSBITS;
83 ts--;
84 ts <<= TSBITS;
85 ts |= options;
86 }
87 return ts;
88}
89
90
54static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport, 91static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
55 __be16 dport, __u32 sseq, __u32 count, 92 __be16 dport, __u32 sseq, __u32 count,
56 __u32 data) 93 __u32 data)
@@ -185,6 +222,35 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
185 return child; 222 return child;
186} 223}
187 224
225
226/*
227 * when syncookies are in effect and tcp timestamps are enabled we stored
228 * additional tcp options in the timestamp.
229 * This extracts these options from the timestamp echo.
230 *
231 * The lowest 4 bits are for snd_wscale
232 * The next 4 lsb are for rcv_wscale
233 * The next lsb is for sack_ok
234 */
235void cookie_check_timestamp(struct tcp_options_received *tcp_opt)
236{
237 /* echoed timestamp, 9 lowest bits contain options */
238 u32 options = tcp_opt->rcv_tsecr & TSMASK;
239
240 tcp_opt->snd_wscale = options & 0xf;
241 options >>= 4;
242 tcp_opt->rcv_wscale = options & 0xf;
243
244 tcp_opt->sack_ok = (options >> 4) & 0x1;
245
246 if (tcp_opt->sack_ok)
247 tcp_sack_reset(tcp_opt);
248
249 if (tcp_opt->snd_wscale || tcp_opt->rcv_wscale)
250 tcp_opt->wscale_ok = 1;
251}
252EXPORT_SYMBOL(cookie_check_timestamp);
253
188struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 254struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
189 struct ip_options *opt) 255 struct ip_options *opt)
190{ 256{
@@ -198,6 +264,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
198 int mss; 264 int mss;
199 struct rtable *rt; 265 struct rtable *rt;
200 __u8 rcv_wscale; 266 __u8 rcv_wscale;
267 struct tcp_options_received tcp_opt;
201 268
202 if (!sysctl_tcp_syncookies || !th->ack) 269 if (!sysctl_tcp_syncookies || !th->ack)
203 goto out; 270 goto out;
@@ -210,6 +277,13 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
210 277
211 NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV); 278 NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV);
212 279
280 /* check for timestamp cookie support */
281 memset(&tcp_opt, 0, sizeof(tcp_opt));
282 tcp_parse_options(skb, &tcp_opt, 0);
283
284 if (tcp_opt.saw_tstamp)
285 cookie_check_timestamp(&tcp_opt);
286
213 ret = NULL; 287 ret = NULL;
214 req = reqsk_alloc(&tcp_request_sock_ops); /* for safety */ 288 req = reqsk_alloc(&tcp_request_sock_ops); /* for safety */
215 if (!req) 289 if (!req)
@@ -228,6 +302,12 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
228 ireq->loc_addr = ip_hdr(skb)->daddr; 302 ireq->loc_addr = ip_hdr(skb)->daddr;
229 ireq->rmt_addr = ip_hdr(skb)->saddr; 303 ireq->rmt_addr = ip_hdr(skb)->saddr;
230 ireq->opt = NULL; 304 ireq->opt = NULL;
305 ireq->snd_wscale = tcp_opt.snd_wscale;
306 ireq->rcv_wscale = tcp_opt.rcv_wscale;
307 ireq->sack_ok = tcp_opt.sack_ok;
308 ireq->wscale_ok = tcp_opt.wscale_ok;
309 ireq->tstamp_ok = tcp_opt.saw_tstamp;
310 req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
231 311
232 /* We throwed the options of the initial SYN away, so we hope 312 /* We throwed the options of the initial SYN away, so we hope
233 * the ACK carries the same options again (see RFC1122 4.2.3.8) 313 * the ACK carries the same options again (see RFC1122 4.2.3.8)
@@ -242,8 +322,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
242 } 322 }
243 } 323 }
244 324
245 ireq->snd_wscale = ireq->rcv_wscale = ireq->tstamp_ok = 0;
246 ireq->wscale_ok = ireq->sack_ok = 0;
247 req->expires = 0UL; 325 req->expires = 0UL;
248 req->retrans = 0; 326 req->retrans = 0;
249 327
@@ -272,11 +350,12 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
272 } 350 }
273 351
274 /* Try to redo what tcp_v4_send_synack did. */ 352 /* Try to redo what tcp_v4_send_synack did. */
275 req->window_clamp = dst_metric(&rt->u.dst, RTAX_WINDOW); 353 req->window_clamp = tp->window_clamp ? :dst_metric(&rt->u.dst, RTAX_WINDOW);
354
276 tcp_select_initial_window(tcp_full_space(sk), req->mss, 355 tcp_select_initial_window(tcp_full_space(sk), req->mss,
277 &req->rcv_wnd, &req->window_clamp, 356 &req->rcv_wnd, &req->window_clamp,
278 0, &rcv_wscale); 357 ireq->wscale_ok, &rcv_wscale);
279 /* BTW win scale with syncookies is 0 by definition */ 358
280 ireq->rcv_wscale = rcv_wscale; 359 ireq->rcv_wscale = rcv_wscale;
281 360
282 ret = get_cookie_sock(sk, skb, req, &rt->u.dst); 361 ret = get_cookie_sock(sk, skb, req, &rt->u.dst);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index df89a566a5a1..52e3ae603ca9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1299,10 +1299,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
1299 1299
1300 tcp_parse_options(skb, &tmp_opt, 0); 1300 tcp_parse_options(skb, &tmp_opt, 0);
1301 1301
1302 if (want_cookie) { 1302 if (want_cookie && !tmp_opt.saw_tstamp)
1303 tcp_clear_options(&tmp_opt); 1303 tcp_clear_options(&tmp_opt);
1304 tmp_opt.saw_tstamp = 0;
1305 }
1306 1304
1307 if (tmp_opt.saw_tstamp && !tmp_opt.rcv_tsval) { 1305 if (tmp_opt.saw_tstamp && !tmp_opt.rcv_tsval) {
1308 /* Some OSes (unknown ones, but I see them on web server, which 1306 /* Some OSes (unknown ones, but I see them on web server, which
@@ -1330,6 +1328,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
1330 if (want_cookie) { 1328 if (want_cookie) {
1331#ifdef CONFIG_SYN_COOKIES 1329#ifdef CONFIG_SYN_COOKIES
1332 syn_flood_warning(skb); 1330 syn_flood_warning(skb);
1331 req->cookie_ts = tmp_opt.tstamp_ok;
1333#endif 1332#endif
1334 isn = cookie_v4_init_sequence(sk, skb, &req->mss); 1333 isn = cookie_v4_init_sequence(sk, skb, &req->mss);
1335 } else if (!isn) { 1334 } else if (!isn) {
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a627616314ba..76b3653e9b4c 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2233,7 +2233,11 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
2233 2233
2234 /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ 2234 /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
2235 th->window = htons(min(req->rcv_wnd, 65535U)); 2235 th->window = htons(min(req->rcv_wnd, 65535U));
2236 2236#ifdef CONFIG_SYN_COOKIES
2237 if (unlikely(req->cookie_ts))
2238 TCP_SKB_CB(skb)->when = cookie_init_timestamp(req);
2239 else
2240#endif
2237 TCP_SKB_CB(skb)->when = tcp_time_stamp; 2241 TCP_SKB_CB(skb)->when = tcp_time_stamp;
2238 tcp_syn_build_options((__be32 *)(th + 1), dst_metric(dst, RTAX_ADVMSS), ireq->tstamp_ok, 2242 tcp_syn_build_options((__be32 *)(th + 1), dst_metric(dst, RTAX_ADVMSS), ireq->tstamp_ok,
2239 ireq->sack_ok, ireq->wscale_ok, ireq->rcv_wscale, 2243 ireq->sack_ok, ireq->wscale_ok, ireq->rcv_wscale,
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 3a622e7abc02..938ce4ecde55 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -170,6 +170,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
170 int mss; 170 int mss;
171 struct dst_entry *dst; 171 struct dst_entry *dst;
172 __u8 rcv_wscale; 172 __u8 rcv_wscale;
173 struct tcp_options_received tcp_opt;
173 174
174 if (!sysctl_tcp_syncookies || !th->ack) 175 if (!sysctl_tcp_syncookies || !th->ack)
175 goto out; 176 goto out;
@@ -182,6 +183,13 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
182 183
183 NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV); 184 NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV);
184 185
186 /* check for timestamp cookie support */
187 memset(&tcp_opt, 0, sizeof(tcp_opt));
188 tcp_parse_options(skb, &tcp_opt, 0);
189
190 if (tcp_opt.saw_tstamp)
191 cookie_check_timestamp(&tcp_opt);
192
185 ret = NULL; 193 ret = NULL;
186 req = inet6_reqsk_alloc(&tcp6_request_sock_ops); 194 req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
187 if (!req) 195 if (!req)
@@ -216,8 +224,12 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
216 224
217 req->expires = 0UL; 225 req->expires = 0UL;
218 req->retrans = 0; 226 req->retrans = 0;
219 ireq->snd_wscale = ireq->rcv_wscale = ireq->tstamp_ok = 0; 227 ireq->snd_wscale = tcp_opt.snd_wscale;
220 ireq->wscale_ok = ireq->sack_ok = 0; 228 ireq->rcv_wscale = tcp_opt.rcv_wscale;
229 ireq->sack_ok = tcp_opt.sack_ok;
230 ireq->wscale_ok = tcp_opt.wscale_ok;
231 ireq->tstamp_ok = tcp_opt.saw_tstamp;
232 req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
221 treq->rcv_isn = ntohl(th->seq) - 1; 233 treq->rcv_isn = ntohl(th->seq) - 1;
222 treq->snt_isn = cookie; 234 treq->snt_isn = cookie;
223 235
@@ -253,10 +265,10 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
253 goto out; 265 goto out;
254 } 266 }
255 267
256 req->window_clamp = dst_metric(dst, RTAX_WINDOW); 268 req->window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW);
257 tcp_select_initial_window(tcp_full_space(sk), req->mss, 269 tcp_select_initial_window(tcp_full_space(sk), req->mss,
258 &req->rcv_wnd, &req->window_clamp, 270 &req->rcv_wnd, &req->window_clamp,
259 0, &rcv_wscale); 271 ireq->wscale_ok, &rcv_wscale);
260 272
261 ireq->rcv_wscale = rcv_wscale; 273 ireq->rcv_wscale = rcv_wscale;
262 274
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 378cc4002a76..8ebf6de29562 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1290,10 +1290,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
1290 1290
1291 tcp_parse_options(skb, &tmp_opt, 0); 1291 tcp_parse_options(skb, &tmp_opt, 0);
1292 1292
1293 if (want_cookie) { 1293 if (want_cookie && !tmp_opt.saw_tstamp)
1294 tcp_clear_options(&tmp_opt); 1294 tcp_clear_options(&tmp_opt);
1295 tmp_opt.saw_tstamp = 0;
1296 }
1297 1295
1298 tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; 1296 tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
1299 tcp_openreq_init(req, &tmp_opt, skb); 1297 tcp_openreq_init(req, &tmp_opt, skb);
@@ -1307,6 +1305,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
1307 1305
1308 if (want_cookie) { 1306 if (want_cookie) {
1309 isn = cookie_v6_init_sequence(sk, skb, &req->mss); 1307 isn = cookie_v6_init_sequence(sk, skb, &req->mss);
1308 req->cookie_ts = tmp_opt.tstamp_ok;
1310 } else if (!isn) { 1309 } else if (!isn) {
1311 if (ipv6_opt_accepted(sk, skb) || 1310 if (ipv6_opt_accepted(sk, skb) ||
1312 np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || 1311 np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||