Commit 8d26d76d authored by YOSHIFUJI Hideaki's avatar YOSHIFUJI Hideaki

tcp md5sig: Share most of hash calcucaltion bits between IPv4 and IPv6.

We can share most part of the hash calculation code because
the only difference between IPv4 and IPv6 is their pseudo headers.
Signed-off-by: default avatarYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
parent 076fb722
......@@ -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,
......
......@@ -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;
......
......@@ -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:
......
......@@ -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);
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]);
err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp),
th, tcplen, hp);
/* Now store the hash into the packet */
err = crypto_hash_init(desc);
if (err) {
printk(KERN_WARNING "%s(): hash_init failed\n", __func__);
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__);
if (err)
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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment