xfrm4_policy.c 7.2 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;
P
Peter Kosyh 已提交
110

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

214 215
	dst_destroy_metrics_generic(dst);

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

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

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

265 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
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,
};
310
#endif
311

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

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

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