Blame view

net/ipv6/xfrm6_tunnel.c 9.12 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   * Copyright (C)2003,2004 USAGI/WIDE Project
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
8
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
13
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
19
20
21
22
23
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   *
   * Authors	Mitsuru KANDA  <mk@linux-ipv6.org>
   * 		YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
   *
   * Based on net/ipv4/xfrm4_tunnel.c
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
  #include <linux/module.h>
  #include <linux/xfrm.h>
  #include <linux/list.h>
  #include <net/ip.h>
  #include <net/xfrm.h>
  #include <net/ipv6.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  #include <linux/ipv6.h>
  #include <linux/icmpv6.h>
4a3e2f711   Arjan van de Ven   [NET] sem2mutex: ...
32
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  /*
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
35
   * xfrm_tunnel_spi things are for allocating unique id ("spi")
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
   * per xfrm_address_t.
   */
  struct xfrm6_tunnel_spi {
  	struct hlist_node list_byaddr;
  	struct hlist_node list_byspi;
  	xfrm_address_t addr;
  	u32 spi;
  	atomic_t refcnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
50
  static DEFINE_RWLOCK(xfrm6_tunnel_spi_lock);
  
  static u32 xfrm6_tunnel_spi;
  
  #define XFRM6_TUNNEL_SPI_MIN	1
  #define XFRM6_TUNNEL_SPI_MAX	0xffffffff
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
51
  static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
57
  
  #define XFRM6_TUNNEL_SPI_BYADDR_HSIZE 256
  #define XFRM6_TUNNEL_SPI_BYSPI_HSIZE 256
  
  static struct hlist_head xfrm6_tunnel_spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE];
  static struct hlist_head xfrm6_tunnel_spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
  static unsigned inline xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr)
  {
  	unsigned h;
8c689a6ea   Al Viro   [XFRM]: misc anno...
61
  	h = (__force u32)(addr->a6[0] ^ addr->a6[1] ^ addr->a6[2] ^ addr->a6[3]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  	h ^= h >> 16;
  	h ^= h >> 8;
  	h &= XFRM6_TUNNEL_SPI_BYADDR_HSIZE - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
69
70
71
72
73
74
75
76
  	return h;
  }
  
  static unsigned inline xfrm6_tunnel_spi_hash_byspi(u32 spi)
  {
  	return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE;
  }
  
  
  static int xfrm6_tunnel_spi_init(void)
  {
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
  	xfrm6_tunnel_spi = 0;
  	xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi",
  						  sizeof(struct xfrm6_tunnel_spi),
  						  0, SLAB_HWCACHE_ALIGN,
  						  NULL, NULL);
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
82
  	if (!xfrm6_tunnel_spi_kmem)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
88
89
90
91
92
93
94
  
  	for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
  		INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byaddr[i]);
  	for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++)
  		INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byspi[i]);
  	return 0;
  }
  
  static void xfrm6_tunnel_spi_fini(void)
  {
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
  	for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) {
  		if (!hlist_empty(&xfrm6_tunnel_spi_byaddr[i]))
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
97
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
  	}
  	for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++) {
  		if (!hlist_empty(&xfrm6_tunnel_spi_byspi[i]))
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
101
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
  	}
  	kmem_cache_destroy(xfrm6_tunnel_spi_kmem);
  	xfrm6_tunnel_spi_kmem = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
108
109
110
  }
  
  static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr)
  {
  	struct xfrm6_tunnel_spi *x6spi;
  	struct hlist_node *pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
  	hlist_for_each_entry(x6spi, pos,
  			     &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)],
  			     list_byaddr) {
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
114
  		if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  			return x6spi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
  	return NULL;
  }
8c689a6ea   Al Viro   [XFRM]: misc anno...
119
  __be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
  {
  	struct xfrm6_tunnel_spi *x6spi;
  	u32 spi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
126
  	read_lock_bh(&xfrm6_tunnel_spi_lock);
  	x6spi = __xfrm6_tunnel_spi_lookup(saddr);
  	spi = x6spi ? x6spi->spi : 0;
  	read_unlock_bh(&xfrm6_tunnel_spi_lock);
5b1225454   Al Viro   [IPV6]: File the ...
127
  	return htonl(spi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
134
135
136
137
  }
  
  EXPORT_SYMBOL(xfrm6_tunnel_spi_lookup);
  
  static u32 __xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr)
  {
  	u32 spi;
  	struct xfrm6_tunnel_spi *x6spi;
  	struct hlist_node *pos;
  	unsigned index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
143
144
145
  	if (xfrm6_tunnel_spi < XFRM6_TUNNEL_SPI_MIN ||
  	    xfrm6_tunnel_spi >= XFRM6_TUNNEL_SPI_MAX)
  		xfrm6_tunnel_spi = XFRM6_TUNNEL_SPI_MIN;
  	else
  		xfrm6_tunnel_spi++;
  
  	for (spi = xfrm6_tunnel_spi; spi <= XFRM6_TUNNEL_SPI_MAX; spi++) {
  		index = xfrm6_tunnel_spi_hash_byspi(spi);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
146
147
  		hlist_for_each_entry(x6spi, pos,
  				     &xfrm6_tunnel_spi_byspi[index],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
150
151
152
153
154
155
156
157
  				     list_byspi) {
  			if (x6spi->spi == spi)
  				goto try_next_1;
  		}
  		xfrm6_tunnel_spi = spi;
  		goto alloc_spi;
  try_next_1:;
  	}
  	for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tunnel_spi; spi++) {
  		index = xfrm6_tunnel_spi_hash_byspi(spi);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
158
159
  		hlist_for_each_entry(x6spi, pos,
  				     &xfrm6_tunnel_spi_byspi[index],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
164
165
166
167
168
169
170
  				     list_byspi) {
  			if (x6spi->spi == spi)
  				goto try_next_2;
  		}
  		xfrm6_tunnel_spi = spi;
  		goto alloc_spi;
  try_next_2:;
  	}
  	spi = 0;
  	goto out;
  alloc_spi:
54e6ecb23   Christoph Lameter   [PATCH] slab: rem...
171
  	x6spi = kmem_cache_alloc(xfrm6_tunnel_spi_kmem, GFP_ATOMIC);
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
172
  	if (!x6spi)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  		goto out;
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
174

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
181
182
  	memcpy(&x6spi->addr, saddr, sizeof(x6spi->addr));
  	x6spi->spi = spi;
  	atomic_set(&x6spi->refcnt, 1);
  
  	hlist_add_head(&x6spi->list_byspi, &xfrm6_tunnel_spi_byspi[index]);
  
  	index = xfrm6_tunnel_spi_hash_byaddr(saddr);
  	hlist_add_head(&x6spi->list_byaddr, &xfrm6_tunnel_spi_byaddr[index]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
  	return spi;
  }
8c689a6ea   Al Viro   [XFRM]: misc anno...
186
  __be32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
  {
  	struct xfrm6_tunnel_spi *x6spi;
  	u32 spi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
197
  	write_lock_bh(&xfrm6_tunnel_spi_lock);
  	x6spi = __xfrm6_tunnel_spi_lookup(saddr);
  	if (x6spi) {
  		atomic_inc(&x6spi->refcnt);
  		spi = x6spi->spi;
  	} else
  		spi = __xfrm6_tunnel_alloc_spi(saddr);
  	write_unlock_bh(&xfrm6_tunnel_spi_lock);
5b1225454   Al Viro   [IPV6]: File the ...
198
  	return htonl(spi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
203
204
205
206
  }
  
  EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi);
  
  void xfrm6_tunnel_free_spi(xfrm_address_t *saddr)
  {
  	struct xfrm6_tunnel_spi *x6spi;
  	struct hlist_node *pos, *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  	write_lock_bh(&xfrm6_tunnel_spi_lock);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
208
  	hlist_for_each_entry_safe(x6spi, pos, n,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
  				  &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)],
  				  list_byaddr)
  	{
  		if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  			if (atomic_dec_and_test(&x6spi->refcnt)) {
  				hlist_del(&x6spi->list_byaddr);
  				hlist_del(&x6spi->list_byspi);
  				kmem_cache_free(xfrm6_tunnel_spi_kmem, x6spi);
  				break;
  			}
  		}
  	}
  	write_unlock_bh(&xfrm6_tunnel_spi_lock);
  }
  
  EXPORT_SYMBOL(xfrm6_tunnel_free_spi);
  
  static int xfrm6_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
  {
  	struct ipv6hdr *top_iph;
  
  	top_iph = (struct ipv6hdr *)skb->data;
  	top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
  
  	return 0;
  }
e695633e2   Herbert Xu   [IPSEC]: Kill unu...
235
  static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
  {
  	return 0;
  }
d2acc3479   Herbert Xu   [INET]: Introduce...
239
  static int xfrm6_tunnel_rcv(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  	struct ipv6hdr *iph = skb->nh.ipv6h;
a252cc237   Al Viro   [XFRM]: xrfm_repl...
242
  	__be32 spi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  	spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr);
d2acc3479   Herbert Xu   [INET]: Introduce...
245
  	return xfrm6_rcv_spi(skb, spi);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  }
d2acc3479   Herbert Xu   [INET]: Introduce...
247
  static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
04ce69093   Al Viro   [IPV6]: 'info' ar...
248
  			    int type, int code, int offset, __be32 info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
  	/* xfrm6_tunnel native err handling */
  	switch (type) {
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
252
  	case ICMPV6_DEST_UNREACH:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  		switch (code) {
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
254
  		case ICMPV6_NOROUTE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
  		case ICMPV6_ADM_PROHIBITED:
  		case ICMPV6_NOT_NEIGHBOUR:
  		case ICMPV6_ADDR_UNREACH:
  		case ICMPV6_PORT_UNREACH:
  		default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
  			break;
  		}
  		break;
  	case ICMPV6_PKT_TOOBIG:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
  		break;
  	case ICMPV6_TIME_EXCEED:
  		switch (code) {
  		case ICMPV6_EXC_HOPLIMIT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
269
  			break;
  		case ICMPV6_EXC_FRAGTIME:
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
270
  		default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
275
276
277
278
279
280
281
282
283
  			break;
  		}
  		break;
  	case ICMPV6_PARAMPROB:
  		switch (code) {
  		case ICMPV6_HDR_FIELD: break;
  		case ICMPV6_UNK_NEXTHDR: break;
  		case ICMPV6_UNK_OPTION: break;
  		}
  		break;
  	default:
  		break;
  	}
d2acc3479   Herbert Xu   [INET]: Introduce...
284
285
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  }
72cb6962a   Herbert Xu   [IPSEC]: Add xfrm...
287
  static int xfrm6_tunnel_init_state(struct xfrm_state *x)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  {
7e49e6de3   Masahide NAKAMURA   [XFRM]: Add XFRM_...
289
  	if (x->props.mode != XFRM_MODE_TUNNEL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  		return -EINVAL;
  
  	if (x->encap)
  		return -EINVAL;
  
  	x->props.header_len = sizeof(struct ipv6hdr);
  
  	return 0;
  }
  
  static void xfrm6_tunnel_destroy(struct xfrm_state *x)
  {
  	xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr);
  }
  
  static struct xfrm_type xfrm6_tunnel_type = {
  	.description	= "IP6IP6",
  	.owner          = THIS_MODULE,
  	.proto		= IPPROTO_IPV6,
  	.init_state	= xfrm6_tunnel_init_state,
  	.destructor	= xfrm6_tunnel_destroy,
  	.input		= xfrm6_tunnel_input,
  	.output		= xfrm6_tunnel_output,
  };
d2acc3479   Herbert Xu   [INET]: Introduce...
314
  static struct xfrm6_tunnel xfrm6_tunnel_handler = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
  	.handler	= xfrm6_tunnel_rcv,
d2acc3479   Herbert Xu   [INET]: Introduce...
316
317
  	.err_handler	= xfrm6_tunnel_err,
  	.priority	= 2,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  };
73d605d1a   Kazunori MIYAZAWA   [IPSEC]: changing...
319
320
321
322
323
  static struct xfrm6_tunnel xfrm46_tunnel_handler = {
  	.handler	= xfrm6_tunnel_rcv,
  	.err_handler	= xfrm6_tunnel_err,
  	.priority	= 2,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
  static int __init xfrm6_tunnel_init(void)
  {
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
326
  	if (xfrm_register_type(&xfrm6_tunnel_type, AF_INET6) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  		return -EAGAIN;
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
328

73d605d1a   Kazunori MIYAZAWA   [IPSEC]: changing...
329
330
331
332
333
334
  	if (xfrm6_tunnel_register(&xfrm6_tunnel_handler, AF_INET6)) {
  		xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
  		return -EAGAIN;
  	}
  	if (xfrm6_tunnel_register(&xfrm46_tunnel_handler, AF_INET)) {
  		xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
  		xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
  		return -EAGAIN;
  	}
  	if (xfrm6_tunnel_spi_init() < 0) {
73d605d1a   Kazunori MIYAZAWA   [IPSEC]: changing...
339
340
  		xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET);
  		xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
345
346
347
348
  		xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
  		return -EAGAIN;
  	}
  	return 0;
  }
  
  static void __exit xfrm6_tunnel_fini(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  	xfrm6_tunnel_spi_fini();
73d605d1a   Kazunori MIYAZAWA   [IPSEC]: changing...
350
351
  	xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET);
  	xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
a922ba551   David S. Miller   [IPV6] xfrm6_tunn...
352
  	xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
  }
  
  module_init(xfrm6_tunnel_init);
  module_exit(xfrm6_tunnel_fini);
  MODULE_LICENSE("GPL");