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>
A
Amerigo Wang 已提交
23
#if IS_ENABLED(CONFIG_IPV6_MIP6)
24 25
#include <net/mip6.h>
#endif
L
Linus Torvalds 已提交
26 27 28

static struct xfrm_policy_afinfo xfrm6_policy_afinfo;

29
static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos,
30 31
					  const xfrm_address_t *saddr,
					  const xfrm_address_t *daddr)
L
Linus Torvalds 已提交
32
{
33
	struct flowi6 fl6;
34 35 36
	struct dst_entry *dst;
	int err;

37 38
	memset(&fl6, 0, sizeof(fl6));
	memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
39
	if (saddr)
40
		memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
41

42
	dst = ip6_route_output(net, NULL, &fl6);
43 44 45

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

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

A
Alexey Dobriyan 已提交
53 54
static int xfrm6_get_saddr(struct net *net,
			   xfrm_address_t *saddr, xfrm_address_t *daddr)
55
{
56
	struct dst_entry *dst;
57
	struct net_device *dev;
58

A
Alexey Dobriyan 已提交
59
	dst = xfrm6_dst_lookup(net, 0, NULL, daddr);
60 61 62
	if (IS_ERR(dst))
		return -EHOSTUNREACH;

63 64
	dev = ip6_dst_idev(dst)->dev;
	ipv6_dev_get_saddr(dev_net(dev), dev,
65
			   (struct in6_addr *)&daddr->a6, 0,
66
			   (struct in6_addr *)&saddr->a6);
67 68
	dst_release(dst);
	return 0;
69 70
}

71
static int xfrm6_get_tos(const struct flowi *fl)
L
Linus Torvalds 已提交
72
{
73 74
	return 0;
}
L
Linus Torvalds 已提交
75

76 77 78 79 80 81 82
static void xfrm6_init_dst(struct net *net, struct xfrm_dst *xdst)
{
	struct rt6_info *rt = (struct rt6_info *)xdst;

	rt6_init_peer(rt, net->ipv6.peers);
}

83 84 85 86 87 88 89 90 91 92 93 94 95 96
static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst,
			   int nfheader_len)
{
	if (dst->ops->family == AF_INET6) {
		struct rt6_info *rt = (struct rt6_info*)dst;
		if (rt->rt6i_node)
			path->path_cookie = rt->rt6i_node->fn_sernum;
	}

	path->u.rt6.rt6i_nfheader_len = nfheader_len;

	return 0;
}

H
Herbert Xu 已提交
97
static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
98
			  const struct flowi *fl)
99 100
{
	struct rt6_info *rt = (struct rt6_info*)xdst->route;
L
Linus Torvalds 已提交
101

102 103
	xdst->u.dst.dev = dev;
	dev_hold(dev);
L
Linus Torvalds 已提交
104

105
	xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
106 107
	if (!xdst->u.rt6.rt6i_idev) {
		dev_put(dev);
108
		return -ENODEV;
109
	}
L
Linus Torvalds 已提交
110

111
	rt6_transfer_peer(&xdst->u.rt6, rt);
112

113 114 115 116 117 118
	/* Sheit... I remember I did this right. Apparently,
	 * it was magically lost, so this code needs audit */
	xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
						   RTF_LOCAL);
	xdst->u.rt6.rt6i_metric = rt->rt6i_metric;
	xdst->u.rt6.rt6i_node = rt->rt6i_node;
119 120
	if (rt->rt6i_node)
		xdst->route_cookie = rt->rt6i_node->fn_sernum;
121 122 123
	xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
	xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
	xdst->u.rt6.rt6i_src = rt->rt6i_src;
L
Linus Torvalds 已提交
124 125 126 127 128

	return 0;
}

static inline void
129
_decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
L
Linus Torvalds 已提交
130
{
131
	struct flowi6 *fl6 = &fl->u.ip6;
N
Nicolas Dichtel 已提交
132
	int onlyproto = 0;
133
	u16 offset = skb_network_header_len(skb);
134
	const struct ipv6hdr *hdr = ipv6_hdr(skb);
135
	struct ipv6_opt_hdr *exthdr;
136 137
	const unsigned char *nh = skb_network_header(skb);
	u8 nexthdr = nh[IP6CB(skb)->nhoff];
L
Linus Torvalds 已提交
138

139 140
	memset(fl6, 0, sizeof(struct flowi6));
	fl6->flowi6_mark = skb->mark;
141
	fl6->flowi6_oif = skb_dst(skb)->dev->ifindex;
P
Peter Kosyh 已提交
142

A
Alexey Dobriyan 已提交
143 144
	fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
	fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
L
Linus Torvalds 已提交
145

146 147
	while (nh + offset + 1 < skb->data ||
	       pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
148 149
		nh = skb_network_header(skb);
		exthdr = (struct ipv6_opt_hdr *)(nh + offset);
150

L
Linus Torvalds 已提交
151
		switch (nexthdr) {
N
Nicolas Dichtel 已提交
152 153
		case NEXTHDR_FRAGMENT:
			onlyproto = 1;
L
Linus Torvalds 已提交
154 155 156 157 158
		case NEXTHDR_ROUTING:
		case NEXTHDR_HOP:
		case NEXTHDR_DEST:
			offset += ipv6_optlen(exthdr);
			nexthdr = exthdr->nexthdr;
159
			exthdr = (struct ipv6_opt_hdr *)(nh + offset);
L
Linus Torvalds 已提交
160 161 162
			break;

		case IPPROTO_UDP:
163
		case IPPROTO_UDPLITE:
L
Linus Torvalds 已提交
164 165
		case IPPROTO_TCP:
		case IPPROTO_SCTP:
166
		case IPPROTO_DCCP:
167 168
			if (!onlyproto && (nh + offset + 4 < skb->data ||
			     pskb_may_pull(skb, nh + offset + 4 - skb->data))) {
A
Al Viro 已提交
169
				__be16 *ports = (__be16 *)exthdr;
L
Linus Torvalds 已提交
170

171 172
				fl6->fl6_sport = ports[!!reverse];
				fl6->fl6_dport = ports[!reverse];
L
Linus Torvalds 已提交
173
			}
174
			fl6->flowi6_proto = nexthdr;
L
Linus Torvalds 已提交
175 176 177
			return;

		case IPPROTO_ICMPV6:
N
Nicolas Dichtel 已提交
178
			if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
L
Linus Torvalds 已提交
179 180
				u8 *icmp = (u8 *)exthdr;

181 182
				fl6->fl6_icmp_type = icmp[0];
				fl6->fl6_icmp_code = icmp[1];
L
Linus Torvalds 已提交
183
			}
184
			fl6->flowi6_proto = nexthdr;
L
Linus Torvalds 已提交
185 186
			return;

A
Amerigo Wang 已提交
187
#if IS_ENABLED(CONFIG_IPV6_MIP6)
188
		case IPPROTO_MH:
N
Nicolas Dichtel 已提交
189
			if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
190 191 192
				struct ip6_mh *mh;
				mh = (struct ip6_mh *)exthdr;

193
				fl6->fl6_mh_type = mh->ip6mh_type;
194
			}
195
			fl6->flowi6_proto = nexthdr;
196 197 198
			return;
#endif

L
Linus Torvalds 已提交
199 200 201 202 203
		/* XXX Why are there these headers? */
		case IPPROTO_AH:
		case IPPROTO_ESP:
		case IPPROTO_COMP:
		default:
204
			fl6->fl6_ipsec_spi = 0;
205
			fl6->flowi6_proto = nexthdr;
L
Linus Torvalds 已提交
206
			return;
207
		}
L
Linus Torvalds 已提交
208 209 210
	}
}

211
static inline int xfrm6_garbage_collect(struct dst_ops *ops)
L
Linus Torvalds 已提交
212
{
213 214 215
	struct net *net = container_of(ops, struct net, xfrm.xfrm6_dst_ops);

	xfrm6_policy_afinfo.garbage_collect(net);
216
	return dst_entries_get_fast(ops) > ops->gc_thresh * 2;
L
Linus Torvalds 已提交
217 218
}

219 220
static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
			      struct sk_buff *skb, u32 mtu)
L
Linus Torvalds 已提交
221 222 223 224
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
	struct dst_entry *path = xdst->route;

225
	path->ops->update_pmtu(path, sk, skb, mtu);
L
Linus Torvalds 已提交
226 227
}

228 229
static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
			   struct sk_buff *skb)
230 231 232 233
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
	struct dst_entry *path = xdst->route;

234
	path->ops->redirect(path, sk, skb);
235 236
}

H
Herbert Xu 已提交
237 238 239 240 241 242
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);
243
	dst_destroy_metrics_generic(dst);
244 245 246 247
	if (rt6_has_peer(&xdst->u.rt6)) {
		struct inet_peer *peer = rt6_peer_ptr(&xdst->u.rt6);
		inet_putpeer(peer);
	}
