aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp.c
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-04-17 13:19:16 +0900
committerYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-06-12 02:38:20 +0900
commit8d26d76dd4a4c87ef037a44a42a0608ffc730199 (patch)
tree884ff53a83e460aa3f1837cc336a5a34f364156e /net/ipv4/tcp.c
parent076fb7223357769c39f3ddf900bba6752369c76a (diff)
downloadkernel_samsung_tuna-8d26d76dd4a4c87ef037a44a42a0608ffc730199.zip
kernel_samsung_tuna-8d26d76dd4a4c87ef037a44a42a0608ffc730199.tar.gz
kernel_samsung_tuna-8d26d76dd4a4c87ef037a44a42a0608ffc730199.tar.bz2
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: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r--net/ipv4/tcp.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index ab66683..6efbae0 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;