diff options
author | Miika Komu <miika@iki.fi> | 2007-02-06 17:27:02 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-02-08 15:39:01 -0500 |
commit | c82f963efe823d3cacaf1f1b7f1a35cc9628b188 (patch) | |
tree | 3352354b8eeebea2fd7f13b8e0b2149146ea0d99 | |
parent | cdca72652adf597f7fef821a27595fd0dd5eea19 (diff) |
[IPSEC]: IPv6 over IPv4 IPsec tunnel
This is the patch to support IPv6 over IPv4 IPsec
Signed-off-by: Miika Komu <miika@iki.fi>
Signed-off-by: Diego Beltrami <Diego.Beltrami@hiit.fi>
Signed-off-by: Kazunori Miyazawa <miyazawa@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/xfrm4_mode_tunnel.c | 57 | ||||
-rw-r--r-- | net/ipv6/xfrm6_policy.c | 46 |
2 files changed, 77 insertions, 26 deletions
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index e23c21d31a53..e54c5494c88f 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c | |||
@@ -23,6 +23,12 @@ static inline void ipip_ecn_decapsulate(struct sk_buff *skb) | |||
23 | IP_ECN_set_ce(inner_iph); | 23 | IP_ECN_set_ce(inner_iph); |
24 | } | 24 | } |
25 | 25 | ||
26 | static 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(skb->nh.ipv6h); | ||
30 | } | ||
31 | |||
26 | /* Add encapsulation header. | 32 | /* Add encapsulation header. |
27 | * | 33 | * |
28 | * The top IP header will be constructed per RFC 2401. The following fields | 34 | * The top IP header will be constructed per RFC 2401. The following fields |
@@ -36,6 +42,7 @@ static inline void ipip_ecn_decapsulate(struct sk_buff *skb) | |||
36 | static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) | 42 | static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) |
37 | { | 43 | { |
38 | struct dst_entry *dst = skb->dst; | 44 | struct dst_entry *dst = skb->dst; |
45 | struct xfrm_dst *xdst = (struct xfrm_dst*)dst; | ||
39 | struct iphdr *iph, *top_iph; | 46 | struct iphdr *iph, *top_iph; |
40 | int flags; | 47 | int flags; |
41 | 48 | ||
@@ -48,15 +55,27 @@ static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) | |||
48 | top_iph->ihl = 5; | 55 | top_iph->ihl = 5; |
49 | top_iph->version = 4; | 56 | top_iph->version = 4; |
50 | 57 | ||
58 | flags = x->props.flags; | ||
59 | |||
51 | /* DS disclosed */ | 60 | /* DS disclosed */ |
52 | top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos); | 61 | if (xdst->route->ops->family == AF_INET) { |
62 | top_iph->protocol = IPPROTO_IPIP; | ||
63 | top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos); | ||
64 | top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? | ||
65 | 0 : (iph->frag_off & htons(IP_DF)); | ||
66 | } | ||
67 | #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | ||
68 | else { | ||
69 | struct ipv6hdr *ipv6h = (struct ipv6hdr*)iph; | ||
70 | top_iph->protocol = IPPROTO_IPV6; | ||
71 | top_iph->tos = INET_ECN_encapsulate(iph->tos, ipv6_get_dsfield(ipv6h)); | ||
72 | top_iph->frag_off = 0; | ||
73 | } | ||
74 | #endif | ||
53 | 75 | ||
54 | flags = x->props.flags; | ||
55 | if (flags & XFRM_STATE_NOECN) | 76 | if (flags & XFRM_STATE_NOECN) |
56 | IP_ECN_clear(top_iph); | 77 | IP_ECN_clear(top_iph); |
57 | 78 | ||
58 | top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? | ||
59 | 0 : (iph->frag_off & htons(IP_DF)); | ||
60 | if (!top_iph->frag_off) | 79 | if (!top_iph->frag_off) |
61 | __ip_select_ident(top_iph, dst->child, 0); | 80 | __ip_select_ident(top_iph, dst->child, 0); |
62 | 81 | ||
@@ -64,7 +83,6 @@ static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) | |||
64 | 83 | ||
65 | top_iph->saddr = x->props.saddr.a4; | 84 | top_iph->saddr = x->props.saddr.a4; |
66 | top_iph->daddr = x->id.daddr.a4; | 85 | top_iph->daddr = x->id.daddr.a4; |
67 | top_iph->protocol = IPPROTO_IPIP; | ||
68 | 86 | ||
69 | memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); | 87 | memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); |
70 | return 0; | 88 | return 0; |
@@ -75,8 +93,16 @@ static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) | |||
75 | struct iphdr *iph = skb->nh.iph; | 93 | struct iphdr *iph = skb->nh.iph; |
76 | int err = -EINVAL; | 94 | int err = -EINVAL; |
77 | 95 | ||
78 | if (iph->protocol != IPPROTO_IPIP) | 96 | switch(iph->protocol){ |
79 | goto out; | 97 | case IPPROTO_IPIP: |
98 | #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | ||
99 | case IPPROTO_IPV6: | ||
100 | break; | ||
101 | #endif | ||
102 | default: | ||
103 | goto out; | ||
104 | } | ||
105 | |||
80 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | 106 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) |
81 | goto out; | 107 | goto out; |
82 | 108 | ||
@@ -84,10 +110,19 @@ static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) | |||
84 | (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) | 110 | (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) |
85 | goto out; | 111 | goto out; |
86 | 112 | ||
87 | if (x->props.flags & XFRM_STATE_DECAP_DSCP) | 113 | if (iph->protocol == IPPROTO_IPIP) { |
88 | ipv4_copy_dscp(iph, skb->h.ipiph); | 114 | if (x->props.flags & XFRM_STATE_DECAP_DSCP) |
89 | if (!(x->props.flags & XFRM_STATE_NOECN)) | 115 | ipv4_copy_dscp(iph, skb->h.ipiph); |
90 | ipip_ecn_decapsulate(skb); | 116 | if (!(x->props.flags & XFRM_STATE_NOECN)) |
117 | ipip_ecn_decapsulate(skb); | ||
118 | } | ||
119 | #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | ||
120 | else { | ||
121 | if (!(x->props.flags & XFRM_STATE_NOECN)) | ||
122 | ipip6_ecn_decapsulate(iph, skb); | ||
123 | skb->protocol = htons(ETH_P_IPV6); | ||
124 | } | ||
125 | #endif | ||
91 | skb->mac.raw = memmove(skb->data - skb->mac_len, | 126 | skb->mac.raw = memmove(skb->data - skb->mac_len, |
92 | skb->mac.raw, skb->mac_len); | 127 | skb->mac.raw, skb->mac_len); |
93 | skb->nh.raw = skb->data; | 128 | skb->nh.raw = skb->data; |
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 8dffd4daae9c..59480e92177d 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c | |||
@@ -131,13 +131,11 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int | |||
131 | struct dst_entry *dst, *dst_prev; | 131 | struct dst_entry *dst, *dst_prev; |
132 | struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); | 132 | struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); |
133 | struct rt6_info *rt = rt0; | 133 | struct rt6_info *rt = rt0; |
134 | struct in6_addr *remote = &fl->fl6_dst; | ||
135 | struct in6_addr *local = &fl->fl6_src; | ||
136 | struct flowi fl_tunnel = { | 134 | struct flowi fl_tunnel = { |
137 | .nl_u = { | 135 | .nl_u = { |
138 | .ip6_u = { | 136 | .ip6_u = { |
139 | .saddr = *local, | 137 | .saddr = fl->fl6_src, |
140 | .daddr = *remote | 138 | .daddr = fl->fl6_dst, |
141 | } | 139 | } |
142 | } | 140 | } |
143 | }; | 141 | }; |
@@ -153,7 +151,6 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int | |||
153 | for (i = 0; i < nx; i++) { | 151 | for (i = 0; i < nx; i++) { |
154 | struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); | 152 | struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); |
155 | struct xfrm_dst *xdst; | 153 | struct xfrm_dst *xdst; |
156 | int tunnel = 0; | ||
157 | 154 | ||
158 | if (unlikely(dst1 == NULL)) { | 155 | if (unlikely(dst1 == NULL)) { |
159 | err = -ENOBUFS; | 156 | err = -ENOBUFS; |
@@ -177,19 +174,27 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int | |||
177 | 174 | ||
178 | dst1->next = dst_prev; | 175 | dst1->next = dst_prev; |
179 | dst_prev = dst1; | 176 | dst_prev = dst1; |
180 | if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { | 177 | |
181 | remote = __xfrm6_bundle_addr_remote(xfrm[i], remote); | ||
182 | local = __xfrm6_bundle_addr_local(xfrm[i], local); | ||
183 | tunnel = 1; | ||
184 | } | ||
185 | __xfrm6_bundle_len_inc(&header_len, &nfheader_len, xfrm[i]); | 178 | __xfrm6_bundle_len_inc(&header_len, &nfheader_len, xfrm[i]); |
186 | trailer_len += xfrm[i]->props.trailer_len; | 179 | trailer_len += xfrm[i]->props.trailer_len; |
187 | 180 | ||
188 | if (tunnel) { | 181 | if (xfrm[i]->props.mode == XFRM_MODE_TUNNEL) { |
189 | ipv6_addr_copy(&fl_tunnel.fl6_dst, remote); | 182 | unsigned short encap_family = xfrm[i]->props.family; |
190 | ipv6_addr_copy(&fl_tunnel.fl6_src, local); | 183 | switch(encap_family) { |
184 | case AF_INET: | ||
185 | fl_tunnel.fl4_dst = xfrm[i]->id.daddr.a4; | ||
186 | fl_tunnel.fl4_src = xfrm[i]->props.saddr.a4; | ||
187 | break; | ||
188 | case AF_INET6: | ||
189 | ipv6_addr_copy(&fl_tunnel.fl6_dst, (struct in6_addr*)&xfrm[i]->id.daddr.a6); | ||
190 | ipv6_addr_copy(&fl_tunnel.fl6_src, (struct in6_addr*)&xfrm[i]->props.saddr.a6); | ||
191 | break; | ||
192 | default: | ||
193 | BUG_ON(1); | ||
194 | } | ||
195 | |||
191 | err = xfrm_dst_lookup((struct xfrm_dst **) &rt, | 196 | err = xfrm_dst_lookup((struct xfrm_dst **) &rt, |
192 | &fl_tunnel, AF_INET6); | 197 | &fl_tunnel, encap_family); |
193 | if (err) | 198 | if (err) |
194 | goto error; | 199 | goto error; |
195 | } else | 200 | } else |
@@ -208,6 +213,7 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int | |||
208 | i = 0; | 213 | i = 0; |
209 | for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { | 214 | for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { |
210 | struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; | 215 | struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; |
216 | struct xfrm_state_afinfo *afinfo; | ||
211 | 217 | ||
212 | dst_prev->xfrm = xfrm[i++]; | 218 | dst_prev->xfrm = xfrm[i++]; |
213 | dst_prev->dev = rt->u.dst.dev; | 219 | dst_prev->dev = rt->u.dst.dev; |
@@ -224,7 +230,17 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int | |||
224 | /* Copy neighbour for reachability confirmation */ | 230 | /* Copy neighbour for reachability confirmation */ |
225 | dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); | 231 | dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); |
226 | dst_prev->input = rt->u.dst.input; | 232 | dst_prev->input = rt->u.dst.input; |
227 | dst_prev->output = xfrm6_output; | 233 | /* XXX: When IPv4 is implemented as module and can be unloaded, |
234 | * we should manage reference to xfrm4_output in afinfo->output. | ||
235 | * Miyazawa | ||
236 | */ | ||
237 | afinfo = xfrm_state_get_afinfo(dst_prev->xfrm->props.family); | ||
238 | if (!afinfo) { | ||
239 | dst = *dst_p; | ||
240 | goto error; | ||
241 | }; | ||
242 | dst_prev->output = afinfo->output; | ||
243 | xfrm_state_put_afinfo(afinfo); | ||
228 | /* Sheit... I remember I did this right. Apparently, | 244 | /* Sheit... I remember I did this right. Apparently, |
229 | * it was magically lost, so this code needs audit */ | 245 | * it was magically lost, so this code needs audit */ |
230 | x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); | 246 | x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); |