diff options
Diffstat (limited to 'net/ipv4/syncookies.c')
| -rw-r--r-- | net/ipv4/syncookies.c | 108 |
1 files changed, 60 insertions, 48 deletions
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 66fd80ef2473..650cace2180d 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c | |||
| @@ -18,8 +18,8 @@ | |||
| 18 | #include <net/tcp.h> | 18 | #include <net/tcp.h> |
| 19 | #include <net/route.h> | 19 | #include <net/route.h> |
| 20 | 20 | ||
| 21 | /* Timestamps: lowest 9 bits store TCP options */ | 21 | /* Timestamps: lowest bits store TCP options */ |
| 22 | #define TSBITS 9 | 22 | #define TSBITS 6 |
| 23 | #define TSMASK (((__u32)1 << TSBITS) - 1) | 23 | #define TSMASK (((__u32)1 << TSBITS) - 1) |
| 24 | 24 | ||
| 25 | extern int sysctl_tcp_syncookies; | 25 | extern int sysctl_tcp_syncookies; |
| @@ -58,7 +58,7 @@ static u32 cookie_hash(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, | |||
| 58 | 58 | ||
| 59 | /* | 59 | /* |
| 60 | * when syncookies are in effect and tcp timestamps are enabled we encode | 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 | 61 | * tcp options in the lower bits of the timestamp value that will be |
| 62 | * sent in the syn-ack. | 62 | * sent in the syn-ack. |
| 63 | * Since subsequent timestamps use the normal tcp_time_stamp value, we | 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. | 64 | * must make sure that the resulting initial timestamp is <= tcp_time_stamp. |
| @@ -70,11 +70,10 @@ __u32 cookie_init_timestamp(struct request_sock *req) | |||
| 70 | u32 options = 0; | 70 | u32 options = 0; |
| 71 | 71 | ||
| 72 | ireq = inet_rsk(req); | 72 | ireq = inet_rsk(req); |
| 73 | if (ireq->wscale_ok) { | 73 | |
| 74 | options = ireq->snd_wscale; | 74 | options = ireq->wscale_ok ? ireq->snd_wscale : 0xf; |
| 75 | options |= ireq->rcv_wscale << 4; | 75 | options |= ireq->sack_ok << 4; |
| 76 | } | 76 | options |= ireq->ecn_ok << 5; |
| 77 | options |= ireq->sack_ok << 8; | ||
| 78 | 77 | ||
| 79 | ts = ts_now & ~TSMASK; | 78 | ts = ts_now & ~TSMASK; |
| 80 | ts |= options; | 79 | ts |= options; |
| @@ -138,23 +137,23 @@ static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr, | |||
| 138 | } | 137 | } |
| 139 | 138 | ||
| 140 | /* | 139 | /* |
| 141 | * This table has to be sorted and terminated with (__u16)-1. | 140 | * MSS Values are taken from the 2009 paper |
| 142 | * XXX generate a better table. | 141 | * 'Measuring TCP Maximum Segment Size' by S. Alcock and R. Nelson: |
| 143 | * Unresolved Issues: HIPPI with a 64k MSS is not well supported. | 142 | * - values 1440 to 1460 accounted for 80% of observed mss values |
| 143 | * - values outside the 536-1460 range are rare (<0.2%). | ||
| 144 | * | ||
| 145 | * Table must be sorted. | ||
| 144 | */ | 146 | */ |
| 145 | static __u16 const msstab[] = { | 147 | static __u16 const msstab[] = { |
| 146 | 64 - 1, | 148 | 64, |
| 147 | 256 - 1, | 149 | 512, |
| 148 | 512 - 1, | 150 | 536, |
| 149 | 536 - 1, | 151 | 1024, |
| 150 | 1024 - 1, | 152 | 1440, |
| 151 | 1440 - 1, | 153 | 1460, |
| 152 | 1460 - 1, | 154 | 4312, |
| 153 | 4312 - 1, | 155 | 8960, |
| 154 | (__u16)-1 | ||
| 155 | }; | 156 | }; |
| 156 | /* The number doesn't include the -1 terminator */ | ||
| 157 | #define NUM_MSS (ARRAY_SIZE(msstab) - 1) | ||
| 158 | 157 | ||
| 159 | /* | 158 | /* |
| 160 | * Generate a syncookie. mssp points to the mss, which is returned | 159 | * Generate a syncookie. mssp points to the mss, which is returned |
| @@ -169,10 +168,10 @@ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) | |||
| 169 | 168 | ||
| 170 | tcp_synq_overflow(sk); | 169 | tcp_synq_overflow(sk); |
| 171 | 170 | ||
| 172 | /* XXX sort msstab[] by probability? Binary search? */ | 171 | for (mssind = ARRAY_SIZE(msstab) - 1; mssind ; mssind--) |
| 173 | for (mssind = 0; mss > msstab[mssind + 1]; mssind++) | 172 | if (mss >= msstab[mssind]) |
| 174 | ; | 173 | break; |
| 175 | *mssp = msstab[mssind] + 1; | 174 | *mssp = msstab[mssind]; |
| 176 | 175 | ||
| 177 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); | 176 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); |
| 178 | 177 | ||
| @@ -202,7 +201,7 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie) | |||
| 202 | jiffies / (HZ * 60), | 201 | jiffies / (HZ * 60), |
| 203 | COUNTER_TRIES); | 202 | COUNTER_TRIES); |
| 204 | 203 | ||
| 205 | return mssind < NUM_MSS ? msstab[mssind] + 1 : 0; | 204 | return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; |
| 206 | } | 205 | } |
| 207 | 206 | ||
| 208 | static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, | 207 | static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, |
| @@ -227,26 +226,38 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, | |||
| 227 | * additional tcp options in the timestamp. | 226 | * additional tcp options in the timestamp. |
| 228 | * This extracts these options from the timestamp echo. | 227 | * This extracts these options from the timestamp echo. |
| 229 | * | 228 | * |
| 230 | * The lowest 4 bits are for snd_wscale | 229 | * The lowest 4 bits store snd_wscale. |
| 231 | * The next 4 lsb are for rcv_wscale | 230 | * next 2 bits indicate SACK and ECN support. |
| 232 | * The next lsb is for sack_ok | 231 | * |
| 232 | * return false if we decode an option that should not be. | ||
| 233 | */ | 233 | */ |
| 234 | void cookie_check_timestamp(struct tcp_options_received *tcp_opt) | 234 | bool cookie_check_timestamp(struct tcp_options_received *tcp_opt, bool *ecn_ok) |
| 235 | { | 235 | { |
| 236 | /* echoed timestamp, 9 lowest bits contain options */ | 236 | /* echoed timestamp, lowest bits contain options */ |
| 237 | u32 options = tcp_opt->rcv_tsecr & TSMASK; | 237 | u32 options = tcp_opt->rcv_tsecr & TSMASK; |
| 238 | 238 | ||
| 239 | tcp_opt->snd_wscale = options & 0xf; | 239 | if (!tcp_opt->saw_tstamp) { |
| 240 | options >>= 4; | 240 | tcp_clear_options(tcp_opt); |
| 241 | tcp_opt->rcv_wscale = options & 0xf; | 241 | return true; |
| 242 | } | ||
| 243 | |||
| 244 | if (!sysctl_tcp_timestamps) | ||
| 245 | return false; | ||
| 242 | 246 | ||
| 243 | tcp_opt->sack_ok = (options >> 4) & 0x1; | 247 | tcp_opt->sack_ok = (options >> 4) & 0x1; |
| 248 | *ecn_ok = (options >> 5) & 1; | ||
| 249 | if (*ecn_ok && !sysctl_tcp_ecn) | ||
| 250 | return false; | ||
| 251 | |||
| 252 | if (tcp_opt->sack_ok && !sysctl_tcp_sack) | ||
| 253 | return false; | ||
| 244 | 254 | ||
| 245 | if (tcp_opt->sack_ok) | 255 | if ((options & 0xf) == 0xf) |
| 246 | tcp_sack_reset(tcp_opt); | 256 | return true; /* no window scaling */ |
| 247 | 257 | ||
| 248 | if (tcp_opt->snd_wscale || tcp_opt->rcv_wscale) | 258 | tcp_opt->wscale_ok = 1; |
| 249 | tcp_opt->wscale_ok = 1; | 259 | tcp_opt->snd_wscale = options & 0xf; |
| 260 | return sysctl_tcp_window_scaling != 0; | ||
| 250 | } | 261 | } |
| 251 | EXPORT_SYMBOL(cookie_check_timestamp); | 262 | EXPORT_SYMBOL(cookie_check_timestamp); |
| 252 | 263 | ||
| @@ -265,8 +276,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | |||
| 265 | int mss; | 276 | int mss; |
| 266 | struct rtable *rt; | 277 | struct rtable *rt; |
| 267 | __u8 rcv_wscale; | 278 | __u8 rcv_wscale; |
| 279 | bool ecn_ok; | ||
| 268 | 280 | ||
| 269 | if (!sysctl_tcp_syncookies || !th->ack) | 281 | if (!sysctl_tcp_syncookies || !th->ack || th->rst) |
| 270 | goto out; | 282 | goto out; |
| 271 | 283 | ||
| 272 | if (tcp_synq_no_recent_overflow(sk) || | 284 | if (tcp_synq_no_recent_overflow(sk) || |
| @@ -281,8 +293,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | |||
| 281 | memset(&tcp_opt, 0, sizeof(tcp_opt)); | 293 | memset(&tcp_opt, 0, sizeof(tcp_opt)); |
| 282 | tcp_parse_options(skb, &tcp_opt, &hash_location, 0); | 294 | tcp_parse_options(skb, &tcp_opt, &hash_location, 0); |
| 283 | 295 | ||
| 284 | if (tcp_opt.saw_tstamp) | 296 | if (!cookie_check_timestamp(&tcp_opt, &ecn_ok)) |
| 285 | cookie_check_timestamp(&tcp_opt); | 297 | goto out; |
| 286 | 298 | ||
| 287 | ret = NULL; | 299 | ret = NULL; |
| 288 | req = inet_reqsk_alloc(&tcp_request_sock_ops); /* for safety */ | 300 | req = inet_reqsk_alloc(&tcp_request_sock_ops); /* for safety */ |
| @@ -298,9 +310,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | |||
| 298 | ireq->rmt_port = th->source; | 310 | ireq->rmt_port = th->source; |
| 299 | ireq->loc_addr = ip_hdr(skb)->daddr; | 311 | ireq->loc_addr = ip_hdr(skb)->daddr; |
| 300 | ireq->rmt_addr = ip_hdr(skb)->saddr; | 312 | ireq->rmt_addr = ip_hdr(skb)->saddr; |
| 301 | ireq->ecn_ok = 0; | 313 | ireq->ecn_ok = ecn_ok; |
| 302 | ireq->snd_wscale = tcp_opt.snd_wscale; | 314 | ireq->snd_wscale = tcp_opt.snd_wscale; |
| 303 | ireq->rcv_wscale = tcp_opt.rcv_wscale; | ||
| 304 | ireq->sack_ok = tcp_opt.sack_ok; | 315 | ireq->sack_ok = tcp_opt.sack_ok; |
| 305 | ireq->wscale_ok = tcp_opt.wscale_ok; | 316 | ireq->wscale_ok = tcp_opt.wscale_ok; |
| 306 | ireq->tstamp_ok = tcp_opt.saw_tstamp; | 317 | ireq->tstamp_ok = tcp_opt.saw_tstamp; |
| @@ -347,21 +358,22 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | |||
| 347 | { .sport = th->dest, | 358 | { .sport = th->dest, |
| 348 | .dport = th->source } } }; | 359 | .dport = th->source } } }; |
| 349 | security_req_classify_flow(req, &fl); | 360 | security_req_classify_flow(req, &fl); |
| 350 | if (ip_route_output_key(&init_net, &rt, &fl)) { | 361 | if (ip_route_output_key(sock_net(sk), &rt, &fl)) { |
| 351 | reqsk_free(req); | 362 | reqsk_free(req); |
| 352 | goto out; | 363 | goto out; |
| 353 | } | 364 | } |
| 354 | } | 365 | } |
| 355 | 366 | ||
| 356 | /* Try to redo what tcp_v4_send_synack did. */ | 367 | /* Try to redo what tcp_v4_send_synack did. */ |
| 357 | req->window_clamp = tp->window_clamp ? :dst_metric(&rt->u.dst, RTAX_WINDOW); | 368 | req->window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); |
| 358 | 369 | ||
| 359 | tcp_select_initial_window(tcp_full_space(sk), req->mss, | 370 | tcp_select_initial_window(tcp_full_space(sk), req->mss, |
| 360 | &req->rcv_wnd, &req->window_clamp, | 371 | &req->rcv_wnd, &req->window_clamp, |
| 361 | ireq->wscale_ok, &rcv_wscale); | 372 | ireq->wscale_ok, &rcv_wscale, |
| 373 | dst_metric(&rt->dst, RTAX_INITRWND)); | ||
| 362 | 374 | ||
| 363 | ireq->rcv_wscale = rcv_wscale; | 375 | ireq->rcv_wscale = rcv_wscale; |
| 364 | 376 | ||
| 365 | ret = get_cookie_sock(sk, skb, req, &rt->u.dst); | 377 | ret = get_cookie_sock(sk, skb, req, &rt->dst); |
| 366 | out: return ret; | 378 | out: return ret; |
| 367 | } | 379 | } |
