Skip to content

Commit

Permalink
[IPSEC] proto: Move transport mode input path into xfrm_mode_transport
Browse files Browse the repository at this point in the history
Now that we have xfrm_mode objects we can move the transport mode specific
input decapsulation code into xfrm_mode_transport.  This removes duplicate
code as well as unnecessary header movement in case of tunnel mode SAs
since we will discard the original IP header immediately.

This also fixes a minor bug for transport-mode ESP where the IP payload
length is set to the correct value minus the header length (with extension
headers for IPv6).

Of course the other neat thing is that we no longer have to allocate
temporary buffers to hold the IP headers for ESP and IPComp.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
herbertx authored and David S. Miller committed Jun 18, 2006
1 parent b59f45d commit 31a4ab9
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 83 deletions.
15 changes: 7 additions & 8 deletions net/ipv4/ah4.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
{
int ah_hlen;
int ihl;
struct iphdr *iph;
struct ip_auth_hdr *ah;
struct ah_data *ahp;
Expand Down Expand Up @@ -149,13 +150,14 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
ah = (struct ip_auth_hdr*)skb->data;
iph = skb->nh.iph;

memcpy(work_buf, iph, iph->ihl*4);
ihl = skb->data - skb->nh.raw;
memcpy(work_buf, iph, ihl);

iph->ttl = 0;
iph->tos = 0;
iph->frag_off = 0;
iph->check = 0;
if (iph->ihl != 5) {
if (ihl > sizeof(*iph)) {
u32 dummy;
if (ip_clear_mutable_options(iph, &dummy))
goto out;
Expand All @@ -164,19 +166,16 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
u8 auth_data[MAX_AH_AUTH_LEN];

memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
skb_push(skb, skb->data - skb->nh.raw);
skb_push(skb, ihl);
ahp->icv(ahp, skb, ah->auth_data);
if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) {
x->stats.integrity_failed++;
goto out;
}
}
((struct iphdr*)work_buf)->protocol = ah->nexthdr;
skb->nh.raw = skb_pull(skb, ah_hlen);
memcpy(skb->nh.raw, work_buf, iph->ihl*4);
skb->nh.iph->tot_len = htons(skb->len);
skb_pull(skb, skb->nh.iph->ihl*4);
skb->h.raw = skb->data;
skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl);
__skb_pull(skb, ah_hlen + ihl);

return 0;

Expand Down
18 changes: 6 additions & 12 deletions net/ipv4/esp4.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,9 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
int alen = esp->auth.icv_trunc_len;
int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen;
int nfrags;
int encap_len = 0;
int ihl;
u8 nexthdr[2];
struct scatterlist *sg;
u8 workbuf[60];
int padlen;

if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
Expand Down Expand Up @@ -177,7 +176,6 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
skb->ip_summed = CHECKSUM_NONE;

esph = (struct ip_esp_hdr*)skb->data;
iph = skb->nh.iph;

/* Get ivec. This can be wrong, check against another impls. */
if (esp->conf.ivlen)
Expand All @@ -204,12 +202,12 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)

/* ... check padding bits here. Silly. :-) */

iph = skb->nh.iph;
ihl = iph->ihl * 4;

if (x->encap) {
struct xfrm_encap_tmpl *encap = x->encap;
struct udphdr *uh;

uh = (struct udphdr *)(iph + 1);
encap_len = (void*)esph - (void*)uh;
struct udphdr *uh = (void *)(skb->nh.raw + ihl);

/*
* 1) if the NAT-T peer's IP or port changed then
Expand Down Expand Up @@ -246,11 +244,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)

iph->protocol = nexthdr[1];
pskb_trim(skb, skb->len - alen - padlen - 2);
memcpy(workbuf, skb->nh.raw, iph->ihl*4);
skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen);
skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
memcpy(skb->nh.raw, workbuf, iph->ihl*4);
skb->nh.iph->tot_len = htons(skb->len);
skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl;

return 0;

Expand Down
23 changes: 5 additions & 18 deletions net/ipv4/ipcomp.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ static LIST_HEAD(ipcomp_tfms_list);
static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
{
int err, plen, dlen;
struct iphdr *iph;
struct ipcomp_data *ipcd = x->data;
u8 *start, *scratch;
struct crypto_tfm *tfm;
Expand Down Expand Up @@ -74,23 +73,16 @@ static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)

skb_put(skb, dlen - plen);
memcpy(skb->data, scratch, dlen);
iph = skb->nh.iph;
iph->tot_len = htons(dlen + iph->ihl * 4);
out:
put_cpu();
return err;
}

static int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)
{
u8 nexthdr;
int err = 0;
struct iphdr *iph;
union {
struct iphdr iph;
char buf[60];
} tmp_iph;

struct ip_comp_hdr *ipch;

if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0) {
Expand All @@ -102,15 +94,10 @@ static int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)

/* Remove ipcomp header and decompress original payload */
iph = skb->nh.iph;
memcpy(&tmp_iph, iph, iph->ihl * 4);
nexthdr = *(u8 *)skb->data;
skb_pull(skb, sizeof(struct ip_comp_hdr));
skb->nh.raw += sizeof(struct ip_comp_hdr);
memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4);
iph = skb->nh.iph;
iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ip_comp_hdr));
iph->protocol = nexthdr;
skb->h.raw = skb->data;
ipch = (void *)skb->data;
iph->protocol = ipch->nexthdr;
skb->h.raw = skb->nh.raw + sizeof(*ipch);
__skb_pull(skb, sizeof(*ipch));
err = ipcomp_decompress(x, skb);

