diff options
Diffstat (limited to 'net/ipv6/xfrm6_policy.c')
-rw-r--r-- | net/ipv6/xfrm6_policy.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c new file mode 100644 index 000000000000..8a4f37de4d2d --- /dev/null +++ b/net/ipv6/xfrm6_policy.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * xfrm6_policy.c: based on xfrm4_policy.c | ||
3 | * | ||
4 | * Authors: | ||
5 | * Mitsuru KANDA @USAGI | ||
6 | * Kazunori MIYAZAWA @USAGI | ||
7 | * Kunihiro Ishiguro <kunihiro@ipinfusion.com> | ||
8 | * IPv6 support | ||
9 | * YOSHIFUJI Hideaki | ||
10 | * Split up af-specific portion | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <net/xfrm.h> | ||
16 | #include <net/ip.h> | ||
17 | #include <net/ipv6.h> | ||
18 | #include <net/ip6_route.h> | ||
19 | |||
20 | static struct dst_ops xfrm6_dst_ops; | ||
21 | static struct xfrm_policy_afinfo xfrm6_policy_afinfo; | ||
22 | |||
23 | static struct xfrm_type_map xfrm6_type_map = { .lock = RW_LOCK_UNLOCKED }; | ||
24 | |||
25 | static int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) | ||
26 | { | ||
27 | int err = 0; | ||
28 | *dst = (struct xfrm_dst*)ip6_route_output(NULL, fl); | ||
29 | if (!*dst) | ||
30 | err = -ENETUNREACH; | ||
31 | return err; | ||
32 | } | ||
33 | |||
34 | static struct dst_entry * | ||
35 | __xfrm6_find_bundle(struct flowi *fl, struct xfrm_policy *policy) | ||
36 | { | ||
37 | struct dst_entry *dst; | ||
38 | |||
39 | /* Still not clear if we should set fl->fl6_{src,dst}... */ | ||
40 | read_lock_bh(&policy->lock); | ||
41 | for (dst = policy->bundles; dst; dst = dst->next) { | ||
42 | struct xfrm_dst *xdst = (struct xfrm_dst*)dst; | ||
43 | struct in6_addr fl_dst_prefix, fl_src_prefix; | ||
44 | |||
45 | ipv6_addr_prefix(&fl_dst_prefix, | ||
46 | &fl->fl6_dst, | ||
47 | xdst->u.rt6.rt6i_dst.plen); | ||
48 | ipv6_addr_prefix(&fl_src_prefix, | ||
49 | &fl->fl6_src, | ||
50 | xdst->u.rt6.rt6i_src.plen); | ||
51 | if (ipv6_addr_equal(&xdst->u.rt6.rt6i_dst.addr, &fl_dst_prefix) && | ||
52 | ipv6_addr_equal(&xdst->u.rt6.rt6i_src.addr, &fl_src_prefix) && | ||
53 | xfrm_bundle_ok(xdst, fl, AF_INET6)) { | ||
54 | dst_clone(dst); | ||
55 | break; | ||
56 | } | ||
57 | } | ||
58 | read_unlock_bh(&policy->lock); | ||
59 | return dst; | ||
60 | } | ||
61 | |||
62 | /* Allocate chain of dst_entry's, attach known xfrm's, calculate | ||
63 | * all the metrics... Shortly, bundle a bundle. | ||
64 | */ | ||
65 | |||
66 | static int | ||
67 | __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, | ||
68 | struct flowi *fl, struct dst_entry **dst_p) | ||
69 | { | ||
70 | struct dst_entry *dst, *dst_prev; | ||
71 | struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); | ||
72 | struct rt6_info *rt = rt0; | ||
73 | struct in6_addr *remote = &fl->fl6_dst; | ||
74 | struct in6_addr *local = &fl->fl6_src; | ||
75 | struct flowi fl_tunnel = { | ||
76 | .nl_u = { | ||
77 | .ip6_u = { | ||
78 | .saddr = *local, | ||
79 | .daddr = *remote | ||
80 | } | ||
81 | } | ||
82 | }; | ||
83 | int i; | ||
84 | int err = 0; | ||
85 | int header_len = 0; | ||
86 | int trailer_len = 0; | ||
87 | |||
88 | dst = dst_prev = NULL; | ||
89 | dst_hold(&rt->u.dst); | ||
90 | |||
91 | for (i = 0; i < nx; i++) { | ||
92 | struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); | ||
93 | struct xfrm_dst *xdst; | ||
94 | int tunnel = 0; | ||
95 | |||
96 | if (unlikely(dst1 == NULL)) { | ||
97 | err = -ENOBUFS; | ||
98 | dst_release(&rt->u.dst); | ||
99 | goto error; | ||
100 | } | ||
101 | |||
102 | if (!dst) | ||
103 | dst = dst1; | ||
104 | else { | ||
105 | dst_prev->child = dst1; | ||
106 | dst1->flags |= DST_NOHASH; | ||
107 | dst_clone(dst1); | ||
108 | } | ||
109 | |||
110 | xdst = (struct xfrm_dst *)dst1; | ||
111 | xdst->route = &rt->u.dst; | ||
112 | |||
113 | dst1->next = dst_prev; | ||
114 | dst_prev = dst1; | ||
115 | if (xfrm[i]->props.mode) { | ||
116 | remote = (struct in6_addr*)&xfrm[i]->id.daddr; | ||
117 | local = (struct in6_addr*)&xfrm[i]->props.saddr; | ||
118 | tunnel = 1; | ||
119 | } | ||
120 | header_len += xfrm[i]->props.header_len; | ||
121 | trailer_len += xfrm[i]->props.trailer_len; | ||
122 | |||
123 | if (tunnel) { | ||
124 | ipv6_addr_copy(&fl_tunnel.fl6_dst, remote); | ||
125 | ipv6_addr_copy(&fl_tunnel.fl6_src, local); | ||
126 | err = xfrm_dst_lookup((struct xfrm_dst **) &rt, | ||
127 | &fl_tunnel, AF_INET6); | ||
128 | if (err) | ||
129 | goto error; | ||
130 | } else | ||
131 | dst_hold(&rt->u.dst); | ||
132 | } | ||
133 | |||
134 | dst_prev->child = &rt->u.dst; | ||
135 | dst->path = &rt->u.dst; | ||
136 | |||
137 | *dst_p = dst; | ||
138 | dst = dst_prev; | ||
139 | |||
140 | dst_prev = *dst_p; | ||
141 | i = 0; | ||
142 | for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { | ||
143 | struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; | ||
144 | |||
145 | dst_prev->xfrm = xfrm[i++]; | ||
146 | dst_prev->dev = rt->u.dst.dev; | ||
147 | if (rt->u.dst.dev) | ||
148 | dev_hold(rt->u.dst.dev); | ||
149 | dst_prev->obsolete = -1; | ||
150 | dst_prev->flags |= DST_HOST; | ||
151 | dst_prev->lastuse = jiffies; | ||
152 | dst_prev->header_len = header_len; | ||
153 | dst_prev->trailer_len = trailer_len; | ||
154 | memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); | ||
155 | |||
156 | /* Copy neighbour for reachability confirmation */ | ||
157 | dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); | ||
158 | dst_prev->input = rt->u.dst.input; | ||
159 | dst_prev->output = xfrm6_output; | ||
160 | /* Sheit... I remember I did this right. Apparently, | ||
161 | * it was magically lost, so this code needs audit */ | ||
162 | x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); | ||
163 | x->u.rt6.rt6i_metric = rt0->rt6i_metric; | ||
164 | x->u.rt6.rt6i_node = rt0->rt6i_node; | ||
165 | x->u.rt6.rt6i_gateway = rt0->rt6i_gateway; | ||
166 | memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); | ||
167 | x->u.rt6.rt6i_dst = rt0->rt6i_dst; | ||
168 | x->u.rt6.rt6i_src = rt0->rt6i_src; | ||
169 | header_len -= x->u.dst.xfrm->props.header_len; | ||
170 | trailer_len -= x->u.dst.xfrm->props.trailer_len; | ||
171 | } | ||
172 | |||
173 | xfrm_init_pmtu(dst); | ||
174 | return 0; | ||
175 | |||
176 | error: | ||
177 | if (dst) | ||
178 | dst_free(dst); | ||
179 | return err; | ||
180 | } | ||
181 | |||
182 | static inline void | ||
183 | _decode_session6(struct sk_buff *skb, struct flowi *fl) | ||
184 | { | ||
185 | u16 offset = sizeof(struct ipv6hdr); | ||
186 | struct ipv6hdr *hdr = skb->nh.ipv6h; | ||
187 | struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | ||
188 | u8 nexthdr = skb->nh.ipv6h->nexthdr; | ||
189 | |||
190 | memset(fl, 0, sizeof(struct flowi)); | ||
191 | ipv6_addr_copy(&fl->fl6_dst, &hdr->daddr); | ||
192 | ipv6_addr_copy(&fl->fl6_src, &hdr->saddr); | ||
193 | |||
194 | while (pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data)) { | ||
195 | switch (nexthdr) { | ||
196 | case NEXTHDR_ROUTING: | ||
197 | case NEXTHDR_HOP: | ||
198 | case NEXTHDR_DEST: | ||
199 | offset += ipv6_optlen(exthdr); | ||
200 | nexthdr = exthdr->nexthdr; | ||
201 | exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | ||
202 | break; | ||
203 | |||
204 | case IPPROTO_UDP: | ||
205 | case IPPROTO_TCP: | ||
206 | case IPPROTO_SCTP: | ||
207 | if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) { | ||
208 | u16 *ports = (u16 *)exthdr; | ||
209 | |||
210 | fl->fl_ip_sport = ports[0]; | ||
211 | fl->fl_ip_dport = ports[1]; | ||
212 | } | ||
213 | fl->proto = nexthdr; | ||
214 | return; | ||
215 | |||
216 | case IPPROTO_ICMPV6: | ||
217 | if (pskb_may_pull(skb, skb->nh.raw + offset + 2 - skb->data)) { | ||
218 | u8 *icmp = (u8 *)exthdr; | ||
219 | |||
220 | fl->fl_icmp_type = icmp[0]; | ||
221 | fl->fl_icmp_code = icmp[1]; | ||
222 | } | ||
223 | fl->proto = nexthdr; | ||
224 | return; | ||
225 | |||
226 | /* XXX Why are there these headers? */ | ||
227 | case IPPROTO_AH: | ||
228 | case IPPROTO_ESP: | ||
229 | case IPPROTO_COMP: | ||
230 | default: | ||
231 | fl->fl_ipsec_spi = 0; | ||
232 | fl->proto = nexthdr; | ||
233 | return; | ||
234 | }; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static inline int xfrm6_garbage_collect(void) | ||
239 | { | ||
240 | read_lock(&xfrm6_policy_afinfo.lock); | ||
241 | xfrm6_policy_afinfo.garbage_collect(); | ||
242 | read_unlock(&xfrm6_policy_afinfo.lock); | ||
243 | return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2); | ||
244 | } | ||
245 | |||
246 | static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu) | ||
247 | { | ||
248 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; | ||
249 | struct dst_entry *path = xdst->route; | ||
250 | |||
251 | path->ops->update_pmtu(path, mtu); | ||
252 | } | ||
253 | |||
254 | static struct dst_ops xfrm6_dst_ops = { | ||
255 | .family = AF_INET6, | ||
256 | .protocol = __constant_htons(ETH_P_IPV6), | ||
257 | .gc = xfrm6_garbage_collect, | ||
258 | .update_pmtu = xfrm6_update_pmtu, | ||
259 | .gc_thresh = 1024, | ||
260 | .entry_size = sizeof(struct xfrm_dst), | ||
261 | }; | ||
262 | |||
263 | static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { | ||
264 | .family = AF_INET6, | ||
265 | .lock = RW_LOCK_UNLOCKED, | ||
266 | .type_map = &xfrm6_type_map, | ||
267 | .dst_ops = &xfrm6_dst_ops, | ||
268 | .dst_lookup = xfrm6_dst_lookup, | ||
269 | .find_bundle = __xfrm6_find_bundle, | ||
270 | .bundle_create = __xfrm6_bundle_create, | ||
271 | .decode_session = _decode_session6, | ||
272 | }; | ||
273 | |||
274 | static void __init xfrm6_policy_init(void) | ||
275 | { | ||
276 | xfrm_policy_register_afinfo(&xfrm6_policy_afinfo); | ||
277 | } | ||
278 | |||
279 | static void xfrm6_policy_fini(void) | ||
280 | { | ||
281 | xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo); | ||
282 | } | ||
283 | |||
284 | void __init xfrm6_init(void) | ||
285 | { | ||
286 | xfrm6_policy_init(); | ||
287 | xfrm6_state_init(); | ||
288 | } | ||
289 | |||
290 | void xfrm6_fini(void) | ||
291 | { | ||
292 | //xfrm6_input_fini(); | ||
293 | xfrm6_policy_fini(); | ||
294 | xfrm6_state_fini(); | ||
295 | } | ||