aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_ipv4.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-06-12 01:33:59 -0400
committerDavid S. Miller <davem@davemloft.net>2008-06-12 01:33:59 -0400
commite6e30add6bd8115af108de2a13ec82d997a55777 (patch)
tree558b4d1c3ac369805aa9c57abca710bdf52aff75 /net/ipv4/tcp_ipv4.c
parentd4c3c0753594adaafbcb77a086f013f1d847b3f0 (diff)
parent9501f9722922f2e80e1f9dc6682311d65c2b5690 (diff)
Merge branch 'net-next-2.6-misc-20080612a' of git://git.linux-ipv6.org/gitroot/yoshfuji/linux-2.6-next
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r--net/ipv4/tcp_ipv4.c164
1 files changed, 33 insertions, 131 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index f2926ae1de57..9088d709725e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -93,8 +93,13 @@ static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
93 __be32 addr); 93 __be32 addr);
94static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, 94static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
95 __be32 saddr, __be32 daddr, 95 __be32 saddr, __be32 daddr,
96 struct tcphdr *th, int protocol, 96 struct tcphdr *th, unsigned int tcplen);
97 unsigned int tcplen); 97#else
98static inline
99struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
100{
101 return NULL;
102}
98#endif 103#endif
99 104
100struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { 105struct inet_hashinfo __cacheline_aligned tcp_hashinfo = {
@@ -584,8 +589,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
584 key, 589 key,
585 ip_hdr(skb)->daddr, 590 ip_hdr(skb)->daddr,
586 ip_hdr(skb)->saddr, 591 ip_hdr(skb)->saddr,
587 &rep.th, IPPROTO_TCP, 592 &rep.th, arg.iov[0].iov_len);
588 arg.iov[0].iov_len);
589 } 593 }
590#endif 594#endif
591 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, 595 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
@@ -604,9 +608,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
604 outside socket context is ugly, certainly. What can I do? 608 outside socket context is ugly, certainly. What can I do?
605 */ 609 */
606 610
607static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk, 611static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
608 struct sk_buff *skb, u32 seq, u32 ack, 612 u32 win, u32 ts, int oif,
609 u32 win, u32 ts) 613 struct tcp_md5sig_key *key)
610{ 614{
611 struct tcphdr *th = tcp_hdr(skb); 615 struct tcphdr *th = tcp_hdr(skb);
612 struct { 616 struct {
@@ -618,10 +622,6 @@ static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk,
618 ]; 622 ];
619 } rep; 623 } rep;
620 struct ip_reply_arg arg; 624 struct ip_reply_arg arg;
621#ifdef CONFIG_TCP_MD5SIG
622 struct tcp_md5sig_key *key;
623 struct tcp_md5sig_key tw_key;
624#endif
625 625
626 memset(&rep.th, 0, sizeof(struct tcphdr)); 626 memset(&rep.th, 0, sizeof(struct tcphdr));
627 memset(&arg, 0, sizeof(arg)); 627 memset(&arg, 0, sizeof(arg));
@@ -647,23 +647,6 @@ static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk,
647 rep.th.window = htons(win); 647 rep.th.window = htons(win);
648 648
649#ifdef CONFIG_TCP_MD5SIG 649#ifdef CONFIG_TCP_MD5SIG
650 /*
651 * The SKB holds an imcoming packet, but may not have a valid ->sk
652 * pointer. This is especially the case when we're dealing with a
653 * TIME_WAIT ack, because the sk structure is long gone, and only
654 * the tcp_timewait_sock remains. So the md5 key is stashed in that
655 * structure, and we use it in preference. I believe that (twsk ||
656 * skb->sk) holds true, but we program defensively.
657 */
658 if (!twsk && skb->sk) {
659 key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr);
660 } else if (twsk && twsk->tw_md5_keylen) {
661 tw_key.key = twsk->tw_md5_key;
662 tw_key.keylen = twsk->tw_md5_keylen;
663 key = &tw_key;
664 } else
665 key = NULL;
666
667 if (key) { 650 if (key) {
668 int offset = (ts) ? 3 : 0; 651 int offset = (ts) ? 3 : 0;
669 652
@@ -678,16 +661,15 @@ static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk,
678 key, 661 key,
679 ip_hdr(skb)->daddr, 662 ip_hdr(skb)->daddr,
680 ip_hdr(skb)->saddr, 663 ip_hdr(skb)->saddr,
681 &rep.th, IPPROTO_TCP, 664 &rep.th, arg.iov[0].iov_len);
682 arg.iov[0].iov_len);
683 } 665 }
684#endif 666#endif
685 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, 667 arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
686 ip_hdr(skb)->saddr, /* XXX */ 668 ip_hdr(skb)->saddr, /* XXX */
687 arg.iov[0].iov_len, IPPROTO_TCP, 0); 669 arg.iov[0].iov_len, IPPROTO_TCP, 0);
688 arg.csumoffset = offsetof(struct tcphdr, check) / 2; 670 arg.csumoffset = offsetof(struct tcphdr, check) / 2;
689 if (twsk) 671 if (oif)
690 arg.bound_dev_if = twsk->tw_sk.tw_bound_dev_if; 672 arg.bound_dev_if = oif;
691 673
692 ip_send_reply(dev_net(skb->dev)->ipv4.tcp_sock, skb, 674 ip_send_reply(dev_net(skb->dev)->ipv4.tcp_sock, skb,
693 &arg, arg.iov[0].iov_len); 675 &arg, arg.iov[0].iov_len);
@@ -700,9 +682,12 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
700 struct inet_timewait_sock *tw = inet_twsk(sk); 682 struct inet_timewait_sock *tw = inet_twsk(sk);
701 struct tcp_timewait_sock *tcptw = tcp_twsk(sk); 683 struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
702 684
703 tcp_v4_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, 685 tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
704 tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, 686 tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
705 tcptw->tw_ts_recent); 687 tcptw->tw_ts_recent,
688 tw->tw_bound_dev_if,
689 tcp_twsk_md5_key(tcptw)
690 );
706 691
707 inet_twsk_put(tw); 692 inet_twsk_put(tw);
708} 693}
@@ -710,9 +695,11 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
710static void tcp_v4_reqsk_send_ack(struct sk_buff *skb, 695static void tcp_v4_reqsk_send_ack(struct sk_buff *skb,
711 struct request_sock *req) 696 struct request_sock *req)
712{ 697{
713 tcp_v4_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1, 698 tcp_v4_send_ack(skb, tcp_rsk(req)->snt_isn + 1,
714 tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, 699 tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,
715 req->ts_recent); 700 req->ts_recent,
701 0,
702 tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr));
716} 703}
717 704
718/* 705/*
@@ -1004,18 +991,12 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
1004 991
1005static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, 992static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
1006 __be32 saddr, __be32 daddr, 993 __be32 saddr, __be32 daddr,
1007 struct tcphdr *th, int protocol, 994 struct tcphdr *th,
1008 unsigned int tcplen) 995 unsigned int tcplen)
1009{ 996{
1010 struct scatterlist sg[4];
1011 __u16 data_len;
1012 int block = 0;
1013 __sum16 old_checksum;
1014 struct tcp_md5sig_pool *hp; 997 struct tcp_md5sig_pool *hp;
1015 struct tcp4_pseudohdr *bp; 998 struct tcp4_pseudohdr *bp;
1016 struct hash_desc *desc;
1017 int err; 999 int err;
1018 unsigned int nbytes = 0;
1019 1000
1020 /* 1001 /*
1021 * Okay, so RFC2385 is turned on for this connection, 1002 * Okay, so RFC2385 is turned on for this connection,
@@ -1027,63 +1008,25 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
1027 goto clear_hash_noput; 1008 goto clear_hash_noput;
1028 1009
1029 bp = &hp->md5_blk.ip4; 1010 bp = &hp->md5_blk.ip4;
1030 desc = &hp->md5_desc;
1031 1011
1032 /* 1012 /*
1033 * 1. the TCP pseudo-header (in the order: source IP address, 1013 * The TCP pseudo-header (in the order: source IP address,
1034 * destination IP address, zero-padded protocol number, and 1014 * destination IP address, zero-padded protocol number, and
1035 * segment length) 1015 * segment length)
1036 */ 1016 */
1037 bp->saddr = saddr; 1017 bp->saddr = saddr;
1038 bp->daddr = daddr; 1018 bp->daddr = daddr;
1039 bp->pad = 0; 1019 bp->pad = 0;
1040 bp->protocol = protocol; 1020 bp->protocol = IPPROTO_TCP;
1041 bp->len = htons(tcplen); 1021 bp->len = htons(tcplen);
1042 1022
1043 sg_init_table(sg, 4); 1023 err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp),
1044 1024 th, tcplen, hp);
1045 sg_set_buf(&sg[block++], bp, sizeof(*bp));
1046 nbytes += sizeof(*bp);
1047
1048 /* 2. the TCP header, excluding options, and assuming a
1049 * checksum of zero/
1050 */
1051 old_checksum = th->check;
1052 th->check = 0;
1053 sg_set_buf(&sg[block++], th, sizeof(struct tcphdr));
1054 nbytes += sizeof(struct tcphdr);
1055
1056 /* 3. the TCP segment data (if any) */
1057 data_len = tcplen - (th->doff << 2);
1058 if (data_len > 0) {
1059 unsigned char *data = (unsigned char *)th + (th->doff << 2);
1060 sg_set_buf(&sg[block++], data, data_len);
1061 nbytes += data_len;
1062 }
1063
1064 /* 4. an independently-specified key or password, known to both
1065 * TCPs and presumably connection-specific
1066 */
1067 sg_set_buf(&sg[block++], key->key, key->keylen);
1068 nbytes += key->keylen;
1069
1070 sg_mark_end(&sg[block - 1]);
1071
1072 /* Now store the Hash into the packet */
1073 err = crypto_hash_init(desc);
1074 if (err)
1075 goto clear_hash;
1076 err = crypto_hash_update(desc, sg, nbytes);
1077 if (err)
1078 goto clear_hash;
1079 err = crypto_hash_final(desc, md5_hash);
1080 if (err) 1025 if (err)
1081 goto clear_hash; 1026 goto clear_hash;
1082 1027
1083 /* Reset header, and free up the crypto */ 1028 /* Free up the crypto pool */
1084 tcp_put_md5sig_pool(); 1029 tcp_put_md5sig_pool();
1085 th->check = old_checksum;
1086
1087out: 1030out:
1088 return 0; 1031 return 0;
1089clear_hash: 1032clear_hash:
@@ -1097,7 +1040,7 @@ int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
1097 struct sock *sk, 1040 struct sock *sk,
1098 struct dst_entry *dst, 1041 struct dst_entry *dst,
1099 struct request_sock *req, 1042 struct request_sock *req,
1100 struct tcphdr *th, int protocol, 1043 struct tcphdr *th,
1101 unsigned int tcplen) 1044 unsigned int tcplen)
1102{ 1045{
1103 __be32 saddr, daddr; 1046 __be32 saddr, daddr;
@@ -1113,7 +1056,7 @@ int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
1113 } 1056 }
1114 return tcp_v4_do_calc_md5_hash(md5_hash, key, 1057 return tcp_v4_do_calc_md5_hash(md5_hash, key,
1115 saddr, daddr, 1058 saddr, daddr,
1116 th, protocol, tcplen); 1059 th, tcplen);
1117} 1060}
1118 1061
1119EXPORT_SYMBOL(tcp_v4_calc_md5_hash); 1062EXPORT_SYMBOL(tcp_v4_calc_md5_hash);
@@ -1132,52 +1075,12 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
1132 struct tcp_md5sig_key *hash_expected; 1075 struct tcp_md5sig_key *hash_expected;
1133 const struct iphdr *iph = ip_hdr(skb); 1076 const struct iphdr *iph = ip_hdr(skb);
1134 struct tcphdr *th = tcp_hdr(skb); 1077 struct tcphdr *th = tcp_hdr(skb);
1135 int length = (th->doff << 2) - sizeof(struct tcphdr);
1136 int genhash; 1078 int genhash;
1137 unsigned char *ptr;
1138 unsigned char newhash[16]; 1079 unsigned char newhash[16];
1139 1080
1140 hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr); 1081 hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr);
1082 hash_location = tcp_parse_md5sig_option(th);
1141 1083
1142 /*
1143 * If the TCP option length is less than the TCP_MD5SIG
1144 * option length, then we can shortcut
1145 */
1146 if (length < TCPOLEN_MD5SIG) {
1147 if (hash_expected)
1148 return 1;
1149 else
1150 return 0;
1151 }
1152
1153 /* Okay, we can't shortcut - we have to grub through the options */
1154 ptr = (unsigned char *)(th + 1);
1155 while (length > 0) {
1156 int opcode = *ptr++;
1157 int opsize;
1158
1159 switch (opcode) {
1160 case TCPOPT_EOL:
1161 goto done_opts;
1162 case TCPOPT_NOP:
1163 length--;
1164 continue;
1165 default:
1166 opsize = *ptr++;
1167 if (opsize < 2)
1168 goto done_opts;
1169 if (opsize > length)
1170 goto done_opts;
1171
1172 if (opcode == TCPOPT_MD5SIG) {
1173 hash_location = ptr;
1174 goto done_opts;
1175 }
1176 }
1177 ptr += opsize-2;
1178 length -= opsize;
1179 }
1180done_opts:
1181 /* We've parsed the options - do we have a hash? */ 1084 /* We've parsed the options - do we have a hash? */
1182 if (!hash_expected && !hash_location) 1085 if (!hash_expected && !hash_location)
1183 return 0; 1086 return 0;
@@ -1204,8 +1107,7 @@ done_opts:
1204 genhash = tcp_v4_do_calc_md5_hash(newhash, 1107 genhash = tcp_v4_do_calc_md5_hash(newhash,
1205 hash_expected, 1108 hash_expected,
1206 iph->saddr, iph->daddr, 1109 iph->saddr, iph->daddr,
1207 th, sk->sk_protocol, 1110 th, skb->len);
1208 skb->len);
1209 1111
1210 if (genhash || memcmp(hash_location, newhash, 16) != 0) { 1112 if (genhash || memcmp(hash_location, newhash, 16) != 0) {
1211 if (net_ratelimit()) { 1113 if (net_ratelimit()) {