Root/
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/err.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/netdevice.h> |
17 | #include <net/addrconf.h> |
18 | #include <net/dst.h> |
19 | #include <net/xfrm.h> |
20 | #include <net/ip.h> |
21 | #include <net/ipv6.h> |
22 | #include <net/ip6_route.h> |
23 | #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) |
24 | #include <net/mip6.h> |
25 | #endif |
26 | |
27 | static struct xfrm_policy_afinfo xfrm6_policy_afinfo; |
28 | |
29 | static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, |
30 | xfrm_address_t *saddr, |
31 | xfrm_address_t *daddr) |
32 | { |
33 | struct flowi fl = {}; |
34 | struct dst_entry *dst; |
35 | int err; |
36 | |
37 | memcpy(&fl.fl6_dst, daddr, sizeof(fl.fl6_dst)); |
38 | if (saddr) |
39 | memcpy(&fl.fl6_src, saddr, sizeof(fl.fl6_src)); |
40 | |
41 | dst = ip6_route_output(net, NULL, &fl); |
42 | |
43 | err = dst->error; |
44 | if (dst->error) { |
45 | dst_release(dst); |
46 | dst = ERR_PTR(err); |
47 | } |
48 | |
49 | return dst; |
50 | } |
51 | |
52 | static int xfrm6_get_saddr(struct net *net, |
53 | xfrm_address_t *saddr, xfrm_address_t *daddr) |
54 | { |
55 | struct dst_entry *dst; |
56 | struct net_device *dev; |
57 | |
58 | dst = xfrm6_dst_lookup(net, 0, NULL, daddr); |
59 | if (IS_ERR(dst)) |
60 | return -EHOSTUNREACH; |
61 | |
62 | dev = ip6_dst_idev(dst)->dev; |
63 | ipv6_dev_get_saddr(dev_net(dev), dev, |
64 | (struct in6_addr *)&daddr->a6, 0, |
65 | (struct in6_addr *)&saddr->a6); |
66 | dst_release(dst); |
67 | return 0; |
68 | } |
69 | |
70 | static struct dst_entry * |
71 | __xfrm6_find_bundle(struct flowi *fl, struct xfrm_policy *policy) |
72 | { |
73 | struct dst_entry *dst; |
74 | |
75 | /* Still not clear if we should set fl->fl6_{src,dst}... */ |
76 | read_lock_bh(&policy->lock); |
77 | for (dst = policy->bundles; dst; dst = dst->next) { |
78 | struct xfrm_dst *xdst = (struct xfrm_dst*)dst; |
79 | struct in6_addr fl_dst_prefix, fl_src_prefix; |
80 | |
81 | ipv6_addr_prefix(&fl_dst_prefix, |
82 | &fl->fl6_dst, |
83 | xdst->u.rt6.rt6i_dst.plen); |
84 | ipv6_addr_prefix(&fl_src_prefix, |
85 | &fl->fl6_src, |
86 | xdst->u.rt6.rt6i_src.plen); |
87 | if (ipv6_addr_equal(&xdst->u.rt6.rt6i_dst.addr, &fl_dst_prefix) && |
88 | ipv6_addr_equal(&xdst->u.rt6.rt6i_src.addr, &fl_src_prefix) && |
89 | xfrm_bundle_ok(policy, xdst, fl, AF_INET6, |
90 | (xdst->u.rt6.rt6i_dst.plen != 128 || |
91 | xdst->u.rt6.rt6i_src.plen != 128))) { |
92 | dst_clone(dst); |
93 | break; |
94 | } |
95 | } |
96 | read_unlock_bh(&policy->lock); |
97 | return dst; |
98 | } |
99 | |
100 | static int xfrm6_get_tos(struct flowi *fl) |
101 | { |
102 | return 0; |
103 | } |
104 | |
105 | static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst, |
106 | int nfheader_len) |
107 | { |
108 | if (dst->ops->family == AF_INET6) { |
109 | struct rt6_info *rt = (struct rt6_info*)dst; |
110 | if (rt->rt6i_node) |
111 | path->path_cookie = rt->rt6i_node->fn_sernum; |
112 | } |
113 | |
114 | path->u.rt6.rt6i_nfheader_len = nfheader_len; |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, |
120 | struct flowi *fl) |
121 | { |
122 | struct rt6_info *rt = (struct rt6_info*)xdst->route; |
123 | |
124 | xdst->u.dst.dev = dev; |
125 | dev_hold(dev); |
126 | |
127 | xdst->u.rt6.rt6i_idev = in6_dev_get(dev); |
128 | if (!xdst->u.rt6.rt6i_idev) |
129 | return -ENODEV; |
130 | |
131 | /* Sheit... I remember I did this right. Apparently, |
132 | * it was magically lost, so this code needs audit */ |
133 | xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | |
134 | RTF_LOCAL); |
135 | xdst->u.rt6.rt6i_metric = rt->rt6i_metric; |
136 | xdst->u.rt6.rt6i_node = rt->rt6i_node; |
137 | if (rt->rt6i_node) |
138 | xdst->route_cookie = rt->rt6i_node->fn_sernum; |
139 | xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; |
140 | xdst->u.rt6.rt6i_dst = rt->rt6i_dst; |
141 | xdst->u.rt6.rt6i_src = rt->rt6i_src; |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | static inline void |
147 | _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) |
148 | { |
149 | int onlyproto = 0; |
150 | u16 offset = skb_network_header_len(skb); |
151 | struct ipv6hdr *hdr = ipv6_hdr(skb); |
152 | struct ipv6_opt_hdr *exthdr; |
153 | const unsigned char *nh = skb_network_header(skb); |
154 | u8 nexthdr = nh[IP6CB(skb)->nhoff]; |
155 | |
156 | memset(fl, 0, sizeof(struct flowi)); |
157 | ipv6_addr_copy(&fl->fl6_dst, reverse ? &hdr->saddr : &hdr->daddr); |
158 | ipv6_addr_copy(&fl->fl6_src, reverse ? &hdr->daddr : &hdr->saddr); |
159 | |
160 | while (nh + offset + 1 < skb->data || |
161 | pskb_may_pull(skb, nh + offset + 1 - skb->data)) { |
162 | nh = skb_network_header(skb); |
163 | exthdr = (struct ipv6_opt_hdr *)(nh + offset); |
164 | |
165 | switch (nexthdr) { |
166 | case NEXTHDR_FRAGMENT: |
167 | onlyproto = 1; |
168 | case NEXTHDR_ROUTING: |
169 | case NEXTHDR_HOP: |
170 | case NEXTHDR_DEST: |
171 | offset += ipv6_optlen(exthdr); |
172 | nexthdr = exthdr->nexthdr; |
173 | exthdr = (struct ipv6_opt_hdr *)(nh + offset); |
174 | break; |
175 | |
176 | case IPPROTO_UDP: |
177 | case IPPROTO_UDPLITE: |
178 | case IPPROTO_TCP: |
179 | case IPPROTO_SCTP: |
180 | case IPPROTO_DCCP: |
181 | if (!onlyproto && (nh + offset + 4 < skb->data || |
182 | pskb_may_pull(skb, nh + offset + 4 - skb->data))) { |
183 | __be16 *ports = (__be16 *)exthdr; |
184 | |
185 | fl->fl_ip_sport = ports[!!reverse]; |
186 | fl->fl_ip_dport = ports[!reverse]; |
187 | } |
188 | fl->proto = nexthdr; |
189 | return; |
190 | |
191 | case IPPROTO_ICMPV6: |
192 | if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) { |
193 | u8 *icmp = (u8 *)exthdr; |
194 | |
195 | fl->fl_icmp_type = icmp[0]; |
196 | fl->fl_icmp_code = icmp[1]; |
197 | } |
198 | fl->proto = nexthdr; |
199 | return; |
200 | |
201 | #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) |
202 | case IPPROTO_MH: |
203 | if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) { |
204 | struct ip6_mh *mh; |
205 | mh = (struct ip6_mh *)exthdr; |
206 | |
207 | fl->fl_mh_type = mh->ip6mh_type; |
208 | } |
209 | fl->proto = nexthdr; |
210 | return; |
211 | #endif |
212 | |
213 | /* XXX Why are there these headers? */ |
214 | case IPPROTO_AH: |
215 | case IPPROTO_ESP: |
216 | case IPPROTO_COMP: |
217 | default: |
218 | fl->fl_ipsec_spi = 0; |
219 | fl->proto = nexthdr; |
220 | return; |
221 | } |
222 | } |
223 | } |
224 | |
225 | static inline int xfrm6_garbage_collect(struct dst_ops *ops) |
226 | { |
227 | struct net *net = container_of(ops, struct net, xfrm.xfrm6_dst_ops); |
228 | |
229 | xfrm6_policy_afinfo.garbage_collect(net); |
230 | return (atomic_read(&ops->entries) > ops->gc_thresh * 2); |
231 | } |
232 | |
233 | static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu) |
234 | { |
235 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; |
236 | struct dst_entry *path = xdst->route; |
237 | |
238 | path->ops->update_pmtu(path, mtu); |
239 | } |
240 | |
241 | static void xfrm6_dst_destroy(struct dst_entry *dst) |
242 | { |
243 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; |
244 | |
245 | if (likely(xdst->u.rt6.rt6i_idev)) |
246 | in6_dev_put(xdst->u.rt6.rt6i_idev); |
247 | xfrm_dst_destroy(xdst); |
248 | } |
249 | |
250 | static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, |
251 | int unregister) |
252 | { |
253 | struct xfrm_dst *xdst; |
254 | |
255 | if (!unregister) |
256 | return; |
257 | |
258 | xdst = (struct xfrm_dst *)dst; |
259 | if (xdst->u.rt6.rt6i_idev->dev == dev) { |
260 | struct inet6_dev *loopback_idev = |
261 | in6_dev_get(dev_net(dev)->loopback_dev); |
262 | BUG_ON(!loopback_idev); |
263 | |
264 | do { |
265 | in6_dev_put(xdst->u.rt6.rt6i_idev); |
266 | xdst->u.rt6.rt6i_idev = loopback_idev; |
267 | in6_dev_hold(loopback_idev); |
268 | xdst = (struct xfrm_dst *)xdst->u.dst.child; |
269 | } while (xdst->u.dst.xfrm); |
270 | |
271 | __in6_dev_put(loopback_idev); |
272 | } |
273 | |
274 | xfrm_dst_ifdown(dst, dev); |
275 | } |
276 | |
277 | static struct dst_ops xfrm6_dst_ops = { |
278 | .family = AF_INET6, |
279 | .protocol = cpu_to_be16(ETH_P_IPV6), |
280 | .gc = xfrm6_garbage_collect, |
281 | .update_pmtu = xfrm6_update_pmtu, |
282 | .destroy = xfrm6_dst_destroy, |
283 | .ifdown = xfrm6_dst_ifdown, |
284 | .local_out = __ip6_local_out, |
285 | .gc_thresh = 1024, |
286 | .entries = ATOMIC_INIT(0), |
287 | }; |
288 | |
289 | static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { |
290 | .family = AF_INET6, |
291 | .dst_ops = &xfrm6_dst_ops, |
292 | .dst_lookup = xfrm6_dst_lookup, |
293 | .get_saddr = xfrm6_get_saddr, |
294 | .find_bundle = __xfrm6_find_bundle, |
295 | .decode_session = _decode_session6, |
296 | .get_tos = xfrm6_get_tos, |
297 | .init_path = xfrm6_init_path, |
298 | .fill_dst = xfrm6_fill_dst, |
299 | }; |
300 | |
301 | static int __init xfrm6_policy_init(void) |
302 | { |
303 | return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo); |
304 | } |
305 | |
306 | static void xfrm6_policy_fini(void) |
307 | { |
308 | xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo); |
309 | } |
310 | |
311 | #ifdef CONFIG_SYSCTL |
312 | static struct ctl_table xfrm6_policy_table[] = { |
313 | { |
314 | .procname = "xfrm6_gc_thresh", |
315 | .data = &init_net.xfrm.xfrm6_dst_ops.gc_thresh, |
316 | .maxlen = sizeof(int), |
317 | .mode = 0644, |
318 | .proc_handler = proc_dointvec, |
319 | }, |
320 | { } |
321 | }; |
322 | |
323 | static struct ctl_table_header *sysctl_hdr; |
324 | #endif |
325 | |
326 | int __init xfrm6_init(void) |
327 | { |
328 | int ret; |
329 | unsigned int gc_thresh; |
330 | |
331 | /* |
332 | * We need a good default value for the xfrm6 gc threshold. |
333 | * In ipv4 we set it to the route hash table size * 8, which |
334 | * is half the size of the maximaum route cache for ipv4. It |
335 | * would be good to do the same thing for v6, except the table is |
336 | * constructed differently here. Here each table for a net namespace |
337 | * can have FIB_TABLE_HASHSZ entries, so lets go with the same |
338 | * computation that we used for ipv4 here. Also, lets keep the initial |
339 | * gc_thresh to a minimum of 1024, since, the ipv6 route cache defaults |
340 | * to that as a minimum as well |
341 | */ |
342 | gc_thresh = FIB6_TABLE_HASHSZ * 8; |
343 | xfrm6_dst_ops.gc_thresh = (gc_thresh < 1024) ? 1024 : gc_thresh; |
344 | |
345 | ret = xfrm6_policy_init(); |
346 | if (ret) |
347 | goto out; |
348 | |
349 | ret = xfrm6_state_init(); |
350 | if (ret) |
351 | goto out_policy; |
352 | |
353 | #ifdef CONFIG_SYSCTL |
354 | sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv6_ctl_path, |
355 | xfrm6_policy_table); |
356 | #endif |
357 | out: |
358 | return ret; |
359 | out_policy: |
360 | xfrm6_policy_fini(); |
361 | goto out; |
362 | } |
363 | |
364 | void xfrm6_fini(void) |
365 | { |
366 | #ifdef CONFIG_SYSCTL |
367 | if (sysctl_hdr) |
368 | unregister_net_sysctl_table(sysctl_hdr); |
369 | #endif |
370 | //xfrm6_input_fini(); |
371 | xfrm6_policy_fini(); |
372 | xfrm6_state_fini(); |
373 | } |
374 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9