Skip to content

Commit 80c9aba

Browse files
Shinta Sugimotodavem330
authored andcommitted
[XFRM]: Extension for dynamic update of endpoint address(es)
Extend the XFRM framework so that endpoint address(es) in the XFRM databases could be dynamically updated according to a request (MIGRATE message) from user application. Target XFRM policy is first identified by the selector in the MIGRATE message. Next, the endpoint addresses of the matching templates and XFRM states are updated according to the MIGRATE message. Signed-off-by: Shinta Sugimoto <shinta.sugimoto@ericsson.com> Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org> Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 9934e81 commit 80c9aba

File tree

4 files changed

+467
-0
lines changed

4 files changed

+467
-0
lines changed

include/linux/xfrm.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ enum {
178178
XFRM_MSG_REPORT,
179179
#define XFRM_MSG_REPORT XFRM_MSG_REPORT
180180

181+
XFRM_MSG_MIGRATE,
182+
#define XFRM_MSG_MIGRATE XFRM_MSG_MIGRATE
183+
181184
__XFRM_MSG_MAX
182185
};
183186
#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@@ -256,6 +259,7 @@ enum xfrm_attr_type_t {
256259
XFRMA_COADDR, /* xfrm_address_t */
257260
XFRMA_LASTUSED,
258261
XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */
262+
XFRMA_MIGRATE,
259263
__XFRMA_MAX
260264

261265
#define XFRMA_MAX (__XFRMA_MAX - 1)
@@ -351,6 +355,19 @@ struct xfrm_user_report {
351355
struct xfrm_selector sel;
352356
};
353357

358+
struct xfrm_user_migrate {
359+
xfrm_address_t old_daddr;
360+
xfrm_address_t old_saddr;
361+
xfrm_address_t new_daddr;
362+
xfrm_address_t new_saddr;
363+
__u8 proto;
364+
__u8 mode;
365+
__u16 reserved;
366+
__u32 reqid;
367+
__u16 old_family;
368+
__u16 new_family;
369+
};
370+
354371
#ifndef __KERNEL__
355372
/* backwards compatibility for userspace */
356373
#define XFRMGRP_ACQUIRE 1
@@ -375,6 +392,8 @@ enum xfrm_nlgroups {
375392
#define XFRMNLGRP_AEVENTS XFRMNLGRP_AEVENTS
376393
XFRMNLGRP_REPORT,
377394
#define XFRMNLGRP_REPORT XFRMNLGRP_REPORT
395+
XFRMNLGRP_MIGRATE,
396+
#define XFRMNLGRP_MIGRATE XFRMNLGRP_MIGRATE
378397
__XFRMNLGRP_MAX
379398
};
380399
#define XFRMNLGRP_MAX (__XFRMNLGRP_MAX - 1)

include/net/xfrm.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,19 @@ struct xfrm_policy
362362
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
363363
};
364364

365+
struct xfrm_migrate {
366+
xfrm_address_t old_daddr;
367+
xfrm_address_t old_saddr;
368+
xfrm_address_t new_daddr;
369+
xfrm_address_t new_saddr;
370+
u8 proto;
371+
u8 mode;
372+
u16 reserved;
373+
u32 reqid;
374+
u16 old_family;
375+
u16 new_family;
376+
};
377+
365378
#define XFRM_KM_TIMEOUT 30
366379
/* which seqno */
367380
#define XFRM_REPLAY_SEQ 1
@@ -388,6 +401,7 @@ struct xfrm_mgr
388401
int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
389402
int (*notify_policy)(struct xfrm_policy *x, int dir, struct km_event *c);
390403
int (*report)(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
404+
int (*migrate)(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles);
391405
};
392406

393407
extern int xfrm_register_km(struct xfrm_mgr *km);
@@ -988,6 +1002,16 @@ extern int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *xdst,
9881002
struct flowi *fl, int family, int strict);
9891003
extern void xfrm_init_pmtu(struct dst_entry *dst);
9901004

1005+
#ifdef CONFIG_XFRM_MIGRATE
1006+
extern int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1007+
struct xfrm_migrate *m, int num_bundles);
1008+
extern struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m);
1009+
extern struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
1010+
struct xfrm_migrate *m);
1011+
extern int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
1012+
struct xfrm_migrate *m, int num_bundles);
1013+
#endif
1014+
9911015
extern wait_queue_head_t km_waitq;
9921016
extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
9931017
extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid);
@@ -1053,5 +1077,25 @@ static inline void xfrm_aevent_doreplay(struct xfrm_state *x)
10531077
xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
10541078
}
10551079

1080+
#ifdef CONFIG_XFRM_MIGRATE
1081+
static inline struct xfrm_algo *xfrm_algo_clone(struct xfrm_algo *orig)
1082+
{
1083+
return (struct xfrm_algo *)kmemdup(orig, sizeof(*orig) + orig->alg_key_len, GFP_KERNEL);
1084+
}
1085+
1086+
static inline void xfrm_states_put(struct xfrm_state **states, int n)
1087+
{
1088+
int i;
1089+
for (i = 0; i < n; i++)
1090+
xfrm_state_put(*(states + i));
1091+
}
1092+
1093+
static inline void xfrm_states_delete(struct xfrm_state **states, int n)
1094+
{
1095+
int i;
1096+
for (i = 0; i < n; i++)
1097+
xfrm_state_delete(*(states + i));
1098+
}
1099+
#endif
10561100

10571101
#endif /* _NET_XFRM_H */

net/xfrm/xfrm_policy.c

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,3 +2236,233 @@ void __init xfrm_init(void)
22362236
xfrm_input_init();
22372237
}
22382238

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+

0 commit comments

Comments
 (0)