xfrm6_policy.c 9.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * xfrm6_policy.c: based on xfrm4_policy.c
 *
 * Authors:
 *	Mitsuru KANDA @USAGI
 * 	Kazunori MIYAZAWA @USAGI
 * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
 * 		IPv6 support
 * 	YOSHIFUJI Hideaki
 * 		Split up af-specific portion
11
 *
L
Linus Torvalds 已提交
12 13
 */

14 15
#include <linux/err.h>
#include <linux/kernel.h>
H
Herbert Xu 已提交
16 17
#include <linux/netdevice.h>
#include <net/addrconf.h>
18
#include <net/dst.h>
L
Linus Torvalds 已提交
19 20 21 22
#include <net/xfrm.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
23
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
24 25
#include <net/mip6.h>
#endif
L
Linus Torvalds 已提交
26 27 28 29

static struct dst_ops xfrm6_dst_ops;
static struct xfrm_policy_afinfo xfrm6_policy_afinfo;

30 31
static struct dst_entry *xfrm6_dst_lookup(int tos, xfrm_address_t *saddr,
					  xfrm_address_t *daddr)
L
Linus Torvalds 已提交
32
{
33 34 35 36 37 38 39 40 41 42 43 44
	struct flowi fl = {};
	struct dst_entry *dst;
	int err;

	memcpy(&fl.fl6_dst, daddr, sizeof(fl.fl6_dst));
	if (saddr)
		memcpy(&fl.fl6_src, saddr, sizeof(fl.fl6_src));

	dst = ip6_route_output(NULL, &fl);

	err = dst->error;
	if (dst->error) {
45
		dst_release(dst);
46 47 48 49
		dst = ERR_PTR(err);
	}

	return dst;
L
Linus Torvalds 已提交
50 51
}

52 53
static int xfrm6_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
{
54 55 56 57 58 59 60 61 62 63
	struct dst_entry *dst;

	dst = xfrm6_dst_lookup(0, NULL, daddr);
	if (IS_ERR(dst))
		return -EHOSTUNREACH;

	ipv6_get_saddr(dst, (struct in6_addr *)&daddr->a6,
		       (struct in6_addr *)&saddr->a6);
	dst_release(dst);
	return 0;
64 65
}

L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
static struct dst_entry *
__xfrm6_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
{
	struct dst_entry *dst;

	/* Still not clear if we should set fl->fl6_{src,dst}... */
	read_lock_bh(&policy->lock);
	for (dst = policy->bundles; dst; dst = dst->next) {
		struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
		struct in6_addr fl_dst_prefix, fl_src_prefix;

		ipv6_addr_prefix(&fl_dst_prefix,
				 &fl->fl6_dst,
				 xdst->u.rt6.rt6i_dst.plen);
		ipv6_addr_prefix(&fl_src_prefix,
				 &fl->fl6_src,
				 xdst->u.rt6.rt6i_src.plen);
		if (ipv6_addr_equal(&xdst->u.rt6.rt6i_dst.addr, &fl_dst_prefix) &&
		    ipv6_addr_equal(&xdst->u.rt6.rt6i_src.addr, &fl_src_prefix) &&
85
		    xfrm_bundle_ok(policy, xdst, fl, AF_INET6,
86 87
				   (xdst->u.rt6.rt6i_dst.plen != 128 ||
				    xdst->u.rt6.rt6i_src.plen != 128))) {
L
Linus Torvalds 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
			dst_clone(dst);
			break;
		}
	}
	read_unlock_bh(&policy->lock);
	return dst;
}

/* Allocate chain of dst_entry's, attach known xfrm's, calculate
 * all the metrics... Shortly, bundle a bundle.
 */

static int
__xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
		      struct flowi *fl, struct dst_entry **dst_p)
{
	struct dst_entry *dst, *dst_prev;
	struct rt6_info *rt0 = (struct rt6_info*)(*dst_p);
	struct rt6_info *rt  = rt0;
	int i;
108
	int err;
L
Linus Torvalds 已提交
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
	int header_len = 0;
	int trailer_len = 0;

	dst = dst_prev = NULL;
	dst_hold(&rt->u.dst);

	for (i = 0; i < nx; i++) {
		struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops);
		struct xfrm_dst *xdst;

		if (unlikely(dst1 == NULL)) {
			err = -ENOBUFS;
			dst_release(&rt->u.dst);
			goto error;
		}

		if (!dst)
			dst = dst1;
		else {
			dst_prev->child = dst1;
			dst1->flags |= DST_NOHASH;
			dst_clone(dst1);
		}

		xdst = (struct xfrm_dst *)dst1;
		xdst->route = &rt->u.dst;
135
		xdst->genid = xfrm[i]->genid;
136 137
		if (rt->rt6i_node)
			xdst->route_cookie = rt->rt6i_node->fn_sernum;
L
Linus Torvalds 已提交
138 139 140

