Skip to content

Commit

Permalink
tcp md5sig: Share most of hash calcucaltion bits between IPv4 and IPv6.
Browse files Browse the repository at this point in the history
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: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
  • Loading branch information
YOSHIFUJI Hideaki committed Jun 11, 2008
1 parent 076fb72 commit 8d26d76
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 96 deletions.
7 changes: 7 additions & 0 deletions include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
70 changes: 70 additions & 0 deletions net/ipv4/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
52 changes: 4 additions & 48 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
*/
Expand All @@ -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:
Expand Down
53 changes: 5 additions & 48 deletions net/ipv6/tcp_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,75 +738,32 @@ 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);
ipv6_addr_copy(&bp->daddr, daddr);
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:
Expand Down

0 comments on commit 8d26d76

Please sign in to comment.