aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/tcp.c127
-rw-r--r--net/ipv4/tcp_ipv4.c140
-rw-r--r--net/ipv4/tcp_output.c14
3 files changed, 147 insertions, 134 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 827e6132af5f..0b491bf03db4 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2465,76 +2465,6 @@ 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;
@@ -2658,6 +2588,63 @@ void __tcp_put_md5sig_pool(void)
}
EXPORT_SYMBOL(__tcp_put_md5sig_pool);
+
+int tcp_md5_hash_header(struct tcp_md5sig_pool *hp,
+ struct tcphdr *th)
+{
+ struct scatterlist sg;
+ int err;
+
+ __sum16 old_checksum = th->check;
+ th->check = 0;
+ /* options aren't included in the hash */
+ sg_init_one(&sg, th, sizeof(struct tcphdr));
+ err = crypto_hash_update(&hp->md5_desc, &sg, sizeof(struct tcphdr));
+ th->check = old_checksum;
+ return err;
+}
+
+EXPORT_SYMBOL(tcp_md5_hash_header);
+
+int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp,
+ struct sk_buff *skb, unsigned header_len)
+{
+ struct scatterlist sg;
+ const struct tcphdr *tp = tcp_hdr(skb);
+ struct hash_desc *desc = &hp->md5_desc;
+ unsigned i;
+ const unsigned head_data_len = skb_headlen(skb) > header_len ?
+ skb_headlen(skb) - header_len : 0;
+ const struct skb_shared_info *shi = skb_shinfo(skb);
+
+ sg_init_table(&sg, 1);
+
+ sg_set_buf(&sg, ((u8 *) tp) + header_len, head_data_len);
+ if (crypto_hash_update(desc, &sg, head_data_len))
+ return 1;
+
+ for (i = 0; i < shi->nr_frags; ++i) {
+ const struct skb_frag_struct *f = &shi->frags[i];
+ sg_set_page(&sg, f->page, f->size, f->page_offset);
+ if (crypto_hash_update(desc, &sg, f->size))
+ return 1;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(tcp_md5_hash_skb_data);
+
+int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, struct tcp_md5sig_key *key)
+{
+ struct scatterlist sg;
+
+ sg_init_one(&sg, key->key, key->keylen);
+ return crypto_hash_update(&hp->md5_desc, &sg, key->keylen);
+}
+
+EXPORT_SYMBOL(tcp_md5_hash_key);
+
#endif
void tcp_done(struct sock *sk)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 29adc668ad51..5400d75ff17a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -87,9 +87,8 @@ int sysctl_tcp_low_latency __read_mostly;
#ifdef CONFIG_TCP_MD5SIG
static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
__be32 addr);
-static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
- __be32 saddr, __be32 daddr,
- struct tcphdr *th, unsigned int tcplen);
+static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
+ __be32 daddr, __be32 saddr, struct tcphdr *th);
#else
static inline
struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
@@ -583,11 +582,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
rep.th.doff = arg.iov[0].iov_len / 4;
- tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[1],
- key,
- ip_hdr(skb)->daddr,
- ip_hdr(skb)->saddr,
- &rep.th, arg.iov[0].iov_len);
+ tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[1],
+ key, ip_hdr(skb)->daddr,
+ ip_hdr(skb)->saddr, &rep.th);
}
#endif
arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
@@ -657,11 +654,9 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
rep.th.doff = arg.iov[0].iov_len/4;
- tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[offset],
- key,
- ip_hdr(skb)->daddr,
- ip_hdr(skb)->saddr,
- &rep.th, arg.iov[0].iov_len);
+ tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[offset],
+ key, ip_hdr(skb)->daddr,
+ ip_hdr(skb)->saddr, &rep.th);
}
#endif
arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
@@ -989,28 +984,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
newkey, cmd.tcpm_keylen);
}
-static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
- __be32 saddr, __be32 daddr,
- struct tcphdr *th,
- unsigned int tcplen)
+static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
+ __be32 daddr, __be32 saddr, int nbytes)
{
- struct tcp_md5sig_pool *hp;
struct tcp4_pseudohdr *bp;
- int err;
-
- /*
- * Okay, so RFC2385 is turned on for this connection,
- * so we need to generate the MD5 hash for the packet now.
- */
-
- hp = tcp_get_md5sig_pool();
- if (!hp)
- goto clear_hash_noput;
+ struct scatterlist sg;
bp = &hp->md5_blk.ip4;
/*
- * The TCP pseudo-header (in the order: source IP address,
+ * 1. the TCP pseudo-header (in the order: source IP address,
* destination IP address, zero-padded protocol number, and
* segment length)
*/
@@ -1018,48 +1001,95 @@ static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
bp->daddr = daddr;
bp->pad = 0;
bp->protocol = IPPROTO_TCP;
- bp->len = htons(tcplen);
+ bp->len = cpu_to_be16(nbytes);
- err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp),
- th, tcplen, hp);
- if (err)
+ sg_init_one(&sg, bp, sizeof(*bp));
+ return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp));
+}
+
+static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
+ __be32 daddr, __be32 saddr, struct tcphdr *th)
+{
+ struct tcp_md5sig_pool *hp;
+ struct hash_desc *desc;
+
+ hp = tcp_get_md5sig_pool();
+ if (!hp)
+ goto clear_hash_noput;
+ desc = &hp->md5_desc;
+
+ if (crypto_hash_init(desc))
+ goto clear_hash;
+ if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, th->doff << 2))
+ goto clear_hash;
+ if (tcp_md5_hash_header(hp, th))
+ goto clear_hash;
+ if (tcp_md5_hash_key(hp, key))
+ goto clear_hash;
+ if (crypto_hash_final(desc, md5_hash))
goto clear_hash;
- /* Free up the crypto pool */
tcp_put_md5sig_pool();
-out:
return 0;
+
clear_hash:
tcp_put_md5sig_pool();
clear_hash_noput:
memset(md5_hash, 0, 16);
- goto out;
+ return 1;
}
-int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
- struct sock *sk,
- struct dst_entry *dst,
- struct request_sock *req,
- struct tcphdr *th,
- unsigned int tcplen)
+int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
+ struct sock *sk, struct request_sock *req,
+ struct sk_buff *skb)
{
+ struct tcp_md5sig_pool *hp;
+ struct hash_desc *desc;
+ struct tcphdr *th = tcp_hdr(skb);
__be32 saddr, daddr;
if (sk) {
saddr = inet_sk(sk)->saddr;
daddr = inet_sk(sk)->daddr;
+ } else if (req) {
+ saddr = inet_rsk(req)->loc_addr;
+ daddr = inet_rsk(req)->rmt_addr;
} else {
- struct rtable *rt = (struct rtable *)dst;
- BUG_ON(!rt);
- saddr = rt->rt_src;
- daddr = rt->rt_dst;
+ const struct iphdr *iph = ip_hdr(skb);
+ saddr = iph->saddr;
+ daddr = iph->daddr;
}
- return tcp_v4_do_calc_md5_hash(md5_hash, key,
- saddr, daddr,
- th, tcplen);
+
+ hp = tcp_get_md5sig_pool();
+ if (!hp)
+ goto clear_hash_noput;
+ desc = &hp->md5_desc;
+
+ if (crypto_hash_init(desc))
+ goto clear_hash;
+
+ if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, skb->len))
+ goto clear_hash;
+ if (tcp_md5_hash_header(hp, th))
+ goto clear_hash;
+ if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
+ goto clear_hash;
+ if (tcp_md5_hash_key(hp, key))
+ goto clear_hash;
+ if (crypto_hash_final(desc, md5_hash))
+ goto clear_hash;
+
+ tcp_put_md5sig_pool();
+ return 0;
+
+clear_hash:
+ tcp_put_md5sig_pool();
+clear_hash_noput:
+ memset(md5_hash, 0, 16);
+ return 1;
}
-EXPORT_SYMBOL(tcp_v4_calc_md5_hash);
+EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
{
@@ -1104,10 +1134,9 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
/* Okay, so this is hash_expected and hash_location -
* so we need to calculate the checksum.
*/
- genhash = tcp_v4_do_calc_md5_hash(newhash,
- hash_expected,
- iph->saddr, iph->daddr,
- th, skb->len);
+ genhash = tcp_v4_md5_hash_skb(newhash,
+ hash_expected,
+ NULL, NULL, skb);
if (genhash || memcmp(hash_location, newhash, 16) != 0) {
if (net_ratelimit()) {
@@ -1356,6 +1385,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
if (newkey != NULL)
tcp_v4_md5_do_add(newsk, inet_sk(sk)->daddr,
newkey, key->keylen);
+ newsk->sk_route_caps &= ~NETIF_F_GSO_MASK;
}
#endif
@@ -1719,7 +1749,7 @@ struct inet_connection_sock_af_ops ipv4_specific = {
#ifdef CONFIG_TCP_MD5SIG
static struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
.md5_lookup = tcp_v4_md5_lookup,
- .calc_md5_hash = tcp_v4_calc_md5_hash,
+ .calc_md5_hash = tcp_v4_md5_hash_skb,
.md5_add = tcp_v4_md5_add_func,
.md5_parse = tcp_v4_parse_md5_keys,
};
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 36a19707f67f..958ff486165f 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -540,8 +540,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
* room for it.
*/
md5 = tp->af_specific->md5_lookup(sk, sk);
- if (md5)
+ if (md5) {
tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
+ sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+ }
#endif
skb_push(skb, tcp_header_size);
@@ -602,10 +604,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
/* Calculate the MD5 hash, as we have all we need now */
if (md5) {
tp->af_specific->calc_md5_hash(md5_hash_location,
- md5,
- sk, NULL, NULL,
- tcp_hdr(skb),
- skb->len);
+ md5, sk, NULL, skb);
}
#endif
@@ -2264,10 +2263,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
/* Okay, we have all we need - do the md5 hash if needed */
if (md5) {
tp->af_specific->calc_md5_hash(md5_hash_location,
- md5,
- NULL, dst, req,
- tcp_hdr(skb),
- skb->len);
+ md5, NULL, req, skb);
}
#endif