aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-11-14 00:40:52 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:53:45 -0500
commit36cf9acf93e8561d9faec24849e57688a81eb9c5 (patch)
treefd4fab84132f1784a23441e03c4a0b9aea6cc43a
parent29bb43b4ec4e625b0659186fc8a7c8f8b7c81982 (diff)
[IPSEC]: Separate inner/outer mode processing on output
With inter-family transforms the inner mode differs from the outer mode. Attempting to handle both sides from the same function means that it needs to handle both IPv4 and IPv6 which creates duplication and confusion. This patch separates the two parts on the output path so that each function deals with one family only. In particular, the functions xfrm4_extract_output/xfrm6_extract_output moves the pertinent fields from the IPv4/IPv6 IP headers into a neutral format stored in skb->cb. This is then used by the outer mode output functions to write the outer IP header. In this way the output function no longer has to know about the inner address family. Since the extract functions are only called by tunnel modes (the only modes that can support inter-family transforms), I've also moved the xfrm*_tunnel_check_size calls into them. This allows the correct ICMP message to be sent as opposed to now where you might call icmp_send with an IPv6 packet and vice versa. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/xfrm.h51
-rw-r--r--net/ipv4/xfrm4_mode_beet.c15
-rw-r--r--net/ipv4/xfrm4_mode_tunnel.c37
-rw-r--r--net/ipv4/xfrm4_output.c41
-rw-r--r--net/ipv4/xfrm4_state.c17
-rw-r--r--net/ipv6/xfrm6_mode_beet.c28
-rw-r--r--net/ipv6/xfrm6_mode_tunnel.c31
-rw-r--r--net/ipv6/xfrm6_output.c39
-rw-r--r--net/ipv6/xfrm6_state.c18
9 files changed, 193 insertions, 84 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index c9345fe3f8d2..138c1868be1d 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -257,6 +257,7 @@ extern int __xfrm_state_delete(struct xfrm_state *x);
257 257
258struct xfrm_state_afinfo { 258struct xfrm_state_afinfo {
259 unsigned int family; 259 unsigned int family;
260 unsigned int proto;
260 struct module *owner; 261 struct module *owner;
261 struct xfrm_type *type_map[IPPROTO_MAX]; 262 struct xfrm_type *type_map[IPPROTO_MAX];
262 struct xfrm_mode *mode_map[XFRM_MODE_MAX]; 263 struct xfrm_mode *mode_map[XFRM_MODE_MAX];
@@ -267,6 +268,8 @@ struct xfrm_state_afinfo {
267 int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); 268 int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n);
268 int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); 269 int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n);
269 int (*output)(struct sk_buff *skb); 270 int (*output)(struct sk_buff *skb);
271 int (*extract_output)(struct xfrm_state *x,
272 struct sk_buff *skb);
270}; 273};
271 274
272extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo); 275extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
@@ -312,7 +315,18 @@ struct xfrm_mode {
312 * header. The value of the network header will always point 315 * header. The value of the network header will always point
313 * to the top IP header while skb->data will point to the payload. 316 * to the top IP header while skb->data will point to the payload.
314 */ 317 */
315 int (*output)(struct xfrm_state *x,struct sk_buff *skb); 318 int (*output2)(struct xfrm_state *x,struct sk_buff *skb);
319
320 /*
321 * This is the actual output entry point.
322 *
323 * For transport mode and equivalent this would be identical to
324 * output2 (which does not need to be set). While tunnel mode
325 * and equivalent would set this to a tunnel encapsulation function
326 * (xfrm4_prepare_output or xfrm6_prepare_output) that would in turn
327 * call output2.
328 */
329 int (*output)(struct xfrm_state *x, struct sk_buff *skb);
316 330
317 struct xfrm_state_afinfo *afinfo; 331 struct xfrm_state_afinfo *afinfo;
318 struct module *owner; 332 struct module *owner;
@@ -454,6 +468,35 @@ struct xfrm_skb_cb {
454 468
455#define XFRM_SKB_CB(__skb) ((struct xfrm_skb_cb *)&((__skb)->cb[0])) 469#define XFRM_SKB_CB(__skb) ((struct xfrm_skb_cb *)&((__skb)->cb[0]))
456 470
471/*
472 * This structure is used by the afinfo prepare_input/prepare_output functions
473 * to transmit header information to the mode input/output functions.
474 */
475struct xfrm_mode_skb_cb {
476 union {
477 struct inet_skb_parm h4;
478 struct inet6_skb_parm h6;
479 } header;
480
481 /* Copied from header for IPv4, always set to zero and DF for IPv6. */
482 __be16 id;
483 __be16 frag_off;
484
485 /* TOS for IPv4, class for IPv6. */
486 u8 tos;
487
488 /* TTL for IPv4, hop limitfor IPv6. */
489 u8 ttl;
490
491 /* Protocol for IPv4, NH for IPv6. */
492 u8 protocol;
493
494 /* Used by IPv6 only, zero for IPv4. */
495 u8 flow_lbl[3];
496};
497
498#define XFRM_MODE_SKB_CB(__skb) ((struct xfrm_mode_skb_cb *)&((__skb)->cb[0]))
499
457/* Audit Information */ 500/* Audit Information */
458struct xfrm_audit 501struct xfrm_audit
459{ 502{
@@ -1051,6 +1094,7 @@ extern void xfrm_replay_notify(struct xfrm_state *x, int event);
1051extern int xfrm_state_mtu(struct xfrm_state *x, int mtu); 1094extern int xfrm_state_mtu(struct xfrm_state *x, int mtu);
1052extern int xfrm_init_state(struct xfrm_state *x); 1095extern int xfrm_init_state(struct xfrm_state *x);
1053extern int xfrm_output(struct sk_buff *skb); 1096extern int xfrm_output(struct sk_buff *skb);
1097extern int xfrm4_extract_header(struct sk_buff *skb);
1054extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, 1098extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
1055 int encap_type); 1099 int encap_type);
1056extern int xfrm4_rcv(struct sk_buff *skb); 1100extern int xfrm4_rcv(struct sk_buff *skb);
@@ -1060,9 +1104,12 @@ static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
1060 return xfrm4_rcv_encap(skb, nexthdr, spi, 0); 1104 return xfrm4_rcv_encap(skb, nexthdr, spi, 0);
1061} 1105}
1062 1106
1107extern int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
1108extern int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
1063extern int xfrm4_output(struct sk_buff *skb); 1109extern int xfrm4_output(struct sk_buff *skb);
1064extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); 1110extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
1065extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); 1111extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
1112extern int xfrm6_extract_header(struct sk_buff *skb);
1066extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi); 1113extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
1067extern int xfrm6_rcv(struct sk_buff *skb); 1114extern int xfrm6_rcv(struct sk_buff *skb);
1068extern int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, 1115extern int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
@@ -1072,6 +1119,8 @@ extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short
1072extern __be32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr); 1119extern __be32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr);
1073extern void xfrm6_tunnel_free_spi(xfrm_address_t *saddr); 1120extern void xfrm6_tunnel_free_spi(xfrm_address_t *saddr);
1074extern __be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr); 1121extern __be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr);
1122extern int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb);
1123extern int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
1075extern int xfrm6_output(struct sk_buff *skb); 1124extern int xfrm6_output(struct sk_buff *skb);
1076extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb, 1125extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
1077 u8 **prevhdr); 1126 u8 **prevhdr);
diff --git a/net/ipv4/xfrm4_mode_beet.c b/net/ipv4/xfrm4_mode_beet.c
index e42e122414be..94842adce144 100644
--- a/net/ipv4/xfrm4_mode_beet.c
+++ b/net/ipv4/xfrm4_mode_beet.c
@@ -43,7 +43,17 @@ static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
43 ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iph) - hdrlen); 43 ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iph) - hdrlen);
44 44
45 top_iph = ip_hdr(skb); 45 top_iph = ip_hdr(skb);
46 memmove(top_iph, iph, sizeof(*iph)); 46
47 top_iph->ihl = 5;
48 top_iph->version = 4;
49
50 top_iph->protocol = XFRM_MODE_SKB_CB(skb)->protocol;
51 top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
52
53 top_iph->id = XFRM_MODE_SKB_CB(skb)->id;
54 top_iph->frag_off = XFRM_MODE_SKB_CB(skb)->frag_off;
55 top_iph->ttl = XFRM_MODE_SKB_CB(skb)->ttl;
56
47 if (unlikely(optlen)) { 57 if (unlikely(optlen)) {
48 BUG_ON(optlen < 0); 58 BUG_ON(optlen < 0);
49 59
@@ -111,7 +121,8 @@ out:
111 121
112static struct xfrm_mode xfrm4_beet_mode = { 122static struct xfrm_mode xfrm4_beet_mode = {
113 .input = xfrm4_beet_input, 123 .input = xfrm4_beet_input,
114 .output = xfrm4_beet_output, 124 .output2 = xfrm4_beet_output,
125 .output = xfrm4_prepare_output,
115 .owner = THIS_MODULE, 126 .owner = THIS_MODULE,
116 .encap = XFRM_MODE_BEET, 127 .encap = XFRM_MODE_BEET,
117 .flags = XFRM_MODE_FLAG_TUNNEL, 128 .flags = XFRM_MODE_FLAG_TUNNEL,
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index 68a9f56ff09f..cc8bbb274e37 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -36,53 +36,37 @@ static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
36static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) 36static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
37{ 37{
38 struct dst_entry *dst = skb->dst; 38 struct dst_entry *dst = skb->dst;
39 struct xfrm_dst *xdst = (struct xfrm_dst*)dst; 39 struct iphdr *top_iph;
40 struct iphdr *iph, *top_iph;
41 int flags; 40 int flags;
42 41
43 iph = ip_hdr(skb);
44
45 skb_set_network_header(skb, -x->props.header_len); 42 skb_set_network_header(skb, -x->props.header_len);
46 skb->mac_header = skb->network_header + 43 skb->mac_header = skb->network_header +
47 offsetof(struct iphdr, protocol); 44 offsetof(struct iphdr, protocol);
48 skb->transport_header = skb->network_header + sizeof(*iph); 45 skb->transport_header = skb->network_header + sizeof(*top_iph);
49 top_iph = ip_hdr(skb); 46 top_iph = ip_hdr(skb);
50 47
51 top_iph->ihl = 5; 48 top_iph->ihl = 5;
52 top_iph->version = 4; 49 top_iph->version = 4;
53 50
54 flags = x->props.flags; 51 top_iph->protocol = x->inner_mode->afinfo->proto;
55 52
56 /* DS disclosed */ 53 /* DS disclosed */
57 if (xdst->route->ops->family == AF_INET) { 54 top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos,
58 top_iph->protocol = IPPROTO_IPIP; 55 XFRM_MODE_SKB_CB(skb)->tos);
59 top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos);
60 top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
61 0 : (iph->frag_off & htons(IP_DF));
62 }
63#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
64 else {
65 struct ipv6hdr *ipv6h = (struct ipv6hdr*)iph;
66 top_iph->protocol = IPPROTO_IPV6;
67 top_iph->tos = INET_ECN_encapsulate(iph->tos, ipv6_get_dsfield(ipv6h));
68 top_iph->frag_off = 0;
69 }
70#endif
71 56
57 flags = x->props.flags;
72 if (flags & XFRM_STATE_NOECN) 58 if (flags & XFRM_STATE_NOECN)
73 IP_ECN_clear(top_iph); 59 IP_ECN_clear(top_iph);
74 60
75 if (!top_iph->frag_off) 61 top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
76 __ip_select_ident(top_iph, dst->child, 0); 62 0 : XFRM_MODE_SKB_CB(skb)->frag_off;
63 ip_select_ident(top_iph, dst->child, NULL);
77 64
78 top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT); 65 top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT);
79 66
80 top_iph->saddr = x->props.saddr.a4; 67 top_iph->saddr = x->props.saddr.a4;
81 top_iph->daddr = x->id.daddr.a4; 68 top_iph->daddr = x->id.daddr.a4;
82 69
83 skb->protocol = htons(ETH_P_IP);
84
85 memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
86 return 0; 70 return 0;
87} 71}
88 72
@@ -136,7 +120,8 @@ out:
136 120
137static struct xfrm_mode xfrm4_tunnel_mode = { 121static struct xfrm_mode xfrm4_tunnel_mode = {
138 .input = xfrm4_tunnel_input, 122 .input = xfrm4_tunnel_input,
139 .output = xfrm4_tunnel_output, 123 .output2 = xfrm4_tunnel_output,
124 .output = xfrm4_prepare_output,
140 .owner = THIS_MODULE, 125 .owner = THIS_MODULE,
141 .encap = XFRM_MODE_TUNNEL, 126 .encap = XFRM_MODE_TUNNEL,
142 .flags = XFRM_MODE_FLAG_TUNNEL, 127 .flags = XFRM_MODE_FLAG_TUNNEL,
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index c4a7156962bd..13fd11335e28 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -8,11 +8,12 @@
8 * 2 of the License, or (at your option) any later version. 8 * 2 of the License, or (at your option) any later version.
9 */ 9 */
10 10
11#include <linux/compiler.h>
12#include <linux/if_ether.h> 11#include <linux/if_ether.h>
13#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/skbuff.h> 14#include <linux/skbuff.h>
15#include <linux/netfilter_ipv4.h> 15#include <linux/netfilter_ipv4.h>
16#include <net/dst.h>
16#include <net/ip.h> 17#include <net/ip.h>
17#include <net/xfrm.h> 18#include <net/xfrm.h>
18#include <net/icmp.h> 19#include <net/icmp.h>
@@ -25,8 +26,6 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
25 if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE) 26 if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
26 goto out; 27 goto out;
27 28
28 IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
29
30 if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df) 29 if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df)
31 goto out; 30 goto out;
32 31
@@ -40,19 +39,39 @@ out:
40 return ret; 39 return ret;
41} 40}
42 41
42int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb)
43{
44 int err;
45
46 err = xfrm4_tunnel_check_size(skb);
47 if (err)
48 return err;
49
50 return xfrm4_extract_header(skb);
51}
52
53int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
54{
55 int err;
56
57 err = x->inner_mode->afinfo->extract_output(x, skb);
58 if (err)
59 return err;
60
61 memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
62 IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
63
64 skb->protocol = htons(ETH_P_IP);
65
66 return x->outer_mode->output2(x, skb);
67}
68EXPORT_SYMBOL(xfrm4_prepare_output);
69
43static inline int xfrm4_output_one(struct sk_buff *skb) 70static inline int xfrm4_output_one(struct sk_buff *skb)
44{ 71{
45 struct dst_entry *dst = skb->dst;
46 struct xfrm_state *x = dst->xfrm;
47 struct iphdr *iph; 72 struct iphdr *iph;
48 int err; 73 int err;
49 74
50 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
51 err = xfrm4_tunnel_check_size(skb);
52 if (err)
53 goto error_nolock;
54 }
55
56 err = xfrm_output(skb); 75 err = xfrm_output(skb);
57 if (err) 76 if (err)
58 goto error_nolock; 77 goto error_nolock;
diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c
index 13d54a1c3337..e6030e74ff65 100644
--- a/net/ipv4/xfrm4_state.c
+++ b/net/ipv4/xfrm4_state.c
@@ -47,12 +47,29 @@ __xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl,
47 x->props.family = AF_INET; 47 x->props.family = AF_INET;
48} 48}
49 49
50int xfrm4_extract_header(struct sk_buff *skb)
51{
52 struct iphdr *iph = ip_hdr(skb);
53
54 XFRM_MODE_SKB_CB(skb)->id = iph->id;
55 XFRM_MODE_SKB_CB(skb)->frag_off = iph->frag_off;
56 XFRM_MODE_SKB_CB(skb)->tos = iph->tos;
57 XFRM_MODE_SKB_CB(skb)->ttl = iph->ttl;
58 XFRM_MODE_SKB_CB(skb)->protocol = iph->protocol;
59 memset(XFRM_MODE_SKB_CB(skb)->flow_lbl, 0,
60 sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl));
61
62 return 0;
63}
64
50static struct xfrm_state_afinfo xfrm4_state_afinfo = { 65static struct xfrm_state_afinfo xfrm4_state_afinfo = {
51 .family = AF_INET, 66 .family = AF_INET,
67 .proto = IPPROTO_IPIP,
52 .owner = THIS_MODULE, 68 .owner = THIS_MODULE,
53 .init_flags = xfrm4_init_flags, 69 .init_flags = xfrm4_init_flags,
54 .init_tempsel = __xfrm4_init_tempsel, 70 .init_tempsel = __xfrm4_init_tempsel,
55 .output = xfrm4_output, 71 .output = xfrm4_output,
72 .extract_output = xfrm4_extract_output,
56}; 73};
57 74
58void __init xfrm4_state_init(void) 75void __init xfrm4_state_init(void)
diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c
index 2bfb4f05c14c..4988ed9c76c6 100644
--- a/net/ipv6/xfrm6_mode_beet.c
+++ b/net/ipv6/xfrm6_mode_beet.c
@@ -25,25 +25,24 @@
25 */ 25 */
26static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb) 26static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb)
27{ 27{
28 struct ipv6hdr *iph, *top_iph; 28 struct ipv6hdr *top_iph;
29 u8 *prevhdr;
30 int hdr_len;
31 29
32 iph = ipv6_hdr(skb);
33
34 hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
35
36 skb_set_mac_header(skb, (prevhdr - x->props.header_len) - skb->data);
37 skb_set_network_header(skb, -x->props.header_len); 30 skb_set_network_header(skb, -x->props.header_len);
38 skb->transport_header = skb->network_header + hdr_len; 31 skb->mac_header = skb->network_header +
39 __skb_pull(skb, hdr_len); 32 offsetof(struct ipv6hdr, nexthdr);
40 33 skb->transport_header = skb->network_header + sizeof(*top_iph);
41 top_iph = ipv6_hdr(skb); 34 top_iph = ipv6_hdr(skb);
42 memmove(top_iph, iph, hdr_len);
43 35
36 top_iph->version = 6;
37
38 memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
39 sizeof(top_iph->flow_lbl));
40 top_iph->nexthdr = XFRM_MODE_SKB_CB(skb)->protocol;
41
42 ipv6_change_dsfield(top_iph, 0, XFRM_MODE_SKB_CB(skb)->tos);
43 top_iph->hop_limit = XFRM_MODE_SKB_CB(skb)->ttl;
44 ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); 44 ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
45 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); 45 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
46
47 return 0; 46 return 0;
48} 47}
49 48
@@ -76,7 +75,8 @@ out:
76 75
77static struct xfrm_mode xfrm6_beet_mode = { 76static struct xfrm_mode xfrm6_beet_mode = {
78 .input = xfrm6_beet_input, 77 .input = xfrm6_beet_input,
79 .output = xfrm6_beet_output, 78 .output2 = xfrm6_beet_output,
79 .output = xfrm6_prepare_output,
80 .owner = THIS_MODULE, 80 .owner = THIS_MODULE,
81 .encap = XFRM_MODE_BEET, 81 .encap = XFRM_MODE_BEET,
82 .flags = XFRM_MODE_FLAG_TUNNEL, 82 .flags = XFRM_MODE_FLAG_TUNNEL,
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index 9a43ea722481..d45ce5d44197 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -38,33 +38,22 @@ static inline void ip6ip_ecn_decapsulate(struct sk_buff *skb)
38static int xfrm6_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) 38static int xfrm6_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
39{ 39{
40 struct dst_entry *dst = skb->dst; 40 struct dst_entry *dst = skb->dst;
41 struct xfrm_dst *xdst = (struct xfrm_dst*)dst; 41 struct ipv6hdr *top_iph;
42 struct ipv6hdr *iph, *top_iph;
43 int dsfield; 42 int dsfield;
44 43
45 iph = ipv6_hdr(skb);
46
47 skb_set_network_header(skb, -x->props.header_len); 44 skb_set_network_header(skb, -x->props.header_len);
48 skb->mac_header = skb->network_header + 45 skb->mac_header = skb->network_header +
49 offsetof(struct ipv6hdr, nexthdr); 46 offsetof(struct ipv6hdr, nexthdr);
50 skb->transport_header = skb->network_header + sizeof(*iph); 47 skb->transport_header = skb->network_header + sizeof(*top_iph);
51 top_iph = ipv6_hdr(skb); 48 top_iph = ipv6_hdr(skb);
52 49
53 top_iph->version = 6; 50 top_iph->version = 6;
54 if (xdst->route->ops->family == AF_INET6) { 51
55 top_iph->priority = iph->priority; 52 memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
56 top_iph->flow_lbl[0] = iph->flow_lbl[0]; 53 sizeof(top_iph->flow_lbl));
57 top_iph->flow_lbl[1] = iph->flow_lbl[1]; 54 top_iph->nexthdr = x->inner_mode->afinfo->proto;
58 top_iph->flow_lbl[2] = iph->flow_lbl[2]; 55
59 top_iph->nexthdr = IPPROTO_IPV6; 56 dsfield = XFRM_MODE_SKB_CB(skb)->tos;
60 } else {
61 top_iph->priority = 0;
62 top_iph->flow_lbl[0] = 0;
63 top_iph->flow_lbl[1] = 0;
64 top_iph->flow_lbl[2] = 0;
65 top_iph->nexthdr = IPPROTO_IPIP;
66 }
67 dsfield = ipv6_get_dsfield(top_iph);
68 dsfield = INET_ECN_encapsulate(dsfield, dsfield); 57 dsfield = INET_ECN_encapsulate(dsfield, dsfield);
69 if (x->props.flags & XFRM_STATE_NOECN) 58 if (x->props.flags & XFRM_STATE_NOECN)
70 dsfield &= ~INET_ECN_MASK; 59 dsfield &= ~INET_ECN_MASK;
@@ -72,7 +61,6 @@ static int xfrm6_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
72 top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT); 61 top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT);
73 ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); 62 ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
74 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); 63 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
75 skb->protocol = htons(ETH_P_IPV6);
76 return 0; 64 return 0;
77} 65}
78 66
@@ -116,7 +104,8 @@ out:
116 104
117static struct xfrm_mode xfrm6_tunnel_mode = { 105static struct xfrm_mode xfrm6_tunnel_mode = {
118 .input = xfrm6_tunnel_input, 106 .input = xfrm6_tunnel_input,
119 .output = xfrm6_tunnel_output, 107 .output2 = xfrm6_tunnel_output,
108 .output = xfrm6_prepare_output,
120 .owner = THIS_MODULE, 109 .owner = THIS_MODULE,
121 .encap = XFRM_MODE_TUNNEL, 110 .encap = XFRM_MODE_TUNNEL,
122 .flags = XFRM_MODE_FLAG_TUNNEL, 111 .flags = XFRM_MODE_FLAG_TUNNEL,
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 656976760ad4..bc2e80e3b0b1 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -10,10 +10,12 @@
10 */ 10 */
11 11
12#include <linux/if_ether.h> 12#include <linux/if_ether.h>
13#include <linux/compiler.h> 13#include <linux/kernel.h>
14#include <linux/module.h>
14#include <linux/skbuff.h> 15#include <linux/skbuff.h>
15#include <linux/icmpv6.h> 16#include <linux/icmpv6.h>
16#include <linux/netfilter_ipv6.h> 17#include <linux/netfilter_ipv6.h>
18#include <net/dst.h>
17#include <net/ipv6.h> 19#include <net/ipv6.h>
18#include <net/xfrm.h> 20#include <net/xfrm.h>
19 21
@@ -43,19 +45,38 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
43 return ret; 45 return ret;
44} 46}
45 47
48int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb)
49{
50 int err;
51
52 err = xfrm6_tunnel_check_size(skb);
53 if (err)
54 return err;
55
56 return xfrm6_extract_header(skb);
57}
58
59int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
60{
61 int err;
62
63 err = x->inner_mode->afinfo->extract_output(x, skb);
64 if (err)
65 return err;
66
67 memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
68
69 skb->protocol = htons(ETH_P_IPV6);
70
71 return x->outer_mode->output2(x, skb);
72}
73EXPORT_SYMBOL(xfrm6_prepare_output);
74
46static inline int xfrm6_output_one(struct sk_buff *skb) 75static inline int xfrm6_output_one(struct sk_buff *skb)
47{ 76{
48 struct dst_entry *dst = skb->dst;
49 struct xfrm_state *x = dst->xfrm;
50 struct ipv6hdr *iph; 77 struct ipv6hdr *iph;
51 int err; 78 int err;
52 79
53 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
54 err = xfrm6_tunnel_check_size(skb);
55 if (err)
56 goto error_nolock;
57 }
58
59 err = xfrm_output(skb); 80 err = xfrm_output(skb);
60 if (err) 81 if (err)
61 goto error_nolock; 82 goto error_nolock;
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c
index b392bee396f1..98b05f472322 100644
--- a/net/ipv6/xfrm6_state.c
+++ b/net/ipv6/xfrm6_state.c
@@ -14,6 +14,7 @@
14#include <net/xfrm.h> 14#include <net/xfrm.h>
15#include <linux/pfkeyv2.h> 15#include <linux/pfkeyv2.h>
16#include <linux/ipsec.h> 16#include <linux/ipsec.h>
17#include <net/dsfield.h>
17#include <net/ipv6.h> 18#include <net/ipv6.h>
18#include <net/addrconf.h> 19#include <net/addrconf.h>
19 20
@@ -168,13 +169,30 @@ __xfrm6_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n)
168 return 0; 169 return 0;
169} 170}
170 171
172int xfrm6_extract_header(struct sk_buff *skb)
173{
174 struct ipv6hdr *iph = ipv6_hdr(skb);
175
176 XFRM_MODE_SKB_CB(skb)->id = 0;
177 XFRM_MODE_SKB_CB(skb)->frag_off = htons(IP_DF);
178 XFRM_MODE_SKB_CB(skb)->tos = ipv6_get_dsfield(iph);
179 XFRM_MODE_SKB_CB(skb)->ttl = iph->hop_limit;
180 XFRM_MODE_SKB_CB(skb)->protocol = iph->nexthdr;
181 memcpy(XFRM_MODE_SKB_CB(skb)->flow_lbl, iph->flow_lbl,
182 sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl));
183
184 return 0;
185}
186
171static struct xfrm_state_afinfo xfrm6_state_afinfo = { 187static struct xfrm_state_afinfo xfrm6_state_afinfo = {
172 .family = AF_INET6, 188 .family = AF_INET6,
189 .proto = IPPROTO_IPV6,
173 .owner = THIS_MODULE, 190 .owner = THIS_MODULE,
174 .init_tempsel = __xfrm6_init_tempsel, 191 .init_tempsel = __xfrm6_init_tempsel,
175 .tmpl_sort = __xfrm6_tmpl_sort, 192 .tmpl_sort = __xfrm6_tmpl_sort,
176 .state_sort = __xfrm6_state_sort, 193 .state_sort = __xfrm6_state_sort,
177 .output = xfrm6_output, 194 .output = xfrm6_output,
195 .extract_output = xfrm6_extract_output,
178}; 196};
179 197
180void __init xfrm6_state_init(void) 198void __init xfrm6_state_init(void)