Commit 67b46fa7 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPSEC]: Move generic encap code into xfrm6_output.

Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@redhat.com>
parent e8d7832c
...@@ -11,7 +11,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ ...@@ -11,7 +11,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
ip6_flowlabel.o ipv6_syms.o ip6_flowlabel.o ipv6_syms.o
ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \ ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
xfrm6_tunnel.o xfrm6_tunnel.o xfrm6_output.o
ipv6-objs += $(ipv6-y) ipv6-objs += $(ipv6-y)
obj-$(CONFIG_INET6_AH) += ah6.o obj-$(CONFIG_INET6_AH) += ah6.o
......
...@@ -158,70 +158,55 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) ...@@ -158,70 +158,55 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
int ah6_output(struct sk_buff **pskb) int ah6_output(struct sk_buff **pskb)
{ {
int err; int err;
int hdr_len = sizeof(struct ipv6hdr); int extlen;
struct dst_entry *dst = (*pskb)->dst; struct dst_entry *dst = (*pskb)->dst;
struct xfrm_state *x = dst->xfrm; struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *iph = NULL; struct ipv6hdr *top_iph;
struct ip_auth_hdr *ah; struct ip_auth_hdr *ah;
struct ah_data *ahp; struct ah_data *ahp;
u8 nexthdr; u8 nexthdr;
char tmp_base[8];
struct {
struct in6_addr daddr;
char hdrs[0];
} *tmp_ext;
if ((*pskb)->ip_summed == CHECKSUM_HW) { top_iph = (struct ipv6hdr *)(*pskb)->data;
err = skb_checksum_help(pskb, 0); top_iph->payload_len = htons((*pskb)->len - sizeof(*top_iph));
if (err)
goto error_nolock;
}
spin_lock_bh(&x->lock); nexthdr = *(*pskb)->nh.raw;
err = xfrm_state_check(x, *pskb); *(*pskb)->nh.raw = IPPROTO_AH;
if (err)
goto error;
if (x->props.mode) { /* When there are no extension headers, we only need to save the first
err = xfrm6_tunnel_check_size(*pskb); * 8 bytes of the base IP header.
if (err) */
goto error; memcpy(tmp_base, top_iph, sizeof(tmp_base));
iph = (*pskb)->nh.ipv6h; tmp_ext = NULL;
(*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len); extlen = (*pskb)->h.raw - (unsigned char *)(top_iph + 1);
(*pskb)->nh.ipv6h->version = 6; if (extlen) {
(*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr)); extlen += sizeof(*tmp_ext);
(*pskb)->nh.ipv6h->nexthdr = IPPROTO_AH; tmp_ext = kmalloc(extlen, GFP_ATOMIC);
ipv6_addr_copy(&(*pskb)->nh.ipv6h->saddr, if (!tmp_ext) {
(struct in6_addr *) &x->props.saddr);
ipv6_addr_copy(&(*pskb)->nh.ipv6h->daddr,
(struct in6_addr *) &x->id.daddr);
ah = (struct ip_auth_hdr*)((*pskb)->nh.ipv6h+1);
ah->nexthdr = IPPROTO_IPV6;
} else {
u8 *prevhdr;
hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
nexthdr = *prevhdr;
*prevhdr = IPPROTO_AH;
iph = kmalloc(hdr_len, GFP_ATOMIC);
if (!iph) {
err = -ENOMEM; err = -ENOMEM;
goto error; goto error;
} }
memcpy(iph, (*pskb)->data, hdr_len); memcpy(tmp_ext, &top_iph->daddr, extlen);
(*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len); err = ipv6_clear_mutable_options(top_iph,
iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr)); extlen - sizeof(*tmp_ext) +
memcpy((*pskb)->nh.ipv6h, iph, hdr_len); sizeof(*top_iph));
err = ipv6_clear_mutable_options((*pskb)->nh.ipv6h, hdr_len);
if (err) if (err)
goto error_free_iph; goto error_free_iph;
ah = (struct ip_auth_hdr*)((*pskb)->nh.raw+hdr_len);
(*pskb)->h.raw = (unsigned char*) ah;
ah->nexthdr = nexthdr;
} }
(*pskb)->nh.ipv6h->priority = 0; ah = (struct ip_auth_hdr *)(*pskb)->h.raw;
(*pskb)->nh.ipv6h->flow_lbl[0] = 0; ah->nexthdr = nexthdr;
(*pskb)->nh.ipv6h->flow_lbl[1] = 0;
(*pskb)->nh.ipv6h->flow_lbl[2] = 0; top_iph->priority = 0;
(*pskb)->nh.ipv6h->hop_limit = 0; top_iph->flow_lbl[0] = 0;
top_iph->flow_lbl[1] = 0;
top_iph->flow_lbl[2] = 0;
top_iph->hop_limit = 0;
ahp = x->data; ahp = x->data;
ah->hdrlen = (XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ah->hdrlen = (XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) +
...@@ -232,35 +217,16 @@ int ah6_output(struct sk_buff **pskb) ...@@ -232,35 +217,16 @@ int ah6_output(struct sk_buff **pskb)
ah->seq_no = htonl(++x->replay.oseq); ah->seq_no = htonl(++x->replay.oseq);
ahp->icv(ahp, *pskb, ah->auth_data); ahp->icv(ahp, *pskb, ah->auth_data);
if (x->props.mode) { err = 0;
(*pskb)->nh.ipv6h->hop_limit = iph->hop_limit;
(*pskb)->nh.ipv6h->priority = iph->priority;
(*pskb)->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
(*pskb)->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
(*pskb)->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
if (x->props.flags & XFRM_STATE_NOECN)
IP6_ECN_clear((*pskb)->nh.ipv6h);
} else {
memcpy((*pskb)->nh.ipv6h, iph, hdr_len);
kfree (iph);
}
(*pskb)->nh.raw = (*pskb)->data;
x->curlft.bytes += (*pskb)->len; memcpy(top_iph, tmp_base, sizeof(tmp_base));
x->curlft.packets++; if (tmp_ext) {
spin_unlock_bh(&x->lock); memcpy(&top_iph->daddr, tmp_ext, extlen);
if (((*pskb)->dst = dst_pop(dst)) == NULL) {
err = -EHOSTUNREACH;
goto error_nolock;
}
return NET_XMIT_BYPASS;
error_free_iph: error_free_iph:
kfree(iph); kfree(tmp_ext);
}
error: error:
spin_unlock_bh(&x->lock);
error_nolock:
kfree_skb(*pskb);
return err; return err;
} }
......
...@@ -41,10 +41,10 @@ ...@@ -41,10 +41,10 @@
int esp6_output(struct sk_buff **pskb) int esp6_output(struct sk_buff **pskb)
{ {
int err; int err;
int hdr_len = 0; int hdr_len;
struct dst_entry *dst = (*pskb)->dst; struct dst_entry *dst = (*pskb)->dst;
struct xfrm_state *x = dst->xfrm; struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *iph = NULL, *top_iph; struct ipv6hdr *top_iph;
struct ipv6_esp_hdr *esph; struct ipv6_esp_hdr *esph;
struct crypto_tfm *tfm; struct crypto_tfm *tfm;
struct esp_data *esp; struct esp_data *esp;
...@@ -53,37 +53,13 @@ int esp6_output(struct sk_buff **pskb) ...@@ -53,37 +53,13 @@ int esp6_output(struct sk_buff **pskb)
int clen; int clen;
int alen; int alen;
int nfrags; int nfrags;
u8 *prevhdr;
u8 nexthdr = 0;
if ((*pskb)->ip_summed == CHECKSUM_HW) { esp = x->data;
err = skb_checksum_help(pskb, 0); hdr_len = (*pskb)->h.raw - (*pskb)->data +
if (err) sizeof(*esph) + esp->conf.ivlen;
goto error_nolock;
}
spin_lock_bh(&x->lock); /* Strip IP+ESP header. */
err = xfrm_state_check(x, *pskb); __skb_pull(*pskb, hdr_len);
if (err)
goto error;
if (x->props.mode) {
err = xfrm6_tunnel_check_size(*pskb);
if (err)
goto error;
} else {
/* Strip IP header in transport mode. Save it. */
hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
nexthdr = *prevhdr;
*prevhdr = IPPROTO_ESP;
iph = kmalloc(hdr_len, GFP_ATOMIC);
if (!iph) {
err = -ENOMEM;
goto error;
}
memcpy(iph, (*pskb)->nh.raw, hdr_len);
__skb_pull(*pskb, hdr_len);
}
/* Now skb is pure payload to encrypt */ /* Now skb is pure payload to encrypt */
err = -ENOMEM; err = -ENOMEM;
...@@ -91,7 +67,6 @@ int esp6_output(struct sk_buff **pskb) ...@@ -91,7 +67,6 @@ int esp6_output(struct sk_buff **pskb)
/* Round to block size */ /* Round to block size */
clen = (*pskb)->len; clen = (*pskb)->len;
esp = x->data;
alen = esp->auth.icv_trunc_len; alen = esp->auth.icv_trunc_len;
tfm = esp->conf.tfm; tfm = esp->conf.tfm;
blksize = (crypto_tfm_alg_blocksize(tfm) + 3) & ~3; blksize = (crypto_tfm_alg_blocksize(tfm) + 3) & ~3;
...@@ -100,7 +75,6 @@ int esp6_output(struct sk_buff **pskb) ...@@ -100,7 +75,6 @@ int esp6_output(struct sk_buff **pskb)
clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1); clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1);
if ((nfrags = skb_cow_data(*pskb, clen-(*pskb)->len+alen, &trailer)) < 0) { if ((nfrags = skb_cow_data(*pskb, clen-(*pskb)->len+alen, &trailer)) < 0) {
if (!x->props.mode && iph) kfree(iph);
goto error; goto error;
} }
...@@ -113,34 +87,11 @@ int esp6_output(struct sk_buff **pskb) ...@@ -113,34 +87,11 @@ int esp6_output(struct sk_buff **pskb)
*(u8*)(trailer->tail + clen-(*pskb)->len - 2) = (clen - (*pskb)->len)-2; *(u8*)(trailer->tail + clen-(*pskb)->len - 2) = (clen - (*pskb)->len)-2;
pskb_put(*pskb, trailer, clen - (*pskb)->len); pskb_put(*pskb, trailer, clen - (*pskb)->len);
if (x->props.mode) { top_iph = (struct ipv6hdr *)__skb_push(*pskb, hdr_len);
iph = (*pskb)->nh.ipv6h; esph = (struct ipv6_esp_hdr *)(*pskb)->h.raw;
top_iph = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len); top_iph->payload_len = htons((*pskb)->len + alen - sizeof(*top_iph));
esph = (struct ipv6_esp_hdr*)(top_iph+1); *(u8*)(trailer->tail - 1) = *(*pskb)->nh.raw;
*(u8*)(trailer->tail - 1) = IPPROTO_IPV6; *(*pskb)->nh.raw = IPPROTO_ESP;
top_iph->version = 6;
top_iph->priority = iph->priority;
top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2];
if (x->props.flags & XFRM_STATE_NOECN)
IP6_ECN_clear(top_iph);
top_iph->nexthdr = IPPROTO_ESP;
top_iph->payload_len = htons((*pskb)->len + alen - sizeof(struct ipv6hdr));
top_iph->hop_limit = iph->hop_limit;
ipv6_addr_copy(&top_iph->saddr,
(struct in6_addr *)&x->props.saddr);
ipv6_addr_copy(&top_iph->daddr,
(struct in6_addr *)&x->id.daddr);
} else {
esph = (struct ipv6_esp_hdr*)skb_push(*pskb, x->props.header_len);
(*pskb)->h.raw = (unsigned char*)esph;
top_iph = (struct ipv6hdr*)skb_push(*pskb, hdr_len);
memcpy(top_iph, iph, hdr_len);
kfree(iph);
top_iph->payload_len = htons((*pskb)->len + alen - sizeof(struct ipv6hdr));
*(u8*)(trailer->tail - 1) = nexthdr;
}
esph->spi = x->id.spi; esph->spi = x->id.spi;
esph->seq_no = htonl(++x->replay.oseq); esph->seq_no = htonl(++x->replay.oseq);
...@@ -173,21 +124,9 @@ int esp6_output(struct sk_buff **pskb) ...@@ -173,21 +124,9 @@ int esp6_output(struct sk_buff **pskb)
pskb_put(*pskb, trailer, alen); pskb_put(*pskb, trailer, alen);
} }
(*pskb)->nh.raw = (*pskb)->data; err = 0;
x->curlft.bytes += (*pskb)->len;
x->curlft.packets++;
spin_unlock_bh(&x->lock);
if (((*pskb)->dst = dst_pop(dst)) == NULL) {
err = -EHOSTUNREACH;
goto error_nolock;
}
return NET_XMIT_BYPASS;
error: error:
spin_unlock_bh(&x->lock);
error_nolock:
kfree_skb(*pskb);
return err; return err;
} }
......
...@@ -120,52 +120,14 @@ static int ipcomp6_output(struct sk_buff **pskb) ...@@ -120,52 +120,14 @@ static int ipcomp6_output(struct sk_buff **pskb)
int err; int err;
struct dst_entry *dst = (*pskb)->dst; struct dst_entry *dst = (*pskb)->dst;
struct xfrm_state *x = dst->xfrm; struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *iph, *top_iph; struct ipv6hdr *top_iph;
int hdr_len = 0; int hdr_len;
struct ipv6_comp_hdr *ipch; struct ipv6_comp_hdr *ipch;
struct ipcomp_data *ipcd = x->data; struct ipcomp_data *ipcd = x->data;
u8 *prevhdr;
u8 nexthdr = 0;
int plen, dlen; int plen, dlen;
u8 *start, *scratch = ipcd->scratch; u8 *start, *scratch = ipcd->scratch;
if ((*pskb)->ip_summed == CHECKSUM_HW) { hdr_len = (*pskb)->h.raw - (*pskb)->data;
err = skb_checksum_help(pskb, 0);
if (err)
goto error_nolock;
}
spin_lock_bh(&x->lock);
err = xfrm_state_check(x, *pskb);
if (err)
goto error;
if (x->props.mode) {
err = xfrm6_tunnel_check_size(*pskb);
if (err)
goto error;
hdr_len = sizeof(struct ipv6hdr);
nexthdr = IPPROTO_IPV6;
iph = (*pskb)->nh.ipv6h;
top_iph = (struct ipv6hdr *)skb_push(*pskb, sizeof(struct ipv6hdr));
top_iph->version = 6;
top_iph->priority = iph->priority;
top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2];
top_iph->nexthdr = IPPROTO_IPV6; /* initial */
top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
top_iph->hop_limit = iph->hop_limit;
memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr));
memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr));
(*pskb)->nh.raw = (*pskb)->data; /* == top_iph */
(*pskb)->h.raw = (*pskb)->nh.raw + hdr_len;
} else {
hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
nexthdr = *prevhdr;
}
/* check whether datagram len is larger than threshold */ /* check whether datagram len is larger than threshold */
if (((*pskb)->len - hdr_len) < ipcd->threshold) { if (((*pskb)->len - hdr_len) < ipcd->threshold) {
...@@ -181,7 +143,7 @@ static int ipcomp6_output(struct sk_buff **pskb) ...@@ -181,7 +143,7 @@ static int ipcomp6_output(struct sk_buff **pskb)
/* compression */ /* compression */
plen = (*pskb)->len - hdr_len; plen = (*pskb)->len - hdr_len;
dlen = IPCOMP_SCRATCH_SIZE; dlen = IPCOMP_SCRATCH_SIZE;
start = (*pskb)->data + hdr_len; start = (*pskb)->h.raw;
err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen); err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen);
if (err) { if (err) {
...@@ -194,39 +156,21 @@ static int ipcomp6_output(struct sk_buff **pskb) ...@@ -194,39 +156,21 @@ static int ipcomp6_output(struct sk_buff **pskb)
pskb_trim(*pskb, hdr_len + dlen + sizeof(struct ip_comp_hdr)); pskb_trim(*pskb, hdr_len + dlen + sizeof(struct ip_comp_hdr));
/* insert ipcomp header and replace datagram */ /* insert ipcomp header and replace datagram */
top_iph = (*pskb)->nh.ipv6h; top_iph = (struct ipv6hdr *)(*pskb)->data;
if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN))
IP6_ECN_clear(top_iph);
top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr)); top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
(*pskb)->nh.raw = (*pskb)->data; /* top_iph */
ip6_find_1stfragopt(*pskb, &prevhdr);
*prevhdr = IPPROTO_COMP;
ipch = (struct ipv6_comp_hdr *)((unsigned char *)top_iph + hdr_len); ipch = (struct ipv6_comp_hdr *)start;
ipch->nexthdr = nexthdr; ipch->nexthdr = *(*pskb)->nh.raw;
ipch->flags = 0; ipch->flags = 0;
ipch->cpi = htons((u16 )ntohl(x->id.spi)); ipch->cpi = htons((u16 )ntohl(x->id.spi));
*(*pskb)->nh.raw = IPPROTO_COMP;
(*pskb)->h.raw = (unsigned char*)ipch;
out_ok: out_ok:
x->curlft.bytes += (*pskb)->len; err = 0;
x->curlft.packets++;
spin_unlock_bh(&x->lock);
if (((*pskb)->dst = dst_pop(dst)) == NULL) {
err = -EHOSTUNREACH;
goto error_nolock;
}
err = NET_XMIT_BYPASS;
out_exit:
return err;
error: error:
spin_unlock_bh(&x->lock); return err;
error_nolock:
kfree_skb(*pskb);
goto out_exit;
} }
static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
......
/*
* xfrm6_output.c - Common IPsec encapsulation code for IPv6.
* Copyright (C) 2002 USAGI/WIDE Project
* Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <net/inet_ecn.h>
#include <net/ipv6.h>
#include <net/xfrm.h>
/* Add encapsulation header.
*
* In transport mode, the IP header and mutable extension headers will be moved
* forward to make space for the encapsulation header.
*
* In tunnel mode, the top IP header will be constructed per RFC 2401.
* The following fields in it shall be filled in by x->type->output:
* payload_len
*
* On exit, skb->h will be set to the start of the encapsulation header to be
* filled in by x->type->output and skb->nh will be set to the nextheader field
* of the extension header directly preceding the encapsulation header, or in
* its absence, that of the top IP header. The value of skb->data will always
* point to the top IP header.
*/
static void xfrm6_encap(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *iph, *top_iph;
skb_push(skb, x->props.header_len);
iph = skb->nh.ipv6h;
if (!x->props.mode) {
u8 *prevhdr;
int hdr_len;
hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
skb->nh.raw = prevhdr - x->props.header_len;
skb->h.raw = skb->data + hdr_len;
memmove(skb->data, iph, hdr_len);
return;
}
skb->nh.raw = skb->data;
top_iph = skb->nh.ipv6h;
skb->nh.raw = &top_iph->nexthdr;
skb->h.ipv6h = top_iph + 1;
top_iph->version = 6;
top_iph->priority = iph->priority;
if (x->props.flags & XFRM_STATE_NOECN)
IP6_ECN_clear(top_iph);
top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2];
top_iph->nexthdr = IPPROTO_IPV6;
top_iph->hop_limit = iph->hop_limit;
ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
}
int xfrm6_output(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
int err;
if (skb->ip_summed == CHECKSUM_HW) {
err = skb_checksum_help(pskb, 0);
skb = *pskb;
if (err)
goto error_nolock;
}
spin_lock_bh(&x->lock);
err = xfrm_state_check(x, skb);
if (err)
goto error;
if (x->props.mode) {
err = xfrm6_tunnel_check_size(skb);
if (err)
goto error;
}
xfrm6_encap(skb);
err = x->type->output(pskb);
skb = *pskb;
if (err)
goto error;
x->curlft.bytes += skb->len;
x->curlft.packets++;
spin_unlock_bh(&x->lock);
skb->nh.raw = skb->data;
if (!(skb->dst = dst_pop(dst))) {
err = -EHOSTUNREACH;
goto error_nolock;
}
err = NET_XMIT_BYPASS;
out_exit:
return err;
error:
spin_unlock_bh(&x->lock);
error_nolock:
kfree_skb(skb);
goto out_exit;
}
...@@ -157,7 +157,7 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int ...@@ -157,7 +157,7 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int
/* Copy neighbour for reachability confirmation */ /* Copy neighbour for reachability confirmation */
dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour);
dst_prev->input = rt->u.dst.input; dst_prev->input = rt->u.dst.input;
dst_prev->output = dst_prev->xfrm->type->output; dst_prev->output = xfrm6_output;
/* Sheit... I remember I did this right. Apparently, /* Sheit... I remember I did this right. Apparently,
* it was magically lost, so this code needs audit */ * it was magically lost, so this code needs audit */
x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
......
...@@ -365,46 +365,12 @@ EXPORT_SYMBOL(xfrm6_tunnel_check_size); ...@@ -365,46 +365,12 @@ EXPORT_SYMBOL(xfrm6_tunnel_check_size);
static int xfrm6_tunnel_output(struct sk_buff **pskb) static int xfrm6_tunnel_output(struct sk_buff **pskb)
{ {
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
struct dst_entry *dst = skb->dst; struct ipv6hdr *top_iph;
struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *iph, *top_iph;
int err;
if ((err = xfrm6_tunnel_check_size(skb)) != 0)
goto error_nolock;
iph = skb->nh.ipv6h;
top_iph = (struct ipv6hdr *)skb_push(skb, x->props.header_len);
top_iph->version = 6;
top_iph->priority = iph->priority;
top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2];
top_iph->nexthdr = IPPROTO_IPV6;
top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
top_iph->hop_limit = iph->hop_limit;
memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr));
memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr));
skb->nh.raw = skb->data;
skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
x->curlft.bytes += skb->len;
x->curlft.packets++;
spin_unlock_bh(&x->lock);
if ((skb->dst = dst_pop(dst)) == NULL) { top_iph = (struct ipv6hdr *)skb->data;
kfree_skb(skb); top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
err = -EHOSTUNREACH;
goto error_nolock;
}
return NET_XMIT_BYPASS;
error_nolock: return 0;
kfree_skb(skb);
return err;
} }
static int xfrm6_tunnel_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) static int xfrm6_tunnel_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
......
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