diff --git a/include/net/tcp.h b/include/net/tcp.h index ae2549c45fc7..eac26b73bcc3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1117,6 +1117,13 @@ struct tcp_md5sig_pool { #define TCP_MD5SIG_MAXKEYS (~(u32)0) /* really?! */ /* - functions */ +extern int tcp_calc_md5_hash(char *md5_hash, + struct tcp_md5sig_key *key, + int bplen, + struct tcphdr *th, + unsigned int tcplen, + struct tcp_md5sig_pool *hp); + extern int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, struct sock *sk, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ab66683b8043..6efbae0e5512 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2459,6 +2459,76 @@ static unsigned long tcp_md5sig_users; static struct tcp_md5sig_pool **tcp_md5sig_pool; static DEFINE_SPINLOCK(tcp_md5sig_pool_lock); +int tcp_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, + int bplen, + struct tcphdr *th, unsigned int tcplen, + struct tcp_md5sig_pool *hp) +{ + struct scatterlist sg[4]; + __u16 data_len; + int block = 0; + __sum16 cksum; + struct hash_desc *desc = &hp->md5_desc; + int err; + unsigned int nbytes = 0; + + sg_init_table(sg, 4); + + /* 1. The TCP pseudo-header */ + sg_set_buf(&sg[block++], &hp->md5_blk, bplen); + nbytes += bplen; + + /* 2. The TCP header, excluding options, and assuming a + * checksum of zero + */ + cksum = th->check; + th->check = 0; + sg_set_buf(&sg[block++], th, sizeof(*th)); + nbytes += sizeof(*th); + + /* 3. The TCP segment data (if any) */ + data_len = tcplen - (th->doff << 2); + if (data_len > 0) { + u8 *data = (u8 *)th + (th->doff << 2); + sg_set_buf(&sg[block++], data, data_len); + nbytes += data_len; + } + + /* 4. an independently-specified key or password, known to both + * TCPs and presumably connection-specific + */ + sg_set_buf(&sg[block++], key->key, key->keylen); + nbytes += key->keylen; + + sg_mark_end(&sg[block - 1]); + + /* Now store the hash into the packet */ + err = crypto_hash_init(desc); + if (err) { + if (net_ratelimit()) + printk(KERN_WARNING "%s(): hash_init failed\n", __func__); + return -1; + } + err = crypto_hash_update(desc, sg, nbytes); + if (err) { + if (net_ratelimit()) + printk(KERN_WARNING "%s(): hash_update failed\n", __func__); + return -1; + } + err = crypto_hash_final(desc, md5_hash); + if (err) { + if (net_ratelimit()) + printk(KERN_WARNING "%s(): hash_final failed\n", __func__); + return -1; + } + + /* Reset header */ + th->check = cksum; + + return 0; +} +EXPORT_SYMBOL(tcp_calc_md5_hash); + static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool **pool) { int cpu; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f25445d21bdf..e331cdbd0953 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1006,15 +1006,9 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, struct tcphdr *th, unsigned int tcplen) { - struct scatterlist sg[4]; - __u16 data_len; - int block = 0; - __sum16 old_checksum; struct tcp_md5sig_pool *hp; struct tcp4_pseudohdr *bp; - struct hash_desc *desc; int err; - unsigned int nbytes = 0; /* * Okay, so RFC2385 is turned on for this connection, @@ -1026,10 +1020,9 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, goto clear_hash_noput; bp = &hp->md5_blk.ip4; - desc = &hp->md5_desc; /* - * 1. the TCP pseudo-header (in the order: source IP address, + * The TCP pseudo-header (in the order: source IP address, * destination IP address, zero-padded protocol number, and * segment length) */ @@ -1039,50 +1032,13 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, bp->protocol = IPPROTO_TCP; bp->len = htons(tcplen); - sg_init_table(sg, 4); - - sg_set_buf(&sg[block++], bp, sizeof(*bp)); - nbytes += sizeof(*bp); - - /* 2. the TCP header, excluding options, and assuming a - * checksum of zero/ - */ - old_checksum = th->check; - th->check = 0; - sg_set_buf(&sg[block++], th, sizeof(struct tcphdr)); - nbytes += sizeof(struct tcphdr); - - /* 3. the TCP segment data (if any) */ - data_len = tcplen - (th->doff << 2); - if (data_len > 0) { - unsigned char *data = (unsigned char *)th + (th->doff << 2); - sg_set_buf(&sg[block++], data, data_len); - nbytes += data_len; - } - - /* 4. an independently-specified key or password, known to both - * TCPs and presumably connection-specific - */ - sg_set_buf(&sg[block++], key->key, key->keylen); - nbytes += key->keylen; - - sg_mark_end(&sg[block - 1]); - - /* Now store the Hash into the packet */ - err = crypto_hash_init(desc); - if (err) - goto clear_hash; - err = crypto_hash_update(desc, sg, nbytes); - if (err) - goto clear_hash; - err = crypto_hash_final(desc, md5_hash); + err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp), + th, tcplen, hp); if (err) goto clear_hash; - /* Reset header, and free up the crypto */ + /* Free up the crypto pool */ tcp_put_md5sig_pool(); - th->check = old_checksum; - out: return 0; clear_hash: diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 334d21c23da9..0ae0311082fb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -738,23 +738,17 @@ static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, struct in6_addr *daddr, struct tcphdr *th, unsigned int tcplen) { - struct scatterlist sg[4]; - __u16 data_len; - int block = 0; - __sum16 cksum; struct tcp_md5sig_pool *hp; struct tcp6_pseudohdr *bp; - struct hash_desc *desc; int err; - unsigned int nbytes = 0; hp = tcp_get_md5sig_pool(); if (!hp) { printk(KERN_WARNING "%s(): hash pool not found...\n", __func__); goto clear_hash_noput; } + bp = &hp->md5_blk.ip6; - desc = &hp->md5_desc; /* 1. TCP pseudo-header (RFC2460) */ ipv6_addr_copy(&bp->saddr, saddr); @@ -762,51 +756,14 @@ static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key, bp->len = htonl(tcplen); bp->protocol = htonl(IPPROTO_TCP); - sg_init_table(sg, 4); + err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp), + th, tcplen, hp); - sg_set_buf(&sg[block++], bp, sizeof(*bp)); - nbytes += sizeof(*bp); - - /* 2. TCP header, excluding options */ - cksum = th->check; - th->check = 0; - sg_set_buf(&sg[block++], th, sizeof(*th)); - nbytes += sizeof(*th); - - /* 3. TCP segment data (if any) */ - data_len = tcplen - (th->doff << 2); - if (data_len > 0) { - u8 *data = (u8 *)th + (th->doff << 2); - sg_set_buf(&sg[block++], data, data_len); - nbytes += data_len; - } - - /* 4. shared key */ - sg_set_buf(&sg[block++], key->key, key->keylen); - nbytes += key->keylen; - - sg_mark_end(&sg[block - 1]); - - /* Now store the hash into the packet */ - err = crypto_hash_init(desc); - if (err) { - printk(KERN_WARNING "%s(): hash_init failed\n", __func__); + if (err) goto clear_hash; - } - err = crypto_hash_update(desc, sg, nbytes); - if (err) { - printk(KERN_WARNING "%s(): hash_update failed\n", __func__); - goto clear_hash; - } - err = crypto_hash_final(desc, md5_hash); - if (err) { - printk(KERN_WARNING "%s(): hash_final failed\n", __func__); - goto clear_hash; - } - /* Reset header, and free up the crypto */ + /* Free up the crypto pool */ tcp_put_md5sig_pool(); - th->check = cksum; out: return 0; clear_hash: