xfrm4_policy.c 7.3 KB
Newer Older
1
/*
L
Linus Torvalds 已提交
2 3 4 5 6 7
 * xfrm4_policy.c
 *
 * Changes:
 *	Kazunori MIYAZAWA @USAGI
 * 	YOSHIFUJI Hideaki @USAGI
 *		Split up af-specific portion
8
 *
L
Linus Torvalds 已提交
9 10
 */

11 12
#include <linux/err.h>
#include <linux/kernel.h>
H
Herbert Xu 已提交
13
#include <linux/inetdevice.h>
14
#include <linux/if_tunnel.h>
15
#include <net/dst.h>
L
Linus Torvalds 已提交
16 17 18 19 20
#include <net/xfrm.h>
#include <net/ip.h>

static struct xfrm_policy_afinfo xfrm4_policy_afinfo;

21 22 23 24
static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
					    int tos,
					    const xfrm_address_t *saddr,
					    const xfrm_address_t *daddr)
L
Linus Torvalds 已提交
25
{
26
	struct rtable *rt;
27

28 29 30
	memset(fl4, 0, sizeof(*fl4));
	fl4->daddr = daddr->a4;
	fl4->flowi4_tos = tos;
31
	if (saddr)
32
		fl4->saddr = saddr->a4;
33

34
	rt = __ip_route_output_key(net, fl4);
35 36 37 38
	if (!IS_ERR(rt))
		return &rt->dst;

	return ERR_CAST(rt);
39 40
}

41 42 43 44 45 46 47 48 49
static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos,
					  const xfrm_address_t *saddr,
					  const xfrm_address_t *daddr)
{
	struct flowi4 fl4;

	return __xfrm4_dst_lookup(net, &fl4, tos, saddr, daddr);
}

A
Alexey Dobriyan 已提交
50 51
static int xfrm4_get_saddr(struct net *net,
			   xfrm_address_t *saddr, xfrm_address_t *daddr)
52 53
{
	struct dst_entry *dst;
54
	struct flowi4 fl4;
55

56
	dst = __xfrm4_dst_lookup(net, &fl4, 0, NULL, daddr);
57 58 59
	if (IS_ERR(dst))
		return -EHOSTUNREACH;

60
	saddr->a4 = fl4.saddr;
61 62
	dst_release(dst);
	return 0;
63 64
}

65
static int xfrm4_get_tos(const struct flowi *fl)
L
Linus Torvalds 已提交
66
{
67
	return IPTOS_RT_MASK & fl->u.ip4.flowi4_tos; /* Strip ECN bits */
68
}
L
Linus Torvalds 已提交
69

70 71 72 73 74 75
static int xfrm4_init_path(struct xfrm_dst *path, struct dst_entry *dst,
			   int nfheader_len)
{
	return 0;
}

H
Herbert Xu 已提交
76
static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
77
			  const struct flowi *fl)
78 79
{
	struct rtable *rt = (struct rtable *)xdst->route;
80
	const struct flowi4 *fl4 = &fl->u.ip4;
L
Linus Torvalds 已提交
81

82
	xdst->u.rt.rt_iif = fl4->flowi4_iif;
L
Linus Torvalds 已提交
83

84 85
	xdst->u.dst.dev = dev;
	dev_hold(dev);
86

87 88
	/* Sheit... I remember I did this right. Apparently,
	 * it was magically lost, so this code needs audit */
89
	xdst->u.rt.rt_is_input = rt->rt_is_input;
90 91 92 93
	xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST |
					      RTCF_LOCAL);
	xdst->u.rt.rt_type = rt->rt_type;
	xdst->u.rt.rt_gateway = rt->rt_gateway;
J
Julian Anastasov 已提交
94
	xdst->u.rt.rt_uses_gateway = rt->rt_uses_gateway;
95
	xdst->u.rt.rt_pmtu = rt->rt_pmtu;
96
	INIT_LIST_HEAD(&xdst->u.rt.rt_uncached);
L
Linus Torvalds 已提交
97 98 99 100 101

	return 0;
}

static void
102
_decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
L
Linus Torvalds 已提交
103
{
104
	const struct iphdr *iph = ip_hdr(skb);
105
	u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
106
	struct flowi4 *fl4 = &fl->u.ip4;
107 108 109 110
	int oif = 0;

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

112 113
	memset(fl4, 0, sizeof(struct flowi4));
	fl4->flowi4_mark = skb->mark;
114
	fl4->flowi4_oif = reverse ? skb->skb_iif : oif;
P
Peter Kosyh 已提交
115

116
	if (!ip_is_fragment(iph)) {
L
Linus Torvalds 已提交
117 118
		switch (iph->protocol) {
		case IPPROTO_UDP:
119
		case IPPROTO_UDPLITE:
L
Linus Torvalds 已提交
120 121
		case IPPROTO_TCP:
		case IPPROTO_SCTP:
122
		case IPPROTO_DCCP:
123 124
			if (xprth + 4 < skb->data ||
			    pskb_may_pull(skb, xprth + 4 - skb->data)) {
A
Al Viro 已提交
125
				__be16 *ports = (__be16 *)xprth;
L
Linus Torvalds 已提交
126

127 128
				fl4->fl4_sport = ports[!!reverse];
				fl4->fl4_dport = ports[!reverse];
L
Linus Torvalds 已提交
129 130 131 132 133 134 135
			}
			break;

		case IPPROTO_ICMP:
			if (pskb_may_pull(skb, xprth + 2 - skb->data)) {
				u8 *icmp = xprth;

136 137
				fl4->fl4_icmp_type = icmp[0];
				fl4->fl4_icmp_code = icmp[1];
L
Linus Torvalds 已提交
138 139 140 141 142
			}
			break;

		case IPPROTO_ESP:
			if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
A
Al Viro 已提交
143
				__be32 *ehdr = (__be32 *)xprth;
L
Linus Torvalds 已提交
144

145
				fl4->fl4_ipsec_spi = ehdr[0];
L
Linus Torvalds 已提交
146 147 148 149 150
			}
			break;

		case IPPROTO_AH:
			if (pskb_may_pull(skb, xprth + 8 - skb->data)) {
D
Daniel Baluta 已提交
151
				__be32 *ah_hdr = (__be32 *)xprth;
L
Linus Torvalds 已提交
152

153
				fl4->fl4_ipsec_spi = ah_hdr[1];
L
Linus Torvalds 已提交
154 155 156 157 158
			}
			break;

		case IPPROTO_COMP:
			if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
A
Al Viro 已提交
159
				__be16 *ipcomp_hdr = (__be16 *)xprth;
L
Linus Torvalds 已提交
160

161
				fl4->fl4_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
L
Linus Torvalds 已提交
162 163
			}
			break;
164 165 166 167 168 169 170 171 172

		case IPPROTO_GRE:
			if (pskb_may_pull(skb, xprth + 12 - skb->data)) {
				__be16 *greflags = (__be16 *)xprth;
				__be32 *gre_hdr = (__be32 *)xprth;

				if (greflags[0] & GRE_KEY) {
					if (greflags[0] & GRE_CSUM)
						gre_hdr++;
173
					fl4->fl4_gre_key = gre_hdr[1];
174 175 176 177
				}
			}
			break;

L
Linus Torvalds 已提交
178
		default:
179
			fl4->fl4_ipsec_spi = 0;
L
Linus Torvalds 已提交
180
			break;
181
		}
L
Linus Torvalds 已提交
182
	}
183 184 185 186
	fl4->flowi4_proto = iph->protocol;
	fl4->daddr = reverse ? iph->saddr : iph->daddr;
	fl4->saddr = reverse ? iph->daddr : iph->saddr;
	fl4->flowi4_tos = iph->tos;
L
Linus Torvalds 已提交
187 188
}

189
static inline int xfrm4_garbage_collect(struct dst_ops *ops)
L
Linus Torvalds 已提交
190
{
191 192 193
	struct net *net = container_of(ops, struct net, xfrm.xfrm4_dst_ops);

	xfrm4_policy_afinfo.garbage_collect(net);
194
	return (dst_entries_get_slow(ops) > ops->gc_thresh * 2);
L
Linus Torvalds 已提交
195 196
}

