xfrm6_policy.c 9.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * xfrm6_policy.c: based on xfrm4_policy.c
 *
 * Authors:
 *	Mitsuru KANDA @USAGI
6 7 8 9 10
 *	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
static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst,
			   int nfheader_len)
{
	if (dst->ops->family == AF_INET6) {
87
		struct rt6_info *rt = (struct rt6_info *)dst;
88 89 90 91 92 93 94 95 96
		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];
138 139 140 141
	int oif = 0;

	if (skb_dst(skb))
		oif = skb_dst(skb)->dev->ifindex;
L
Linus Torvalds 已提交
142

143 144
	memset(fl6, 0, sizeof(struct flowi6));
	fl6->flowi6_mark = skb->mark;
145
	fl6->flowi6_oif = reverse ? skb->skb_iif : oif;
P
Peter Kosyh 已提交
146

A
Alexey Dobriyan 已提交
147 148
	fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
	fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
L
Linus Torvalds 已提交
149

150 151
	while (nh + offset + 1 < skb->data ||
	       pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
152 153
		nh = skb_network_header(skb);
		exthdr = (struct ipv6_opt_hdr *)(nh + offset);
154

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

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

175 176
				fl6->fl6_sport = ports[!!reverse];
				fl6->fl6_dport = ports[!reverse];
L
Linus Torvalds 已提交
177
			}
178
			fl6->flowi6_proto = nexthdr;
L
Linus Torvalds 已提交
179 180 181
			return;

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

185 186
				fl6->fl6_icmp_type = icmp[0];
				fl6->fl6_icmp_code = icmp[1];
L
Linus Torvalds 已提交
187
			}
188
			fl6->flowi6_proto = nexthdr;
L
Linus Torvalds 已提交
189 190
			return;

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

197
				fl6->fl6_mh_type = mh->ip6mh_type;
198
			}
199
			fl6->flowi6_proto = nexthdr;
200 201 202
			return;
#endif

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

215
static inline int xfrm6_garbage_collect(struct dst_ops *ops)
L
Linus Torvalds 已提交
216
{
217 218 219
	struct net *net = container_of(ops, struct net, xfrm.xfrm6_dst_ops);

	xfrm6_policy_afinfo.garbage_collect(net);
220
	return dst_entries_get_fast(ops) > ops->gc_thresh * 2;
L
Linus Torvalds 已提交
221 222
}

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

229
	path->ops->update_pmtu(path, sk, skb, mtu);
L
Linus Torvalds 已提交
230 231
}

232 233
static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
			   struct sk_buff *skb)
234 235 236 237
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
	struct dst_entry *path = xdst->route;

238
	path->ops->redirect(path, sk, skb);
239 240
}

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

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

308
static int __init xfrm6_policy_init(void)
L
Linus Torvalds 已提交
309
{
310
	return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo);
L
Linus Torvalds 已提交
311 312 313 314 315 316 317
}

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

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

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 371 372 373 374
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,
};
375
#endif
376

377
int __init xfrm6_init(void)
L
Linus Torvalds 已提交
378
{
379
	int ret;
380

381
	dst_entries_init(&xfrm6_dst_ops);
382 383

	ret = xfrm6_policy_init();
384 385
	if (ret) {
		dst_entries_destroy(&xfrm6_dst_ops);
386
		goto out;
387
	}
388 389 390 391
	ret = xfrm6_state_init();
	if (ret)
		goto out_policy;

392 393 394 395
	ret = xfrm6_protocol_init();
	if (ret)
		goto out_state;

396
#ifdef CONFIG_SYSCTL
397
	register_pernet_subsys(&xfrm6_net_ops);
398
#endif
399 400
out:
	return ret;
401 402
out_state:
	xfrm6_state_fini();
403 404 405
out_policy:
	xfrm6_policy_fini();
	goto out;
L
Linus Torvalds 已提交
406 407 408 409
}

void xfrm6_fini(void)
{
410
#ifdef CONFIG_SYSCTL
411
	unregister_pernet_subsys(&xfrm6_net_ops);
412
#endif
413
	xfrm6_protocol_fini();
L
Linus Torvalds 已提交
414 415
	xfrm6_policy_fini();
	xfrm6_state_fini();
416
	dst_entries_destroy(&xfrm6_dst_ops);
L
Linus Torvalds 已提交
417
}