xfrm6_state.c 4.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * 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
11
 *
L
Linus Torvalds 已提交
12 13 14 15 16
 */

#include <net/xfrm.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
17
#include <linux/netfilter_ipv6.h>
18
#include <net/dsfield.h>
L
Linus Torvalds 已提交
19
#include <net/ipv6.h>
20
#include <net/addrconf.h>
L
Linus Torvalds 已提交
21 22 23 24 25 26 27 28 29 30 31 32 33

static struct xfrm_state_afinfo xfrm6_state_afinfo;

static void
__xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl,
		     struct xfrm_tmpl *tmpl,
		     xfrm_address_t *daddr, xfrm_address_t *saddr)
{
	/* Initialize temporary selector matching only
	 * to current session. */
	ipv6_addr_copy((struct in6_addr *)&x->sel.daddr, &fl->fl6_dst);
	ipv6_addr_copy((struct in6_addr *)&x->sel.saddr, &fl->fl6_src);
	x->sel.dport = xfrm_flowi_dport(fl);
34
	x->sel.dport_mask = htons(0xffff);
L
Linus Torvalds 已提交
35
	x->sel.sport = xfrm_flowi_sport(fl);
36
	x->sel.sport_mask = htons(0xffff);
L
Linus Torvalds 已提交
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
	x->sel.prefixlen_d = 128;
	x->sel.prefixlen_s = 128;
	x->sel.proto = fl->proto;
	x->sel.ifindex = fl->oif;
	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;
}

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
static int
__xfrm6_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n)
{
	int i;
	int j = 0;

	/* Rule 1: select IPsec transport except AH */
	for (i = 0; i < n; i++) {
		if (src[i]->props.mode == XFRM_MODE_TRANSPORT &&
		    src[i]->id.proto != IPPROTO_AH) {
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}
	if (j == n)
		goto end;

69
	/* Rule 2: select MIPv6 RO or inbound trigger */
70
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
71 72 73 74 75 76 77 78 79 80 81
	for (i = 0; i < n; i++) {
		if (src[i] &&
		    (src[i]->props.mode == XFRM_MODE_ROUTEOPTIMIZATION ||
		     src[i]->props.mode == XFRM_MODE_IN_TRIGGER)) {
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}
	if (j == n)
		goto end;
#endif
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

	/* Rule 3: select IPsec transport AH */
	for (i = 0; i < n; i++) {
		if (src[i] &&
		    src[i]->props.mode == XFRM_MODE_TRANSPORT &&
		    src[i]->id.proto == IPPROTO_AH) {
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}
	if (j == n)
		goto end;

	/* Rule 4: select IPsec tunnel */
	for (i = 0; i < n; i++) {
		if (src[i] &&
H
Herbert Xu 已提交
98 99
		    (src[i]->props.mode == XFRM_MODE_TUNNEL ||
		     src[i]->props.mode == XFRM_MODE_BEET)) {
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}
	if (likely(j == n))
		goto end;

	/* Final rule */
	for (i = 0; i < n; i++) {
		if (src[i]) {
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}

 end:
	return 0;
}

static int
__xfrm6_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n)
{
	int i;
	int j = 0;

	/* Rule 1: select IPsec transport */
	for (i = 0; i < n; i++) {
		if (src[i]->mode == XFRM_MODE_TRANSPORT) {
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}
	if (j == n)
		goto end;

135
	/* Rule 2: select MIPv6 RO or inbound trigger */
136
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
137 138 139 140 141 142 143 144 145 146 147
	for (i = 0; i < n; i++) {
		if (src[i] &&
		    (src[i]->mode == XFRM_MODE_ROUTEOPTIMIZATION ||
		     src[i]->mode == XFRM_MODE_IN_TRIGGER)) {
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}
	if (j == n)
		goto end;
#endif
148 149 150 151

	/* Rule 3: select IPsec tunnel */
	for (i = 0; i < n; i++) {
		if (src[i] &&
H
Herbert Xu 已提交
152 153
		    (src[i]->mode == XFRM_MODE_TUNNEL ||
		     src[i]->mode == XFRM_MODE_BEET)) {
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}
	if (likely(j == n))
		goto end;

	/* Final rule */
	for (i = 0; i < n; i++) {
		if (src[i]) {
			dst[j++] = src[i];
			src[i] = NULL;
		}
	}

 end:
	return 0;
}

173 174 175 176 177 178 179 180 181 182 183 184 185 186
int xfrm6_extract_header(struct sk_buff *skb)
{
	struct ipv6hdr *iph = ipv6_hdr(skb);

	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;
	memcpy(XFRM_MODE_SKB_CB(skb)->flow_lbl, iph->flow_lbl,
	       sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl));

	return 0;
}

L
Linus Torvalds 已提交
187 188
static struct xfrm_state_afinfo xfrm6_state_afinfo = {
	.family			= AF_INET6,
189
	.proto			= IPPROTO_IPV6,
190
	.eth_proto		= htons(ETH_P_IPV6),
191
	.owner			= THIS_MODULE,
L
Linus Torvalds 已提交
192
	.init_tempsel		= __xfrm6_init_tempsel,
193 194
	.tmpl_sort		= __xfrm6_tmpl_sort,
	.state_sort		= __xfrm6_state_sort,
195
	.output			= xfrm6_output,
196
	.extract_input		= xfrm6_extract_input,
197
	.extract_output		= xfrm6_extract_output,
198
	.transport_finish	= xfrm6_transport_finish,
L
Linus Torvalds 已提交
199 200
};

201
int __init xfrm6_state_init(void)
L
Linus Torvalds 已提交
202
{
203
	return xfrm_state_register_afinfo(&xfrm6_state_afinfo);
L
Linus Torvalds 已提交
204 205 206 207 208 209 210
}

void xfrm6_state_fini(void)
{
	xfrm_state_unregister_afinfo(&xfrm6_state_afinfo);
}