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;
L
Linus Torvalds 已提交
107

108 109
	memset(fl4, 0, sizeof(struct flowi4));
	fl4->flowi4_mark = skb->mark;
110
	fl4->flowi4_oif = skb_dst(skb)->dev->ifindex;
P
Peter Kosyh 已提交
111

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

123 124
				fl4->fl4_sport = ports[!!reverse];
				fl4->fl4_dport = ports[!reverse];
L
Linus Torvalds 已提交
125 126 127 128 129 130 131
			}
			break;

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

132 133
				fl4->fl4_icmp_type = icmp[0];
				fl4->fl4_icmp_code = icmp[1];
L
Linus Torvalds 已提交
134 135 136 137 138
			}
			break;

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

141
				fl4->fl4_ipsec_spi = ehdr[0];
L
Linus Torvalds 已提交
142 143 144 145 146
			}
			break;

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

149
				fl4->fl4_ipsec_spi = ah_hdr[1];
L
Linus Torvalds 已提交
150 151 152 153 154
			}
			break;

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

157
				fl4->fl4_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
L
Linus Torvalds 已提交
158 159
			}
			break;
160 161 162 163 164 165 166 167 168

		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++;
169
					fl4->fl4_gre_key = gre_hdr[1];
170 171 172 173
				}
			}
			break;

L
Linus Torvalds 已提交
174
		default:
175
			fl4->fl4_ipsec_spi = 0;
L
Linus Torvalds 已提交
176
			break;
177
		}
L
Linus Torvalds 已提交
178
	}
179 180 181 182
	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 已提交
183 184
}

185
static inline int xfrm4_garbage_collect(struct dst_ops *ops)
L
Linus Torvalds 已提交
186
{
187 188 189
	struct net *net = container_of(ops, struct net, xfrm.xfrm4_dst_ops);

	xfrm4_policy_afinfo.garbage_collect(net);
190
	return (dst_entries_get_slow(ops) > ops->gc_thresh * 2);
L
Linus Torvalds 已提交
191 192
}

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

199
	path->ops->update_pmtu(path, sk, skb, mtu);
L
Linus Torvalds 已提交
200 201
}

202 203
static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk,
			   struct sk_buff *skb)
204 205 206 207
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
	struct dst_entry *path = xdst->route;

208
	path->ops->redirect(path, sk, skb);
209 210
}

H
Herbert Xu 已提交
211 212 213 214
static void xfrm4_dst_destroy(struct dst_entry *dst)
{
	struct xfrm_dst *xdst = (struct xfrm_dst *)dst;

215 216
	dst_destroy_metrics_generic(dst);

H
Herbert Xu 已提交
217 218 219 220 221 222 223 224 225 226 227 228
	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 已提交
229 230
static struct dst_ops xfrm4_dst_ops = {
	.family =		AF_INET,
231
	.protocol =		cpu_to_be16(ETH_P_IP),
L
Linus Torvalds 已提交
232 233
	.gc =			xfrm4_garbage_collect,
	.update_pmtu =		xfrm4_update_pmtu,
234
	.redirect =		xfrm4_redirect,
235
	.cow_metrics =		dst_cow_metrics_generic,
H
Herbert Xu 已提交
236 237
	.destroy =		xfrm4_dst_destroy,
	.ifdown =		xfrm4_dst_ifdown,
238
	.local_out =		__ip_local_out,
239
	.gc_thresh =		32768,
L
Linus Torvalds 已提交
240 241 242 243 244 245
};

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

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

266 267 268 269 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
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,
};
311
#endif
312

L
Linus Torvalds 已提交
313 314 315 316 317
static void __init xfrm4_policy_init(void)
{
	xfrm_policy_register_afinfo(&xfrm4_policy_afinfo);
}

318
void __init xfrm4_init(void)
L
Linus Torvalds 已提交
319
{
320
	dst_entries_init(&xfrm4_dst_ops);
321 322 323

	xfrm4_state_init();
	xfrm4_policy_init();
324
#ifdef CONFIG_SYSCTL
325
	register_pernet_subsys(&xfrm4_net_ops);
326
#endif
L
Linus Torvalds 已提交
327 328
}