diff options
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 170 |
1 files changed, 34 insertions, 136 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 12695be2c255..0db9b75c1fa2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -5,8 +5,6 @@ | |||
5 | * | 5 | * |
6 | * Implementation of the Transmission Control Protocol(TCP). | 6 | * Implementation of the Transmission Control Protocol(TCP). |
7 | * | 7 | * |
8 | * Version: $Id: tcp_ipv4.c,v 1.240 2002/02/01 22:01:04 davem Exp $ | ||
9 | * | ||
10 | * IPv4 specific functions | 8 | * IPv4 specific functions |
11 | * | 9 | * |
12 | * | 10 | * |
@@ -91,8 +89,13 @@ static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, | |||
91 | __be32 addr); | 89 | __be32 addr); |
92 | static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, | 90 | static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, |
93 | __be32 saddr, __be32 daddr, | 91 | __be32 saddr, __be32 daddr, |
94 | struct tcphdr *th, int protocol, | 92 | struct tcphdr *th, unsigned int tcplen); |
95 | unsigned int tcplen); | 93 | #else |
94 | static inline | ||
95 | struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) | ||
96 | { | ||
97 | return NULL; | ||
98 | } | ||
96 | #endif | 99 | #endif |
97 | 100 | ||
98 | struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { | 101 | struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { |
@@ -582,8 +585,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) | |||
582 | key, | 585 | key, |
583 | ip_hdr(skb)->daddr, | 586 | ip_hdr(skb)->daddr, |
584 | ip_hdr(skb)->saddr, | 587 | ip_hdr(skb)->saddr, |
585 | &rep.th, IPPROTO_TCP, | 588 | &rep.th, arg.iov[0].iov_len); |
586 | arg.iov[0].iov_len); | ||
587 | } | 589 | } |
588 | #endif | 590 | #endif |
589 | arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, | 591 | arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, |
@@ -602,9 +604,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) | |||
602 | outside socket context is ugly, certainly. What can I do? | 604 | outside socket context is ugly, certainly. What can I do? |
603 | */ | 605 | */ |
604 | 606 | ||
605 | static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk, | 607 | static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, |
606 | struct sk_buff *skb, u32 seq, u32 ack, | 608 | u32 win, u32 ts, int oif, |
607 | u32 win, u32 ts) | 609 | struct tcp_md5sig_key *key) |
608 | { | 610 | { |
609 | struct tcphdr *th = tcp_hdr(skb); | 611 | struct tcphdr *th = tcp_hdr(skb); |
610 | struct { | 612 | struct { |
@@ -616,10 +618,6 @@ static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk, | |||
616 | ]; | 618 | ]; |
617 | } rep; | 619 | } rep; |
618 | struct ip_reply_arg arg; | 620 | struct ip_reply_arg arg; |
619 | #ifdef CONFIG_TCP_MD5SIG | ||
620 | struct tcp_md5sig_key *key; | ||
621 | struct tcp_md5sig_key tw_key; | ||
622 | #endif | ||
623 | 621 | ||
624 | memset(&rep.th, 0, sizeof(struct tcphdr)); | 622 | memset(&rep.th, 0, sizeof(struct tcphdr)); |
625 | memset(&arg, 0, sizeof(arg)); | 623 | memset(&arg, 0, sizeof(arg)); |
@@ -645,23 +643,6 @@ static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk, | |||
645 | rep.th.window = htons(win); | 643 | rep.th.window = htons(win); |
646 | 644 | ||
647 | #ifdef CONFIG_TCP_MD5SIG | 645 | #ifdef CONFIG_TCP_MD5SIG |
648 | /* | ||
649 | * The SKB holds an imcoming packet, but may not have a valid ->sk | ||
650 | * pointer. This is especially the case when we're dealing with a | ||
651 | * TIME_WAIT ack, because the sk structure is long gone, and only | ||
652 | * the tcp_timewait_sock remains. So the md5 key is stashed in that | ||
653 | * structure, and we use it in preference. I believe that (twsk || | ||
654 | * skb->sk) holds true, but we program defensively. | ||
655 | */ | ||
656 | if (!twsk && skb->sk) { | ||
657 | key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr); | ||
658 | } else if (twsk && twsk->tw_md5_keylen) { | ||
659 | tw_key.key = twsk->tw_md5_key; | ||
660 | tw_key.keylen = twsk->tw_md5_keylen; | ||
661 | key = &tw_key; | ||
662 | } else | ||
663 | key = NULL; | ||
664 | |||
665 | if (key) { | 646 | if (key) { |
666 | int offset = (ts) ? 3 : 0; | 647 | int offset = (ts) ? 3 : 0; |
667 | 648 | ||
@@ -676,16 +657,15 @@ static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk, | |||
676 | key, | 657 | key, |
677 | ip_hdr(skb)->daddr, | 658 | ip_hdr(skb)->daddr, |
678 | ip_hdr(skb)->saddr, | 659 | ip_hdr(skb)->saddr, |
679 | &rep.th, IPPROTO_TCP, | 660 | &rep.th, arg.iov[0].iov_len); |
680 | arg.iov[0].iov_len); | ||
681 | } | 661 | } |
682 | #endif | 662 | #endif |
683 | arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, | 663 | arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, |
684 | ip_hdr(skb)->saddr, /* XXX */ | 664 | ip_hdr(skb)->saddr, /* XXX */ |
685 | arg.iov[0].iov_len, IPPROTO_TCP, 0); | 665 | arg.iov[0].iov_len, IPPROTO_TCP, 0); |
686 | arg.csumoffset = offsetof(struct tcphdr, check) / 2; | 666 | arg.csumoffset = offsetof(struct tcphdr, check) / 2; |
687 | if (twsk) | 667 | if (oif) |
688 | arg.bound_dev_if = twsk->tw_sk.tw_bound_dev_if; | 668 | arg.bound_dev_if = oif; |
689 | 669 | ||
690 | ip_send_reply(dev_net(skb->dev)->ipv4.tcp_sock, skb, | 670 | ip_send_reply(dev_net(skb->dev)->ipv4.tcp_sock, skb, |
691 | &arg, arg.iov[0].iov_len); | 671 | &arg, arg.iov[0].iov_len); |
@@ -698,9 +678,12 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) | |||
698 | struct inet_timewait_sock *tw = inet_twsk(sk); | 678 | struct inet_timewait_sock *tw = inet_twsk(sk); |
699 | struct tcp_timewait_sock *tcptw = tcp_twsk(sk); | 679 | struct tcp_timewait_sock *tcptw = tcp_twsk(sk); |
700 | 680 | ||
701 | tcp_v4_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, | 681 | tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, |
702 | tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, | 682 | tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, |
703 | tcptw->tw_ts_recent); | 683 | tcptw->tw_ts_recent, |
684 | tw->tw_bound_dev_if, | ||
685 | tcp_twsk_md5_key(tcptw) | ||
686 | ); | ||
704 | 687 | ||
705 | inet_twsk_put(tw); | 688 | inet_twsk_put(tw); |
706 | } | 689 | } |
@@ -708,9 +691,11 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) | |||
708 | static void tcp_v4_reqsk_send_ack(struct sk_buff *skb, | 691 | static void tcp_v4_reqsk_send_ack(struct sk_buff *skb, |
709 | struct request_sock *req) | 692 | struct request_sock *req) |
710 | { | 693 | { |
711 | tcp_v4_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1, | 694 | tcp_v4_send_ack(skb, tcp_rsk(req)->snt_isn + 1, |
712 | tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, | 695 | tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, |
713 | req->ts_recent); | 696 | req->ts_recent, |
697 | 0, | ||
698 | tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr)); | ||
714 | } | 699 | } |
715 | 700 | ||
716 | /* | 701 | /* |
@@ -1002,18 +987,12 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, | |||
1002 | 987 | ||
1003 | static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, | 988 | static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, |
1004 | __be32 saddr, __be32 daddr, | 989 | __be32 saddr, __be32 daddr, |
1005 | struct tcphdr *th, int protocol, | 990 | struct tcphdr *th, |
1006 | unsigned int tcplen) | 991 | unsigned int tcplen) |
1007 | { | 992 | { |
1008 | struct scatterlist sg[4]; | ||
1009 | __u16 data_len; | ||
1010 | int block = 0; | ||
1011 | __sum16 old_checksum; | ||
1012 | struct tcp_md5sig_pool *hp; | 993 | struct tcp_md5sig_pool *hp; |
1013 | struct tcp4_pseudohdr *bp; | 994 | struct tcp4_pseudohdr *bp; |
1014 | struct hash_desc *desc; | ||
1015 | int err; | 995 | int err; |
1016 | unsigned int nbytes = 0; | ||
1017 | 996 | ||
1018 | /* | 997 | /* |
1019 | * Okay, so RFC2385 is turned on for this connection, | 998 | * Okay, so RFC2385 is turned on for this connection, |
@@ -1025,63 +1004,25 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, | |||
1025 | goto clear_hash_noput; | 1004 | goto clear_hash_noput; |
1026 | 1005 | ||
1027 | bp = &hp->md5_blk.ip4; | 1006 | bp = &hp->md5_blk.ip4; |
1028 | desc = &hp->md5_desc; | ||
1029 | 1007 | ||
1030 | /* | 1008 | /* |
1031 | * 1. the TCP pseudo-header (in the order: source IP address, | 1009 | * The TCP pseudo-header (in the order: source IP address, |
1032 | * destination IP address, zero-padded protocol number, and | 1010 | * destination IP address, zero-padded protocol number, and |
1033 | * segment length) | 1011 | * segment length) |
1034 | */ | 1012 | */ |
1035 | bp->saddr = saddr; | 1013 | bp->saddr = saddr; |
1036 | bp->daddr = daddr; | 1014 | bp->daddr = daddr; |
1037 | bp->pad = 0; | 1015 | bp->pad = 0; |
1038 | bp->protocol = protocol; | 1016 | bp->protocol = IPPROTO_TCP; |
1039 | bp->len = htons(tcplen); | 1017 | bp->len = htons(tcplen); |
1040 | 1018 | ||
1041 | sg_init_table(sg, 4); | 1019 | err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp), |
1042 | 1020 | th, tcplen, hp); | |
1043 | sg_set_buf(&sg[block++], bp, sizeof(*bp)); | ||
1044 | nbytes += sizeof(*bp); | ||
1045 | |||
1046 | /* 2. the TCP header, excluding options, and assuming a | ||
1047 | * checksum of zero/ | ||
1048 | */ | ||
1049 | old_checksum = th->check; | ||
1050 | th->check = 0; | ||
1051 | sg_set_buf(&sg[block++], th, sizeof(struct tcphdr)); | ||
1052 | nbytes += sizeof(struct tcphdr); | ||
1053 | |||
1054 | /* 3. the TCP segment data (if any) */ | ||
1055 | data_len = tcplen - (th->doff << 2); | ||
1056 | if (data_len > 0) { | ||
1057 | unsigned char *data = (unsigned char *)th + (th->doff << 2); | ||
1058 | sg_set_buf(&sg[block++], data, data_len); | ||
1059 | nbytes += data_len; | ||
1060 | } | ||
1061 | |||
1062 | /* 4. an independently-specified key or password, known to both | ||
1063 | * TCPs and presumably connection-specific | ||
1064 | */ | ||
1065 | sg_set_buf(&sg[block++], key->key, key->keylen); | ||
1066 | nbytes += key->keylen; | ||
1067 | |||
1068 | sg_mark_end(&sg[block - 1]); | ||
1069 | |||
1070 | /* Now store the Hash into the packet */ | ||
1071 | err = crypto_hash_init(desc); | ||
1072 | if (err) | ||
1073 | goto clear_hash; | ||
1074 | err = crypto_hash_update(desc, sg, nbytes); | ||
1075 | if (err) | ||
1076 | goto clear_hash; | ||
1077 | err = crypto_hash_final(desc, md5_hash); | ||
1078 | if (err) | 1021 | if (err) |
1079 | goto clear_hash; | 1022 | goto clear_hash; |
1080 | 1023 | ||
1081 | /* Reset header, and free up the crypto */ | 1024 | /* Free up the crypto pool */ |
1082 | tcp_put_md5sig_pool(); | 1025 | tcp_put_md5sig_pool(); |
1083 | th->check = old_checksum; | ||
1084 | |||
1085 | out: | 1026 | out: |
1086 | return 0; | 1027 | return 0; |
1087 | clear_hash: | 1028 | clear_hash: |
@@ -1095,7 +1036,7 @@ int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, | |||
1095 | struct sock *sk, | 1036 | struct sock *sk, |
1096 | struct dst_entry *dst, | 1037 | struct dst_entry *dst, |
1097 | struct request_sock *req, | 1038 | struct request_sock *req, |
1098 | struct tcphdr *th, int protocol, | 1039 | struct tcphdr *th, |
1099 | unsigned int tcplen) | 1040 | unsigned int tcplen) |
1100 | { | 1041 | { |
1101 | __be32 saddr, daddr; | 1042 | __be32 saddr, daddr; |
@@ -1111,7 +1052,7 @@ int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, | |||
1111 | } | 1052 | } |
1112 | return tcp_v4_do_calc_md5_hash(md5_hash, key, | 1053 | return tcp_v4_do_calc_md5_hash(md5_hash, key, |
1113 | saddr, daddr, | 1054 | saddr, daddr, |
1114 | th, protocol, tcplen); | 1055 | th, tcplen); |
1115 | } | 1056 | } |
1116 | 1057 | ||
1117 | EXPORT_SYMBOL(tcp_v4_calc_md5_hash); | 1058 | EXPORT_SYMBOL(tcp_v4_calc_md5_hash); |
@@ -1130,52 +1071,12 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb) | |||
1130 | struct tcp_md5sig_key *hash_expected; | 1071 | struct tcp_md5sig_key *hash_expected; |
1131 | const struct iphdr *iph = ip_hdr(skb); | 1072 | const struct iphdr *iph = ip_hdr(skb); |
1132 | struct tcphdr *th = tcp_hdr(skb); | 1073 | struct tcphdr *th = tcp_hdr(skb); |
1133 | int length = (th->doff << 2) - sizeof(struct tcphdr); | ||
1134 | int genhash; | 1074 | int genhash; |
1135 | unsigned char *ptr; | ||
1136 | unsigned char newhash[16]; | 1075 | unsigned char newhash[16]; |
1137 | 1076 | ||
1138 | hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr); | 1077 | hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr); |
1078 | hash_location = tcp_parse_md5sig_option(th); | ||
1139 | 1079 | ||
1140 | /* | ||
1141 | * If the TCP option length is less than the TCP_MD5SIG | ||
1142 | * option length, then we can shortcut | ||
1143 | */ | ||
1144 | if (length < TCPOLEN_MD5SIG) { | ||
1145 | if (hash_expected) | ||
1146 | return 1; | ||
1147 | else | ||
1148 | return 0; | ||
1149 | } | ||
1150 | |||
1151 | /* Okay, we can't shortcut - we have to grub through the options */ | ||
1152 | ptr = (unsigned char *)(th + 1); | ||
1153 | while (length > 0) { | ||
1154 | int opcode = *ptr++; | ||
1155 | int opsize; | ||
1156 | |||
1157 | switch (opcode) { | ||
1158 | case TCPOPT_EOL: | ||
1159 | goto done_opts; | ||
1160 | case TCPOPT_NOP: | ||
1161 | length--; | ||
1162 | continue; | ||
1163 | default: | ||
1164 | opsize = *ptr++; | ||
1165 | if (opsize < 2) | ||
1166 | goto done_opts; | ||
1167 | if (opsize > length) | ||
1168 | goto done_opts; | ||
1169 | |||
1170 | if (opcode == TCPOPT_MD5SIG) { | ||
1171 | hash_location = ptr; | ||
1172 | goto done_opts; | ||
1173 | } | ||
1174 | } | ||
1175 | ptr += opsize-2; | ||
1176 | length -= opsize; | ||
1177 | } | ||
1178 | done_opts: | ||
1179 | /* We've parsed the options - do we have a hash? */ | 1080 | /* We've parsed the options - do we have a hash? */ |
1180 | if (!hash_expected && !hash_location) | 1081 | if (!hash_expected && !hash_location) |
1181 | return 0; | 1082 | return 0; |
@@ -1202,8 +1103,7 @@ done_opts: | |||
1202 | genhash = tcp_v4_do_calc_md5_hash(newhash, | 1103 | genhash = tcp_v4_do_calc_md5_hash(newhash, |
1203 | hash_expected, | 1104 | hash_expected, |
1204 | iph->saddr, iph->daddr, | 1105 | iph->saddr, iph->daddr, |
1205 | th, sk->sk_protocol, | 1106 | th, skb->len); |
1206 | skb->len); | ||
1207 | 1107 | ||
1208 | if (genhash || memcmp(hash_location, newhash, 16) != 0) { | 1108 | if (genhash || memcmp(hash_location, newhash, 16) != 0) { |
1209 | if (net_ratelimit()) { | 1109 | if (net_ratelimit()) { |
@@ -1871,7 +1771,7 @@ static int tcp_v4_init_sock(struct sock *sk) | |||
1871 | return 0; | 1771 | return 0; |
1872 | } | 1772 | } |
1873 | 1773 | ||
1874 | int tcp_v4_destroy_sock(struct sock *sk) | 1774 | void tcp_v4_destroy_sock(struct sock *sk) |
1875 | { | 1775 | { |
1876 | struct tcp_sock *tp = tcp_sk(sk); | 1776 | struct tcp_sock *tp = tcp_sk(sk); |
1877 | 1777 | ||
@@ -1915,8 +1815,6 @@ int tcp_v4_destroy_sock(struct sock *sk) | |||
1915 | } | 1815 | } |
1916 | 1816 | ||
1917 | atomic_dec(&tcp_sockets_allocated); | 1817 | atomic_dec(&tcp_sockets_allocated); |
1918 | |||
1919 | return 0; | ||
1920 | } | 1818 | } |
1921 | 1819 | ||
1922 | EXPORT_SYMBOL(tcp_v4_destroy_sock); | 1820 | EXPORT_SYMBOL(tcp_v4_destroy_sock); |