Skip to content

Commit

Permalink
[XFRM]: Extension for dynamic update of endpoint address(es)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Shinta Sugimoto authored and David S. Miller committed Feb 8, 2007
1 parent 9934e81 commit 80c9aba
Show file tree
Hide file tree
Showing 4 changed files with 467 additions and 0 deletions.
19 changes: 19 additions & 0 deletions include/linux/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ enum {
XFRM_MSG_REPORT,
#define XFRM_MSG_REPORT XFRM_MSG_REPORT

XFRM_MSG_MIGRATE,
#define XFRM_MSG_MIGRATE XFRM_MSG_MIGRATE

__XFRM_MSG_MAX
};
#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
Expand Down Expand Up @@ -256,6 +259,7 @@ enum xfrm_attr_type_t {
XFRMA_COADDR, /* xfrm_address_t */
XFRMA_LASTUSED,
XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */
XFRMA_MIGRATE,
__XFRMA_MAX

#define XFRMA_MAX (__XFRMA_MAX - 1)
Expand Down Expand Up @@ -351,6 +355,19 @@ struct xfrm_user_report {
struct xfrm_selector sel;
};

struct xfrm_user_migrate {
xfrm_address_t old_daddr;
xfrm_address_t old_saddr;
xfrm_address_t new_daddr;
xfrm_address_t new_saddr;
__u8 proto;
__u8 mode;
__u16 reserved;
__u32 reqid;
__u16 old_family;
__u16 new_family;
};

#ifndef __KERNEL__
/* backwards compatibility for userspace */
#define XFRMGRP_ACQUIRE 1
Expand All @@ -375,6 +392,8 @@ enum xfrm_nlgroups {
#define XFRMNLGRP_AEVENTS XFRMNLGRP_AEVENTS
XFRMNLGRP_REPORT,
#define XFRMNLGRP_REPORT XFRMNLGRP_REPORT
XFRMNLGRP_MIGRATE,
#define XFRMNLGRP_MIGRATE XFRMNLGRP_MIGRATE
__XFRMNLGRP_MAX
};
#define XFRMNLGRP_MAX (__XFRMNLGRP_MAX - 1)
Expand Down
44 changes: 44 additions & 0 deletions include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,19 @@ struct xfrm_policy
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
};

struct xfrm_migrate {
xfrm_address_t old_daddr;
xfrm_address_t old_saddr;
xfrm_address_t new_daddr;
xfrm_address_t new_saddr;
u8 proto;
u8 mode;
u16 reserved;
u32 reqid;
u16 old_family;
u16 new_family;
};

#define XFRM_KM_TIMEOUT 30
/* which seqno */
#define XFRM_REPLAY_SEQ 1
Expand All @@ -388,6 +401,7 @@ struct xfrm_mgr
int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
int (*notify_policy)(struct xfrm_policy *x, int dir, struct km_event *c);
int (*report)(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
int (*migrate)(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles);
};

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

#ifdef CONFIG_XFRM_MIGRATE
extern int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_bundles);
extern struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m);
extern struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x,
struct xfrm_migrate *m);
extern int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_bundles);
#endif

extern wait_queue_head_t km_waitq;
extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid);
Expand Down Expand Up @@ -1053,5 +1077,25 @@ static inline void xfrm_aevent_doreplay(struct xfrm_state *x)
xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
}

#ifdef CONFIG_XFRM_MIGRATE
static inline struct xfrm_algo *xfrm_algo_clone(struct xfrm_algo *orig)
{
return (struct xfrm_algo *)kmemdup(orig, sizeof(*orig) + orig->alg_key_len, GFP_KERNEL);
}

static inline void xfrm_states_put(struct xfrm_state **states, int n)
{
int i;
for (i = 0; i < n; i++)
xfrm_state_put(*(states + i));
}

static inline void xfrm_states_delete(struct xfrm_state **states, int n)
{
int i;
for (i = 0; i < n; i++)
xfrm_state_delete(*(states + i));
}
#endif

#endif /* _NET_XFRM_H */
230 changes: 230 additions & 0 deletions net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -2236,3 +2236,233 @@ void __init xfrm_init(void)
xfrm_input_init();
}

#ifdef CONFIG_XFRM_MIGRATE
static int xfrm_migrate_selector_match(struct xfrm_selector *sel_cmp,
struct xfrm_selector *sel_tgt)
{
if (sel_cmp->proto == IPSEC_ULPROTO_ANY) {
if (sel_tgt->family == sel_cmp->family &&
xfrm_addr_cmp(&sel_tgt->daddr, &sel_cmp->daddr,
sel_cmp->family) == 0 &&
xfrm_addr_cmp(&sel_tgt->saddr, &sel_cmp->saddr,
sel_cmp->family) == 0 &&
sel_tgt->prefixlen_d == sel_cmp->prefixlen_d &&
sel_tgt->prefixlen_s == sel_cmp->prefixlen_s) {
return 1;
}
} else {
if (memcmp(sel_tgt, sel_cmp, sizeof(*sel_tgt)) == 0) {
return 1;
}
}
return 0;
}

static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel,
u8 dir, u8 type)
{
struct xfrm_policy *pol, *ret = NULL;
struct hlist_node *entry;
struct hlist_head *chain;
u32 priority = ~0U;

read_lock_bh(&xfrm_policy_lock);
chain = policy_hash_direct(&sel->daddr, &sel->saddr, sel->family, dir);
hlist_for_each_entry(pol, entry, chain, bydst) {
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
pol->type == type) {
ret = pol;
priority = ret->priority;
break;
}
}
chain = &xfrm_policy_inexact[dir];
hlist_for_each_entry(pol, entry, chain, bydst) {
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
pol->type == type &&
pol->priority < priority) {
ret = pol;
break;
}
}

if (ret)
xfrm_pol_hold(ret);

read_unlock_bh(&xfrm_policy_lock);

return ret;
}

static int migrate_tmpl_match(struct xfrm_migrate *m, struct xfrm_tmpl *t)
{
int match = 0;

if (t->mode == m->mode && t->id.proto == m->proto &&
(m->reqid == 0 || t->reqid == m->reqid)) {
switch (t->mode) {
case XFRM_MODE_TUNNEL:
case XFRM_MODE_BEET:
if (xfrm_addr_cmp(&t->id.daddr, &m->old_daddr,
m->old_family) == 0 &&
xfrm_addr_cmp(&t->saddr, &m->old_saddr,
m->old_family) == 0) {
match = 1;
}
break;
case XFRM_MODE_TRANSPORT:
/* in case of transport mode, template does not store
any IP addresses, hence we just compare mode and
protocol */
match = 1;
break;
default:
break;
}
}
return match;
}

/* update endpoint address(es) of template(s) */
static int xfrm_policy_migrate(struct xfrm_policy *pol,
struct xfrm_migrate *m, int num_migrate)
{
struct xfrm_migrate *mp;
struct dst_entry *dst;
int i, j, n = 0;

write_lock_bh(&pol->lock);
if (unlikely(pol->dead)) {
/* target policy has been deleted */
write_unlock_bh(&pol->lock);
return -ENOENT;
}

for (i = 0; i < pol->xfrm_nr; i++) {
for (j = 0, mp = m; j < num_migrate; j++, mp++) {
if (!migrate_tmpl_match(mp, &pol->xfrm_vec[i]))
continue;
n++;
if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL)
continue;
/* update endpoints */
memcpy(&pol->xfrm_vec[i].id.daddr, &mp->new_daddr,
sizeof(pol->xfrm_vec[i].id.daddr));
memcpy(&pol->xfrm_vec[i].saddr, &mp->new_saddr,
sizeof(pol->xfrm_vec[i].saddr));
pol->xfrm_vec[i].encap_family = mp->new_family;
/* flush bundles */
while ((dst = pol->bundles) != NULL) {
pol->bundles = dst->next;
dst_free(dst);
}
}
}

write_unlock_bh(&pol->lock);

if (!n)
return -ENODATA;

return 0;
}

static int xfrm_migrate_check(struct xfrm_migrate *m, int num_migrate)
{
int i, j;

if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH)
return -EINVAL;

for (i = 0; i < num_migrate; i++) {
if ((xfrm_addr_cmp(&m[i].old_daddr, &m[i].new_daddr,
m[i].old_family) == 0) &&
(xfrm_addr_cmp(&m[i].old_saddr, &m[i].new_saddr,
m[i].old_family) == 0))
return -EINVAL;
if (xfrm_addr_any(&m[i].new_daddr, m[i].new_family) ||
xfrm_addr_any(&m[i].new_saddr, m[i].new_family))
return -EINVAL;

/* check if there is any duplicated entry */
for (j = i + 1; j < num_migrate; j++) {
if (!memcmp(&m[i].old_daddr, &m[j].old_daddr,
sizeof(m[i].old_daddr)) &&
!memcmp(&m[i].old_saddr, &m[j].old_saddr,
sizeof(m[i].old_saddr)) &&
m[i].proto == m[j].proto &&
m[i].mode == m[j].mode &&
m[i].reqid == m[j].reqid &&
m[i].old_family == m[j].old_family)
return -EINVAL;
}
}

return 0;
}

int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_migrate)
{
int i, err, nx_cur = 0, nx_new = 0;
struct xfrm_policy *pol = NULL;
struct xfrm_state *x, *xc;
struct xfrm_state *x_cur[XFRM_MAX_DEPTH];
struct xfrm_state *x_new[XFRM_MAX_DEPTH];
struct xfrm_migrate *mp;

if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
goto out;

/* Stage 1 - find policy */
if ((pol = xfrm_migrate_policy_find(sel, dir, type)) == NULL) {
err = -ENOENT;
goto out;
}

/* Stage 2 - find and update state(s) */
for (i = 0, mp = m; i < num_migrate; i++, mp++) {
if ((x = xfrm_migrate_state_find(mp))) {
x_cur[nx_cur] = x;
nx_cur++;
if ((xc = xfrm_state_migrate(x, mp))) {
x_new[nx_new] = xc;
nx_new++;
} else {
err = -ENODATA;
goto restore_state;
}
}
}

/* Stage 3 - update policy */
if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0)
goto restore_state;

/* Stage 4 - delete old state(s) */
if (nx_cur) {
xfrm_states_put(x_cur, nx_cur);
xfrm_states_delete(x_cur, nx_cur);
}

/* Stage 5 - announce */
km_migrate(sel, dir, type, m, num_migrate);

xfrm_pol_put(pol);

return 0;
out:
return err;

restore_state:
if (pol)
xfrm_pol_put(pol);
if (nx_cur)
xfrm_states_put(x_cur, nx_cur);
if (nx_new)
xfrm_states_delete(x_new, nx_new);

return err;
}
#endif

Loading

0 comments on commit 80c9aba

Please sign in to comment.