diff options
Diffstat (limited to 'net/ipv4/syncookies.c')
-rw-r--r-- | net/ipv4/syncookies.c | 89 |
1 files changed, 84 insertions, 5 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); |