diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/ipv6/af_inet6.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/ipv6/af_inet6.c')
-rw-r--r-- | net/ipv6/af_inet6.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c new file mode 100644 index 000000000000..768b11703daf --- /dev/null +++ b/net/ipv6/af_inet6.c | |||
@@ -0,0 +1,867 @@ | |||
1 | /* | ||
2 | * PF_INET6 socket protocol family | ||
3 | * Linux INET6 implementation | ||
4 | * | ||
5 | * Authors: | ||
6 | * Pedro Roque <roque@di.fc.ul.pt> | ||
7 | * | ||
8 | * Adapted from linux/net/ipv4/af_inet.c | ||
9 | * | ||
10 | * $Id: af_inet6.c,v 1.66 2002/02/01 22:01:04 davem Exp $ | ||
11 | * | ||
12 | * Fixes: | ||
13 | * piggy, Karl Knutson : Socket protocol table | ||
14 | * Hideaki YOSHIFUJI : sin6_scope_id support | ||
15 | * Arnaldo Melo : check proc_net_create return, cleanups | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or | ||
18 | * modify it under the terms of the GNU General Public License | ||
19 | * as published by the Free Software Foundation; either version | ||
20 | * 2 of the License, or (at your option) any later version. | ||
21 | */ | ||
22 | |||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/config.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/types.h> | ||
28 | #include <linux/socket.h> | ||
29 | #include <linux/in.h> | ||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/major.h> | ||
32 | #include <linux/sched.h> | ||
33 | #include <linux/timer.h> | ||
34 | #include <linux/string.h> | ||
35 | #include <linux/sockios.h> | ||
36 | #include <linux/net.h> | ||
37 | #include <linux/fcntl.h> | ||
38 | #include <linux/mm.h> | ||
39 | #include <linux/interrupt.h> | ||
40 | #include <linux/proc_fs.h> | ||
41 | #include <linux/stat.h> | ||
42 | #include <linux/init.h> | ||
43 | |||
44 | #include <linux/inet.h> | ||
45 | #include <linux/netdevice.h> | ||
46 | #include <linux/icmpv6.h> | ||
47 | #include <linux/smp_lock.h> | ||
48 | |||
49 | #include <net/ip.h> | ||
50 | #include <net/ipv6.h> | ||
51 | #include <net/udp.h> | ||
52 | #include <net/tcp.h> | ||
53 | #include <net/ipip.h> | ||
54 | #include <net/protocol.h> | ||
55 | #include <net/inet_common.h> | ||
56 | #include <net/transp_v6.h> | ||
57 | #include <net/ip6_route.h> | ||
58 | #include <net/addrconf.h> | ||
59 | #ifdef CONFIG_IPV6_TUNNEL | ||
60 | #include <net/ip6_tunnel.h> | ||
61 | #endif | ||
62 | |||
63 | #include <asm/uaccess.h> | ||
64 | #include <asm/system.h> | ||
65 | |||
66 | MODULE_AUTHOR("Cast of dozens"); | ||
67 | MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); | ||
68 | MODULE_LICENSE("GPL"); | ||
69 | |||
70 | /* IPv6 procfs goodies... */ | ||
71 | |||
72 | #ifdef CONFIG_PROC_FS | ||
73 | extern int raw6_proc_init(void); | ||
74 | extern void raw6_proc_exit(void); | ||
75 | extern int tcp6_proc_init(void); | ||
76 | extern void tcp6_proc_exit(void); | ||
77 | extern int udp6_proc_init(void); | ||
78 | extern void udp6_proc_exit(void); | ||
79 | extern int ipv6_misc_proc_init(void); | ||
80 | extern void ipv6_misc_proc_exit(void); | ||
81 | extern int ac6_proc_init(void); | ||
82 | extern void ac6_proc_exit(void); | ||
83 | extern int if6_proc_init(void); | ||
84 | extern void if6_proc_exit(void); | ||
85 | #endif | ||
86 | |||
87 | int sysctl_ipv6_bindv6only; | ||
88 | |||
89 | #ifdef INET_REFCNT_DEBUG | ||
90 | atomic_t inet6_sock_nr; | ||
91 | #endif | ||
92 | |||
93 | /* The inetsw table contains everything that inet_create needs to | ||
94 | * build a new socket. | ||
95 | */ | ||
96 | static struct list_head inetsw6[SOCK_MAX]; | ||
97 | static DEFINE_SPINLOCK(inetsw6_lock); | ||
98 | |||
99 | static void inet6_sock_destruct(struct sock *sk) | ||
100 | { | ||
101 | inet_sock_destruct(sk); | ||
102 | |||
103 | #ifdef INET_REFCNT_DEBUG | ||
104 | atomic_dec(&inet6_sock_nr); | ||
105 | #endif | ||
106 | } | ||
107 | |||
108 | static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) | ||
109 | { | ||
110 | const int offset = sk->sk_prot->obj_size - sizeof(struct ipv6_pinfo); | ||
111 | |||
112 | return (struct ipv6_pinfo *)(((u8 *)sk) + offset); | ||
113 | } | ||
114 | |||
115 | static int inet6_create(struct socket *sock, int protocol) | ||
116 | { | ||
117 | struct inet_sock *inet; | ||
118 | struct ipv6_pinfo *np; | ||
119 | struct sock *sk; | ||
120 | struct list_head *p; | ||
121 | struct inet_protosw *answer; | ||
122 | struct proto *answer_prot; | ||
123 | unsigned char answer_flags; | ||
124 | char answer_no_check; | ||
125 | int rc; | ||
126 | |||
127 | /* Look for the requested type/protocol pair. */ | ||
128 | answer = NULL; | ||
129 | rcu_read_lock(); | ||
130 | list_for_each_rcu(p, &inetsw6[sock->type]) { | ||
131 | answer = list_entry(p, struct inet_protosw, list); | ||
132 | |||
133 | /* Check the non-wild match. */ | ||
134 | if (protocol == answer->protocol) { | ||
135 | if (protocol != IPPROTO_IP) | ||
136 | break; | ||
137 | } else { | ||
138 | /* Check for the two wild cases. */ | ||
139 | if (IPPROTO_IP == protocol) { | ||
140 | protocol = answer->protocol; | ||
141 | break; | ||
142 | } | ||
143 | if (IPPROTO_IP == answer->protocol) | ||
144 | break; | ||
145 | } | ||
146 | answer = NULL; | ||
147 | } | ||
148 | |||
149 | rc = -ESOCKTNOSUPPORT; | ||
150 | if (!answer) | ||
151 | goto out_rcu_unlock; | ||
152 | rc = -EPERM; | ||
153 | if (answer->capability > 0 && !capable(answer->capability)) | ||
154 | goto out_rcu_unlock; | ||
155 | rc = -EPROTONOSUPPORT; | ||
156 | if (!protocol) | ||
157 | goto out_rcu_unlock; | ||
158 | |||
159 | sock->ops = answer->ops; | ||
160 | |||
161 | answer_prot = answer->prot; | ||
162 | answer_no_check = answer->no_check; | ||
163 | answer_flags = answer->flags; | ||
164 | rcu_read_unlock(); | ||
165 | |||
166 | BUG_TRAP(answer_prot->slab != NULL); | ||
167 | |||
168 | rc = -ENOBUFS; | ||
169 | sk = sk_alloc(PF_INET6, GFP_KERNEL, answer_prot, 1); | ||
170 | if (sk == NULL) | ||
171 | goto out; | ||
172 | |||
173 | sock_init_data(sock, sk); | ||
174 | |||
175 | rc = 0; | ||
176 | sk->sk_no_check = answer_no_check; | ||
177 | if (INET_PROTOSW_REUSE & answer_flags) | ||
178 | sk->sk_reuse = 1; | ||
179 | |||
180 | inet = inet_sk(sk); | ||
181 | |||
182 | if (SOCK_RAW == sock->type) { | ||
183 | inet->num = protocol; | ||
184 | if (IPPROTO_RAW == protocol) | ||
185 | inet->hdrincl = 1; | ||
186 | } | ||
187 | |||
188 | sk->sk_destruct = inet6_sock_destruct; | ||
189 | sk->sk_family = PF_INET6; | ||
190 | sk->sk_protocol = protocol; | ||
191 | |||
192 | sk->sk_backlog_rcv = answer->prot->backlog_rcv; | ||
193 | |||
194 | inet_sk(sk)->pinet6 = np = inet6_sk_generic(sk); | ||
195 | np->hop_limit = -1; | ||
196 | np->mcast_hops = -1; | ||
197 | np->mc_loop = 1; | ||
198 | np->pmtudisc = IPV6_PMTUDISC_WANT; | ||
199 | np->ipv6only = sysctl_ipv6_bindv6only; | ||
200 | |||
201 | /* Init the ipv4 part of the socket since we can have sockets | ||
202 | * using v6 API for ipv4. | ||
203 | */ | ||
204 | inet->uc_ttl = -1; | ||
205 | |||
206 | inet->mc_loop = 1; | ||
207 | inet->mc_ttl = 1; | ||
208 | inet->mc_index = 0; | ||
209 | inet->mc_list = NULL; | ||
210 | |||
211 | if (ipv4_config.no_pmtu_disc) | ||
212 | inet->pmtudisc = IP_PMTUDISC_DONT; | ||
213 | else | ||
214 | inet->pmtudisc = IP_PMTUDISC_WANT; | ||
215 | |||
216 | |||
217 | #ifdef INET_REFCNT_DEBUG | ||
218 | atomic_inc(&inet6_sock_nr); | ||
219 | atomic_inc(&inet_sock_nr); | ||
220 | #endif | ||
221 | if (inet->num) { | ||
222 | /* It assumes that any protocol which allows | ||
223 | * the user to assign a number at socket | ||
224 | * creation time automatically shares. | ||
225 | */ | ||
226 | inet->sport = ntohs(inet->num); | ||
227 | sk->sk_prot->hash(sk); | ||
228 | } | ||
229 | if (sk->sk_prot->init) { | ||
230 | rc = sk->sk_prot->init(sk); | ||
231 | if (rc) { | ||
232 | sk_common_release(sk); | ||
233 | goto out; | ||
234 | } | ||
235 | } | ||
236 | out: | ||
237 | return rc; | ||
238 | out_rcu_unlock: | ||
239 | rcu_read_unlock(); | ||
240 | goto out; | ||
241 | } | ||
242 | |||
243 | |||
244 | /* bind for INET6 API */ | ||
245 | int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | ||
246 | { | ||
247 | struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr; | ||
248 | struct sock *sk = sock->sk; | ||
249 | struct inet_sock *inet = inet_sk(sk); | ||
250 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
251 | __u32 v4addr = 0; | ||
252 | unsigned short snum; | ||
253 | int addr_type = 0; | ||
254 | int err = 0; | ||
255 | |||
256 | /* If the socket has its own bind function then use it. */ | ||
257 | if (sk->sk_prot->bind) | ||
258 | return sk->sk_prot->bind(sk, uaddr, addr_len); | ||
259 | |||
260 | if (addr_len < SIN6_LEN_RFC2133) | ||
261 | return -EINVAL; | ||
262 | addr_type = ipv6_addr_type(&addr->sin6_addr); | ||
263 | if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) | ||
264 | return -EINVAL; | ||
265 | |||
266 | snum = ntohs(addr->sin6_port); | ||
267 | if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) | ||
268 | return -EACCES; | ||
269 | |||
270 | lock_sock(sk); | ||
271 | |||
272 | /* Check these errors (active socket, double bind). */ | ||
273 | if (sk->sk_state != TCP_CLOSE || inet->num) { | ||
274 | err = -EINVAL; | ||
275 | goto out; | ||
276 | } | ||
277 | |||
278 | /* Check if the address belongs to the host. */ | ||
279 | if (addr_type == IPV6_ADDR_MAPPED) { | ||
280 | v4addr = addr->sin6_addr.s6_addr32[3]; | ||
281 | if (inet_addr_type(v4addr) != RTN_LOCAL) { | ||
282 | err = -EADDRNOTAVAIL; | ||
283 | goto out; | ||
284 | } | ||
285 | } else { | ||
286 | if (addr_type != IPV6_ADDR_ANY) { | ||
287 | struct net_device *dev = NULL; | ||
288 | |||
289 | if (addr_type & IPV6_ADDR_LINKLOCAL) { | ||
290 | if (addr_len >= sizeof(struct sockaddr_in6) && | ||
291 | addr->sin6_scope_id) { | ||
292 | /* Override any existing binding, if another one | ||
293 | * is supplied by user. | ||
294 | */ | ||
295 | sk->sk_bound_dev_if = addr->sin6_scope_id; | ||
296 | } | ||
297 | |||
298 | /* Binding to link-local address requires an interface */ | ||
299 | if (!sk->sk_bound_dev_if) { | ||
300 | err = -EINVAL; | ||
301 | goto out; | ||
302 | } | ||
303 | dev = dev_get_by_index(sk->sk_bound_dev_if); | ||
304 | if (!dev) { | ||
305 | err = -ENODEV; | ||
306 | goto out; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | /* ipv4 addr of the socket is invalid. Only the | ||
311 | * unspecified and mapped address have a v4 equivalent. | ||
312 | */ | ||
313 | v4addr = LOOPBACK4_IPV6; | ||
314 | if (!(addr_type & IPV6_ADDR_MULTICAST)) { | ||
315 | if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) { | ||
316 | if (dev) | ||
317 | dev_put(dev); | ||
318 | err = -EADDRNOTAVAIL; | ||
319 | goto out; | ||
320 | } | ||
321 | } | ||
322 | if (dev) | ||
323 | dev_put(dev); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | inet->rcv_saddr = v4addr; | ||
328 | inet->saddr = v4addr; | ||
329 | |||
330 | ipv6_addr_copy(&np->rcv_saddr, &addr->sin6_addr); | ||
331 | |||
332 | if (!(addr_type & IPV6_ADDR_MULTICAST)) | ||
333 | ipv6_addr_copy(&np->saddr, &addr->sin6_addr); | ||
334 | |||
335 | /* Make sure we are allowed to bind here. */ | ||
336 | if (sk->sk_prot->get_port(sk, snum)) { | ||
337 | inet_reset_saddr(sk); | ||
338 | err = -EADDRINUSE; | ||
339 | goto out; | ||
340 | } | ||
341 | |||
342 | if (addr_type != IPV6_ADDR_ANY) | ||
343 | sk->sk_userlocks |= SOCK_BINDADDR_LOCK; | ||
344 | if (snum) | ||
345 | sk->sk_userlocks |= SOCK_BINDPORT_LOCK; | ||
346 | inet->sport = ntohs(inet->num); | ||
347 | inet->dport = 0; | ||
348 | inet->daddr = 0; | ||
349 | out: | ||
350 | release_sock(sk); | ||
351 | return err; | ||
352 | } | ||
353 | |||
354 | int inet6_release(struct socket *sock) | ||
355 | { | ||
356 | struct sock *sk = sock->sk; | ||
357 | |||
358 | if (sk == NULL) | ||
359 | return -EINVAL; | ||
360 | |||
361 | /* Free mc lists */ | ||
362 | ipv6_sock_mc_close(sk); | ||
363 | |||
364 | /* Free ac lists */ | ||
365 | ipv6_sock_ac_close(sk); | ||
366 | |||
367 | return inet_release(sock); | ||
368 | } | ||
369 | |||
370 | int inet6_destroy_sock(struct sock *sk) | ||
371 | { | ||
372 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
373 | struct sk_buff *skb; | ||
374 | struct ipv6_txoptions *opt; | ||
375 | |||
376 | /* | ||
377 | * Release destination entry | ||
378 | */ | ||
379 | |||
380 | sk_dst_reset(sk); | ||
381 | |||
382 | /* Release rx options */ | ||
383 | |||
384 | if ((skb = xchg(&np->pktoptions, NULL)) != NULL) | ||
385 | kfree_skb(skb); | ||
386 | |||
387 | /* Free flowlabels */ | ||
388 | fl6_free_socklist(sk); | ||
389 | |||
390 | /* Free tx options */ | ||
391 | |||
392 | if ((opt = xchg(&np->opt, NULL)) != NULL) | ||
393 | sock_kfree_s(sk, opt, opt->tot_len); | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * This does both peername and sockname. | ||
400 | */ | ||
401 | |||
402 | int inet6_getname(struct socket *sock, struct sockaddr *uaddr, | ||
403 | int *uaddr_len, int peer) | ||
404 | { | ||
405 | struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr; | ||
406 | struct sock *sk = sock->sk; | ||
407 | struct inet_sock *inet = inet_sk(sk); | ||
408 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
409 | |||
410 | sin->sin6_family = AF_INET6; | ||
411 | sin->sin6_flowinfo = 0; | ||
412 | sin->sin6_scope_id = 0; | ||
413 | if (peer) { | ||
414 | if (!inet->dport) | ||
415 | return -ENOTCONN; | ||
416 | if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)) && | ||
417 | peer == 1) | ||
418 | return -ENOTCONN; | ||
419 | sin->sin6_port = inet->dport; | ||
420 | ipv6_addr_copy(&sin->sin6_addr, &np->daddr); | ||
421 | if (np->sndflow) | ||
422 | sin->sin6_flowinfo = np->flow_label; | ||
423 | } else { | ||
424 | if (ipv6_addr_any(&np->rcv_saddr)) | ||
425 | ipv6_addr_copy(&sin->sin6_addr, &np->saddr); | ||
426 | else | ||
427 | ipv6_addr_copy(&sin->sin6_addr, &np->rcv_saddr); | ||
428 | |||
429 | sin->sin6_port = inet->sport; | ||
430 | } | ||
431 | if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) | ||
432 | sin->sin6_scope_id = sk->sk_bound_dev_if; | ||
433 | *uaddr_len = sizeof(*sin); | ||
434 | return(0); | ||
435 | } | ||
436 | |||
437 | int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
438 | { | ||
439 | struct sock *sk = sock->sk; | ||
440 | int err = -EINVAL; | ||
441 | |||
442 | switch(cmd) | ||
443 | { | ||
444 | case SIOCGSTAMP: | ||
445 | return sock_get_timestamp(sk, (struct timeval __user *)arg); | ||
446 | |||
447 | case SIOCADDRT: | ||
448 | case SIOCDELRT: | ||
449 | |||
450 | return(ipv6_route_ioctl(cmd,(void __user *)arg)); | ||
451 | |||
452 | case SIOCSIFADDR: | ||
453 | return addrconf_add_ifaddr((void __user *) arg); | ||
454 | case SIOCDIFADDR: | ||
455 | return addrconf_del_ifaddr((void __user *) arg); | ||
456 | case SIOCSIFDSTADDR: | ||
457 | return addrconf_set_dstaddr((void __user *) arg); | ||
458 | default: | ||
459 | if (!sk->sk_prot->ioctl || | ||
460 | (err = sk->sk_prot->ioctl(sk, cmd, arg)) == -ENOIOCTLCMD) | ||
461 | return(dev_ioctl(cmd,(void __user *) arg)); | ||
462 | return err; | ||
463 | } | ||
464 | /*NOTREACHED*/ | ||
465 | return(0); | ||
466 | } | ||
467 | |||
468 | struct proto_ops inet6_stream_ops = { | ||
469 | .family = PF_INET6, | ||
470 | .owner = THIS_MODULE, | ||
471 | .release = inet6_release, | ||
472 | .bind = inet6_bind, | ||
473 | .connect = inet_stream_connect, /* ok */ | ||
474 | .socketpair = sock_no_socketpair, /* a do nothing */ | ||
475 | .accept = inet_accept, /* ok */ | ||
476 | .getname = inet6_getname, | ||
477 | .poll = tcp_poll, /* ok */ | ||
478 | .ioctl = inet6_ioctl, /* must change */ | ||
479 | .listen = inet_listen, /* ok */ | ||
480 | .shutdown = inet_shutdown, /* ok */ | ||
481 | .setsockopt = sock_common_setsockopt, /* ok */ | ||
482 | .getsockopt = sock_common_getsockopt, /* ok */ | ||
483 | .sendmsg = inet_sendmsg, /* ok */ | ||
484 | .recvmsg = sock_common_recvmsg, /* ok */ | ||
485 | .mmap = sock_no_mmap, | ||
486 | .sendpage = tcp_sendpage | ||
487 | }; | ||
488 | |||
489 | struct proto_ops inet6_dgram_ops = { | ||
490 | .family = PF_INET6, | ||
491 | .owner = THIS_MODULE, | ||
492 | .release = inet6_release, | ||
493 | .bind = inet6_bind, | ||
494 | .connect = inet_dgram_connect, /* ok */ | ||
495 | .socketpair = sock_no_socketpair, /* a do nothing */ | ||
496 | .accept = sock_no_accept, /* a do nothing */ | ||
497 | .getname = inet6_getname, | ||
498 | .poll = udp_poll, /* ok */ | ||
499 | .ioctl = inet6_ioctl, /* must change */ | ||
500 | .listen = sock_no_listen, /* ok */ | ||
501 | .shutdown = inet_shutdown, /* ok */ | ||
502 | .setsockopt = sock_common_setsockopt, /* ok */ | ||
503 | .getsockopt = sock_common_getsockopt, /* ok */ | ||
504 | .sendmsg = inet_sendmsg, /* ok */ | ||
505 | .recvmsg = sock_common_recvmsg, /* ok */ | ||
506 | .mmap = sock_no_mmap, | ||
507 | .sendpage = sock_no_sendpage, | ||
508 | }; | ||
509 | |||
510 | static struct net_proto_family inet6_family_ops = { | ||
511 | .family = PF_INET6, | ||
512 | .create = inet6_create, | ||
513 | .owner = THIS_MODULE, | ||
514 | }; | ||
515 | |||
516 | #ifdef CONFIG_SYSCTL | ||
517 | extern void ipv6_sysctl_register(void); | ||
518 | extern void ipv6_sysctl_unregister(void); | ||
519 | #endif | ||
520 | |||
521 | /* Same as inet6_dgram_ops, sans udp_poll. */ | ||
522 | static struct proto_ops inet6_sockraw_ops = { | ||
523 | .family = PF_INET6, | ||
524 | .owner = THIS_MODULE, | ||
525 | .release = inet6_release, | ||
526 | .bind = inet6_bind, | ||
527 | .connect = inet_dgram_connect, /* ok */ | ||
528 | .socketpair = sock_no_socketpair, /* a do nothing */ | ||
529 | .accept = sock_no_accept, /* a do nothing */ | ||
530 | .getname = inet6_getname, | ||
531 | .poll = datagram_poll, /* ok */ | ||
532 | .ioctl = inet6_ioctl, /* must change */ | ||
533 | .listen = sock_no_listen, /* ok */ | ||
534 | .shutdown = inet_shutdown, /* ok */ | ||
535 | .setsockopt = sock_common_setsockopt, /* ok */ | ||
536 | .getsockopt = sock_common_getsockopt, /* ok */ | ||
537 | .sendmsg = inet_sendmsg, /* ok */ | ||
538 | .recvmsg = sock_common_recvmsg, /* ok */ | ||
539 | .mmap = sock_no_mmap, | ||
540 | .sendpage = sock_no_sendpage, | ||
541 | }; | ||
542 | |||
543 | static struct inet_protosw rawv6_protosw = { | ||
544 | .type = SOCK_RAW, | ||
545 | .protocol = IPPROTO_IP, /* wild card */ | ||
546 | .prot = &rawv6_prot, | ||
547 | .ops = &inet6_sockraw_ops, | ||
548 | .capability = CAP_NET_RAW, | ||
549 | .no_check = UDP_CSUM_DEFAULT, | ||
550 | .flags = INET_PROTOSW_REUSE, | ||
551 | }; | ||
552 | |||
553 | void | ||
554 | inet6_register_protosw(struct inet_protosw *p) | ||
555 | { | ||
556 | struct list_head *lh; | ||
557 | struct inet_protosw *answer; | ||
558 | int protocol = p->protocol; | ||
559 | struct list_head *last_perm; | ||
560 | |||
561 | spin_lock_bh(&inetsw6_lock); | ||
562 | |||
563 | if (p->type >= SOCK_MAX) | ||
564 | goto out_illegal; | ||
565 | |||
566 | /* If we are trying to override a permanent protocol, bail. */ | ||
567 | answer = NULL; | ||
568 | last_perm = &inetsw6[p->type]; | ||
569 | list_for_each(lh, &inetsw6[p->type]) { | ||
570 | answer = list_entry(lh, struct inet_protosw, list); | ||
571 | |||
572 | /* Check only the non-wild match. */ | ||
573 | if (INET_PROTOSW_PERMANENT & answer->flags) { | ||
574 | if (protocol == answer->protocol) | ||
575 | break; | ||
576 | last_perm = lh; | ||
577 | } | ||
578 | |||
579 | answer = NULL; | ||
580 | } | ||
581 | if (answer) | ||
582 | goto out_permanent; | ||
583 | |||
584 | /* Add the new entry after the last permanent entry if any, so that | ||
585 | * the new entry does not override a permanent entry when matched with | ||
586 | * a wild-card protocol. But it is allowed to override any existing | ||
587 | * non-permanent entry. This means that when we remove this entry, the | ||
588 | * system automatically returns to the old behavior. | ||
589 | */ | ||
590 | list_add_rcu(&p->list, last_perm); | ||
591 | out: | ||
592 | spin_unlock_bh(&inetsw6_lock); | ||
593 | return; | ||
594 | |||
595 | out_permanent: | ||
596 | printk(KERN_ERR "Attempt to override permanent protocol %d.\n", | ||
597 | protocol); | ||
598 | goto out; | ||
599 | |||
600 | out_illegal: | ||
601 | printk(KERN_ERR | ||
602 | "Ignoring attempt to register invalid socket type %d.\n", | ||
603 | p->type); | ||
604 | goto out; | ||
605 | } | ||
606 | |||
607 | void | ||
608 | inet6_unregister_protosw(struct inet_protosw *p) | ||
609 | { | ||
610 | if (INET_PROTOSW_PERMANENT & p->flags) { | ||
611 | printk(KERN_ERR | ||
612 | "Attempt to unregister permanent protocol %d.\n", | ||
613 | p->protocol); | ||
614 | } else { | ||
615 | spin_lock_bh(&inetsw6_lock); | ||
616 | list_del_rcu(&p->list); | ||
617 | spin_unlock_bh(&inetsw6_lock); | ||
618 | |||
619 | synchronize_net(); | ||
620 | } | ||
621 | } | ||
622 | |||
623 | int | ||
624 | snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign) | ||
625 | { | ||
626 | if (ptr == NULL) | ||
627 | return -EINVAL; | ||
628 | |||
629 | ptr[0] = __alloc_percpu(mibsize, mibalign); | ||
630 | if (!ptr[0]) | ||
631 | goto err0; | ||
632 | |||
633 | ptr[1] = __alloc_percpu(mibsize, mibalign); | ||
634 | if (!ptr[1]) | ||
635 | goto err1; | ||
636 | |||
637 | return 0; | ||
638 | |||
639 | err1: | ||
640 | free_percpu(ptr[0]); | ||
641 | ptr[0] = NULL; | ||
642 | err0: | ||
643 | return -ENOMEM; | ||
644 | } | ||
645 | |||
646 | void | ||
647 | snmp6_mib_free(void *ptr[2]) | ||
648 | { | ||
649 | if (ptr == NULL) | ||
650 | return; | ||
651 | if (ptr[0]) | ||
652 | free_percpu(ptr[0]); | ||
653 | if (ptr[1]) | ||
654 | free_percpu(ptr[1]); | ||
655 | ptr[0] = ptr[1] = NULL; | ||
656 | } | ||
657 | |||
658 | static int __init init_ipv6_mibs(void) | ||
659 | { | ||
660 | if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipstats_mib), | ||
661 | __alignof__(struct ipstats_mib)) < 0) | ||
662 | goto err_ip_mib; | ||
663 | if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib), | ||
664 | __alignof__(struct icmpv6_mib)) < 0) | ||
665 | goto err_icmp_mib; | ||
666 | if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib), | ||
667 | __alignof__(struct udp_mib)) < 0) | ||
668 | goto err_udp_mib; | ||
669 | return 0; | ||
670 | |||
671 | err_udp_mib: | ||
672 | snmp6_mib_free((void **)icmpv6_statistics); | ||
673 | err_icmp_mib: | ||
674 | snmp6_mib_free((void **)ipv6_statistics); | ||
675 | err_ip_mib: | ||
676 | return -ENOMEM; | ||
677 | |||
678 | } | ||
679 | |||
680 | static void cleanup_ipv6_mibs(void) | ||
681 | { | ||
682 | snmp6_mib_free((void **)ipv6_statistics); | ||
683 | snmp6_mib_free((void **)icmpv6_statistics); | ||
684 | snmp6_mib_free((void **)udp_stats_in6); | ||
685 | } | ||
686 | |||
687 | extern int ipv6_misc_proc_init(void); | ||
688 | |||
689 | static int __init inet6_init(void) | ||
690 | { | ||
691 | struct sk_buff *dummy_skb; | ||
692 | struct list_head *r; | ||
693 | int err; | ||
694 | |||
695 | #ifdef MODULE | ||
696 | #if 0 /* FIXME --RR */ | ||
697 | if (!mod_member_present(&__this_module, can_unload)) | ||
698 | return -EINVAL; | ||
699 | |||
700 | __this_module.can_unload = &ipv6_unload; | ||
701 | #endif | ||
702 | #endif | ||
703 | |||
704 | if (sizeof(struct inet6_skb_parm) > sizeof(dummy_skb->cb)) { | ||
705 | printk(KERN_CRIT "inet6_proto_init: size fault\n"); | ||
706 | return -EINVAL; | ||
707 | } | ||
708 | |||
709 | err = proto_register(&tcpv6_prot, 1); | ||
710 | if (err) | ||
711 | goto out; | ||
712 | |||
713 | err = proto_register(&udpv6_prot, 1); | ||
714 | if (err) | ||
715 | goto out_unregister_tcp_proto; | ||
716 | |||
717 | err = proto_register(&rawv6_prot, 1); | ||
718 | if (err) | ||
719 | goto out_unregister_udp_proto; | ||
720 | |||
721 | |||
722 | /* Register the socket-side information for inet6_create. */ | ||
723 | for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r) | ||
724 | INIT_LIST_HEAD(r); | ||
725 | |||
726 | /* We MUST register RAW sockets before we create the ICMP6, | ||
727 | * IGMP6, or NDISC control sockets. | ||
728 | */ | ||
729 | inet6_register_protosw(&rawv6_protosw); | ||
730 | |||
731 | /* Register the family here so that the init calls below will | ||
732 | * be able to create sockets. (?? is this dangerous ??) | ||
733 | */ | ||
734 | (void) sock_register(&inet6_family_ops); | ||
735 | |||
736 | /* Initialise ipv6 mibs */ | ||
737 | err = init_ipv6_mibs(); | ||
738 | if (err) | ||
739 | goto out_unregister_raw_proto; | ||
740 | |||
741 | /* | ||
742 | * ipngwg API draft makes clear that the correct semantics | ||
743 | * for TCP and UDP is to consider one TCP and UDP instance | ||
744 | * in a host availiable by both INET and INET6 APIs and | ||
745 | * able to communicate via both network protocols. | ||
746 | */ | ||
747 | |||
748 | #ifdef CONFIG_SYSCTL | ||
749 | ipv6_sysctl_register(); | ||
750 | #endif | ||
751 | err = icmpv6_init(&inet6_family_ops); | ||
752 | if (err) | ||
753 | goto icmp_fail; | ||
754 | err = ndisc_init(&inet6_family_ops); | ||
755 | if (err) | ||
756 | goto ndisc_fail; | ||
757 | err = igmp6_init(&inet6_family_ops); | ||
758 | if (err) | ||
759 | goto igmp_fail; | ||
760 | /* Create /proc/foo6 entries. */ | ||
761 | #ifdef CONFIG_PROC_FS | ||
762 | err = -ENOMEM; | ||
763 | if (raw6_proc_init()) | ||
764 | goto proc_raw6_fail; | ||
765 | if (tcp6_proc_init()) | ||
766 | goto proc_tcp6_fail; | ||
767 | if (udp6_proc_init()) | ||
768 | goto proc_udp6_fail; | ||
769 | if (ipv6_misc_proc_init()) | ||
770 | goto proc_misc6_fail; | ||
771 | |||
772 | if (ac6_proc_init()) | ||
773 | goto proc_anycast6_fail; | ||
774 | if (if6_proc_init()) | ||
775 | goto proc_if6_fail; | ||
776 | #endif | ||
777 | ipv6_packet_init(); | ||
778 | ip6_route_init(); | ||
779 | ip6_flowlabel_init(); | ||
780 | err = addrconf_init(); | ||
781 | if (err) | ||
782 | goto addrconf_fail; | ||
783 | sit_init(); | ||
784 | |||
785 | /* Init v6 extension headers. */ | ||
786 | ipv6_rthdr_init(); | ||
787 | ipv6_frag_init(); | ||
788 | ipv6_nodata_init(); | ||
789 | ipv6_destopt_init(); | ||
790 | |||
791 | /* Init v6 transport protocols. */ | ||
792 | udpv6_init(); | ||
793 | tcpv6_init(); | ||
794 | err = 0; | ||
795 | out: | ||
796 | return err; | ||
797 | |||
798 | addrconf_fail: | ||
799 | ip6_flowlabel_cleanup(); | ||
800 | ip6_route_cleanup(); | ||
801 | ipv6_packet_cleanup(); | ||
802 | #ifdef CONFIG_PROC_FS | ||
803 | if6_proc_exit(); | ||
804 | proc_if6_fail: | ||
805 | ac6_proc_exit(); | ||
806 | proc_anycast6_fail: | ||
807 | ipv6_misc_proc_exit(); | ||
808 | proc_misc6_fail: | ||
809 | udp6_proc_exit(); | ||
810 | proc_udp6_fail: | ||
811 | tcp6_proc_exit(); | ||
812 | proc_tcp6_fail: | ||
813 | raw6_proc_exit(); | ||
814 | proc_raw6_fail: | ||
815 | #endif | ||
816 | igmp6_cleanup(); | ||
817 | igmp_fail: | ||
818 | ndisc_cleanup(); | ||
819 | ndisc_fail: | ||
820 | icmpv6_cleanup(); | ||
821 | icmp_fail: | ||
822 | #ifdef CONFIG_SYSCTL | ||
823 | ipv6_sysctl_unregister(); | ||
824 | #endif | ||
825 | cleanup_ipv6_mibs(); | ||
826 | out_unregister_raw_proto: | ||
827 | proto_unregister(&rawv6_prot); | ||
828 | out_unregister_udp_proto: | ||
829 | proto_unregister(&udpv6_prot); | ||
830 | out_unregister_tcp_proto: | ||
831 | proto_unregister(&tcpv6_prot); | ||
832 | goto out; | ||
833 | } | ||
834 | module_init(inet6_init); | ||
835 | |||
836 | static void __exit inet6_exit(void) | ||
837 | { | ||
838 | /* First of all disallow new sockets creation. */ | ||
839 | sock_unregister(PF_INET6); | ||
840 | #ifdef CONFIG_PROC_FS | ||
841 | if6_proc_exit(); | ||
842 | ac6_proc_exit(); | ||
843 | ipv6_misc_proc_exit(); | ||
844 | udp6_proc_exit(); | ||
845 | tcp6_proc_exit(); | ||
846 | raw6_proc_exit(); | ||
847 | #endif | ||
848 | /* Cleanup code parts. */ | ||
849 | sit_cleanup(); | ||
850 | ip6_flowlabel_cleanup(); | ||
851 | addrconf_cleanup(); | ||
852 | ip6_route_cleanup(); | ||
853 | ipv6_packet_cleanup(); | ||
854 | igmp6_cleanup(); | ||
855 | ndisc_cleanup(); | ||
856 | icmpv6_cleanup(); | ||
857 | #ifdef CONFIG_SYSCTL | ||
858 | ipv6_sysctl_unregister(); | ||
859 | #endif | ||
860 | cleanup_ipv6_mibs(); | ||
861 | proto_unregister(&rawv6_prot); | ||
862 | proto_unregister(&udpv6_prot); | ||
863 | proto_unregister(&tcpv6_prot); | ||
864 | } | ||
865 | module_exit(inet6_exit); | ||
866 | |||
867 | MODULE_ALIAS_NETPROTO(PF_INET6); | ||