		dst1->next = dst_prev;
		dst_prev = dst1;
141

142
		if (xfrm[i]->type->flags & XFRM_TYPE_NON_FRAGMENT)
143 144
			((struct rt6_info *)dst)->nfheader_len +=
				xfrm[i]->props.header_len;
145
		header_len += xfrm[i]->props.header_len;
L
Linus Torvalds 已提交
146 147
		trailer_len += xfrm[i]->props.trailer_len;

H
Herbert Xu 已提交
148
		if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
149 150 151
			dst1 = xfrm_dst_lookup(xfrm[i], 0);
			err = PTR_ERR(dst1);
			if (IS_ERR(dst1))
L
Linus Torvalds 已提交
152
				goto error;
153 154

			rt = (struct rt6_info *)dst1;
L
Linus Torvalds 已提交
155 156 157 158 159 160
		} else
			dst_hold(&rt->u.dst);
	}

	dst_prev->child = &rt->u.dst;
	dst->path = &rt->u.dst;
161 162 163 164

	/* Copy neighbour for reachability confirmation */
	dst->neighbour = neigh_clone(rt->u.dst.neighbour);

165 166
	if (rt->rt6i_node)
		((struct xfrm_dst *)dst)->path_cookie = rt->rt6i_node->fn_sernum;
L
Linus Torvalds 已提交
167 168 169 170 171 172

	*dst_p = dst;
	dst = dst_prev;

	dst_prev = *dst_p;
	i = 0;
173
	err = -ENODEV;
L
Linus Torvalds 已提交
174 175 176 177 178
	for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
		struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;

		dst_prev->xfrm = xfrm[i++];
		dst_prev->dev = rt->u.dst.dev;
179 180 181 182 183 184 185 186
		if (!rt->u.dst.dev)
			goto error;
		dev_hold(rt->u.dst.dev);

		x->u.rt6.rt6i_idev = in6_dev_get(rt->u.dst.dev);
		if (!x->u.rt6.rt6i_idev)
			goto error;

L
Linus Torvalds 已提交
187 188 189 190 191 192 193
		dst_prev->obsolete	= -1;
		dst_prev->flags	       |= DST_HOST;
		dst_prev->lastuse	= jiffies;
		dst_prev->header_len	= header_len;
		dst_prev->trailer_len	= trailer_len;
		memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics));

194
		dst_prev->input = dst_discard;
195
		dst_prev->output = dst_prev->xfrm->outer_mode->afinfo->output;
L
Linus Torvalds 已提交
196 197
		/* Sheit... I remember I did this right. Apparently,
		 * it was magically lost, so this code needs audit */
198
		x->u.rt6.rt6i_flags    = rt0->rt6i_flags&(RTF_ANYCAST|RTF_LOCAL);
L
Linus Torvalds 已提交
199 200 201
		x->u.rt6.rt6i_metric   = rt0->rt6i_metric;
		x->u.rt6.rt6i_node     = rt0->rt6i_node;
		x->u.rt6.rt6i_gateway  = rt0->rt6i_gateway;
202
		memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway));
L
Linus Torvalds 已提交
203
		x->u.rt6.rt6i_dst      = rt0->rt6i_dst;
204
		x->u.rt6.rt6i_src      = rt0->rt6i_src;
205
		header_len -= x->u.dst.xfrm->props.header_len;
L
Linus Torvalds 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
		trailer_len -= x->u.dst.xfrm->props.trailer_len;
	}

	xfrm_init_pmtu(dst);
	return 0;

error:
	if (dst)
		dst_free(dst);
	return err;
}

