aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-11-14 00:41:28 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:53:46 -0500
commit227620e295090629fcb2c46ad3828222ab65438d (patch)
tree534162a74628fdfa7a8201cb776a45ca07510e5b /net
parent36cf9acf93e8561d9faec24849e57688a81eb9c5 (diff)
[IPSEC]: Separate inner/outer mode processing on input
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 input path so that each function deals with one family only. In particular, the functions xfrm4_extract_inut/xfrm6_extract_inut moves the pertinent fields from the IPv4/IPv6 IP headers into a neutral format stored in skb->cb. This is then used by the inner mode input functions to modify the inner IP header. In this way the input function no longer has to know about the outer address family. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/xfrm4_input.c7
-rw-r--r--net/ipv4/xfrm4_mode_beet.c67
-rw-r--r--net/ipv4/xfrm4_mode_tunnel.c44
-rw-r--r--net/ipv4/xfrm4_state.c2
-rw-r--r--net/ipv6/xfrm6_input.c7
-rw-r--r--net/ipv6/xfrm6_mode_beet.c36
-rw-r--r--net/ipv6/xfrm6_mode_tunnel.c31
-rw-r--r--net/ipv6/xfrm6_output.c1
-rw-r--r--net/ipv6/xfrm6_state.c5
-rw-r--r--net/xfrm/xfrm_input.c13
10 files changed, 114 insertions, 99 deletions
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
index 5e95c8a07efb..c0323d05ab69 100644
--- a/net/ipv4/xfrm4_input.c
+++ b/net/ipv4/xfrm4_input.c
@@ -16,6 +16,11 @@
16#include <net/ip.h> 16#include <net/ip.h>
17#include <net/xfrm.h> 17#include <net/xfrm.h>
18 18
19int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb)
20{
21 return xfrm4_extract_header(skb);
22}
23
19#ifdef CONFIG_NETFILTER 24#ifdef CONFIG_NETFILTER
20static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb) 25static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb)
21{ 26{
@@ -91,7 +96,7 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
91 96
92 xfrm_vec[xfrm_nr++] = x; 97 xfrm_vec[xfrm_nr++] = x;
93 98
94 if (x->outer_mode->input(x, skb)) 99 if (x->inner_mode->input(x, skb))
95 goto drop; 100 goto drop;
96 101
97 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) { 102 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
diff --git a/net/ipv4/xfrm4_mode_beet.c b/net/ipv4/xfrm4_mode_beet.c
index 94842adce144..e093a7b59e18 100644
--- a/net/ipv4/xfrm4_mode_beet.c
+++ b/net/ipv4/xfrm4_mode_beet.c
@@ -17,6 +17,21 @@
17#include <net/ip.h> 17#include <net/ip.h>
18#include <net/xfrm.h> 18#include <net/xfrm.h>
19 19
20static void xfrm4_beet_make_header(struct sk_buff *skb)
21{
22 struct iphdr *iph = ip_hdr(skb);
23
24 iph->ihl = 5;
25 iph->version = 4;
26
27 iph->protocol = XFRM_MODE_SKB_CB(skb)->protocol;
28 iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
29
30 iph->id = XFRM_MODE_SKB_CB(skb)->id;
31 iph->frag_off = XFRM_MODE_SKB_CB(skb)->frag_off;
32 iph->ttl = XFRM_MODE_SKB_CB(skb)->ttl;
33}
34
20/* Add encapsulation header. 35/* Add encapsulation header.
21 * 36 *
22 * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt. 37 * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt.
@@ -40,20 +55,12 @@ static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
40 offsetof(struct iphdr, protocol); 55 offsetof(struct iphdr, protocol);
41 skb->transport_header = skb->network_header + sizeof(*iph); 56 skb->transport_header = skb->network_header + sizeof(*iph);
42 57
58 xfrm4_beet_make_header(skb);
59
43 ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iph) - hdrlen); 60 ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iph) - hdrlen);
44 61
45 top_iph = ip_hdr(skb); 62 top_iph = ip_hdr(skb);
46 63
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
57 if (unlikely(optlen)) { 64 if (unlikely(optlen)) {
58 BUG_ON(optlen < 0); 65 BUG_ON(optlen < 0);
59 66
@@ -75,43 +82,46 @@ static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
75 82
76static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb) 83static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb)
77{ 84{
78 struct iphdr *iph = ip_hdr(skb); 85 struct iphdr *iph;
79 int phlen = 0;
80 int optlen = 0; 86 int optlen = 0;
81 u8 ph_nexthdr = 0;
82 int err = -EINVAL; 87 int err = -EINVAL;
83 88
84 if (unlikely(iph->protocol == IPPROTO_BEETPH)) { 89 if (unlikely(XFRM_MODE_SKB_CB(skb)->protocol == IPPROTO_BEETPH)) {
85 struct ip_beet_phdr *ph; 90 struct ip_beet_phdr *ph;
91 int phlen;
86 92
87 if (!pskb_may_pull(skb, sizeof(*ph))) 93 if (!pskb_may_pull(skb, sizeof(*ph)))
88 goto out; 94 goto out;
89 ph = (struct ip_beet_phdr *)(ipip_hdr(skb) + 1); 95
96 ph = (struct ip_beet_phdr *)skb->data;
90 97
91 phlen = sizeof(*ph) + ph->padlen; 98 phlen = sizeof(*ph) + ph->padlen;
92 optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen); 99 optlen = ph->hdrlen * 8 + (IPV4_BEET_PHMAXLEN - phlen);
93 if (optlen < 0 || optlen & 3 || optlen > 250) 100 if (optlen < 0 || optlen & 3 || optlen > 250)
94 goto out; 101 goto out;
95 102
96 if (!pskb_may_pull(skb, phlen + optlen)) 103 XFRM_MODE_SKB_CB(skb)->protocol = ph->nexthdr;
97 goto out;
98 skb->len -= phlen + optlen;
99 104
100 ph_nexthdr = ph->nexthdr; 105 if (!pskb_may_pull(skb, phlen));
106 goto out;
107 __skb_pull(skb, phlen);
101 } 108 }
102 109
103 skb_set_network_header(skb, phlen - sizeof(*iph)); 110 skb_push(skb, sizeof(*iph));
104 memmove(skb_network_header(skb), iph, sizeof(*iph)); 111 skb_reset_network_header(skb);
105 skb_set_transport_header(skb, phlen + optlen); 112
106 skb->data = skb_transport_header(skb); 113 memmove(skb->data - skb->mac_len, skb_mac_header(skb),
114 skb->mac_len);
115 skb_set_mac_header(skb, -skb->mac_len);
116
117 xfrm4_beet_make_header(skb);
107 118
108 iph = ip_hdr(skb); 119 iph = ip_hdr(skb);
109 iph->ihl = (sizeof(*iph) + optlen) / 4; 120
110 iph->tot_len = htons(skb->len + iph->ihl * 4); 121 iph->ihl += optlen / 4;
122 iph->tot_len = htons(skb->len);
111 iph->daddr = x->sel.daddr.a4; 123 iph->daddr = x->sel.daddr.a4;
112 iph->saddr = x->sel.saddr.a4; 124 iph->saddr = x->sel.saddr.a4;
113 if (ph_nexthdr)
114 iph->protocol = ph_nexthdr;
115 iph->check = 0; 125 iph->check = 0;
116 iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); 126 iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl);
117 err = 0; 127 err = 0;
@@ -120,7 +130,8 @@ out:
120} 130}
121 131
122static struct xfrm_mode xfrm4_beet_mode = { 132static struct xfrm_mode xfrm4_beet_mode = {
123 .input = xfrm4_beet_input, 133 .input2 = xfrm4_beet_input,
134 .input = xfrm_prepare_input,
124 .output2 = xfrm4_beet_output, 135 .output2 = xfrm4_beet_output,
125 .output = xfrm4_prepare_output, 136 .output = xfrm4_prepare_output,
126 .owner = THIS_MODULE, 137 .owner = THIS_MODULE,
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index cc8bbb274e37..aa335dba8ffa 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -16,19 +16,12 @@
16 16
17static inline void ipip_ecn_decapsulate(struct sk_buff *skb) 17static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
18{ 18{
19 struct iphdr *outer_iph = ip_hdr(skb);
20 struct iphdr *inner_iph = ipip_hdr(skb); 19 struct iphdr *inner_iph = ipip_hdr(skb);
21 20
22 if (INET_ECN_is_ce(outer_iph->tos)) 21 if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
23 IP_ECN_set_ce(inner_iph); 22 IP_ECN_set_ce(inner_iph);
24} 23}
25 24
26static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
27{
28 if (INET_ECN_is_ce(iph->tos))
29 IP6_ECN_set_ce(ipv6_hdr(skb));
30}
31
32/* Add encapsulation header. 25/* Add encapsulation header.
33 * 26 *
34 * The top IP header will be constructed per RFC 2401. 27 * The top IP header will be constructed per RFC 2401.
@@ -72,20 +65,11 @@ static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
72 65
73static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) 66static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
74{ 67{
75 struct iphdr *iph = ip_hdr(skb);
76 const unsigned char *old_mac; 68 const unsigned char *old_mac;
77 int err = -EINVAL; 69 int err = -EINVAL;
78 70
79 switch (iph->protocol){ 71 if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
80 case IPPROTO_IPIP: 72 goto out;
81 break;
82#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
83 case IPPROTO_IPV6:
84 break;
85#endif
86 default:
87 goto out;
88 }
89 73
90 if (!pskb_may_pull(skb, sizeof(struct iphdr))) 74 if (!pskb_may_pull(skb, sizeof(struct iphdr)))
91 goto out; 75 goto out;
@@ -94,20 +78,11 @@ static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
94 (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) 78 (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
95 goto out; 79 goto out;
96 80
97 iph = ip_hdr(skb); 81 if (x->props.flags & XFRM_STATE_DECAP_DSCP)
98 if (iph->protocol == IPPROTO_IPIP) { 82 ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb));
99 if (x->props.flags & XFRM_STATE_DECAP_DSCP) 83 if (!(x->props.flags & XFRM_STATE_NOECN))
100 ipv4_copy_dscp(ipv4_get_dsfield(iph), ipip_hdr(skb)); 84 ipip_ecn_decapsulate(skb);
101 if (!(x->props.flags & XFRM_STATE_NOECN)) 85
102 ipip_ecn_decapsulate(skb);
103 }
104#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
105 else {
106 if (!(x->props.flags & XFRM_STATE_NOECN))
107 ipip6_ecn_decapsulate(iph, skb);
108 skb->protocol = htons(ETH_P_IPV6);
109 }
110#endif
111 old_mac = skb_mac_header(skb); 86 old_mac = skb_mac_header(skb);
112 skb_set_mac_header(skb, -skb->mac_len); 87 skb_set_mac_header(skb, -skb->mac_len);
113 memmove(skb_mac_header(skb), old_mac, skb->mac_len); 88 memmove(skb_mac_header(skb), old_mac, skb->mac_len);
@@ -119,7 +94,8 @@ out:
119} 94}
120 95
121static struct xfrm_mode xfrm4_tunnel_mode = { 96static struct xfrm_mode xfrm4_tunnel_mode = {
122 .input = xfrm4_tunnel_input, 97 .input2 = xfrm4_tunnel_input,
98 .input = xfrm_prepare_input,
123 .output2 = xfrm4_tunnel_output, 99 .output2 = xfrm4_tunnel_output,
124 .output = xfrm4_prepare_output, 100 .output = xfrm4_prepare_output,
125 .owner = THIS_MODULE, 101 .owner = THIS_MODULE,
diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c
index e6030e74ff65..85f04b7b237f 100644
--- a/net/ipv4/xfrm4_state.c
+++ b/net/ipv4/xfrm4_state.c
@@ -65,10 +65,12 @@ int xfrm4_extract_header(struct sk_buff *skb)
65static struct xfrm_state_afinfo xfrm4_state_afinfo = { 65static struct xfrm_state_afinfo xfrm4_state_afinfo = {
66 .family = AF_INET, 66 .family = AF_INET,
67 .proto = IPPROTO_IPIP, 67 .proto = IPPROTO_IPIP,
68 .eth_proto = htons(ETH_P_IP),
68 .owner = THIS_MODULE, 69 .owner = THIS_MODULE,
69 .init_flags = xfrm4_init_flags, 70 .init_flags = xfrm4_init_flags,
70 .init_tempsel = __xfrm4_init_tempsel, 71 .init_tempsel = __xfrm4_init_tempsel,
71 .output = xfrm4_output, 72 .output = xfrm4_output,
73 .extract_input = xfrm4_extract_input,
72 .extract_output = xfrm4_extract_output, 74 .extract_output = xfrm4_extract_output,
73}; 75};
74 76
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index 515783707e86..c458d0a2e684 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -16,6 +16,11 @@
16#include <net/ipv6.h> 16#include <net/ipv6.h>
17#include <net/xfrm.h> 17#include <net/xfrm.h>
18 18
19int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb)
20{
21 return xfrm6_extract_header(skb);
22}
23
19int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) 24int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
20{ 25{
21 int err; 26 int err;
@@ -68,7 +73,7 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
68 73
69 xfrm_vec[xfrm_nr++] = x; 74 xfrm_vec[xfrm_nr++] = x;
70 75
71 if (x->outer_mode->input(x, skb)) 76 if (x->inner_mode->input(x, skb))
72 goto drop; 77 goto drop;
73 78
74 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) { 79 if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c
index 4988ed9c76c6..0527d11c1ae3 100644
--- a/net/ipv6/xfrm6_mode_beet.c
+++ b/net/ipv6/xfrm6_mode_beet.c
@@ -19,6 +19,20 @@
19#include <net/ipv6.h> 19#include <net/ipv6.h>
20#include <net/xfrm.h> 20#include <net/xfrm.h>
21 21
22static void xfrm6_beet_make_header(struct sk_buff *skb)
23{
24 struct ipv6hdr *iph = ipv6_hdr(skb);
25
26 iph->version = 6;
27
28 memcpy(iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
29 sizeof(iph->flow_lbl));
30 iph->nexthdr = XFRM_MODE_SKB_CB(skb)->protocol;
31
32 ipv6_change_dsfield(iph, 0, XFRM_MODE_SKB_CB(skb)->tos);
33 iph->hop_limit = XFRM_MODE_SKB_CB(skb)->ttl;
34}
35
22/* Add encapsulation header. 36/* Add encapsulation header.
23 * 37 *
24 * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt. 38 * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt.
@@ -31,16 +45,11 @@ static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb)
31 skb->mac_header = skb->network_header + 45 skb->mac_header = skb->network_header +
32 offsetof(struct ipv6hdr, nexthdr); 46 offsetof(struct ipv6hdr, nexthdr);
33 skb->transport_header = skb->network_header + sizeof(*top_iph); 47 skb->transport_header = skb->network_header + sizeof(*top_iph);
34 top_iph = ipv6_hdr(skb);
35 48
36 top_iph->version = 6; 49 xfrm6_beet_make_header(skb);
37 50
38 memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl, 51 top_iph = ipv6_hdr(skb);
39 sizeof(top_iph->flow_lbl));
40 top_iph->nexthdr = XFRM_MODE_SKB_CB(skb)->protocol;
41 52
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); 53 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); 54 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
46 return 0; 55 return 0;
@@ -51,19 +60,21 @@ static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb)
51 struct ipv6hdr *ip6h; 60 struct ipv6hdr *ip6h;
52 const unsigned char *old_mac; 61 const unsigned char *old_mac;
53 int size = sizeof(struct ipv6hdr); 62 int size = sizeof(struct ipv6hdr);
54 int err = -EINVAL; 63 int err;
55 64
56 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 65 err = skb_cow_head(skb, size + skb->mac_len);
66 if (err)
57 goto out; 67 goto out;
58 68
59 skb_push(skb, size); 69 __skb_push(skb, size);
60 memmove(skb->data, skb_network_header(skb), size);
61 skb_reset_network_header(skb); 70 skb_reset_network_header(skb);
62 71
63 old_mac = skb_mac_header(skb); 72 old_mac = skb_mac_header(skb);
64 skb_set_mac_header(skb, -skb->mac_len); 73 skb_set_mac_header(skb, -skb->mac_len);
65 memmove(skb_mac_header(skb), old_mac, skb->mac_len); 74 memmove(skb_mac_header(skb), old_mac, skb->mac_len);
66 75
76 xfrm6_beet_make_header(skb);
77
67 ip6h = ipv6_hdr(skb); 78 ip6h = ipv6_hdr(skb);
68 ip6h->payload_len = htons(skb->len - size); 79 ip6h->payload_len = htons(skb->len - size);
69 ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6); 80 ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6);
@@ -74,7 +85,8 @@ out:
74} 85}
75 86
76static struct xfrm_mode xfrm6_beet_mode = { 87static struct xfrm_mode xfrm6_beet_mode = {
77 .input = xfrm6_beet_input, 88 .input2 = xfrm6_beet_input,
89 .input = xfrm_prepare_input,
78 .output2 = xfrm6_beet_output, 90 .output2 = xfrm6_beet_output,
79 .output = xfrm6_prepare_output, 91 .output = xfrm6_prepare_output,
80 .owner = THIS_MODULE, 92 .owner = THIS_MODULE,
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index d45ce5d44197..f7d0d6612650 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -25,12 +25,6 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
25 IP6_ECN_set_ce(inner_iph); 25 IP6_ECN_set_ce(inner_iph);
26} 26}
27 27
28static inline void ip6ip_ecn_decapsulate(struct sk_buff *skb)
29{
30 if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6_hdr(skb))))
31 IP_ECN_set_ce(ipip_hdr(skb));
32}
33
34/* Add encapsulation header. 28/* Add encapsulation header.
35 * 29 *
36 * The top IP header will be constructed per RFC 2401. 30 * The top IP header will be constructed per RFC 2401.
@@ -68,10 +62,8 @@ static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
68{ 62{
69 int err = -EINVAL; 63 int err = -EINVAL;
70 const unsigned char *old_mac; 64 const unsigned char *old_mac;
71 const unsigned char *nh = skb_network_header(skb);
72 65
73 if (nh[IP6CB(skb)->nhoff] != IPPROTO_IPV6 && 66 if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6)
74 nh[IP6CB(skb)->nhoff] != IPPROTO_IPIP)
75 goto out; 67 goto out;
76 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 68 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
77 goto out; 69 goto out;
@@ -80,18 +72,12 @@ static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
80 (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) 72 (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
81 goto out; 73 goto out;
82 74
83 nh = skb_network_header(skb); 75 if (x->props.flags & XFRM_STATE_DECAP_DSCP)
84 if (nh[IP6CB(skb)->nhoff] == IPPROTO_IPV6) { 76 ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)),
85 if (x->props.flags & XFRM_STATE_DECAP_DSCP) 77 ipipv6_hdr(skb));
86 ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)), 78 if (!(x->props.flags & XFRM_STATE_NOECN))
87 ipipv6_hdr(skb)); 79 ipip6_ecn_decapsulate(skb);
88 if (!(x->props.flags & XFRM_STATE_NOECN)) 80
89 ipip6_ecn_decapsulate(skb);
90 } else {
91 if (!(x->props.flags & XFRM_STATE_NOECN))
92 ip6ip_ecn_decapsulate(skb);
93 skb->protocol = htons(ETH_P_IP);
94 }
95 old_mac = skb_mac_header(skb); 81 old_mac = skb_mac_header(skb);
96 skb_set_mac_header(skb, -skb->mac_len); 82 skb_set_mac_header(skb, -skb->mac_len);
97 memmove(skb_mac_header(skb), old_mac, skb->mac_len); 83 memmove(skb_mac_header(skb), old_mac, skb->mac_len);
@@ -103,7 +89,8 @@ out:
103} 89}
104 90
105static struct xfrm_mode xfrm6_tunnel_mode = { 91static struct xfrm_mode xfrm6_tunnel_mode = {
106 .input = xfrm6_tunnel_input, 92 .input2 = xfrm6_tunnel_input,
93 .input = xfrm_prepare_input,
107 .output2 = xfrm6_tunnel_output, 94 .output2 = xfrm6_tunnel_output,
108 .output = xfrm6_prepare_output, 95 .output = xfrm6_prepare_output,
109 .owner = THIS_MODULE, 96 .owner = THIS_MODULE,
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index bc2e80e3b0b1..c45050cfe72b 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -53,6 +53,7 @@ int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb)
53 if (err) 53 if (err)
54 return err; 54 return err;
55 55
56 IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
56 return xfrm6_extract_header(skb); 57 return xfrm6_extract_header(skb);
57} 58}
58 59
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c
index 98b05f472322..90fef0a4726f 100644
--- a/net/ipv6/xfrm6_state.c
+++ b/net/ipv6/xfrm6_state.c
@@ -177,7 +177,8 @@ int xfrm6_extract_header(struct sk_buff *skb)
177 XFRM_MODE_SKB_CB(skb)->frag_off = htons(IP_DF); 177 XFRM_MODE_SKB_CB(skb)->frag_off = htons(IP_DF);
178 XFRM_MODE_SKB_CB(skb)->tos = ipv6_get_dsfield(iph); 178 XFRM_MODE_SKB_CB(skb)->tos = ipv6_get_dsfield(iph);
179 XFRM_MODE_SKB_CB(skb)->ttl = iph->hop_limit; 179 XFRM_MODE_SKB_CB(skb)->ttl = iph->hop_limit;
180 XFRM_MODE_SKB_CB(skb)->protocol = iph->nexthdr; 180 XFRM_MODE_SKB_CB(skb)->protocol =
181 skb_network_header(skb)[IP6CB(skb)->nhoff];
181 memcpy(XFRM_MODE_SKB_CB(skb)->flow_lbl, iph->flow_lbl, 182 memcpy(XFRM_MODE_SKB_CB(skb)->flow_lbl, iph->flow_lbl,
182 sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl)); 183 sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl));
183 184
@@ -187,11 +188,13 @@ int xfrm6_extract_header(struct sk_buff *skb)
187static struct xfrm_state_afinfo xfrm6_state_afinfo = { 188static struct xfrm_state_afinfo xfrm6_state_afinfo = {
188 .family = AF_INET6, 189 .family = AF_INET6,
189 .proto = IPPROTO_IPV6, 190 .proto = IPPROTO_IPV6,
191 .eth_proto = htons(ETH_P_IPV6),
190 .owner = THIS_MODULE, 192 .owner = THIS_MODULE,
191 .init_tempsel = __xfrm6_init_tempsel, 193 .init_tempsel = __xfrm6_init_tempsel,
192 .tmpl_sort = __xfrm6_tmpl_sort, 194 .tmpl_sort = __xfrm6_tmpl_sort,
193 .state_sort = __xfrm6_state_sort, 195 .state_sort = __xfrm6_state_sort,
194 .output = xfrm6_output, 196 .output = xfrm6_output,
197 .extract_input = xfrm6_extract_input,
195 .extract_output = xfrm6_extract_output, 198 .extract_output = xfrm6_extract_output,
196}; 199};
197 200
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index cb97fda1b6df..4c803f7e74e5 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -81,6 +81,19 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
81} 81}
82EXPORT_SYMBOL(xfrm_parse_spi); 82EXPORT_SYMBOL(xfrm_parse_spi);
83 83
84int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
85{
86 int err;
87
88 err = x->outer_mode->afinfo->extract_input(x, skb);
89 if (err)
90 return err;
91
92 skb->protocol = x->inner_mode->afinfo->eth_proto;
93 return x->inner_mode->input2(x, skb);
94}
95EXPORT_SYMBOL(xfrm_prepare_input);
96
84void __init xfrm_input_init(void) 97void __init xfrm_input_init(void)
85{ 98{
86 secpath_cachep = kmem_cache_create("secpath_cache", 99 secpath_cachep = kmem_cache_create("secpath_cache",