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 /net/xfrm/xfrm_policy.c | |
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>
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 183 |
1 files changed, 159 insertions, 24 deletions
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 | */ |