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

static struct xfrm_policy_afinfo xfrm6_policy_afinfo;

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

38
	memset(&fl6, 0, sizeof(fl6));
D
David Ahern 已提交
39
	fl6.flowi6_oif = oif;
40
	memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
41
	if (saddr)
42
		memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
43

44
	dst = ip6_route_output(net, NULL, &fl6);
45 46 47

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

	return dst;
L
Linus Torvalds 已提交
53 54
}

D
David Ahern 已提交
55
static int xfrm6_get_saddr(struct net *net, int oif,
A
Alexey Dobriyan 已提交
56
			   xfrm_address_t *saddr, xfrm_address_t *daddr)
57
{
58
	struct dst_entry *dst;
59
	struct net_device *dev;
60

D
David Ahern 已提交
61
	dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr);
62 63 64
	if (IS_ERR(dst))
		return -EHOSTUNREACH;

65
	dev = ip6_dst_idev(dst)->dev;
J
Jiri Benc 已提交
66
	ipv6_dev_get_saddr(dev_net(dev), dev, &daddr->in6, 0, &saddr->in6);
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
static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst,
			   int nfheader_len)
{
	if (dst->ops->family == AF_INET6) {
80
		struct rt6_info *rt = (struct rt6_info *)dst;
81
		path->path_cookie = rt6_get_cookie(rt);
82 83 84 85 86 87 88
	}

	path->u.rt6.rt6i_nfheader_len = nfheader_len;

	return 0;
}

H
Herbert Xu 已提交
89
static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
90
			  const struct flowi *fl)
91
{
92
	struct rt6_info *rt = (struct rt6_info *)xdst->route;
L
Linus Torvalds 已提交
93

94 95
	xdst->u.dst.dev = dev;
	dev_hold(dev);
L
Linus Torvalds 已提交
96

97
	xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
98 99
	if (!xdst->u.rt6.rt6i_idev) {
		dev_put(dev);
100
		return -ENODEV;
101
	}
L
Linus Torvalds 已提交
102

103 104 105 106 107 108
	/* 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;
109
	xdst->route_cookie = rt6_get_cookie(rt);
110 111 112
	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 已提交
113 114 115 116 117

	return 0;
}

static inline void
118
_decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
L
Linus Torvalds 已提交
119
{
120
	struct flowi6 *fl6 = &fl->u.ip6;
N
Nicolas Dichtel 已提交
121
	int onlyproto = 0;
122
	const struct ipv6hdr *hdr = ipv6_hdr(skb);
123
	u16 offset = sizeof(*hdr);
124
	struct ipv6_opt_hdr *exthdr;
125
	const unsigned char *nh = skb_network_header(skb);
126
	u16 nhoff = IP6CB(skb)->nhoff;
127
	int oif = 0;
128 129 130 131 132 133
	u8 nexthdr;

	if (!nhoff)
		nhoff = offsetof(struct ipv6hdr, nexthdr);

	nexthdr = nh[nhoff];
134

135 136 137 138
	if (skb_dst(skb)) {
		oif = vrf_master_ifindex(skb_dst(skb)->dev) ?
			: skb_dst(skb)->dev->ifindex;
	}
L
Linus Torvalds 已提交
139

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

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

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

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

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

172 173
				nh = skb_network_header(skb);
				ports = (__be16 *)(nh + offset);
174 175
				fl6->fl6_sport = ports[!!reverse];
				fl6->fl6_dport = ports[!reverse];
L
Linus Torvalds 已提交
176
			}
177
			fl6->flowi6_proto = nexthdr;
L
Linus Torvalds 已提交
178 179 180
			return;

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

184 185
				nh = skb_network_header(skb);
				icmp = (u8 *)(nh + offset);
186 187
				fl6->fl6_icmp_type = icmp[0];
				fl6->fl6_icmp_code = icmp[1];
L
Linus Torvalds 已提交
188
			}
189
			fl6->flowi6_proto = nexthdr;
L
Linus Torvalds 已提交
190 191
			return;

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

198 199
				nh = skb_network_header(skb);
				mh = (struct ip6_mh *)(nh + offset);
200
				fl6->fl6_mh_type = mh->ip6mh_type;
201
			}
202
			fl6->flowi6_proto = nexthdr;
203 204 205
			return;
#endif

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

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

	xfrm6_policy_afinfo.garbage_collect(net);
223
	return dst_entries_get_fast(ops) > ops->gc_thresh * 2;
L
Linus Torvalds 已提交
224 225
}

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

232
	path->ops->update_pmtu(path, sk, skb, mtu);
L
Linus Torvalds 已提交
233 234
}

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

241
	path->ops->redirect(path, sk, skb);
242 243
}

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

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

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

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

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

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
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;

359
	if (!net->ipv6.sysctl.xfrm6_hdr)
360 361 362 363 364 365 366 367 368 369 370 371
		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,
};
372
#endif
373

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

378
	dst_entries_init(&xfrm6_dst_ops);
379 380

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

389 390 391 392
	ret = xfrm6_protocol_init();
	if (ret)
		goto out_state;

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

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