diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2007-12-11 12:32:34 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:53:43 -0500 |
commit | 25ee3286dcbc830a833354bb1d15567956844813 (patch) | |
tree | bb5597d9e148f363bf0bbcd1a7269b5f677f0103 | |
parent | 66cdb3ca27323a92712d289fc5edc7841d74a139 (diff) |
[IPSEC]: Merge common code into xfrm_bundle_create
Half of the code in xfrm4_bundle_create and xfrm6_bundle_create are
common. This patch extracts that logic and puts it into
xfrm_bundle_create. The rest of it are then accessed through afinfo.
As a result this fixes the problem with inter-family transforms where
we treat every xfrm dst in the bundle as if it belongs to the top
family.
This patch also fixes a long-standing error-path bug where we may free
the xfrm states twice.
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.h | 11 | ||||
-rw-r--r-- | net/ipv4/xfrm4_policy.c | 134 | ||||
-rw-r--r-- | net/ipv6/xfrm6_policy.c | 136 | ||||
-rw-r--r-- | net/xfrm/xfrm_policy.c | 183 |
4 files changed, 211 insertions, 253 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d427343f527b..c9345fe3f8d2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -227,6 +227,7 @@ struct km_event | |||
227 | u32 event; | 227 | u32 event; |
228 | }; | 228 | }; |
229 | 229 | ||
230 | struct net_device; | ||
230 | struct xfrm_type; | 231 | struct xfrm_type; |
231 | struct xfrm_dst; | 232 | struct xfrm_dst; |
232 | struct xfrm_policy_afinfo { | 233 | struct xfrm_policy_afinfo { |
@@ -237,13 +238,11 @@ struct xfrm_policy_afinfo { | |||
237 | xfrm_address_t *daddr); | 238 | xfrm_address_t *daddr); |
238 | int (*get_saddr)(xfrm_address_t *saddr, xfrm_address_t *daddr); | 239 | int (*get_saddr)(xfrm_address_t *saddr, xfrm_address_t *daddr); |
239 | struct dst_entry *(*find_bundle)(struct flowi *fl, struct xfrm_policy *policy); | 240 | struct dst_entry *(*find_bundle)(struct flowi *fl, struct xfrm_policy *policy); |
240 | int (*bundle_create)(struct xfrm_policy *policy, | ||
241 | struct xfrm_state **xfrm, | ||
242 | int nx, | ||
243 | struct flowi *fl, | ||
244 | struct dst_entry **dst_p); | ||
245 | void (*decode_session)(struct sk_buff *skb, | 241 | void (*decode_session)(struct sk_buff *skb, |
246 | struct flowi *fl); | 242 | struct flowi *fl); |
243 | int (*get_tos)(struct flowi *fl); | ||
244 | int (*fill_dst)(struct xfrm_dst *xdst, | ||
245 | struct net_device *dev); | ||
247 | }; | 246 | }; |
248 | 247 | ||
249 | extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); | 248 | extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); |
@@ -1094,7 +1093,6 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) | |||
1094 | } | 1093 | } |
1095 | #endif | 1094 | #endif |
1096 | 1095 | ||
1097 | extern struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos); | ||
1098 | struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp); | 1096 | struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp); |
1099 | extern int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*), void *); | 1097 | extern int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*), void *); |
1100 | int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl); | 1098 | int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl); |
@@ -1113,7 +1111,6 @@ extern int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info); | |||
1113 | extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); | 1111 | extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); |
1114 | extern int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *xdst, | 1112 | extern int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *xdst, |
1115 | struct flowi *fl, int family, int strict); | 1113 | struct flowi *fl, int family, int strict); |
1116 | extern void xfrm_init_pmtu(struct dst_entry *dst); | ||
1117 | 1114 | ||
1118 | #ifdef CONFIG_XFRM_MIGRATE | 1115 | #ifdef CONFIG_XFRM_MIGRATE |
1119 | extern int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, | 1116 | extern int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, |
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index cebc84731969..1d7524375b49 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c | |||
@@ -79,122 +79,39 @@ __xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy) | |||
79 | return dst; | 79 | return dst; |
80 | } | 80 | } |
81 | 81 | ||
82 | /* Allocate chain of dst_entry's, attach known xfrm's, calculate | 82 | static int xfrm4_get_tos(struct flowi *fl) |
83 | * all the metrics... Shortly, bundle a bundle. | ||
84 | */ | ||
85 | |||
86 | static int | ||
87 | __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, | ||
88 | struct flowi *fl, struct dst_entry **dst_p) | ||
89 | { | 83 | { |
90 | struct dst_entry *dst, *dst_prev; | 84 | return fl->fl4_tos; |
91 | struct rtable *rt0 = (struct rtable*)(*dst_p); | 85 | } |
92 | struct rtable *rt = rt0; | ||
93 | int tos = fl->fl4_tos; | ||
94 | int i; | ||
95 | int err; | ||
96 | int header_len = 0; | ||
97 | int trailer_len = 0; | ||
98 | |||
99 | dst = dst_prev = NULL; | ||
100 | dst_hold(&rt->u.dst); | ||
101 | |||
102 | for (i = 0; i < nx; i++) { | ||
103 | struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops); | ||
104 | struct xfrm_dst *xdst; | ||
105 | |||
106 | if (unlikely(dst1 == NULL)) { | ||
107 | err = -ENOBUFS; | ||
108 | dst_release(&rt->u.dst); | ||
109 | goto error; | ||
110 | } | ||
111 | 86 | ||
112 | if (!dst) | 87 | static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) |
113 | dst = dst1; | 88 | { |
114 | else { | 89 | struct rtable *rt = (struct rtable *)xdst->route; |
115 | dst_prev->child = dst1; | ||
116 | dst1->flags |= DST_NOHASH; | ||
117 | dst_clone(dst1); | ||
118 | } | ||
119 | 90 | ||
120 | xdst = (struct xfrm_dst *)dst1; | 91 | xdst->u.rt.fl = rt->fl; |
121 | xdst->route = &rt->u.dst; | ||
122 | xdst->genid = xfrm[i]->genid; | ||
123 | 92 | ||
124 | dst1->next = dst_prev; | 93 | xdst->u.dst.dev = dev; |
125 | dst_prev = dst1; | 94 | dev_hold(dev); |
126 | 95 | ||
127 | header_len += xfrm[i]->props.header_len; | 96 | xdst->u.rt.idev = in_dev_get(dev); |
128 | trailer_len += xfrm[i]->props.trailer_len; | 97 | if (!xdst->u.rt.idev) |
98 | return -ENODEV; | ||
129 | 99 | ||
130 | if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { | 100 | xdst->u.rt.peer = rt->peer; |
131 | dst1 = xfrm_dst_lookup(xfrm[i], tos); | 101 | if (rt->peer) |
132 | err = PTR_ERR(dst1); | 102 | atomic_inc(&rt->peer->refcnt); |
133 | if (IS_ERR(dst1)) | ||
134 | goto error; | ||
135 | 103 | ||
136 | rt = (struct rtable *)dst1; | 104 | /* Sheit... I remember I did this right. Apparently, |
137 | } else | 105 | * it was magically lost, so this code needs audit */ |
138 | dst_hold(&rt->u.dst); | 106 | xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST | |
139 | } | 107 | RTCF_LOCAL); |
108 | xdst->u.rt.rt_type = rt->rt_type; | ||
109 | xdst->u.rt.rt_src = rt->rt_src; | ||
110 | xdst->u.rt.rt_dst = rt->rt_dst; | ||
111 | xdst->u.rt.rt_gateway = rt->rt_gateway; | ||
112 | xdst->u.rt.rt_spec_dst = rt->rt_spec_dst; | ||
140 | 113 | ||
141 | dst_prev->child = &rt->u.dst; | ||
142 | dst->path = &rt->u.dst; | ||
143 | |||
144 | /* Copy neighbout for reachability confirmation */ | ||
145 | dst->neighbour = neigh_clone(rt->u.dst.neighbour); | ||
146 | |||
147 | *dst_p = dst; | ||
148 | dst = dst_prev; | ||
149 | |||
150 | dst_prev = *dst_p; | ||
151 | i = 0; | ||
152 | err = -ENODEV; | ||
153 | for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { | ||
154 | struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; | ||
155 | x->u.rt.fl = *fl; | ||
156 | |||
157 | dst_prev->xfrm = xfrm[i++]; | ||
158 | dst_prev->dev = rt->u.dst.dev; | ||
159 | if (!rt->u.dst.dev) | ||
160 | goto error; | ||
161 | dev_hold(rt->u.dst.dev); | ||
162 | |||
163 | x->u.rt.idev = in_dev_get(rt->u.dst.dev); | ||
164 | if (!x->u.rt.idev) | ||
165 | goto error; | ||
166 | |||
167 | dst_prev->obsolete = -1; | ||
168 | dst_prev->flags |= DST_HOST; | ||
169 | dst_prev->lastuse = jiffies; | ||
170 | dst_prev->header_len = header_len; | ||
171 | dst_prev->trailer_len = trailer_len; | ||
172 | memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); | ||
173 | |||
174 | dst_prev->input = dst_discard; | ||
175 | dst_prev->output = dst_prev->xfrm->outer_mode->afinfo->output; | ||
176 | if (rt0->peer) | ||
177 | atomic_inc(&rt0->peer->refcnt); | ||
178 | x->u.rt.peer = rt0->peer; | ||
179 | /* Sheit... I remember I did this right. Apparently, | ||
180 | * it was magically lost, so this code needs audit */ | ||
181 | x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); | ||
182 | x->u.rt.rt_type = rt0->rt_type; | ||
183 | x->u.rt.rt_src = rt0->rt_src; | ||
184 | x->u.rt.rt_dst = rt0->rt_dst; | ||
185 | x->u.rt.rt_gateway = rt0->rt_gateway; | ||
186 | x->u.rt.rt_spec_dst = rt0->rt_spec_dst; | ||
187 | header_len -= x->u.dst.xfrm->props.header_len; | ||
188 | trailer_len -= x->u.dst.xfrm->props.trailer_len; | ||
189 | } | ||
190 | |||
191 | xfrm_init_pmtu(dst); | ||
192 | return 0; | 114 | return 0; |
193 | |||
194 | error: | ||
195 | if (dst) | ||
196 | dst_free(dst); | ||
197 | return err; | ||
198 | } | 115 | } |
199 | 116 | ||
200 | static void | 117 | static void |
@@ -330,8 +247,9 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { | |||
330 | .dst_lookup = xfrm4_dst_lookup, | 247 | .dst_lookup = xfrm4_dst_lookup, |
331 | .get_saddr = xfrm4_get_saddr, | 248 | .get_saddr = xfrm4_get_saddr, |
332 | .find_bundle = __xfrm4_find_bundle, | 249 | .find_bundle = __xfrm4_find_bundle, |
333 | .bundle_create = __xfrm4_bundle_create, | ||
334 | .decode_session = _decode_session4, | 250 | .decode_session = _decode_session4, |
251 | .get_tos = xfrm4_get_tos, | ||
252 | .fill_dst = xfrm4_fill_dst, | ||
335 | }; | 253 | }; |
336 | 254 | ||
337 | static void __init xfrm4_policy_init(void) | 255 | static void __init xfrm4_policy_init(void) |
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 8e78530865a6..63932c5fd3c7 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c | |||
@@ -93,126 +93,33 @@ __xfrm6_find_bundle(struct flowi *fl, struct xfrm_policy *policy) | |||
93 | return dst; | 93 | return dst; |
94 | } | 94 | } |
95 | 95 | ||
96 | /* Allocate chain of dst_entry's, attach known xfrm's, calculate | 96 | static int xfrm6_get_tos(struct flowi *fl) |
97 | * all the metrics... Shortly, bundle a bundle. | ||
98 | */ | ||
99 | |||
100 | static int | ||
101 | __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, | ||
102 | struct flowi *fl, struct dst_entry **dst_p) | ||
103 | { | 97 | { |
104 | struct dst_entry *dst, *dst_prev; | 98 | return 0; |
105 | struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); | 99 | } |
106 | struct rt6_info *rt = rt0; | ||
107 | int i; | ||
108 | int err; | ||
109 | int header_len = 0; | ||
110 | int trailer_len = 0; | ||
111 | |||
112 | dst = dst_prev = NULL; | ||
113 | dst_hold(&rt->u.dst); | ||
114 | |||
115 | for (i = 0; i < nx; i++) { | ||
116 | struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); | ||
117 | struct xfrm_dst *xdst; | ||
118 | 100 | ||
119 | if (unlikely(dst1 == NULL)) { | 101 | static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) |
120 | err = -ENOBUFS; | 102 | { |
121 | dst_release(&rt->u.dst); | 103 | struct rt6_info *rt = (struct rt6_info*)xdst->route; |
122 | goto error; | ||
123 | } | ||
124 | 104 | ||
125 | if (!dst) | 105 | xdst->u.dst.dev = dev; |
126 | dst = dst1; | 106 | dev_hold(dev); |
127 | else { | ||
128 | dst_prev->child = dst1; | ||
129 | dst1->flags |= DST_NOHASH; | ||
130 | dst_clone(dst1); | ||
131 | } | ||
132 | 107 | ||
133 | xdst = (struct xfrm_dst *)dst1; | 108 | xdst->u.rt6.rt6i_idev = in6_dev_get(rt->u.dst.dev); |
134 | xdst->route = &rt->u.dst; | 109 | if (!xdst->u.rt6.rt6i_idev) |
135 | xdst->genid = xfrm[i]->genid; | 110 | return -ENODEV; |
136 | if (rt->rt6i_node) | ||
137 | xdst->route_cookie = rt->rt6i_node->fn_sernum; | ||
138 | |||
139 | dst1->next = dst_prev; | ||
140 | dst_prev = dst1; | ||
141 | |||
142 | if (xfrm[i]->type->flags & XFRM_TYPE_NON_FRAGMENT) | ||
143 | ((struct rt6_info *)dst)->nfheader_len += | ||
144 | xfrm[i]->props.header_len; | ||
145 | header_len += xfrm[i]->props.header_len; | ||
146 | trailer_len += xfrm[i]->props.trailer_len; | ||
147 | |||
148 | if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { | ||
149 | dst1 = xfrm_dst_lookup(xfrm[i], 0); | ||
150 | err = PTR_ERR(dst1); | ||
151 | if (IS_ERR(dst1)) | ||
152 | goto error; | ||
153 | |||
154 | rt = (struct rt6_info *)dst1; | ||
155 | } else | ||
156 | dst_hold(&rt->u.dst); | ||
157 | } | ||
158 | 111 | ||
159 | dst_prev->child = &rt->u.dst; | 112 | /* Sheit... I remember I did this right. Apparently, |
160 | dst->path = &rt->u.dst; | 113 | * it was magically lost, so this code needs audit */ |
161 | 114 | xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | | |
162 | /* Copy neighbour for reachability confirmation */ | 115 | RTF_LOCAL); |
163 | dst->neighbour = neigh_clone(rt->u.dst.neighbour); | 116 | xdst->u.rt6.rt6i_metric = rt->rt6i_metric; |
164 | 117 | xdst->u.rt6.rt6i_node = rt->rt6i_node; | |
165 | if (rt->rt6i_node) | 118 | xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; |
166 | ((struct xfrm_dst *)dst)->path_cookie = rt->rt6i_node->fn_sernum; | 119 | xdst->u.rt6.rt6i_dst = rt->rt6i_dst; |
167 | 120 | xdst->u.rt6.rt6i_src = rt->rt6i_src; | |
168 | *dst_p = dst; | ||
169 | dst = dst_prev; | ||
170 | |||
171 | dst_prev = *dst_p; | ||
172 | i = 0; | ||
173 | err = -ENODEV; | ||
174 | for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { | ||
175 | struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; | ||
176 | |||
177 | dst_prev->xfrm = xfrm[i++]; | ||
178 | dst_prev->dev = rt->u.dst.dev; | ||
179 | if (!rt->u.dst.dev) | ||
180 | goto error; | ||
181 | dev_hold(rt->u.dst.dev); | ||
182 | |||
183 | x->u.rt6.rt6i_idev = in6_dev_get(rt->u.dst.dev); | ||
184 | if (!x->u.rt6.rt6i_idev) | ||
185 | goto error; | ||
186 | |||
187 | dst_prev->obsolete = -1; | ||
188 | dst_prev->flags |= DST_HOST; | ||
189 | dst_prev->lastuse = jiffies; | ||
190 | dst_prev->header_len = header_len; | ||
191 | dst_prev->trailer_len = trailer_len; | ||
192 | memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); | ||
193 | |||
194 | dst_prev->input = dst_discard; | ||
195 | dst_prev->output = dst_prev->xfrm->outer_mode->afinfo->output; | ||
196 | /* Sheit... I remember I did this right. Apparently, | ||
197 | * it was magically lost, so this code needs audit */ | ||
198 | x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTF_ANYCAST|RTF_LOCAL); | ||
199 | x->u.rt6.rt6i_metric = rt0->rt6i_metric; | ||
200 | x->u.rt6.rt6i_node = rt0->rt6i_node; | ||
201 | x->u.rt6.rt6i_gateway = rt0->rt6i_gateway; | ||
202 | memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); | ||
203 | x->u.rt6.rt6i_dst = rt0->rt6i_dst; | ||
204 | x->u.rt6.rt6i_src = rt0->rt6i_src; | ||
205 | header_len -= x->u.dst.xfrm->props.header_len; | ||
206 | trailer_len -= x->u.dst.xfrm->props.trailer_len; | ||
207 | } | ||
208 | 121 | ||
209 | xfrm_init_pmtu(dst); | ||
210 | return 0; | 122 | return 0; |
211 | |||
212 | error: | ||
213 | if (dst) | ||
214 | dst_free(dst); | ||
215 | return err; | ||
216 | } | 123 | } |
217 | 124 | ||
218 | static inline void | 125 | static inline void |
@@ -355,8 +262,9 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { | |||
355 | .dst_lookup = xfrm6_dst_lookup, | 262 | .dst_lookup = xfrm6_dst_lookup, |
356 | .get_saddr = xfrm6_get_saddr, | 263 | .get_saddr = xfrm6_get_saddr, |
357 | .find_bundle = __xfrm6_find_bundle, | 264 | .find_bundle = __xfrm6_find_bundle, |
358 | .bundle_create = __xfrm6_bundle_create, | ||
359 | .decode_session = _decode_session6, | 265 | .decode_session = _decode_session6, |
266 | .get_tos = xfrm6_get_tos, | ||
267 | .fill_dst = xfrm6_fill_dst, | ||
360 | }; | 268 | }; |
361 | 269 | ||
362 | static void __init xfrm6_policy_init(void) | 270 | static void __init xfrm6_policy_init(void) |
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 085c19d4d1b7..b153f7482052 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/netfilter.h> | 24 | #include <linux/netfilter.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/cache.h> | 26 | #include <linux/cache.h> |
27 | #include <net/dst.h> | ||
27 | #include <net/xfrm.h> | 28 | #include <net/xfrm.h> |
28 | #include <net/ip.h> | 29 | #include <net/ip.h> |
29 | 30 | ||
@@ -50,6 +51,7 @@ static DEFINE_SPINLOCK(xfrm_policy_gc_lock); | |||
50 | 51 | ||
51 | static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); | 52 | static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); |
52 | static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo); | 53 | static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo); |
54 | static void xfrm_init_pmtu(struct dst_entry *dst); | ||
53 | 55 | ||
54 | static inline int | 56 | static inline int |
55 | __xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl) | 57 | __xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl) |
@@ -85,7 +87,8 @@ int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl, | |||
85 | return 0; | 87 | return 0; |
86 | } | 88 | } |
87 | 89 | ||
88 | struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos) | 90 | static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, |
91 | int family) | ||
89 | { | 92 | { |
90 | xfrm_address_t *saddr = &x->props.saddr; | 93 | xfrm_address_t *saddr = &x->props.saddr; |
91 | xfrm_address_t *daddr = &x->id.daddr; | 94 | xfrm_address_t *daddr = &x->id.daddr; |
@@ -97,7 +100,7 @@ struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos) | |||
97 | if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) | 100 | if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) |
98 | daddr = x->coaddr; | 101 | daddr = x->coaddr; |
99 | 102 | ||
100 | afinfo = xfrm_policy_get_afinfo(x->props.family); | 103 | afinfo = xfrm_policy_get_afinfo(family); |
101 | if (unlikely(afinfo == NULL)) | 104 | if (unlikely(afinfo == NULL)) |
102 | return ERR_PTR(-EAFNOSUPPORT); | 105 | return ERR_PTR(-EAFNOSUPPORT); |
103 | 106 | ||
@@ -105,7 +108,6 @@ struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos) | |||
105 | xfrm_policy_put_afinfo(afinfo); | 108 | xfrm_policy_put_afinfo(afinfo); |
106 | return dst; | 109 | return dst; |
107 | } | 110 | } |
108 | EXPORT_SYMBOL(xfrm_dst_lookup); | ||
109 | 111 | ||
110 | static inline unsigned long make_jiffies(long secs) | 112 | static inline unsigned long make_jiffies(long secs) |
111 | { | 113 | { |
@@ -1234,24 +1236,164 @@ xfrm_find_bundle(struct flowi *fl, struct xfrm_policy *policy, unsigned short fa | |||
1234 | return x; | 1236 | return x; |
1235 | } | 1237 | } |
1236 | 1238 | ||
1237 | /* Allocate chain of dst_entry's, attach known xfrm's, calculate | 1239 | static inline int xfrm_get_tos(struct flowi *fl, int family) |
1238 | * all the metrics... Shortly, bundle a bundle. | 1240 | { |
1239 | */ | 1241 | struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); |
1242 | int tos; | ||
1240 | 1243 | ||
1241 | static int | 1244 | if (!afinfo) |
1242 | xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, | 1245 | return -EINVAL; |
1243 | struct flowi *fl, struct dst_entry **dst_p, | 1246 | |
1244 | unsigned short family) | 1247 | tos = afinfo->get_tos(fl); |
1248 | |||
1249 | xfrm_policy_put_afinfo(afinfo); | ||
1250 | |||
1251 | return tos; | ||
1252 | } | ||
1253 | |||
1254 | static inline struct xfrm_dst *xfrm_alloc_dst(int family) | ||
1245 | { | 1255 | { |
1246 | int err; | ||
1247 | struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); | 1256 | struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); |
1248 | if (unlikely(afinfo == NULL)) | 1257 | struct xfrm_dst *xdst; |
1258 | |||
1259 | if (!afinfo) | ||
1260 | return ERR_PTR(-EINVAL); | ||
1261 | |||
1262 | xdst = dst_alloc(afinfo->dst_ops) ?: ERR_PTR(-ENOBUFS); | ||
1263 | |||
1264 | xfrm_policy_put_afinfo(afinfo); | ||
1265 | |||
1266 | return xdst; | ||
1267 | } | ||
1268 | |||
1269 | static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) | ||
1270 | { | ||
1271 | struct xfrm_policy_afinfo *afinfo = | ||
1272 | xfrm_policy_get_afinfo(xdst->u.dst.ops->family); | ||
1273 | int err; | ||
1274 | |||
1275 | if (!afinfo) | ||
1249 | return -EINVAL; | 1276 | return -EINVAL; |
1250 | err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p); | 1277 | |
1278 | err = afinfo->fill_dst(xdst, dev); | ||
1279 | |||
1251 | xfrm_policy_put_afinfo(afinfo); | 1280 | xfrm_policy_put_afinfo(afinfo); |
1281 | |||
1252 | return err; | 1282 | return err; |
1253 | } | 1283 | } |
1254 | 1284 | ||
1285 | /* Allocate chain of dst_entry's, attach known xfrm's, calculate | ||
1286 | * all the metrics... Shortly, bundle a bundle. | ||
1287 | */ | ||
1288 | |||
1289 | static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, | ||
1290 | struct xfrm_state **xfrm, int nx, | ||
1291 | struct flowi *fl, | ||
1292 | struct dst_entry *dst) | ||
1293 | { | ||
1294 | unsigned long now = jiffies; | ||
1295 | struct net_device *dev; | ||
1296 | struct dst_entry *dst_prev = NULL; | ||
1297 | struct dst_entry *dst0 = NULL; | ||
1298 | int i = 0; | ||
1299 | int err; | ||
1300 | int header_len = 0; | ||
1301 | int trailer_len = 0; | ||
1302 | int tos; | ||
1303 | int family = policy->selector.family; | ||
1304 | |||
1305 | tos = xfrm_get_tos(fl, family); | ||
1306 | err = tos; | ||
1307 | if (tos < 0) | ||
1308 | goto put_states; | ||
1309 | |||
1310 | dst_hold(dst); | ||
1311 | |||
1312 | for (; i < nx; i++) { | ||
1313 | struct xfrm_dst *xdst = xfrm_alloc_dst(family); | ||
1314 | struct dst_entry *dst1 = &xdst->u.dst; | ||
1315 | |||
1316 | err = PTR_ERR(xdst); | ||
1317 | if (IS_ERR(xdst)) { | ||
1318 | dst_release(dst); | ||
1319 | goto put_states; | ||
1320 | } | ||
1321 | |||
1322 | if (!dst_prev) | ||
1323 | dst0 = dst1; | ||
1324 | else { | ||
1325 | dst_prev->child = dst_clone(dst1); | ||
1326 | dst1->flags |= DST_NOHASH; | ||
1327 | } | ||
1328 | |||
1329 | xdst->route = dst; | ||
1330 | memcpy(&dst1->metrics, &dst->metrics, sizeof(dst->metrics)); | ||
1331 | |||
1332 | if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { | ||
1333 | family = xfrm[i]->props.family; | ||
1334 | dst = xfrm_dst_lookup(xfrm[i], tos, family); | ||
1335 | err = PTR_ERR(dst); | ||
1336 | if (IS_ERR(dst)) | ||
1337 | goto put_states; | ||
1338 | } else | ||
1339 | dst_hold(dst); | ||
1340 | |||
1341 | dst1->xfrm = xfrm[i]; | ||
1342 | xdst->genid = xfrm[i]->genid; | ||
1343 | |||
1344 | dst1->obsolete = -1; | ||
1345 | dst1->flags |= DST_HOST; | ||
1346 | dst1->lastuse = now; | ||
1347 | |||
1348 | dst1->input = dst_discard; | ||
1349 | dst1->output = xfrm[i]->outer_mode->afinfo->output; | ||
1350 | |||
1351 | dst1->next = dst_prev; | ||
1352 | dst_prev = dst1; | ||
1353 | |||
1354 | header_len += xfrm[i]->props.header_len; | ||
1355 | trailer_len += xfrm[i]->props.trailer_len; | ||
1356 | } | ||
1357 | |||
1358 | dst_prev->child = dst; | ||
1359 | dst0->path = dst; | ||
1360 | |||
1361 | err = -ENODEV; | ||
1362 | dev = dst->dev; | ||
1363 | if (!dev) | ||
1364 | goto free_dst; | ||
1365 | |||
1366 | /* Copy neighbout for reachability confirmation */ | ||
1367 | dst0->neighbour = neigh_clone(dst->neighbour); | ||
1368 | |||
1369 | xfrm_init_pmtu(dst_prev); | ||
1370 | |||
1371 | for (dst_prev = dst0; dst_prev != dst; dst_prev = dst_prev->child) { | ||
1372 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst_prev; | ||
1373 | |||
1374 | err = xfrm_fill_dst(xdst, dev); | ||
1375 | if (err) | ||
1376 | goto free_dst; | ||
1377 | |||
1378 | dst_prev->header_len = header_len; | ||
1379 | dst_prev->trailer_len = trailer_len; | ||
1380 | header_len -= xdst->u.dst.xfrm->props.header_len; | ||
1381 | trailer_len -= xdst->u.dst.xfrm->props.trailer_len; | ||
1382 | } | ||
1383 | |||
1384 | out: | ||
1385 | return dst0; | ||
1386 | |||
1387 | put_states: | ||
1388 | for (; i < nx; i++) | ||
1389 | xfrm_state_put(xfrm[i]); | ||
1390 | free_dst: | ||
1391 | if (dst0) | ||
1392 | dst_free(dst0); | ||
1393 | dst0 = ERR_PTR(err); | ||
1394 | goto out; | ||
1395 | } | ||
1396 | |||
1255 | static int inline | 1397 | static int inline |
1256 | xfrm_dst_alloc_copy(void **target, void *src, int size) | 1398 | xfrm_dst_alloc_copy(void **target, void *src, int size) |
1257 | { | 1399 | { |
@@ -1454,15 +1596,10 @@ restart: | |||
1454 | return 0; | 1596 | return 0; |
1455 | } | 1597 | } |
1456 | 1598 | ||
1457 | dst = dst_orig; | 1599 | dst = xfrm_bundle_create(policy, xfrm, nx, fl, dst_orig); |
1458 | err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family); | 1600 | err = PTR_ERR(dst); |
1459 | 1601 | if (IS_ERR(dst)) | |
1460 | if (unlikely(err)) { | ||
1461 | int i; | ||
1462 | for (i=0; i<nx; i++) | ||
1463 | xfrm_state_put(xfrm[i]); | ||
1464 | goto error; | 1602 | goto error; |
1465 | } | ||
1466 | 1603 | ||
1467 | for (pi = 0; pi < npols; pi++) { | 1604 | for (pi = 0; pi < npols; pi++) { |
1468 | read_lock_bh(&pols[pi]->lock); | 1605 | read_lock_bh(&pols[pi]->lock); |
@@ -1886,7 +2023,7 @@ static int xfrm_flush_bundles(void) | |||
1886 | return 0; | 2023 | return 0; |
1887 | } | 2024 | } |
1888 | 2025 | ||
1889 | void xfrm_init_pmtu(struct dst_entry *dst) | 2026 | static void xfrm_init_pmtu(struct dst_entry *dst) |
1890 | { | 2027 | { |
1891 | do { | 2028 | do { |
1892 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; | 2029 | struct xfrm_dst *xdst = (struct xfrm_dst *)dst; |
@@ -1907,8 +2044,6 @@ void xfrm_init_pmtu(struct dst_entry *dst) | |||
1907 | } while ((dst = dst->next)); | 2044 | } while ((dst = dst->next)); |
1908 | } | 2045 | } |
1909 | 2046 | ||
1910 | EXPORT_SYMBOL(xfrm_init_pmtu); | ||
1911 | |||
1912 | /* Check that the bundle accepts the flow and its components are | 2047 | /* Check that the bundle accepts the flow and its components are |
1913 | * still valid. | 2048 | * still valid. |
1914 | */ | 2049 | */ |