You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
4.6 KiB
198 lines
4.6 KiB
/* |
|
* xfrm6_state.c: based on xfrm4_state.c |
|
* |
|
* Authors: |
|
* Mitsuru KANDA @USAGI |
|
* Kazunori MIYAZAWA @USAGI |
|
* Kunihiro Ishiguro <kunihiro@ipinfusion.com> |
|
* IPv6 support |
|
* YOSHIFUJI Hideaki @USAGI |
|
* Split up af-specific portion |
|
* |
|
*/ |
|
|
|
#include <net/xfrm.h> |
|
#include <linux/pfkeyv2.h> |
|
#include <linux/ipsec.h> |
|
#include <linux/netfilter_ipv6.h> |
|
#include <linux/export.h> |
|
#include <net/dsfield.h> |
|
#include <net/ipv6.h> |
|
#include <net/addrconf.h> |
|
|
|
static void |
|
__xfrm6_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) |
|
{ |
|
const struct flowi6 *fl6 = &fl->u.ip6; |
|
|
|
/* Initialize temporary selector matching only |
|
* to current session. */ |
|
*(struct in6_addr *)&sel->daddr = fl6->daddr; |
|
*(struct in6_addr *)&sel->saddr = fl6->saddr; |
|
sel->dport = xfrm_flowi_dport(fl, &fl6->uli); |
|
sel->dport_mask = htons(0xffff); |
|
sel->sport = xfrm_flowi_sport(fl, &fl6->uli); |
|
sel->sport_mask = htons(0xffff); |
|
sel->family = AF_INET6; |
|
sel->prefixlen_d = 128; |
|
sel->prefixlen_s = 128; |
|
sel->proto = fl6->flowi6_proto; |
|
sel->ifindex = fl6->flowi6_oif; |
|
} |
|
|
|
static void |
|
xfrm6_init_temprop(struct xfrm_state *x, const struct xfrm_tmpl *tmpl, |
|
const xfrm_address_t *daddr, const xfrm_address_t *saddr) |
|
{ |
|
x->id = tmpl->id; |
|
if (ipv6_addr_any((struct in6_addr *)&x->id.daddr)) |
|
memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); |
|
memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr)); |
|
if (ipv6_addr_any((struct in6_addr *)&x->props.saddr)) |
|
memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr)); |
|
x->props.mode = tmpl->mode; |
|
x->props.reqid = tmpl->reqid; |
|
x->props.family = AF_INET6; |
|
} |
|
|
|
/* distribution counting sort function for xfrm_state and xfrm_tmpl */ |
|
static int |
|
__xfrm6_sort(void **dst, void **src, int n, int (*cmp)(void *p), int maxclass) |
|
{ |
|
int i; |
|
int class[XFRM_MAX_DEPTH]; |
|
int count[maxclass]; |
|
|
|
memset(count, 0, sizeof(count)); |
|
|
|
for (i = 0; i < n; i++) { |
|
int c; |
|
class[i] = c = cmp(src[i]); |
|
count[c]++; |
|
} |
|
|
|
for (i = 2; i < maxclass; i++) |
|
count[i] += count[i - 1]; |
|
|
|
for (i = 0; i < n; i++) { |
|
dst[count[class[i] - 1]++] = src[i]; |
|
src[i] = NULL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/* |
|
* Rule for xfrm_state: |
|
* |
|
* rule 1: select IPsec transport except AH |
|
* rule 2: select MIPv6 RO or inbound trigger |
|
* rule 3: select IPsec transport AH |
|
* rule 4: select IPsec tunnel |
|
* rule 5: others |
|
*/ |
|
static int __xfrm6_state_sort_cmp(void *p) |
|
{ |
|
struct xfrm_state *v = p; |
|
|
|
switch (v->props.mode) { |
|
case XFRM_MODE_TRANSPORT: |
|
if (v->id.proto != IPPROTO_AH) |
|
return 1; |
|
else |
|
return 3; |
|
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
|
case XFRM_MODE_ROUTEOPTIMIZATION: |
|
case XFRM_MODE_IN_TRIGGER: |
|
return 2; |
|
#endif |
|
case XFRM_MODE_TUNNEL: |
|
case XFRM_MODE_BEET: |
|
return 4; |
|
} |
|
return 5; |
|
} |
|
|
|
static int |
|
__xfrm6_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n) |
|
{ |
|
return __xfrm6_sort((void **)dst, (void **)src, n, |
|
__xfrm6_state_sort_cmp, 6); |
|
} |
|
|
|
/* |
|
* Rule for xfrm_tmpl: |
|
* |
|
* rule 1: select IPsec transport |
|
* rule 2: select MIPv6 RO or inbound trigger |
|
* rule 3: select IPsec tunnel |
|
* rule 4: others |
|
*/ |
|
static int __xfrm6_tmpl_sort_cmp(void *p) |
|
{ |
|
struct xfrm_tmpl *v = p; |
|
switch (v->mode) { |
|
case XFRM_MODE_TRANSPORT: |
|
return 1; |
|
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
|
case XFRM_MODE_ROUTEOPTIMIZATION: |
|
case XFRM_MODE_IN_TRIGGER: |
|
return 2; |
|
#endif |
|
case XFRM_MODE_TUNNEL: |
|
case XFRM_MODE_BEET: |
|
return 3; |
|
} |
|
return 4; |
|
} |
|
|
|
static int |
|
__xfrm6_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n) |
|
{ |
|
return __xfrm6_sort((void **)dst, (void **)src, n, |
|
__xfrm6_tmpl_sort_cmp, 5); |
|
} |
|
|
|
int xfrm6_extract_header(struct sk_buff *skb) |
|
{ |
|
struct ipv6hdr *iph = ipv6_hdr(skb); |
|
|
|
XFRM_MODE_SKB_CB(skb)->ihl = sizeof(*iph); |
|
XFRM_MODE_SKB_CB(skb)->id = 0; |
|
XFRM_MODE_SKB_CB(skb)->frag_off = htons(IP_DF); |
|
XFRM_MODE_SKB_CB(skb)->tos = ipv6_get_dsfield(iph); |
|
XFRM_MODE_SKB_CB(skb)->ttl = iph->hop_limit; |
|
XFRM_MODE_SKB_CB(skb)->optlen = 0; |
|
memcpy(XFRM_MODE_SKB_CB(skb)->flow_lbl, iph->flow_lbl, |
|
sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl)); |
|
|
|
return 0; |
|
} |
|
|
|
static struct xfrm_state_afinfo xfrm6_state_afinfo = { |
|
.family = AF_INET6, |
|
.proto = IPPROTO_IPV6, |
|
.eth_proto = htons(ETH_P_IPV6), |
|
.owner = THIS_MODULE, |
|
.init_tempsel = __xfrm6_init_tempsel, |
|
.init_temprop = xfrm6_init_temprop, |
|
.tmpl_sort = __xfrm6_tmpl_sort, |
|
.state_sort = __xfrm6_state_sort, |
|
.output = xfrm6_output, |
|
.output_finish = xfrm6_output_finish, |
|
.extract_input = xfrm6_extract_input, |
|
.extract_output = xfrm6_extract_output, |
|
.transport_finish = xfrm6_transport_finish, |
|
.local_error = xfrm6_local_error, |
|
}; |
|
|
|
int __init xfrm6_state_init(void) |
|
{ |
|
return xfrm_state_register_afinfo(&xfrm6_state_afinfo); |
|
} |
|
|
|
void xfrm6_state_fini(void) |
|
{ |
|
xfrm_state_unregister_afinfo(&xfrm6_state_afinfo); |
|
} |
|
|
|
|