static inline void
_decode_session6(struct sk_buff *skb, struct flowi *fl)
{
221
	u16 offset = skb_network_header_len(skb);
222
	struct ipv6hdr *hdr = ipv6_hdr(skb);
223
	struct ipv6_opt_hdr *exthdr;
224 225
	const unsigned char *nh = skb_network_header(skb);
	u8 nexthdr = nh[IP6CB(skb)->nhoff];
L
Linus Torvalds 已提交
226 227 228 229 230

	memset(fl, 0, sizeof(struct flowi));
	ipv6_addr_copy(&fl->fl6_dst, &hdr->daddr);
	ipv6_addr_copy(&fl->fl6_src, &hdr->saddr);

231 232 233
	while (pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
		nh = skb_network_header(skb);
		exthdr = (struct ipv6_opt_hdr *)(nh + offset);
234

L
Linus Torvalds 已提交
235 236 237 238 239 240
		switch (nexthdr) {
		case NEXTHDR_ROUTING:
		case NEXTHDR_HOP:
		case NEXTHDR_DEST:
			offset += ipv6_optlen(exthdr);
			nexthdr = exthdr->nexthdr;
241
			exthdr = (struct ipv6_opt_hdr *)(nh + offset);
L
Linus Torvalds 已提交
242 243 244
			break;

		case IPPROTO_UDP:
245
		case IPPROTO_UDPLITE:
L
Linus Torvalds 已提交
246 247
		case IPPROTO_TCP:
		case IPPROTO_SCTP:
248
		case IPPROTO_DCCP:
249
			if (pskb_may_pull(skb, nh + offset + 4 - skb->data)) {
A
Al Viro 已提交
250
				__be16 *ports = (__be16 *)exthdr;
L
Linus Torvalds 已提交
251 252 253 254 255 256 257 258

				fl->fl_ip_sport = ports[0];
				fl->fl_ip_dport = ports[1];
			}
			fl->proto = nexthdr;
			return;

		case IPPROTO_ICMPV6:
259
			if (pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
L
Linus Torvalds 已提交
260 261 262 263 264 265 266 267
				u8 *icmp = (u8 *)exthdr;

				fl->fl_icmp_type = icmp[0];
				fl->fl_icmp_code = icmp[1];
			}
			fl->proto = nexthdr;
			return;

268
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
269
		case IPPROTO_MH:
270
			if (pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
271 272 273 274 275 276 277 278 279
				struct ip6_mh *mh;
				mh = (struct ip6_mh *)exthdr;

				fl->fl_mh_type = mh->ip6mh_type;
			}
			fl->proto = nexthdr;
			return;
#endif

L
Linus Torvalds 已提交
280 281 282 283 284 285 286 287
		/* XXX Why are there these headers? */
		case IPPROTO_AH:
		case IPPROTO_ESP:
		case IPPROTO_COMP:
		default:
			fl->fl_ipsec_spi = 0;
			fl->proto = nexthdr;
			return;
288
		}
L
Linus Torvalds 已提交
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
	}
}

static inline int xfrm6_garbage_collect(void)
{
	xfrm6_policy_afinfo.garbage_collect();
	return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2);
}

static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu)
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
	struct dst_entry *path = xdst->route;

	path->ops->update_pmtu(path, mtu);
}

H
Herbert Xu 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
static void xfrm6_dst_destroy(struct dst_entry *dst)
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;

	if (likely(xdst->u.rt6.rt6i_idev))
		in6_dev_put(xdst->u.rt6.rt6i_idev);
	xfrm_dst_destroy(xdst);
}

static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
			     int unregister)
{
	struct xfrm_dst *xdst;

	if (!unregister)
		return;

	xdst = (struct xfrm_dst *)dst;
	if (xdst->u.rt6.rt6i_idev->dev == dev) {
325
		struct inet6_dev *loopback_idev = in6_dev_get(init_net.loopback_dev);
H
Herbert Xu 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
		BUG_ON(!loopback_idev);

		do {
			in6_dev_put(xdst->u.rt6.rt6i_idev);
			xdst->u.rt6.rt6i_idev = loopback_idev;
			in6_dev_hold(loopback_idev);
			xdst = (struct xfrm_dst *)xdst->u.dst.child;
		} while (xdst->u.dst.xfrm);

		__in6_dev_put(loopback_idev);
	}

	xfrm_dst_ifdown(dst, dev);
}

L
Linus Torvalds 已提交
341 342 343 344 345
static struct dst_ops xfrm6_dst_ops = {
	.family =		AF_INET6,
	.protocol =		__constant_htons(ETH_P_IPV6),
	.gc =			xfrm6_garbage_collect,
	.update_pmtu =		xfrm6_update_pmtu,
H
Herbert Xu 已提交
346 347
	.destroy =		xfrm6_dst_destroy,
	.ifdown =		xfrm6_dst_ifdown,
L
Linus Torvalds 已提交
348 349 350 351 352 353 354 355
	.gc_thresh =		1024,
	.entry_size =		sizeof(struct xfrm_dst),
};

static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
	.family =		AF_INET6,
	.dst_ops =		&xfrm6_dst_ops,
	.dst_lookup =		xfrm6_dst_lookup,
356
	.get_saddr = 		xfrm6_get_saddr,
L
Linus Torvalds 已提交
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
	.find_bundle =		__xfrm6_find_bundle,
	.bundle_create =	__xfrm6_bundle_create,
	.decode_session =	_decode_session6,
};

static void __init xfrm6_policy_init(void)
{
	xfrm_policy_register_afinfo(&xfrm6_policy_afinfo);
}

static void xfrm6_policy_fini(void)
{
	xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
}

void __init xfrm6_init(void)
{
	xfrm6_policy_init();
	xfrm6_state_init();
}

void xfrm6_fini(void)
{
	//xfrm6_input_fini();
	xfrm6_policy_fini();
	xfrm6_state_fini();
}