197 198
static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk,
			      struct sk_buff *skb, u32 mtu)
L
Linus Torvalds 已提交
199 200 201 202
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
	struct dst_entry *path = xdst->route;

203
	path->ops->update_pmtu(path, sk, skb, mtu);
L
Linus Torvalds 已提交
204 205
}

206 207
static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk,
			   struct sk_buff *skb)
208 209 210 211
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
	struct dst_entry *path = xdst->route;

212
	path->ops->redirect(path, sk, skb);
213 214
}

H
Herbert Xu 已提交
215 216 217 218
static void xfrm4_dst_destroy(struct dst_entry *dst)
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;

219 220
	dst_destroy_metrics_generic(dst);

H
Herbert Xu 已提交
221 222 223 224 225 226 227 228 229 230 231 232
	xfrm_dst_destroy(xdst);
}

static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
			     int unregister)
{
	if (!unregister)
		return;

	xfrm_dst_ifdown(dst, dev);
}

L
Linus Torvalds 已提交
233 234
static struct dst_ops xfrm4_dst_ops = {
	.family =		AF_INET,
235
	.protocol =		cpu_to_be16(ETH_P_IP),
L
Linus Torvalds 已提交
236 237
	.gc =			xfrm4_garbage_collect,
	.update_pmtu =		xfrm4_update_pmtu,
238
	.redirect =		xfrm4_redirect,
239
	.cow_metrics =		dst_cow_metrics_generic,
H
Herbert Xu 已提交
240 241
	.destroy =		xfrm4_dst_destroy,
	.ifdown =		xfrm4_dst_ifdown,
242
	.local_out =		__ip_local_out,
243
	.gc_thresh =		32768,
L
Linus Torvalds 已提交
244 245 246 247 248 249
};

static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
	.family = 		AF_INET,
	.dst_ops =		&xfrm4_dst_ops,
	.dst_lookup =		xfrm4_dst_lookup,
250
	.get_saddr =		xfrm4_get_saddr,
L
Linus Torvalds 已提交
251
	.decode_session =	_decode_session4,
252
	.get_tos =		xfrm4_get_tos,
253
	.init_path =		xfrm4_init_path,
254
	.fill_dst =		xfrm4_fill_dst,
255
	.blackhole_route =	ipv4_blackhole_route,
L
Linus Torvalds 已提交
256 257
};

258
#ifdef CONFIG_SYSCTL
259 260 261
static struct ctl_table xfrm4_policy_table[] = {
	{
		.procname       = "xfrm4_gc_thresh",
262
		.data           = &init_net.xfrm.xfrm4_dst_ops.gc_thresh,
263 264 265 266 267 268 269
		.maxlen         = sizeof(int),
		.mode           = 0644,
		.proc_handler   = proc_dointvec,
	},
	{ }
};

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
static int __net_init xfrm4_net_init(struct net *net)
{
	struct ctl_table *table;
	struct ctl_table_header *hdr;

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

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

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

	net->ipv4.xfrm4_hdr = hdr;
	return 0;

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

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

	if (net->ipv4.xfrm4_hdr == NULL)
		return;

	table = net->ipv4.xfrm4_hdr->ctl_table_arg;
	unregister_net_sysctl_table(net->ipv4.xfrm4_hdr);
	if (!net_eq(net, &init_net))
		kfree(table);
}

static struct pernet_operations __net_initdata xfrm4_net_ops = {
	.init	= xfrm4_net_init,
	.exit	= xfrm4_net_exit,
};
315
#endif
316

L
Linus Torvalds 已提交
317 318 319 320 321
static void __init xfrm4_policy_init(void)
{
	xfrm_policy_register_afinfo(&xfrm4_policy_afinfo);
}

322
void __init xfrm4_init(void)
L
Linus Torvalds 已提交
323
{
324
	dst_entries_init(&xfrm4_dst_ops);
325 326 327

	xfrm4_state_init();
	xfrm4_policy_init();
328
#ifdef CONFIG_SYSCTL
329
	register_pernet_subsys(&xfrm4_net_ops);
330
#endif
L
Linus Torvalds 已提交
331 332
}