diff options
author | William Allen Simpson <william.allen.simpson@gmail.com> | 2009-12-02 13:23:05 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-03 01:07:26 -0500 |
commit | bd0388ae77075026d6a9f9eb6026dfd1d52ce0e9 (patch) | |
tree | b2262d2bf3f60e42f8c3573c89e6a47042b20122 /net/ipv4/tcp_output.c | |
parent | e56fb50f2b7958b931c8a2fc0966061b3f3c8f3a (diff) |
TCPCT part 1f: Initiator Cookie => Responder
Calculate and format <SYN> TCP_COOKIE option.
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 1c: sysctl_tcp_cookie_size, socket option TCP_COOKIE_TRANSACTIONS
TCPCT part 1d: define TCP cookie option, extend existing struct's
Signed-off-by: William.Allen.Simpson@gmail.com
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_output.c')
-rw-r--r-- | net/ipv4/tcp_output.c | 193 |
1 files changed, 163 insertions, 30 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 307f318fe931..35dd983a8a99 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -365,15 +365,45 @@ static inline int tcp_urg_mode(const struct tcp_sock *tp) | |||
365 | #define OPTION_TS (1 << 1) | 365 | #define OPTION_TS (1 << 1) |
366 | #define OPTION_MD5 (1 << 2) | 366 | #define OPTION_MD5 (1 << 2) |
367 | #define OPTION_WSCALE (1 << 3) | 367 | #define OPTION_WSCALE (1 << 3) |
368 | #define OPTION_COOKIE_EXTENSION (1 << 4) | ||
368 | 369 | ||
369 | struct tcp_out_options { | 370 | struct tcp_out_options { |
370 | u8 options; /* bit field of OPTION_* */ | 371 | u8 options; /* bit field of OPTION_* */ |
371 | u8 ws; /* window scale, 0 to disable */ | 372 | u8 ws; /* window scale, 0 to disable */ |
372 | u8 num_sack_blocks; /* number of SACK blocks to include */ | 373 | u8 num_sack_blocks; /* number of SACK blocks to include */ |
374 | u8 hash_size; /* bytes in hash_location */ | ||
373 | u16 mss; /* 0 to disable */ | 375 | u16 mss; /* 0 to disable */ |
374 | __u32 tsval, tsecr; /* need to include OPTION_TS */ | 376 | __u32 tsval, tsecr; /* need to include OPTION_TS */ |
377 | __u8 *hash_location; /* temporary pointer, overloaded */ | ||
375 | }; | 378 | }; |
376 | 379 | ||
380 | /* The sysctl int routines are generic, so check consistency here. | ||
381 | */ | ||
382 | static u8 tcp_cookie_size_check(u8 desired) | ||
383 | { | ||
384 | if (desired > 0) { | ||
385 | /* previously specified */ | ||
386 | return desired; | ||
387 | } | ||
388 | if (sysctl_tcp_cookie_size <= 0) { | ||
389 | /* no default specified */ | ||
390 | return 0; | ||
391 | } | ||
392 | if (sysctl_tcp_cookie_size <= TCP_COOKIE_MIN) { | ||
393 | /* value too small, specify minimum */ | ||
394 | return TCP_COOKIE_MIN; | ||
395 | } | ||
396 | if (sysctl_tcp_cookie_size >= TCP_COOKIE_MAX) { | ||
397 | /* value too large, specify maximum */ | ||
398 | return TCP_COOKIE_MAX; | ||
399 | } | ||
400 | if (0x1 & sysctl_tcp_cookie_size) { | ||
401 | /* 8-bit multiple, illegal, fix it */ | ||
402 | return (u8)(sysctl_tcp_cookie_size + 0x1); | ||
403 | } | ||
404 | return (u8)sysctl_tcp_cookie_size; | ||
405 | } | ||
406 | |||
377 | /* Write previously computed TCP options to the packet. | 407 | /* Write previously computed TCP options to the packet. |
378 | * | 408 | * |
379 | * Beware: Something in the Internet is very sensitive to the ordering of | 409 | * Beware: Something in the Internet is very sensitive to the ordering of |
@@ -388,17 +418,34 @@ struct tcp_out_options { | |||
388 | * (but it may well be that other scenarios fail similarly). | 418 | * (but it may well be that other scenarios fail similarly). |
389 | */ | 419 | */ |
390 | static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, | 420 | static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, |
391 | const struct tcp_out_options *opts, | 421 | struct tcp_out_options *opts) |
392 | __u8 **md5_hash) { | 422 | { |
393 | if (unlikely(OPTION_MD5 & opts->options)) { | 423 | u8 options = opts->options; /* mungable copy */ |
394 | *ptr++ = htonl((TCPOPT_NOP << 24) | | 424 | |
395 | (TCPOPT_NOP << 16) | | 425 | /* Having both authentication and cookies for security is redundant, |
396 | (TCPOPT_MD5SIG << 8) | | 426 | * and there's certainly not enough room. Instead, the cookie-less |
397 | TCPOLEN_MD5SIG); | 427 | * extension variant is proposed. |
398 | *md5_hash = (__u8 *)ptr; | 428 | * |
429 | * Consider the pessimal case with authentication. The options | ||
430 | * could look like: | ||
431 | * COOKIE|MD5(20) + MSS(4) + SACK|TS(12) + WSCALE(4) == 40 | ||
432 | */ | ||
433 | if (unlikely(OPTION_MD5 & options)) { | ||
434 | if (unlikely(OPTION_COOKIE_EXTENSION & options)) { | ||
435 | *ptr++ = htonl((TCPOPT_COOKIE << 24) | | ||
436 | (TCPOLEN_COOKIE_BASE << 16) | | ||
437 | (TCPOPT_MD5SIG << 8) | | ||
438 | TCPOLEN_MD5SIG); | ||
439 | } else { | ||
440 | *ptr++ = htonl((TCPOPT_NOP << 24) | | ||
441 | (TCPOPT_NOP << 16) | | ||
442 | (TCPOPT_MD5SIG << 8) | | ||
443 | TCPOLEN_MD5SIG); | ||
444 | } | ||
445 | options &= ~OPTION_COOKIE_EXTENSION; | ||
446 | /* overload cookie hash location */ | ||
447 | opts->hash_location = (__u8 *)ptr; | ||
399 | ptr += 4; | 448 | ptr += 4; |
400 | } else { | ||
401 | *md5_hash = NULL; | ||
402 | } | 449 | } |
403 | 450 | ||
404 | if (unlikely(opts->mss)) { | 451 | if (unlikely(opts->mss)) { |
@@ -407,12 +454,13 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, | |||
407 | opts->mss); | 454 | opts->mss); |
408 | } | 455 | } |
409 | 456 | ||
410 | if (likely(OPTION_TS & opts->options)) { | 457 | if (likely(OPTION_TS & options)) { |
411 | if (unlikely(OPTION_SACK_ADVERTISE & opts->options)) { | 458 | if (unlikely(OPTION_SACK_ADVERTISE & options)) { |
412 | *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | | 459 | *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | |
413 | (TCPOLEN_SACK_PERM << 16) | | 460 | (TCPOLEN_SACK_PERM << 16) | |
414 | (TCPOPT_TIMESTAMP << 8) | | 461 | (TCPOPT_TIMESTAMP << 8) | |
415 | TCPOLEN_TIMESTAMP); | 462 | TCPOLEN_TIMESTAMP); |
463 | options &= ~OPTION_SACK_ADVERTISE; | ||
416 | } else { | 464 | } else { |
417 | *ptr++ = htonl((TCPOPT_NOP << 24) | | 465 | *ptr++ = htonl((TCPOPT_NOP << 24) | |
418 | (TCPOPT_NOP << 16) | | 466 | (TCPOPT_NOP << 16) | |
@@ -423,15 +471,52 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, | |||
423 | *ptr++ = htonl(opts->tsecr); | 471 | *ptr++ = htonl(opts->tsecr); |
424 | } | 472 | } |
425 | 473 | ||
426 | if (unlikely(OPTION_SACK_ADVERTISE & opts->options && | 474 | /* Specification requires after timestamp, so do it now. |
427 | !(OPTION_TS & opts->options))) { | 475 | * |
476 | * Consider the pessimal case without authentication. The options | ||
477 | * could look like: | ||
478 | * MSS(4) + SACK|TS(12) + COOKIE(20) + WSCALE(4) == 40 | ||
479 | */ | ||
480 | if (unlikely(OPTION_COOKIE_EXTENSION & options)) { | ||
481 | __u8 *cookie_copy = opts->hash_location; | ||
482 | u8 cookie_size = opts->hash_size; | ||
483 | |||
484 | /* 8-bit multiple handled in tcp_cookie_size_check() above, | ||
485 | * and elsewhere. | ||
486 | */ | ||
487 | if (0x2 & cookie_size) { | ||
488 | __u8 *p = (__u8 *)ptr; | ||
489 | |||
490 | /* 16-bit multiple */ | ||
491 | *p++ = TCPOPT_COOKIE; | ||
492 | *p++ = TCPOLEN_COOKIE_BASE + cookie_size; | ||
493 | *p++ = *cookie_copy++; | ||
494 | *p++ = *cookie_copy++; | ||
495 | ptr++; | ||
496 | cookie_size -= 2; | ||
497 | } else { | ||
498 | /* 32-bit multiple */ | ||
499 | *ptr++ = htonl(((TCPOPT_NOP << 24) | | ||
500 | (TCPOPT_NOP << 16) | | ||
501 | (TCPOPT_COOKIE << 8) | | ||
502 | TCPOLEN_COOKIE_BASE) + | ||
503 | cookie_size); | ||
504 | } | ||
505 | |||
506 | if (cookie_size > 0) { | ||
507 | memcpy(ptr, cookie_copy, cookie_size); | ||
508 | ptr += (cookie_size / 4); | ||
509 | } | ||
510 | } | ||
511 | |||
512 | if (unlikely(OPTION_SACK_ADVERTISE & options)) { | ||
428 | *ptr++ = htonl((TCPOPT_NOP << 24) | | 513 | *ptr++ = htonl((TCPOPT_NOP << 24) | |
429 | (TCPOPT_NOP << 16) | | 514 | (TCPOPT_NOP << 16) | |
430 | (TCPOPT_SACK_PERM << 8) | | 515 | (TCPOPT_SACK_PERM << 8) | |
431 | TCPOLEN_SACK_PERM); | 516 | TCPOLEN_SACK_PERM); |
432 | } | 517 | } |
433 | 518 | ||
434 | if (unlikely(OPTION_WSCALE & opts->options)) { | 519 | if (unlikely(OPTION_WSCALE & options)) { |
435 | *ptr++ = htonl((TCPOPT_NOP << 24) | | 520 | *ptr++ = htonl((TCPOPT_NOP << 24) | |
436 | (TCPOPT_WINDOW << 16) | | 521 | (TCPOPT_WINDOW << 16) | |
437 | (TCPOLEN_WINDOW << 8) | | 522 | (TCPOLEN_WINDOW << 8) | |
@@ -466,14 +551,18 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb, | |||
466 | struct tcp_out_options *opts, | 551 | struct tcp_out_options *opts, |
467 | struct tcp_md5sig_key **md5) { | 552 | struct tcp_md5sig_key **md5) { |
468 | struct tcp_sock *tp = tcp_sk(sk); | 553 | struct tcp_sock *tp = tcp_sk(sk); |
469 | unsigned size = 0; | 554 | struct tcp_cookie_values *cvp = tp->cookie_values; |
470 | struct dst_entry *dst = __sk_dst_get(sk); | 555 | struct dst_entry *dst = __sk_dst_get(sk); |
556 | unsigned remaining = MAX_TCP_OPTION_SPACE; | ||
557 | u8 cookie_size = (!tp->rx_opt.cookie_out_never && cvp != NULL) ? | ||
558 | tcp_cookie_size_check(cvp->cookie_desired) : | ||
559 | 0; | ||
471 | 560 | ||
472 | #ifdef CONFIG_TCP_MD5SIG | 561 | #ifdef CONFIG_TCP_MD5SIG |
473 | *md5 = tp->af_specific->md5_lookup(sk, sk); | 562 | *md5 = tp->af_specific->md5_lookup(sk, sk); |
474 | if (*md5) { | 563 | if (*md5) { |
475 | opts->options |= OPTION_MD5; | 564 | opts->options |= OPTION_MD5; |
476 | size += TCPOLEN_MD5SIG_ALIGNED; | 565 | remaining -= TCPOLEN_MD5SIG_ALIGNED; |
477 | } | 566 | } |
478 | #else | 567 | #else |
479 | *md5 = NULL; | 568 | *md5 = NULL; |
@@ -489,7 +578,7 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb, | |||
489 | * SACKs don't matter, we never delay an ACK when we have any of those | 578 | * SACKs don't matter, we never delay an ACK when we have any of those |
490 | * going out. */ | 579 | * going out. */ |
491 | opts->mss = tcp_advertise_mss(sk); | 580 | opts->mss = tcp_advertise_mss(sk); |
492 | size += TCPOLEN_MSS_ALIGNED; | 581 | remaining -= TCPOLEN_MSS_ALIGNED; |
493 | 582 | ||
494 | if (likely(sysctl_tcp_timestamps && | 583 | if (likely(sysctl_tcp_timestamps && |
495 | !dst_feature(dst, RTAX_FEATURE_NO_TSTAMP) && | 584 | !dst_feature(dst, RTAX_FEATURE_NO_TSTAMP) && |
@@ -497,22 +586,68 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb, | |||
497 | opts->options |= OPTION_TS; | 586 | opts->options |= OPTION_TS; |
498 | opts->tsval = TCP_SKB_CB(skb)->when; | 587 | opts->tsval = TCP_SKB_CB(skb)->when; |
499 | opts->tsecr = tp->rx_opt.ts_recent; | 588 | opts->tsecr = tp->rx_opt.ts_recent; |
500 | size += TCPOLEN_TSTAMP_ALIGNED; | 589 | remaining -= TCPOLEN_TSTAMP_ALIGNED; |
501 | } | 590 | } |
502 | if (likely(sysctl_tcp_window_scaling && | 591 | if (likely(sysctl_tcp_window_scaling && |
503 | !dst_feature(dst, RTAX_FEATURE_NO_WSCALE))) { | 592 | !dst_feature(dst, RTAX_FEATURE_NO_WSCALE))) { |
504 | opts->ws = tp->rx_opt.rcv_wscale; | 593 | opts->ws = tp->rx_opt.rcv_wscale; |
505 | opts->options |= OPTION_WSCALE; | 594 | opts->options |= OPTION_WSCALE; |
506 | size += TCPOLEN_WSCALE_ALIGNED; | 595 | remaining -= TCPOLEN_WSCALE_ALIGNED; |
507 | } | 596 | } |
508 | if (likely(sysctl_tcp_sack && | 597 | if (likely(sysctl_tcp_sack && |
509 | !dst_feature(dst, RTAX_FEATURE_NO_SACK))) { | 598 | !dst_feature(dst, RTAX_FEATURE_NO_SACK))) { |
510 | opts->options |= OPTION_SACK_ADVERTISE; | 599 | opts->options |= OPTION_SACK_ADVERTISE; |
511 | if (unlikely(!(OPTION_TS & opts->options))) | 600 | if (unlikely(!(OPTION_TS & opts->options))) |
512 | size += TCPOLEN_SACKPERM_ALIGNED; | 601 | remaining -= TCPOLEN_SACKPERM_ALIGNED; |
513 | } | 602 | } |
514 | 603 | ||
515 | return size; | 604 | /* Note that timestamps are required by the specification. |
605 | * | ||
606 | * Odd numbers of bytes are prohibited by the specification, ensuring | ||
607 | * that the cookie is 16-bit aligned, and the resulting cookie pair is | ||
608 | * 32-bit aligned. | ||
609 | */ | ||
610 | if (*md5 == NULL && | ||
611 | (OPTION_TS & opts->options) && | ||
612 | cookie_size > 0) { | ||
613 | int need = TCPOLEN_COOKIE_BASE + cookie_size; | ||
614 | |||
615 | if (0x2 & need) { | ||
616 | /* 32-bit multiple */ | ||
617 | need += 2; /* NOPs */ | ||
618 | |||
619 | if (need > remaining) { | ||
620 | /* try shrinking cookie to fit */ | ||
621 | cookie_size -= 2; | ||
622 | need -= 4; | ||
623 | } | ||
624 | } | ||
625 | while (need > remaining && TCP_COOKIE_MIN <= cookie_size) { | ||
626 | cookie_size -= 4; | ||
627 | need -= 4; | ||
628 | } | ||
629 | if (TCP_COOKIE_MIN <= cookie_size) { | ||
630 | opts->options |= OPTION_COOKIE_EXTENSION; | ||
631 | opts->hash_location = (__u8 *)&cvp->cookie_pair[0]; | ||
632 | opts->hash_size = cookie_size; | ||
633 | |||
634 | /* Remember for future incarnations. */ | ||
635 | cvp->cookie_desired = cookie_size; | ||
636 | |||
637 | if (cvp->cookie_desired != cvp->cookie_pair_size) { | ||
638 | /* Currently use random bytes as a nonce, | ||
639 | * assuming these are completely unpredictable | ||
640 | * by hostile users of the same system. | ||
641 | */ | ||
642 | get_random_bytes(&cvp->cookie_pair[0], | ||
643 | cookie_size); | ||
644 | cvp->cookie_pair_size = cookie_size; | ||
645 | } | ||
646 | |||
647 | remaining -= need; | ||
648 | } | ||
649 | } | ||
650 | return MAX_TCP_OPTION_SPACE - remaining; | ||
516 | } | 651 | } |
517 | 652 | ||
518 | /* Set up TCP options for SYN-ACKs. */ | 653 | /* Set up TCP options for SYN-ACKs. */ |
@@ -627,7 +762,6 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, | |||
627 | struct tcp_out_options opts; | 762 | struct tcp_out_options opts; |
628 | unsigned tcp_options_size, tcp_header_size; | 763 | unsigned tcp_options_size, tcp_header_size; |
629 | struct tcp_md5sig_key *md5; | 764 | struct tcp_md5sig_key *md5; |
630 | __u8 *md5_hash_location; | ||
631 | struct tcphdr *th; | 765 | struct tcphdr *th; |
632 | int err; | 766 | int err; |
633 | 767 | ||
@@ -698,7 +832,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, | |||
698 | } | 832 | } |
699 | } | 833 | } |
700 | 834 | ||
701 | tcp_options_write((__be32 *)(th + 1), tp, &opts, &md5_hash_location); | 835 | tcp_options_write((__be32 *)(th + 1), tp, &opts); |
702 | if (likely((tcb->flags & TCPCB_FLAG_SYN) == 0)) | 836 | if (likely((tcb->flags & TCPCB_FLAG_SYN) == 0)) |
703 | TCP_ECN_send(sk, skb, tcp_header_size); | 837 | TCP_ECN_send(sk, skb, tcp_header_size); |
704 | 838 | ||
@@ -706,7 +840,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, | |||
706 | /* Calculate the MD5 hash, as we have all we need now */ | 840 | /* Calculate the MD5 hash, as we have all we need now */ |
707 | if (md5) { | 841 | if (md5) { |
708 | sk->sk_route_caps &= ~NETIF_F_GSO_MASK; | 842 | sk->sk_route_caps &= ~NETIF_F_GSO_MASK; |
709 | tp->af_specific->calc_md5_hash(md5_hash_location, | 843 | tp->af_specific->calc_md5_hash(opts.hash_location, |
710 | md5, sk, NULL, skb); | 844 | md5, sk, NULL, skb); |
711 | } | 845 | } |
712 | #endif | 846 | #endif |
@@ -2230,14 +2364,13 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, | |||
2230 | struct request_sock *req, | 2364 | struct request_sock *req, |
2231 | struct request_values *rvp) | 2365 | struct request_values *rvp) |
2232 | { | 2366 | { |
2367 | struct tcp_out_options opts; | ||
2233 | struct inet_request_sock *ireq = inet_rsk(req); | 2368 | struct inet_request_sock *ireq = inet_rsk(req); |
2234 | struct tcp_sock *tp = tcp_sk(sk); | 2369 | struct tcp_sock *tp = tcp_sk(sk); |
2235 | struct tcphdr *th; | 2370 | struct tcphdr *th; |
2236 | int tcp_header_size; | ||
2237 | struct tcp_out_options opts; | ||
2238 | struct sk_buff *skb; | 2371 | struct sk_buff *skb; |
2239 | struct tcp_md5sig_key *md5; | 2372 | struct tcp_md5sig_key *md5; |
2240 | __u8 *md5_hash_location; | 2373 | int tcp_header_size; |
2241 | int mss; | 2374 | int mss; |
2242 | 2375 | ||
2243 | skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC); | 2376 | skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC); |
@@ -2298,14 +2431,14 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, | |||
2298 | 2431 | ||
2299 | /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ | 2432 | /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ |
2300 | th->window = htons(min(req->rcv_wnd, 65535U)); | 2433 | th->window = htons(min(req->rcv_wnd, 65535U)); |
2301 | tcp_options_write((__be32 *)(th + 1), tp, &opts, &md5_hash_location); | 2434 | tcp_options_write((__be32 *)(th + 1), tp, &opts); |
2302 | th->doff = (tcp_header_size >> 2); | 2435 | th->doff = (tcp_header_size >> 2); |
2303 | TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS); | 2436 | TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS); |
2304 | 2437 | ||
2305 | #ifdef CONFIG_TCP_MD5SIG | 2438 | #ifdef CONFIG_TCP_MD5SIG |
2306 | /* Okay, we have all we need - do the md5 hash if needed */ | 2439 | /* Okay, we have all we need - do the md5 hash if needed */ |
2307 | if (md5) { | 2440 | if (md5) { |
2308 | tcp_rsk(req)->af_specific->calc_md5_hash(md5_hash_location, | 2441 | tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location, |
2309 | md5, NULL, req, skb); | 2442 | md5, NULL, req, skb); |
2310 | } | 2443 | } |
2311 | #endif | 2444 | #endif |