diff options
| author | William Allen Simpson <william.allen.simpson@gmail.com> | 2009-12-02 13:25:27 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2009-12-03 01:07:26 -0500 |
| commit | 4957faade11b3a278c3b3cade3411ddc20afa791 (patch) | |
| tree | 57f994bab69353baf5f554b89cf9107c3372ecce /net | |
| parent | bd0388ae77075026d6a9f9eb6026dfd1d52ce0e9 (diff) | |
TCPCT part 1g: Responder Cookie => Initiator
Parse incoming TCP_COOKIE option(s).
Calculate <SYN,ACK> TCP_COOKIE option.
Send optional <SYN,ACK> data.
This is a significantly revised implementation of an earlier (year-old)
patch that no longer applies cleanly, with permission of the original
author (Adam Langley):
http://thread.gmane.org/gmane.linux.network/102586
Requires:
TCPCT part 1a: add request_values parameter for sending SYNACK
TCPCT part 1b: generate Responder Cookie secret
TCPCT part 1c: sysctl_tcp_cookie_size, socket option TCP_COOKIE_TRANSACTIONS
TCPCT part 1d: define TCP cookie option, extend existing struct's
TCPCT part 1e: implement socket option TCP_COOKIE_TRANSACTIONS
TCPCT part 1f: Initiator Cookie => Responder
Signed-off-by: William.Allen.Simpson@gmail.com
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
| -rw-r--r-- | net/ipv4/syncookies.c | 5 | ||||
| -rw-r--r-- | net/ipv4/tcp_input.c | 75 | ||||
| -rw-r--r-- | net/ipv4/tcp_ipv4.c | 47 | ||||
| -rw-r--r-- | net/ipv4/tcp_minisocks.c | 14 | ||||
| -rw-r--r-- | net/ipv4/tcp_output.c | 103 | ||||
| -rw-r--r-- | net/ipv6/syncookies.c | 5 | ||||
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 52 |
7 files changed, 258 insertions, 43 deletions
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 3146cc401748..26399ad2a289 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c | |||
| @@ -253,6 +253,8 @@ EXPORT_SYMBOL(cookie_check_timestamp); | |||
| 253 | struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | 253 | struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, |
| 254 | struct ip_options *opt) | 254 | struct ip_options *opt) |
| 255 | { | 255 | { |
| 256 | struct tcp_options_received tcp_opt; | ||
| 257 | u8 *hash_location; | ||
| 256 | struct inet_request_sock *ireq; | 258 | struct inet_request_sock *ireq; |
| 257 | struct tcp_request_sock *treq; | 259 | struct tcp_request_sock *treq; |
| 258 | struct tcp_sock *tp = tcp_sk(sk); | 260 | struct tcp_sock *tp = tcp_sk(sk); |
| @@ -263,7 +265,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | |||
| 263 | int mss; | 265 | int mss; |
| 264 | struct rtable *rt; | 266 | struct rtable *rt; |
| 265 | __u8 rcv_wscale; | 267 | __u8 rcv_wscale; |
| 266 | struct tcp_options_received tcp_opt; | ||
| 267 | 268 | ||
| 268 | if (!sysctl_tcp_syncookies || !th->ack) | 269 | if (!sysctl_tcp_syncookies || !th->ack) |
| 269 | goto out; | 270 | goto out; |
| @@ -341,7 +342,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, | |||
| 341 | 342 | ||
| 342 | /* check for timestamp cookie support */ | 343 | /* check for timestamp cookie support */ |
| 343 | memset(&tcp_opt, 0, sizeof(tcp_opt)); | 344 | memset(&tcp_opt, 0, sizeof(tcp_opt)); |
| 344 | tcp_parse_options(skb, &tcp_opt, 0, &rt->u.dst); | 345 | tcp_parse_options(skb, &tcp_opt, &hash_location, 0, &rt->u.dst); |
| 345 | 346 | ||
| 346 | if (tcp_opt.saw_tstamp) | 347 | if (tcp_opt.saw_tstamp) |
| 347 | cookie_check_timestamp(&tcp_opt); | 348 | cookie_check_timestamp(&tcp_opt); |
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index be166e0e11c5..57ae96a04220 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
| @@ -3698,7 +3698,7 @@ old_ack: | |||
| 3698 | * the fast version below fails. | 3698 | * the fast version below fails. |
| 3699 | */ | 3699 | */ |
| 3700 | void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, | 3700 | void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, |
| 3701 | int estab, struct dst_entry *dst) | 3701 | u8 **hvpp, int estab, struct dst_entry *dst) |
| 3702 | { | 3702 | { |
| 3703 | unsigned char *ptr; | 3703 | unsigned char *ptr; |
| 3704 | struct tcphdr *th = tcp_hdr(skb); | 3704 | struct tcphdr *th = tcp_hdr(skb); |
| @@ -3785,7 +3785,30 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, | |||
| 3785 | */ | 3785 | */ |
| 3786 | break; | 3786 | break; |
| 3787 | #endif | 3787 | #endif |
| 3788 | } | 3788 | case TCPOPT_COOKIE: |
| 3789 | /* This option is variable length. | ||
| 3790 | */ | ||
| 3791 | switch (opsize) { | ||
| 3792 | case TCPOLEN_COOKIE_BASE: | ||
| 3793 | /* not yet implemented */ | ||
| 3794 | break; | ||
| 3795 | case TCPOLEN_COOKIE_PAIR: | ||
| 3796 | /* not yet implemented */ | ||
| 3797 | break; | ||
| 3798 | case TCPOLEN_COOKIE_MIN+0: | ||
| 3799 | case TCPOLEN_COOKIE_MIN+2: | ||
| 3800 | case TCPOLEN_COOKIE_MIN+4: | ||
| 3801 | case TCPOLEN_COOKIE_MIN+6: | ||
| 3802 | case TCPOLEN_COOKIE_MAX: | ||
| 3803 | /* 16-bit multiple */ | ||
| 3804 | opt_rx->cookie_plus = opsize; | ||
| 3805 | *hvpp = ptr; | ||
| 3806 | default: | ||
| 3807 | /* ignore option */ | ||
| 3808 | break; | ||
| 3809 | }; | ||
| 3810 | break; | ||
| 3811 | }; | ||
| 3789 | 3812 | ||
| 3790 | ptr += opsize-2; | 3813 | ptr += opsize-2; |
| 3791 | length -= opsize; | 3814 | length -= opsize; |
| @@ -3813,17 +3836,20 @@ static int tcp_parse_aligned_timestamp(struct tcp_sock *tp, struct tcphdr *th) | |||
| 3813 | * If it is wrong it falls back on tcp_parse_options(). | 3836 | * If it is wrong it falls back on tcp_parse_options(). |
| 3814 | */ | 3837 | */ |
| 3815 | static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th, | 3838 | static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th, |
| 3816 | struct tcp_sock *tp) | 3839 | struct tcp_sock *tp, u8 **hvpp) |
| 3817 | { | 3840 | { |
| 3818 | if (th->doff == sizeof(struct tcphdr) >> 2) { | 3841 | /* In the spirit of fast parsing, compare doff directly to constant |
| 3842 | * values. Because equality is used, short doff can be ignored here. | ||
| 3843 | */ | ||
| 3844 | if (th->doff == (sizeof(*th) / 4)) { | ||
| 3819 | tp->rx_opt.saw_tstamp = 0; | 3845 | tp->rx_opt.saw_tstamp = 0; |
| 3820 | return 0; | 3846 | return 0; |
| 3821 | } else if (tp->rx_opt.tstamp_ok && | 3847 | } else if (tp->rx_opt.tstamp_ok && |
| 3822 | th->doff == (sizeof(struct tcphdr)>>2)+(TCPOLEN_TSTAMP_ALIGNED>>2)) { | 3848 | th->doff == ((sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4)) { |
| 3823 | if (tcp_parse_aligned_timestamp(tp, th)) | 3849 | if (tcp_parse_aligned_timestamp(tp, th)) |
| 3824 | return 1; | 3850 | return 1; |
| 3825 | } | 3851 | } |
| 3826 | tcp_parse_options(skb, &tp->rx_opt, 1, NULL); | 3852 | tcp_parse_options(skb, &tp->rx_opt, hvpp, 1, NULL); |
| 3827 | return 1; | 3853 | return 1; |
| 3828 | } | 3854 | } |
| 3829 | 3855 | ||
| @@ -5077,10 +5103,12 @@ out: | |||
| 5077 | static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, | 5103 | static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, |
| 5078 | struct tcphdr *th, int syn_inerr) | 5104 | struct tcphdr *th, int syn_inerr) |
| 5079 | { | 5105 | { |
| 5106 | u8 *hash_location; | ||
| 5080 | struct tcp_sock *tp = tcp_sk(sk); | 5107 | struct tcp_sock *tp = tcp_sk(sk); |
| 5081 | 5108 | ||
| 5082 | /* RFC1323: H1. Apply PAWS check first. */ | 5109 | /* RFC1323: H1. Apply PAWS check first. */ |
| 5083 | if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp && | 5110 | if (tcp_fast_parse_options(skb, th, tp, &hash_location) && |
| 5111 | tp->rx_opt.saw_tstamp && | ||
| 5084 | tcp_paws_discard(sk, skb)) { | 5112 | tcp_paws_discard(sk, skb)) { |
| 5085 | if (!th->rst) { | 5113 | if (!th->rst) { |
| 5086 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED); | 5114 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED); |
| @@ -5368,12 +5396,14 @@ discard: | |||
| 5368 | static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, | 5396 | static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, |
| 5369 | struct tcphdr *th, unsigned len) | 5397 | struct tcphdr *th, unsigned len) |
| 5370 | { | 5398 | { |
| 5371 | struct tcp_sock *tp = tcp_sk(sk); | 5399 | u8 *hash_location; |
| 5372 | struct inet_connection_sock *icsk = inet_csk(sk); | 5400 | struct inet_connection_sock *icsk = inet_csk(sk); |
| 5373 | int saved_clamp = tp->rx_opt.mss_clamp; | 5401 | struct tcp_sock *tp = tcp_sk(sk); |
| 5374 | struct dst_entry *dst = __sk_dst_get(sk); | 5402 | struct dst_entry *dst = __sk_dst_get(sk); |
| 5403 | struct tcp_cookie_values *cvp = tp->cookie_values; | ||
| 5404 | int saved_clamp = tp->rx_opt.mss_clamp; | ||
| 5375 | 5405 | ||
| 5376 | tcp_parse_options(skb, &tp->rx_opt, 0, dst); | 5406 | tcp_parse_options(skb, &tp->rx_opt, &hash_location, 0, dst); |
| 5377 | 5407 | ||
| 5378 | if (th->ack) { | 5408 | if (th->ack) { |
| 5379 | /* rfc793: | 5409 | /* rfc793: |
| @@ -5470,6 +5500,31 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, | |||
| 5470 | * Change state from SYN-SENT only after copied_seq | 5500 | * Change state from SYN-SENT only after copied_seq |
| 5471 | * is initialized. */ | 5501 | * is initialized. */ |
| 5472 | tp->copied_seq = tp->rcv_nxt; | 5502 | tp->copied_seq = tp->rcv_nxt; |
| 5503 | |||
| 5504 | if (cvp != NULL && | ||
| 5505 | cvp->cookie_pair_size > 0 && | ||
| 5506 | tp->rx_opt.cookie_plus > 0) { | ||
| 5507 | int cookie_size = tp->rx_opt.cookie_plus | ||
| 5508 | - TCPOLEN_COOKIE_BASE; | ||
| 5509 | int cookie_pair_size = cookie_size | ||
| 5510 | + cvp->cookie_desired; | ||
| 5511 | |||
| 5512 | /* A cookie extension option was sent and returned. | ||
| 5513 | * Note that each incoming SYNACK replaces the | ||
| 5514 | * Responder cookie. The initial exchange is most | ||
| 5515 | * fragile, as protection against spoofing relies | ||
| 5516 | * entirely upon the sequence and timestamp (above). | ||
| 5517 | * This replacement strategy allows the correct pair to | ||
| 5518 | * pass through, while any others will be filtered via | ||
| 5519 | * Responder verification later. | ||
| 5520 | */ | ||
| 5521 | if (sizeof(cvp->cookie_pair) >= cookie_pair_size) { | ||
| 5522 | memcpy(&cvp->cookie_pair[cvp->cookie_desired], | ||
| 5523 | hash_location, cookie_size); | ||
| 5524 | cvp->cookie_pair_size = cookie_pair_size; | ||
| 5525 | } | ||
| 5526 | } | ||
| 5527 | |||
| 5473 | smp_mb(); | 5528 | smp_mb(); |
| 5474 | tcp_set_state(sk, TCP_ESTABLISHED); | 5529 | tcp_set_state(sk, TCP_ESTABLISHED); |
| 5475 | 5530 | ||
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a2bcac9b388e..59c911f3889d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
| @@ -1213,9 +1213,12 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = { | |||
| 1213 | 1213 | ||
| 1214 | int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | 1214 | int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) |
| 1215 | { | 1215 | { |
| 1216 | struct tcp_extend_values tmp_ext; | ||
| 1216 | struct tcp_options_received tmp_opt; | 1217 | struct tcp_options_received tmp_opt; |
| 1218 | u8 *hash_location; | ||
| 1217 | struct request_sock *req; | 1219 | struct request_sock *req; |
| 1218 | struct inet_request_sock *ireq; | 1220 | struct inet_request_sock *ireq; |
| 1221 | struct tcp_sock *tp = tcp_sk(sk); | ||
| 1219 | struct dst_entry *dst = NULL; | 1222 | struct dst_entry *dst = NULL; |
| 1220 | __be32 saddr = ip_hdr(skb)->saddr; | 1223 | __be32 saddr = ip_hdr(skb)->saddr; |
| 1221 | __be32 daddr = ip_hdr(skb)->daddr; | 1224 | __be32 daddr = ip_hdr(skb)->daddr; |
| @@ -1271,15 +1274,49 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | |||
| 1271 | 1274 | ||
| 1272 | tcp_clear_options(&tmp_opt); | 1275 | tcp_clear_options(&tmp_opt); |
| 1273 | tmp_opt.mss_clamp = TCP_MSS_DEFAULT; | 1276 | tmp_opt.mss_clamp = TCP_MSS_DEFAULT; |
| 1274 | tmp_opt.user_mss = tcp_sk(sk)->rx_opt.user_mss; | 1277 | tmp_opt.user_mss = tp->rx_opt.user_mss; |
| 1278 | tcp_parse_options(skb, &tmp_opt, &hash_location, 0, dst); | ||
| 1279 | |||
| 1280 | if (tmp_opt.cookie_plus > 0 && | ||
| 1281 | tmp_opt.saw_tstamp && | ||
| 1282 | !tp->rx_opt.cookie_out_never && | ||
| 1283 | (sysctl_tcp_cookie_size > 0 || | ||
| 1284 | (tp->cookie_values != NULL && | ||
| 1285 | tp->cookie_values->cookie_desired > 0))) { | ||
| 1286 | u8 *c; | ||
| 1287 | u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS]; | ||
| 1288 | int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE; | ||
| 1289 | |||
| 1290 | if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0) | ||
| 1291 | goto drop_and_release; | ||
| 1292 | |||
| 1293 | /* Secret recipe starts with IP addresses */ | ||
| 1294 | *mess++ ^= daddr; | ||
| 1295 | *mess++ ^= saddr; | ||
| 1275 | 1296 | ||
| 1276 | tcp_parse_options(skb, &tmp_opt, 0, dst); | 1297 | /* plus variable length Initiator Cookie */ |
| 1298 | c = (u8 *)mess; | ||
| 1299 | while (l-- > 0) | ||
| 1300 | *c++ ^= *hash_location++; | ||
| 1301 | |||
| 1302 | #ifdef CONFIG_SYN_COOKIES | ||
| 1303 | want_cookie = 0; /* not our kind of cookie */ | ||
| 1304 | #endif | ||
| 1305 | tmp_ext.cookie_out_never = 0; /* false */ | ||
| 1306 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; | ||
| 1307 | } else if (!tp->rx_opt.cookie_in_always) { | ||
| 1308 | /* redundant indications, but ensure initialization. */ | ||
| 1309 | tmp_ext.cookie_out_never = 1; /* true */ | ||
| 1310 | tmp_ext.cookie_plus = 0; | ||
| 1311 | } else { | ||
| 1312 | goto drop_and_release; | ||
| 1313 | } | ||
| 1314 | tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always; | ||
| 1277 | 1315 | ||
| 1278 | if (want_cookie && !tmp_opt.saw_tstamp) | 1316 | if (want_cookie && !tmp_opt.saw_tstamp) |
| 1279 | tcp_clear_options(&tmp_opt); | 1317 | tcp_clear_options(&tmp_opt); |
| 1280 | 1318 | ||
| 1281 | tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; | 1319 | tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; |
| 1282 | |||
| 1283 | tcp_openreq_init(req, &tmp_opt, skb); | 1320 | tcp_openreq_init(req, &tmp_opt, skb); |
| 1284 | 1321 | ||
| 1285 | if (security_inet_conn_request(sk, skb, req)) | 1322 | if (security_inet_conn_request(sk, skb, req)) |
| @@ -1339,7 +1376,9 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | |||
| 1339 | } | 1376 | } |
| 1340 | tcp_rsk(req)->snt_isn = isn; | 1377 | tcp_rsk(req)->snt_isn = isn; |
| 1341 | 1378 | ||
| 1342 | if (__tcp_v4_send_synack(sk, dst, req, NULL) || want_cookie) | 1379 | if (__tcp_v4_send_synack(sk, dst, req, |
| 1380 | (struct request_values *)&tmp_ext) || | ||
| 1381 | want_cookie) | ||
| 1343 | goto drop_and_free; | 1382 | goto drop_and_free; |
| 1344 | 1383 | ||
| 1345 | inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); | 1384 | inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); |
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 96852af43ca7..87accec8d097 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c | |||
| @@ -90,13 +90,14 @@ enum tcp_tw_status | |||
| 90 | tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, | 90 | tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, |
| 91 | const struct tcphdr *th) | 91 | const struct tcphdr *th) |
| 92 | { | 92 | { |
| 93 | struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); | ||
| 94 | struct tcp_options_received tmp_opt; | 93 | struct tcp_options_received tmp_opt; |
| 94 | u8 *hash_location; | ||
| 95 | struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); | ||
| 95 | int paws_reject = 0; | 96 | int paws_reject = 0; |
| 96 | 97 | ||
| 97 | if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { | 98 | if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { |
| 98 | tmp_opt.tstamp_ok = 1; | 99 | tmp_opt.tstamp_ok = 1; |
| 99 | tcp_parse_options(skb, &tmp_opt, 1, NULL); | 100 | tcp_parse_options(skb, &tmp_opt, &hash_location, 1, NULL); |
| 100 | 101 | ||
| 101 | if (tmp_opt.saw_tstamp) { | 102 | if (tmp_opt.saw_tstamp) { |
| 102 | tmp_opt.ts_recent = tcptw->tw_ts_recent; | 103 | tmp_opt.ts_recent = tcptw->tw_ts_recent; |
| @@ -518,15 +519,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, | |||
| 518 | struct request_sock *req, | 519 | struct request_sock *req, |
| 519 | struct request_sock **prev) | 520 | struct request_sock **prev) |
| 520 | { | 521 | { |
| 522 | struct tcp_options_received tmp_opt; | ||
| 523 | u8 *hash_location; | ||
| 524 | struct sock *child; | ||
| 521 | const struct tcphdr *th = tcp_hdr(skb); | 525 | const struct tcphdr *th = tcp_hdr(skb); |
| 522 | __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); | 526 | __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); |
| 523 | int paws_reject = 0; | 527 | int paws_reject = 0; |
| 524 | struct tcp_options_received tmp_opt; | ||
| 525 | struct sock *child; | ||
| 526 | 528 | ||
| 527 | if ((th->doff > (sizeof(struct tcphdr)>>2)) && (req->ts_recent)) { | 529 | if ((th->doff > (sizeof(*th) >> 2)) && (req->ts_recent)) { |
| 528 | tmp_opt.tstamp_ok = 1; | 530 | tmp_opt.tstamp_ok = 1; |
| 529 | tcp_parse_options(skb, &tmp_opt, 1, NULL); | 531 | tcp_parse_options(skb, &tmp_opt, &hash_location, 1, NULL); |
| 530 | 532 | ||
| 531 | if (tmp_opt.saw_tstamp) { | 533 | if (tmp_opt.saw_tstamp) { |
| 532 | tmp_opt.ts_recent = req->ts_recent; | 534 | tmp_opt.ts_recent = req->ts_recent; |
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 35dd983a8a99..2ac8beff4d77 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
| @@ -655,48 +655,77 @@ static unsigned tcp_synack_options(struct sock *sk, | |||
| 655 | struct request_sock *req, | 655 | struct request_sock *req, |
| 656 | unsigned mss, struct sk_buff *skb, | 656 | unsigned mss, struct sk_buff *skb, |
| 657 | struct tcp_out_options *opts, | 657 | struct tcp_out_options *opts, |
| 658 | struct tcp_md5sig_key **md5) { | 658 | struct tcp_md5sig_key **md5, |
| 659 | unsigned size = 0; | 659 | struct tcp_extend_values *xvp) |
| 660 | { | ||
| 660 | struct inet_request_sock *ireq = inet_rsk(req); | 661 | struct inet_request_sock *ireq = inet_rsk(req); |
| 661 | char doing_ts; | 662 | unsigned remaining = MAX_TCP_OPTION_SPACE; |
| 663 | u8 cookie_plus = (xvp != NULL && !xvp->cookie_out_never) ? | ||
| 664 | xvp->cookie_plus : | ||
| 665 | 0; | ||
| 666 | bool doing_ts = ireq->tstamp_ok; | ||
| 662 | 667 | ||
| 663 | #ifdef CONFIG_TCP_MD5SIG | 668 | #ifdef CONFIG_TCP_MD5SIG |
| 664 | *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req); | 669 | *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req); |
| 665 | if (*md5) { | 670 | if (*md5) { |
| 666 | opts->options |= OPTION_MD5; | 671 | opts->options |= OPTION_MD5; |
| 667 | size += TCPOLEN_MD5SIG_ALIGNED; | 672 | remaining -= TCPOLEN_MD5SIG_ALIGNED; |
| 673 | |||
| 674 | /* We can't fit any SACK blocks in a packet with MD5 + TS | ||
| 675 | * options. There was discussion about disabling SACK | ||
| 676 | * rather than TS in order to fit in better with old, | ||
| 677 | * buggy kernels, but that was deemed to be unnecessary. | ||
| 678 | */ | ||
| 679 | doing_ts &= !ireq->sack_ok; | ||
| 668 | } | 680 | } |
| 669 | #else | 681 | #else |
| 670 | *md5 = NULL; | 682 | *md5 = NULL; |
| 671 | #endif | 683 | #endif |
| 672 | 684 | ||
| 673 | /* we can't fit any SACK blocks in a packet with MD5 + TS | 685 | /* We always send an MSS option. */ |
| 674 | options. There was discussion about disabling SACK rather than TS in | ||
| 675 | order to fit in better with old, buggy kernels, but that was deemed | ||
| 676 | to be unnecessary. */ | ||
| 677 | doing_ts = ireq->tstamp_ok && !(*md5 && ireq->sack_ok); | ||
| 678 | |||
| 679 | opts->mss = mss; | 686 | opts->mss = mss; |
| 680 | size += TCPOLEN_MSS_ALIGNED; | 687 | remaining -= TCPOLEN_MSS_ALIGNED; |
| 681 | 688 | ||
| 682 | if (likely(ireq->wscale_ok)) { | 689 | if (likely(ireq->wscale_ok)) { |
| 683 | opts->ws = ireq->rcv_wscale; | 690 | opts->ws = ireq->rcv_wscale; |
| 684 | opts->options |= OPTION_WSCALE; | 691 | opts->options |= OPTION_WSCALE; |
| 685 | size += TCPOLEN_WSCALE_ALIGNED; | 692 | remaining -= TCPOLEN_WSCALE_ALIGNED; |
| 686 | } | 693 | } |
| 687 | if (likely(doing_ts)) { | 694 | if (likely(doing_ts)) { |
| 688 | opts->options |= OPTION_TS; | 695 | opts->options |= OPTION_TS; |
| 689 | opts->tsval = TCP_SKB_CB(skb)->when; | 696 | opts->tsval = TCP_SKB_CB(skb)->when; |
| 690 | opts->tsecr = req->ts_recent; | 697 | opts->tsecr = req->ts_recent; |
| 691 | size += TCPOLEN_TSTAMP_ALIGNED; | 698 | remaining -= TCPOLEN_TSTAMP_ALIGNED; |
| 692 | } | 699 | } |
| 693 | if (likely(ireq->sack_ok)) { | 700 | if (likely(ireq->sack_ok)) { |
| 694 | opts->options |= OPTION_SACK_ADVERTISE; | 701 | opts->options |= OPTION_SACK_ADVERTISE; |
| 695 | if (unlikely(!doing_ts)) | 702 | if (unlikely(!doing_ts)) |
| 696 | size += TCPOLEN_SACKPERM_ALIGNED; | 703 | remaining -= TCPOLEN_SACKPERM_ALIGNED; |
| 697 | } | 704 | } |
| 698 | 705 | ||
| 699 | return size; | 706 | /* Similar rationale to tcp_syn_options() applies here, too. |
| 707 | * If the <SYN> options fit, the same options should fit now! | ||
| 708 | */ | ||
| 709 | if (*md5 == NULL && | ||
| 710 | doing_ts && | ||
| 711 | cookie_plus > TCPOLEN_COOKIE_BASE) { | ||
| 712 | int need = cookie_plus; /* has TCPOLEN_COOKIE_BASE */ | ||
| 713 | |||
| 714 | if (0x2 & need) { | ||
| 715 | /* 32-bit multiple */ | ||
| 716 | need += 2; /* NOPs */ | ||
| 717 | } | ||
| 718 | if (need <= remaining) { | ||
| 719 | opts->options |= OPTION_COOKIE_EXTENSION; | ||
| 720 | opts->hash_size = cookie_plus - TCPOLEN_COOKIE_BASE; | ||
| 721 | remaining -= need; | ||
| 722 | } else { | ||
| 723 | /* There's no error return, so flag it. */ | ||
| 724 | xvp->cookie_out_never = 1; /* true */ | ||
| 725 | opts->hash_size = 0; | ||
| 726 | } | ||
| 727 | } | ||
| 728 | return MAX_TCP_OPTION_SPACE - remaining; | ||
| 700 | } | 729 | } |
| 701 | 730 | ||
| 702 | /* Compute TCP options for ESTABLISHED sockets. This is not the | 731 | /* Compute TCP options for ESTABLISHED sockets. This is not the |
| @@ -2365,6 +2394,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, | |||
| 2365 | struct request_values *rvp) | 2394 | struct request_values *rvp) |
| 2366 | { | 2395 | { |
| 2367 | struct tcp_out_options opts; | 2396 | struct tcp_out_options opts; |
| 2397 | struct tcp_extend_values *xvp = tcp_xv(rvp); | ||
| 2368 | struct inet_request_sock *ireq = inet_rsk(req); | 2398 | struct inet_request_sock *ireq = inet_rsk(req); |
| 2369 | struct tcp_sock *tp = tcp_sk(sk); | 2399 | struct tcp_sock *tp = tcp_sk(sk); |
| 2370 | struct tcphdr *th; | 2400 | struct tcphdr *th; |
| @@ -2408,8 +2438,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, | |||
| 2408 | #endif | 2438 | #endif |
| 2409 | TCP_SKB_CB(skb)->when = tcp_time_stamp; | 2439 | TCP_SKB_CB(skb)->when = tcp_time_stamp; |
| 2410 | tcp_header_size = tcp_synack_options(sk, req, mss, | 2440 | tcp_header_size = tcp_synack_options(sk, req, mss, |
| 2411 | skb, &opts, &md5) + | 2441 | skb, &opts, &md5, xvp) |
| 2412 | sizeof(struct tcphdr); | 2442 | + sizeof(*th); |
| 2413 | 2443 | ||
| 2414 | skb_push(skb, tcp_header_size); | 2444 | skb_push(skb, tcp_header_size); |
| 2415 | skb_reset_transport_header(skb); | 2445 | skb_reset_transport_header(skb); |
| @@ -2426,6 +2456,45 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, | |||
| 2426 | */ | 2456 | */ |
| 2427 | tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn, | 2457 | tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn, |
| 2428 | TCPCB_FLAG_SYN | TCPCB_FLAG_ACK); | 2458 | TCPCB_FLAG_SYN | TCPCB_FLAG_ACK); |
| 2459 | |||
| 2460 | if (OPTION_COOKIE_EXTENSION & opts.options) { | ||
| 2461 | const struct tcp_cookie_values *cvp = tp->cookie_values; | ||
| 2462 | |||
| 2463 | if (cvp != NULL && | ||
| 2464 | cvp->s_data_constant && | ||
| 2465 | cvp->s_data_desired > 0) { | ||
| 2466 | u8 *buf = skb_put(skb, cvp->s_data_desired); | ||
| 2467 | |||
| 2468 | /* copy data directly from the listening socket. */ | ||
| 2469 | memcpy(buf, cvp->s_data_payload, cvp->s_data_desired); | ||
| 2470 | TCP_SKB_CB(skb)->end_seq += cvp->s_data_desired; | ||
| 2471 | } | ||
| 2472 | |||
| 2473 | if (opts.hash_size > 0) { | ||
| 2474 | __u32 workspace[SHA_WORKSPACE_WORDS]; | ||
| 2475 | u32 *mess = &xvp->cookie_bakery[COOKIE_DIGEST_WORDS]; | ||
| 2476 | u32 *tail = &mess[COOKIE_MESSAGE_WORDS-1]; | ||
| 2477 | |||
| 2478 | /* Secret recipe depends on the Timestamp, (future) | ||
| 2479 | * Sequence and Acknowledgment Numbers, Initiator | ||
| 2480 | * Cookie, and others handled by IP variant caller. | ||
| 2481 | */ | ||
| 2482 | *tail-- ^= opts.tsval; | ||
| 2483 | *tail-- ^= tcp_rsk(req)->rcv_isn + 1; | ||
| 2484 | *tail-- ^= TCP_SKB_CB(skb)->seq + 1; | ||
| 2485 | |||
| 2486 | /* recommended */ | ||
| 2487 | *tail-- ^= ((th->dest << 16) | th->source); | ||
| 2488 | *tail-- ^= (u32)cvp; /* per sockopt */ | ||
| 2489 | |||
| 2490 | sha_transform((__u32 *)&xvp->cookie_bakery[0], | ||
| 2491 | (char *)mess, | ||
| 2492 | &workspace[0]); | ||
| 2493 | opts.hash_location = | ||
| 2494 | (__u8 *)&xvp->cookie_bakery[0]; | ||
| 2495 | } | ||
| 2496 | } | ||
| 2497 | |||
| 2429 | th->seq = htonl(TCP_SKB_CB(skb)->seq); | 2498 | th->seq = htonl(TCP_SKB_CB(skb)->seq); |
| 2430 | th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1); | 2499 | th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1); |
| 2431 | 2500 | ||
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 612fc53e0bb9..5b9af508b8f2 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c | |||
| @@ -159,6 +159,8 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie) | |||
| 159 | 159 | ||
| 160 | struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) | 160 | struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) |
| 161 | { | 161 | { |
| 162 | struct tcp_options_received tcp_opt; | ||
| 163 | u8 *hash_location; | ||
| 162 | struct inet_request_sock *ireq; | 164 | struct inet_request_sock *ireq; |
| 163 | struct inet6_request_sock *ireq6; | 165 | struct inet6_request_sock *ireq6; |
| 164 | struct tcp_request_sock *treq; | 166 | struct tcp_request_sock *treq; |
| @@ -171,7 +173,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) | |||
| 171 | int mss; | 173 | int mss; |
| 172 | struct dst_entry *dst; | 174 | struct dst_entry *dst; |
| 173 | __u8 rcv_wscale; | 175 | __u8 rcv_wscale; |
| 174 | struct tcp_options_received tcp_opt; | ||
| 175 | 176 | ||
| 176 | if (!sysctl_tcp_syncookies || !th->ack) | 177 | if (!sysctl_tcp_syncookies || !th->ack) |
| 177 | goto out; | 178 | goto out; |
| @@ -254,7 +255,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) | |||
| 254 | 255 | ||
| 255 | /* check for timestamp cookie support */ | 256 | /* check for timestamp cookie support */ |
| 256 | memset(&tcp_opt, 0, sizeof(tcp_opt)); | 257 | memset(&tcp_opt, 0, sizeof(tcp_opt)); |
| 257 | tcp_parse_options(skb, &tcp_opt, 0, dst); | 258 | tcp_parse_options(skb, &tcp_opt, &hash_location, 0, dst); |
| 258 | 259 | ||
| 259 | if (tcp_opt.saw_tstamp) | 260 | if (tcp_opt.saw_tstamp) |
| 260 | cookie_check_timestamp(&tcp_opt); | 261 | cookie_check_timestamp(&tcp_opt); |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f2ec38289a4a..fc0a4e5895ee 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
| @@ -1162,7 +1162,9 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) | |||
| 1162 | */ | 1162 | */ |
| 1163 | static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) | 1163 | static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) |
| 1164 | { | 1164 | { |
| 1165 | struct tcp_extend_values tmp_ext; | ||
| 1165 | struct tcp_options_received tmp_opt; | 1166 | struct tcp_options_received tmp_opt; |
| 1167 | u8 *hash_location; | ||
| 1166 | struct request_sock *req; | 1168 | struct request_sock *req; |
| 1167 | struct inet6_request_sock *treq; | 1169 | struct inet6_request_sock *treq; |
| 1168 | struct ipv6_pinfo *np = inet6_sk(sk); | 1170 | struct ipv6_pinfo *np = inet6_sk(sk); |
| @@ -1206,8 +1208,52 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) | |||
| 1206 | tcp_clear_options(&tmp_opt); | 1208 | tcp_clear_options(&tmp_opt); |
| 1207 | tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); | 1209 | tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); |
| 1208 | tmp_opt.user_mss = tp->rx_opt.user_mss; | 1210 | tmp_opt.user_mss = tp->rx_opt.user_mss; |
| 1211 | tcp_parse_options(skb, &tmp_opt, &hash_location, 0, dst); | ||
| 1212 | |||
| 1213 | if (tmp_opt.cookie_plus > 0 && | ||
| 1214 | tmp_opt.saw_tstamp && | ||
| 1215 | !tp->rx_opt.cookie_out_never && | ||
| 1216 | (sysctl_tcp_cookie_size > 0 || | ||
| 1217 | (tp->cookie_values != NULL && | ||
| 1218 | tp->cookie_values->cookie_desired > 0))) { | ||
| 1219 | u8 *c; | ||
| 1220 | u32 *d; | ||
| 1221 | u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS]; | ||
| 1222 | int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE; | ||
| 1223 | |||
| 1224 | if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0) | ||
| 1225 | goto drop_and_free; | ||
| 1226 | |||
| 1227 | /* Secret recipe starts with IP addresses */ | ||
| 1228 | d = &ipv6_hdr(skb)->daddr.s6_addr32[0]; | ||
| 1229 | *mess++ ^= *d++; | ||
| 1230 | *mess++ ^= *d++; | ||
| 1231 | *mess++ ^= *d++; | ||
| 1232 | *mess++ ^= *d++; | ||
| 1233 | d = &ipv6_hdr(skb)->saddr.s6_addr32[0]; | ||
| 1234 | *mess++ ^= *d++; | ||
| 1235 | *mess++ ^= *d++; | ||
| 1236 | *mess++ ^= *d++; | ||
| 1237 | *mess++ ^= *d++; | ||
| 1238 | |||
| 1239 | /* plus variable length Initiator Cookie */ | ||
| 1240 | c = (u8 *)mess; | ||
| 1241 | while (l-- > 0) | ||
| 1242 | *c++ ^= *hash_location++; | ||
| 1209 | 1243 | ||
| 1210 | tcp_parse_options(skb, &tmp_opt, 0, dst); | 1244 | #ifdef CONFIG_SYN_COOKIES |
| 1245 | want_cookie = 0; /* not our kind of cookie */ | ||
| 1246 | #endif | ||
| 1247 | tmp_ext.cookie_out_never = 0; /* false */ | ||
| 1248 | tmp_ext.cookie_plus = tmp_opt.cookie_plus; | ||
| 1249 | } else if (!tp->rx_opt.cookie_in_always) { | ||
| 1250 | /* redundant indications, but ensure initialization. */ | ||
| 1251 | tmp_ext.cookie_out_never = 1; /* true */ | ||
| 1252 | tmp_ext.cookie_plus = 0; | ||
| 1253 | } else { | ||
| 1254 | goto drop_and_free; | ||
| 1255 | } | ||
| 1256 | tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always; | ||
| 1211 | 1257 | ||
| 1212 | if (want_cookie && !tmp_opt.saw_tstamp) | 1258 | if (want_cookie && !tmp_opt.saw_tstamp) |
| 1213 | tcp_clear_options(&tmp_opt); | 1259 | tcp_clear_options(&tmp_opt); |
| @@ -1244,7 +1290,9 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) | |||
| 1244 | 1290 | ||
| 1245 | security_inet_conn_request(sk, skb, req); | 1291 | security_inet_conn_request(sk, skb, req); |
| 1246 | 1292 | ||
| 1247 | if (tcp_v6_send_synack(sk, req, NULL) || want_cookie) | 1293 | if (tcp_v6_send_synack(sk, req, |
| 1294 | (struct request_values *)&tmp_ext) || | ||
| 1295 | want_cookie) | ||
| 1248 | goto drop_and_free; | 1296 | goto drop_and_free; |
| 1249 | 1297 | ||
| 1250 | inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); | 1298 | inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); |