out:
Expand Down
14 changes: 14 additions & 0 deletions net/ipv4/xfrm4_mode_transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,22 @@ static int xfrm4_transport_output(struct sk_buff *skb)
return 0;
}

/* Remove encapsulation header.
*
* The IP header will be moved over the top of the encapsulation header.
*
* On entry, skb->h shall point to where the IP header should be and skb->nh
* shall be set to where the IP header currently is. skb->data shall point
* to the start of the payload.
*/
static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
{
int ihl = skb->data - skb->h.raw;

if (skb->h.raw != skb->nh.raw)
skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl);
skb->nh.iph->tot_len = htons(skb->len + ihl);
skb->h.raw = skb->data;
return 0;
}

Expand Down
10 changes: 3 additions & 7 deletions net/ipv6/ah6.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)

memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
memset(ah->auth_data, 0, ahp->icv_trunc_len);
skb_push(skb, skb->data - skb->nh.raw);
skb_push(skb, hdr_len);
ahp->icv(ahp, skb, ah->auth_data);
if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) {
LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n");
Expand All @@ -301,12 +301,8 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
}
}

skb->nh.raw = skb_pull(skb, ah_hlen);
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
skb_pull(skb, hdr_len);
skb->h.raw = skb->data;

skb->h.raw = memcpy(skb->nh.raw += ah_hlen, tmp_hdr, hdr_len);
__skb_pull(skb, ah_hlen + hdr_len);

kfree(tmp_hdr);

Expand Down
20 changes: 4 additions & 16 deletions net/ipv6/esp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,17 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)

int hdr_len = skb->h.raw - skb->nh.raw;
int nfrags;
unsigned char *tmp_hdr = NULL;
int ret = 0;

if (!pskb_may_pull(skb, sizeof(struct ipv6_esp_hdr))) {
ret = -EINVAL;
goto out_nofree;
goto out;
}

if (elen <= 0 || (elen & (blksize-1))) {
ret = -EINVAL;
goto out_nofree;
}

tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr) {
ret = -ENOMEM;
goto out_nofree;
goto out;
}
memcpy(tmp_hdr, skb->nh.raw, hdr_len);

/* If integrity check is required, do this. */
if (esp->auth.icv_full_len) {
Expand Down Expand Up @@ -222,16 +214,12 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
/* ... check padding bits here. Silly. :-) */

pskb_trim(skb, skb->len - alen - padlen - 2);
skb->h.raw = skb_pull(skb, sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen);
skb->nh.raw += sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen;
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
ret = nexthdr[1];
}

skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - hdr_len;

out:
kfree(tmp_hdr);
out_nofree:
return ret;
}

Expand Down
27 changes: 5 additions & 22 deletions net/ipv6/ipcomp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ static LIST_HEAD(ipcomp6_tfms_list);
static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)
{
int err = 0;
u8 nexthdr = 0;
int hdr_len = skb->h.raw - skb->nh.raw;
unsigned char *tmp_hdr = NULL;
struct ipv6hdr *iph;
struct ipv6_comp_hdr *ipch;
int plen, dlen;
struct ipcomp_data *ipcd = x->data;
u8 *start, *scratch;
Expand All @@ -86,17 +84,9 @@ static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)

/* Remove ipcomp header and decompress original payload */
iph = skb->nh.ipv6h;
tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr)
goto out;
memcpy(tmp_hdr, iph, hdr_len);
nexthdr = *(u8 *)skb->data;
skb_pull(skb, sizeof(struct ipv6_comp_hdr));
skb->nh.raw += sizeof(struct ipv6_comp_hdr);
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
iph = skb->nh.ipv6h;
iph->payload_len = htons(ntohs(iph->payload_len) - sizeof(struct ipv6_comp_hdr));
skb->h.raw = skb->data;
ipch = (void *)skb->data;
skb->h.raw = skb->nh.raw + sizeof(*ipch);
__skb_pull(skb, sizeof(*ipch));

/* decompression */
plen = skb->len;
Expand Down Expand Up @@ -125,18 +115,11 @@ static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)

skb_put(skb, dlen - plen);
memcpy(skb->data, scratch, dlen);
err = ipch->nexthdr;

iph = skb->nh.ipv6h;
iph->payload_len = htons(skb->len);

out_put_cpu:
put_cpu();
out:
kfree(tmp_hdr);
if (err)
goto error_out;
return nexthdr;
error_out:
return err;
}

Expand Down
15 changes: 15 additions & 0 deletions net/ipv6/xfrm6_mode_transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,23 @@ static int xfrm6_transport_output(struct sk_buff *skb)
return 0;
}

/* Remove encapsulation header.
*
* The IP header will be moved over the top of the encapsulation header.
*
* On entry, skb->h shall point to where the IP header should be and skb->nh
* shall be set to where the IP header currently is. skb->data shall point
* to the start of the payload.
*/
static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
{
int ihl = skb->data - skb->h.raw;

if (skb->h.raw != skb->nh.raw)
skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl);
skb->nh.ipv6h->payload_len = htons(skb->len + ihl -
sizeof(struct ipv6hdr));
skb->h.raw = skb->data;
return 0;
}

Expand Down

0 comments on commit 31a4ab9

Please sign in to comment.