diff options
author | Florian Westphal <fw@strlen.de> | 2008-04-10 06:12:40 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-04-10 06:12:40 -0400 |
commit | 4dfc2817025965a2fc78a18c50f540736a6b5c24 (patch) | |
tree | f8f2f76e660d9d5c7a0f39ed8a79cb6d6d181206 /net | |
parent | 15be75cdb5db442d0e33d37b20832b88f3ccd383 (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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/syncookies.c | 89 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 5 | ||||
-rw-r--r-- | net/ipv4/tcp_output.c | 6 | ||||
-rw-r--r-- | net/ipv6/syncookies.c | 20 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 5 |
5 files changed, 109 insertions, 16 deletions
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 | |||
22 | extern int sysctl_tcp_syncookies; | 26 | extern 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 | |||
54 | static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport, | 91 | static __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 | */ | ||
235 | void 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 | } | ||
252 | EXPORT_SYMBOL(cookie_check_timestamp); | ||
253 | |||
188 | struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | 254 | struct 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 || |