H
Herbert Xu 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260
	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) {
261
		struct inet6_dev *loopback_idev =
262
			in6_dev_get(dev_net(dev)->loopback_dev);
H
Herbert Xu 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
		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 已提交
278 279
static struct dst_ops xfrm6_dst_ops = {
	.family =		AF_INET6,
280
	.protocol =		cpu_to_be16(ETH_P_IPV6),
L
Linus Torvalds 已提交
281 282
	.gc =			xfrm6_garbage_collect,
	.update_pmtu =		xfrm6_update_pmtu,
283
	.redirect =		xfrm6_redirect,
284
	.cow_metrics =		dst_cow_metrics_generic,
H
Herbert Xu 已提交
285 286
	.destroy =		xfrm6_dst_destroy,
	.ifdown =		xfrm6_dst_ifdown,
287
	.local_out =		__ip6_local_out,
L
Linus Torvalds 已提交
288 289 290 291 292 293 294
	.gc_thresh =		1024,
};

static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
	.family =		AF_INET6,
	.dst_ops =		&xfrm6_dst_ops,
	.dst_lookup =		xfrm6_dst_lookup,
295
	.get_saddr = 		xfrm6_get_saddr,
L
Linus Torvalds 已提交
296
	.decode_session =	_decode_session6,
297
	.get_tos =		xfrm6_get_tos,
298
	.init_dst =		xfrm6_init_dst,
299
	.init_path =		xfrm6_init_path,
300
	.fill_dst =		xfrm6_fill_dst,
301
	.blackhole_route =	ip6_blackhole_route,
L
Linus Torvalds 已提交
302 303
};

304
static int __init xfrm6_policy_init(void)
L
Linus Torvalds 已提交
305
{
306
	return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo);
L
Linus Torvalds 已提交
307 308 309 310 311 312 313
}

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

314
#ifdef CONFIG_SYSCTL
315 316 317
static struct ctl_table xfrm6_policy_table[] = {
	{
		.procname       = "xfrm6_gc_thresh",
318
		.data	   	= &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
319 320 321 322 323 324 325
		.maxlen	 	= sizeof(int),
		.mode	   	= 0644,
		.proc_handler   = proc_dointvec,
	},
	{ }
};

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
static int __net_init xfrm6_net_init(struct net *net)
{
	struct ctl_table *table;
	struct ctl_table_header *hdr;

	table = xfrm6_policy_table;
	if (!net_eq(net, &init_net)) {
		table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
		if (!table)
			goto err_alloc;

		table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
	}

	hdr = register_net_sysctl(net, "net/ipv6", table);
	if (!hdr)
		goto err_reg;

	net->ipv6.sysctl.xfrm6_hdr = hdr;
	return 0;

err_reg:
	if (!net_eq(net, &init_net))
		kfree(table);
err_alloc:
	return -ENOMEM;
}

static void __net_exit xfrm6_net_exit(struct net *net)
{
	struct ctl_table *table;

	if (net->ipv6.sysctl.xfrm6_hdr == NULL)
		return;

	table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
	unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
	if (!net_eq(net, &init_net))
		kfree(table);
}

static struct pernet_operations xfrm6_net_ops = {
	.init	= xfrm6_net_init,
	.exit	= xfrm6_net_exit,
};
371
#endif
372

373
int __init xfrm6_init(void)
L
Linus Torvalds 已提交
374
{
375
	int ret;
376

377
	dst_entries_init(&xfrm6_dst_ops);
378 379

	ret = xfrm6_policy_init();
380 381
	if (ret) {
		dst_entries_destroy(&xfrm6_dst_ops);
382
		goto out;
383
	}
384 385 386 387
	ret = xfrm6_state_init();
	if (ret)
		goto out_policy;

388
#ifdef CONFIG_SYSCTL
389
	register_pernet_subsys(&xfrm6_net_ops);
390
#endif
391 392 393 394 395
out:
	return ret;
out_policy:
	xfrm6_policy_fini();
	goto out;
L
Linus Torvalds 已提交
396 397 398 399
}

void xfrm6_fini(void)
{
400
#ifdef CONFIG_SYSCTL
401
	unregister_pernet_subsys(&xfrm6_net_ops);
402
#endif
L
Linus Torvalds 已提交
403 404
	xfrm6_policy_fini();
	xfrm6_state_fini();
405
	dst_entries_destroy(&xfrm6_dst_ops);
L
Linus Torvalds 已提交
406
}