diff options
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b7e537fe2d75..825c60af7723 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -2236,3 +2236,233 @@ void __init xfrm_init(void) | |||
2236 | xfrm_input_init(); | 2236 | xfrm_input_init(); |
2237 | } | 2237 | } |
2238 | 2238 | ||
2239 | #ifdef CONFIG_XFRM_MIGRATE | ||
2240 | static int xfrm_migrate_selector_match(struct xfrm_selector *sel_cmp, | ||
2241 | struct xfrm_selector *sel_tgt) | ||
2242 | { | ||
2243 | if (sel_cmp->proto == IPSEC_ULPROTO_ANY) { | ||
2244 | if (sel_tgt->family == sel_cmp->family && | ||
2245 | xfrm_addr_cmp(&sel_tgt->daddr, &sel_cmp->daddr, | ||
2246 | sel_cmp->family) == 0 && | ||
2247 | xfrm_addr_cmp(&sel_tgt->saddr, &sel_cmp->saddr, | ||
2248 | sel_cmp->family) == 0 && | ||
2249 | sel_tgt->prefixlen_d == sel_cmp->prefixlen_d && | ||
2250 | sel_tgt->prefixlen_s == sel_cmp->prefixlen_s) { | ||
2251 | return 1; | ||
2252 | } | ||
2253 | } else { | ||
2254 | if (memcmp(sel_tgt, sel_cmp, sizeof(*sel_tgt)) == 0) { | ||
2255 | return 1; | ||
2256 | } | ||
2257 | } | ||
2258 | return 0; | ||
2259 | } | ||
2260 | |||
2261 | static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel, | ||
2262 | u8 dir, u8 type) | ||
2263 | { | ||
2264 | struct xfrm_policy *pol, *ret = NULL; | ||
2265 | struct hlist_node *entry; | ||
2266 | struct hlist_head *chain; | ||
2267 | u32 priority = ~0U; | ||
2268 | |||
2269 | read_lock_bh(&xfrm_policy_lock); | ||
2270 | chain = policy_hash_direct(&sel->daddr, &sel->saddr, sel->family, dir); | ||
2271 | hlist_for_each_entry(pol, entry, chain, bydst) { | ||
2272 | if (xfrm_migrate_selector_match(sel, &pol->selector) && | ||
2273 | pol->type == type) { | ||
2274 | ret = pol; | ||
2275 | priority = ret->priority; | ||
2276 | break; | ||
2277 | } | ||
2278 | } | ||
2279 | chain = &xfrm_policy_inexact[dir]; | ||
2280 | hlist_for_each_entry(pol, entry, chain, bydst) { | ||
2281 | if (xfrm_migrate_selector_match(sel, &pol->selector) && | ||
2282 | pol->type == type && | ||
2283 | pol->priority < priority) { | ||
2284 | ret = pol; | ||
2285 | break; | ||
2286 | } | ||
2287 | } | ||
2288 | |||
2289 | if (ret) | ||
2290 | xfrm_pol_hold(ret); | ||
2291 | |||
2292 | read_unlock_bh(&xfrm_policy_lock); | ||
2293 | |||
2294 | return ret; | ||
2295 | } | ||
2296 | |||
2297 | static int migrate_tmpl_match(struct xfrm_migrate *m, struct xfrm_tmpl *t) | ||
2298 | { | ||
2299 | int match = 0; | ||
2300 | |||
2301 | if (t->mode == m->mode && t->id.proto == m->proto && | ||
2302 | (m->reqid == 0 || t->reqid == m->reqid)) { | ||
2303 | switch (t->mode) { | ||
2304 | case XFRM_MODE_TUNNEL: | ||
2305 | case XFRM_MODE_BEET: | ||
2306 | if (xfrm_addr_cmp(&t->id.daddr, &m->old_daddr, | ||
2307 | m->old_family) == 0 && | ||
2308 | xfrm_addr_cmp(&t->saddr, &m->old_saddr, | ||
2309 | m->old_family) == 0) { | ||
2310 | match = 1; | ||
2311 | } | ||
2312 | break; | ||
2313 | case XFRM_MODE_TRANSPORT: | ||
2314 | /* in case of transport mode, template does not store | ||
2315 | any IP addresses, hence we just compare mode and | ||
2316 | protocol */ | ||
2317 | match = 1; | ||
2318 | break; | ||
2319 | default: | ||
2320 | break; | ||
2321 | } | ||
2322 | } | ||
2323 | return match; | ||
2324 | } | ||
2325 | |||
2326 | /* update endpoint address(es) of template(s) */ | ||
2327 | static int xfrm_policy_migrate(struct xfrm_policy *pol, | ||
2328 | struct xfrm_migrate *m, int num_migrate) | ||
2329 | { | ||
2330 | struct xfrm_migrate *mp; | ||
2331 | struct dst_entry *dst; | ||
2332 | int i, j, n = 0; | ||
2333 | |||
2334 | write_lock_bh(&pol->lock); | ||
2335 | if (unlikely(pol->dead)) { | ||
2336 | /* target policy has been deleted */ | ||
2337 | write_unlock_bh(&pol->lock); | ||
2338 | return -ENOENT; | ||
2339 | } | ||
2340 | |||
2341 | for (i = 0; i < pol->xfrm_nr; i++) { | ||
2342 | for (j = 0, mp = m; j < num_migrate; j++, mp++) { | ||
2343 | if (!migrate_tmpl_match(mp, &pol->xfrm_vec[i])) | ||
2344 | continue; | ||
2345 | n++; | ||
2346 | if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL) | ||
2347 | continue; | ||
2348 | /* update endpoints */ | ||
2349 | memcpy(&pol->xfrm_vec[i].id.daddr, &mp->new_daddr, | ||
2350 | sizeof(pol->xfrm_vec[i].id.daddr)); | ||
2351 | memcpy(&pol->xfrm_vec[i].saddr, &mp->new_saddr, | ||
2352 | sizeof(pol->xfrm_vec[i].saddr)); | ||
2353 | pol->xfrm_vec[i].encap_family = mp->new_family; | ||
2354 | /* flush bundles */ | ||
2355 | while ((dst = pol->bundles) != NULL) { | ||
2356 | pol->bundles = dst->next; | ||
2357 | dst_free(dst); | ||
2358 | } | ||
2359 | } | ||
2360 | } | ||
2361 | |||
2362 | write_unlock_bh(&pol->lock); | ||
2363 | |||
2364 | if (!n) | ||
2365 | return -ENODATA; | ||
2366 | |||
2367 | return 0; | ||
2368 | } | ||
2369 | |||
2370 | static int xfrm_migrate_check(struct xfrm_migrate *m, int num_migrate) | ||
2371 | { | ||
2372 | int i, j; | ||
2373 | |||
2374 | if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH) | ||
2375 | return -EINVAL; | ||
2376 | |||
2377 | for (i = 0; i < num_migrate; i++) { | ||
2378 | if ((xfrm_addr_cmp(&m[i].old_daddr, &m[i].new_daddr, | ||
2379 | m[i].old_family) == 0) && | ||
2380 | (xfrm_addr_cmp(&m[i].old_saddr, &m[i].new_saddr, | ||
2381 | m[i].old_family) == 0)) | ||
2382 | return -EINVAL; | ||
2383 | if (xfrm_addr_any(&m[i].new_daddr, m[i].new_family) || | ||
2384 | xfrm_addr_any(&m[i].new_saddr, m[i].new_family)) | ||
2385 | return -EINVAL; | ||
2386 | |||
2387 | /* check if there is any duplicated entry */ | ||
2388 | for (j = i + 1; j < num_migrate; j++) { | ||
2389 | if (!memcmp(&m[i].old_daddr, &m[j].old_daddr, | ||
2390 | sizeof(m[i].old_daddr)) && | ||
2391 | !memcmp(&m[i].old_saddr, &m[j].old_saddr, | ||
2392 | sizeof(m[i].old_saddr)) && | ||
2393 | m[i].proto == m[j].proto && | ||
2394 | m[i].mode == m[j].mode && | ||
2395 | m[i].reqid == m[j].reqid && | ||
2396 | m[i].old_family == m[j].old_family) | ||
2397 | return -EINVAL; | ||
2398 | } | ||
2399 | } | ||
2400 | |||
2401 | return 0; | ||
2402 | } | ||
2403 | |||
2404 | int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type, | ||
2405 | struct xfrm_migrate *m, int num_migrate) | ||
2406 | { | ||
2407 | int i, err, nx_cur = 0, nx_new = 0; | ||
2408 | struct xfrm_policy *pol = NULL; | ||
2409 | struct xfrm_state *x, *xc; | ||
2410 | struct xfrm_state *x_cur[XFRM_MAX_DEPTH]; | ||
2411 | struct xfrm_state *x_new[XFRM_MAX_DEPTH]; | ||
2412 | struct xfrm_migrate *mp; | ||
2413 | |||
2414 | if ((err = xfrm_migrate_check(m, num_migrate)) < 0) | ||
2415 | goto out; | ||
2416 | |||
2417 | /* Stage 1 - find policy */ | ||
2418 | if ((pol = xfrm_migrate_policy_find(sel, dir, type)) == NULL) { | ||
2419 | err = -ENOENT; | ||
2420 | goto out; | ||
2421 | } | ||
2422 | |||
2423 | /* Stage 2 - find and update state(s) */ | ||
2424 | for (i = 0, mp = m; i < num_migrate; i++, mp++) { | ||
2425 | if ((x = xfrm_migrate_state_find(mp))) { | ||
2426 | x_cur[nx_cur] = x; | ||
2427 | nx_cur++; | ||
2428 | if ((xc = xfrm_state_migrate(x, mp))) { | ||
2429 | x_new[nx_new] = xc; | ||
2430 | nx_new++; | ||
2431 | } else { | ||
2432 | err = -ENODATA; | ||
2433 | goto restore_state; | ||
2434 | } | ||
2435 | } | ||
2436 | } | ||
2437 | |||
2438 | /* Stage 3 - update policy */ | ||
2439 | if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0) | ||
2440 | goto restore_state; | ||
2441 | |||
2442 | /* Stage 4 - delete old state(s) */ | ||
2443 | if (nx_cur) { | ||
2444 | xfrm_states_put(x_cur, nx_cur); | ||
2445 | xfrm_states_delete(x_cur, nx_cur); | ||
2446 | } | ||
2447 | |||
2448 | /* Stage 5 - announce */ | ||
2449 | km_migrate(sel, dir, type, m, num_migrate); | ||
2450 | |||
2451 | xfrm_pol_put(pol); | ||
2452 | |||
2453 | return 0; | ||
2454 | out: | ||
2455 | return err; | ||
2456 | |||
2457 | restore_state: | ||
2458 | if (pol) | ||
2459 | xfrm_pol_put(pol); | ||
2460 | if (nx_cur) | ||
2461 | xfrm_states_put(x_cur, nx_cur); | ||
2462 | if (nx_new) | ||
2463 | xfrm_states_delete(x_new, nx_new); | ||
2464 | |||
2465 | return err; | ||
2466 | } | ||
2467 | #endif | ||
2468 | |||