2005-04-17 02:20:36 +04:00
/*
* PF_INET6 socket protocol family
2007-02-09 17:24:49 +03:00
* Linux INET6 implementation
2005-04-17 02:20:36 +04:00
*
* Authors :
2007-02-09 17:24:49 +03:00
* Pedro Roque < roque @ di . fc . ul . pt >
2005-04-17 02:20:36 +04:00
*
* Adapted from linux / net / ipv4 / af_inet . c
*
* $ Id : af_inet6 . c , v 1.66 2002 / 02 / 01 22 : 01 : 04 davem Exp $
*
* Fixes :
* piggy , Karl Knutson : Socket protocol table
* Hideaki YOSHIFUJI : sin6_scope_id support
* Arnaldo Melo : check proc_net_create return , cleanups
*
* 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 .
*/
# include <linux/module.h>
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/in.h>
# include <linux/kernel.h>
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <linux/fcntl.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/proc_fs.h>
# include <linux/stat.h>
# include <linux/init.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <linux/icmpv6.h>
2005-08-10 06:42:34 +04:00
# include <linux/netfilter_ipv6.h>
2005-04-17 02:20:36 +04:00
# include <net/ip.h>
# include <net/ipv6.h>
# include <net/udp.h>
2006-11-27 22:10:57 +03:00
# include <net/udplite.h>
2005-04-17 02:20:36 +04:00
# include <net/tcp.h>
# include <net/ipip.h>
# include <net/protocol.h>
# include <net/inet_common.h>
# include <net/transp_v6.h>
# include <net/ip6_route.h>
# include <net/addrconf.h>
# ifdef CONFIG_IPV6_TUNNEL
# include <net/ip6_tunnel.h>
# endif
# include <asm/uaccess.h>
# include <asm/system.h>
MODULE_AUTHOR ( " Cast of dozens " ) ;
MODULE_DESCRIPTION ( " IPv6 protocol stack for Linux " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-11-23 16:28:44 +03:00
/* The inetsw6 table contains everything that inet6_create needs to
2005-04-17 02:20:36 +04:00
* build a new socket .
*/
static struct list_head inetsw6 [ SOCK_MAX ] ;
static DEFINE_SPINLOCK ( inetsw6_lock ) ;
static __inline__ struct ipv6_pinfo * inet6_sk_generic ( struct sock * sk )
{
const int offset = sk - > sk_prot - > obj_size - sizeof ( struct ipv6_pinfo ) ;
return ( struct ipv6_pinfo * ) ( ( ( u8 * ) sk ) + offset ) ;
}
2007-10-09 10:24:22 +04:00
static int inet6_create ( struct net * net , struct socket * sock , int protocol )
2005-04-17 02:20:36 +04:00
{
struct inet_sock * inet ;
struct ipv6_pinfo * np ;
struct sock * sk ;
struct list_head * p ;
struct inet_protosw * answer ;
struct proto * answer_prot ;
unsigned char answer_flags ;
char answer_no_check ;
2005-12-03 07:56:57 +03:00
int try_loading_module = 0 ;
int err ;
2005-04-17 02:20:36 +04:00
2007-10-09 10:24:22 +04:00
if ( net ! = & init_net )
return - EAFNOSUPPORT ;
2007-03-23 21:40:27 +03:00
if ( sock - > type ! = SOCK_RAW & &
sock - > type ! = SOCK_DGRAM & &
! inet_ehash_secret )
build_ehash_secret ( ) ;
2005-04-17 02:20:36 +04:00
/* Look for the requested type/protocol pair. */
answer = NULL ;
2005-12-03 07:56:57 +03:00
lookup_protocol :
err = - ESOCKTNOSUPPORT ;
2005-04-17 02:20:36 +04:00
rcu_read_lock ( ) ;
list_for_each_rcu ( p , & inetsw6 [ sock - > type ] ) {
answer = list_entry ( p , struct inet_protosw , list ) ;
/* Check the non-wild match. */
if ( protocol = = answer - > protocol ) {
if ( protocol ! = IPPROTO_IP )
break ;
} else {
/* Check for the two wild cases. */
if ( IPPROTO_IP = = protocol ) {
protocol = answer - > protocol ;
break ;
}
if ( IPPROTO_IP = = answer - > protocol )
break ;
}
2005-12-03 07:56:57 +03:00
err = - EPROTONOSUPPORT ;
2005-04-17 02:20:36 +04:00
answer = NULL ;
}
2005-12-03 07:56:57 +03:00
if ( ! answer ) {
if ( try_loading_module < 2 ) {
rcu_read_unlock ( ) ;
/*
* Be more specific , e . g . net - pf - 10 - proto - 132 - type - 1
* ( net - pf - PF_INET6 - proto - IPPROTO_SCTP - type - SOCK_STREAM )
*/
if ( + + try_loading_module = = 1 )
request_module ( " net-pf-%d-proto-%d-type-%d " ,
PF_INET6 , protocol , sock - > type ) ;
/*
* Fall back to generic , e . g . net - pf - 10 - proto - 132
* ( net - pf - PF_INET6 - proto - IPPROTO_SCTP )
*/
else
request_module ( " net-pf-%d-proto-%d " ,
PF_INET6 , protocol ) ;
goto lookup_protocol ;
} else
goto out_rcu_unlock ;
}
err = - EPERM ;
2005-04-17 02:20:36 +04:00
if ( answer - > capability > 0 & & ! capable ( answer - > capability ) )
goto out_rcu_unlock ;
sock - > ops = answer - > ops ;
answer_prot = answer - > prot ;
answer_no_check = answer - > no_check ;
answer_flags = answer - > flags ;
rcu_read_unlock ( ) ;
BUG_TRAP ( answer_prot - > slab ! = NULL ) ;
2005-12-03 07:56:57 +03:00
err = - ENOBUFS ;
2007-11-01 10:39:31 +03:00
sk = sk_alloc ( net , PF_INET6 , GFP_KERNEL , answer_prot ) ;
2005-04-17 02:20:36 +04:00
if ( sk = = NULL )
goto out ;
sock_init_data ( sock , sk ) ;
2005-12-03 07:56:57 +03:00
err = 0 ;
2005-04-17 02:20:36 +04:00
sk - > sk_no_check = answer_no_check ;
if ( INET_PROTOSW_REUSE & answer_flags )
sk - > sk_reuse = 1 ;
inet = inet_sk ( sk ) ;
2007-01-10 01:37:06 +03:00
inet - > is_icsk = ( INET_PROTOSW_ICSK & answer_flags ) ! = 0 ;
2005-04-17 02:20:36 +04:00
if ( SOCK_RAW = = sock - > type ) {
inet - > num = protocol ;
if ( IPPROTO_RAW = = protocol )
inet - > hdrincl = 1 ;
}
2005-08-10 06:45:38 +04:00
sk - > sk_destruct = inet_sock_destruct ;
2005-04-17 02:20:36 +04:00
sk - > sk_family = PF_INET6 ;
sk - > sk_protocol = protocol ;
sk - > sk_backlog_rcv = answer - > prot - > backlog_rcv ;
inet_sk ( sk ) - > pinet6 = np = inet6_sk_generic ( sk ) ;
np - > hop_limit = - 1 ;
np - > mcast_hops = - 1 ;
np - > mc_loop = 1 ;
np - > pmtudisc = IPV6_PMTUDISC_WANT ;
2008-01-10 13:54:53 +03:00
np - > ipv6only = init_net . ipv6 . sysctl . bindv6only ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
/* Init the ipv4 part of the socket since we can have sockets
* using v6 API for ipv4 .
*/
inet - > uc_ttl = - 1 ;
inet - > mc_loop = 1 ;
inet - > mc_ttl = 1 ;
inet - > mc_index = 0 ;
inet - > mc_list = NULL ;
if ( ipv4_config . no_pmtu_disc )
inet - > pmtudisc = IP_PMTUDISC_DONT ;
else
inet - > pmtudisc = IP_PMTUDISC_WANT ;
2007-02-09 17:24:49 +03:00
/*
2005-08-10 06:45:38 +04:00
* Increment only the relevant sk_prot - > socks debug field , this changes
* the previous behaviour of incrementing both the equivalent to
* answer - > prot - > socks ( inet6_sock_nr ) and inet_sock_nr .
*
* This allows better debug granularity as we ' ll know exactly how many
* UDPv6 , TCPv6 , etc socks were allocated , not the sum of all IPv6
* transport protocol socks . - acme
*/
sk_refcnt_debug_inc ( sk ) ;
2005-04-17 02:20:36 +04:00
if ( inet - > num ) {
/* It assumes that any protocol which allows
* the user to assign a number at socket
* creation time automatically shares .
*/
2006-11-15 07:56:00 +03:00
inet - > sport = htons ( inet - > num ) ;
2005-04-17 02:20:36 +04:00
sk - > sk_prot - > hash ( sk ) ;
}
if ( sk - > sk_prot - > init ) {
2005-12-03 07:56:57 +03:00
err = sk - > sk_prot - > init ( sk ) ;
if ( err ) {
2005-04-17 02:20:36 +04:00
sk_common_release ( sk ) ;
goto out ;
}
}
out :
2005-12-03 07:56:57 +03:00
return err ;
2005-04-17 02:20:36 +04:00
out_rcu_unlock :
rcu_read_unlock ( ) ;
goto out ;
}
/* bind for INET6 API */
int inet6_bind ( struct socket * sock , struct sockaddr * uaddr , int addr_len )
{
struct sockaddr_in6 * addr = ( struct sockaddr_in6 * ) uaddr ;
struct sock * sk = sock - > sk ;
struct inet_sock * inet = inet_sk ( sk ) ;
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
2006-09-27 09:17:51 +04:00
__be32 v4addr = 0 ;
2005-04-17 02:20:36 +04:00
unsigned short snum ;
int addr_type = 0 ;
int err = 0 ;
/* If the socket has its own bind function then use it. */
if ( sk - > sk_prot - > bind )
return sk - > sk_prot - > bind ( sk , uaddr , addr_len ) ;
if ( addr_len < SIN6_LEN_RFC2133 )
return - EINVAL ;
addr_type = ipv6_addr_type ( & addr - > sin6_addr ) ;
if ( ( addr_type & IPV6_ADDR_MULTICAST ) & & sock - > type = = SOCK_STREAM )
return - EINVAL ;
snum = ntohs ( addr - > sin6_port ) ;
if ( snum & & snum < PROT_SOCK & & ! capable ( CAP_NET_BIND_SERVICE ) )
return - EACCES ;
lock_sock ( sk ) ;
/* Check these errors (active socket, double bind). */
if ( sk - > sk_state ! = TCP_CLOSE | | inet - > num ) {
err = - EINVAL ;
goto out ;
}
/* Check if the address belongs to the host. */
if ( addr_type = = IPV6_ADDR_MAPPED ) {
v4addr = addr - > sin6_addr . s6_addr32 [ 3 ] ;
2008-01-10 14:25:28 +03:00
if ( inet_addr_type ( & init_net , v4addr ) ! = RTN_LOCAL ) {
2005-04-17 02:20:36 +04:00
err = - EADDRNOTAVAIL ;
goto out ;
}
} else {
if ( addr_type ! = IPV6_ADDR_ANY ) {
struct net_device * dev = NULL ;
if ( addr_type & IPV6_ADDR_LINKLOCAL ) {
if ( addr_len > = sizeof ( struct sockaddr_in6 ) & &
addr - > sin6_scope_id ) {
/* Override any existing binding, if another one
* is supplied by user .
*/
sk - > sk_bound_dev_if = addr - > sin6_scope_id ;
}
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
/* Binding to link-local address requires an interface */
if ( ! sk - > sk_bound_dev_if ) {
err = - EINVAL ;
goto out ;
}
2007-09-17 22:56:21 +04:00
dev = dev_get_by_index ( & init_net , sk - > sk_bound_dev_if ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev ) {
err = - ENODEV ;
goto out ;
}
}
/* ipv4 addr of the socket is invalid. Only the
* unspecified and mapped address have a v4 equivalent .
*/
v4addr = LOOPBACK4_IPV6 ;
if ( ! ( addr_type & IPV6_ADDR_MULTICAST ) ) {
2008-01-11 09:43:18 +03:00
if ( ! ipv6_chk_addr ( & init_net , & addr - > sin6_addr ,
dev , 0 ) ) {
2005-04-17 02:20:36 +04:00
if ( dev )
dev_put ( dev ) ;
err = - EADDRNOTAVAIL ;
goto out ;
}
}
if ( dev )
dev_put ( dev ) ;
}
}
inet - > rcv_saddr = v4addr ;
inet - > saddr = v4addr ;
ipv6_addr_copy ( & np - > rcv_saddr , & addr - > sin6_addr ) ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
if ( ! ( addr_type & IPV6_ADDR_MULTICAST ) )
ipv6_addr_copy ( & np - > saddr , & addr - > sin6_addr ) ;
/* Make sure we are allowed to bind here. */
if ( sk - > sk_prot - > get_port ( sk , snum ) ) {
inet_reset_saddr ( sk ) ;
err = - EADDRINUSE ;
goto out ;
}
if ( addr_type ! = IPV6_ADDR_ANY )
sk - > sk_userlocks | = SOCK_BINDADDR_LOCK ;
if ( snum )
sk - > sk_userlocks | = SOCK_BINDPORT_LOCK ;
2006-11-15 07:56:00 +03:00
inet - > sport = htons ( inet - > num ) ;
2005-04-17 02:20:36 +04:00
inet - > dport = 0 ;
inet - > daddr = 0 ;
out :
release_sock ( sk ) ;
return err ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( inet6_bind ) ;
2005-04-17 02:20:36 +04:00
int inet6_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
if ( sk = = NULL )
return - EINVAL ;
/* Free mc lists */
ipv6_sock_mc_close ( sk ) ;
/* Free ac lists */
ipv6_sock_ac_close ( sk ) ;
return inet_release ( sock ) ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( inet6_release ) ;
2005-04-17 02:20:36 +04:00
int inet6_destroy_sock ( struct sock * sk )
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
struct sk_buff * skb ;
struct ipv6_txoptions * opt ;
/* Release rx options */
if ( ( skb = xchg ( & np - > pktoptions , NULL ) ) ! = NULL )
kfree_skb ( skb ) ;
/* Free flowlabels */
fl6_free_socklist ( sk ) ;
/* Free tx options */
if ( ( opt = xchg ( & np - > opt , NULL ) ) ! = NULL )
sock_kfree_s ( sk , opt , opt - > tot_len ) ;
return 0 ;
}
2005-12-14 10:23:20 +03:00
EXPORT_SYMBOL_GPL ( inet6_destroy_sock ) ;
2005-04-17 02:20:36 +04:00
/*
* This does both peername and sockname .
*/
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
int inet6_getname ( struct socket * sock , struct sockaddr * uaddr ,
int * uaddr_len , int peer )
{
struct sockaddr_in6 * sin = ( struct sockaddr_in6 * ) uaddr ;
struct sock * sk = sock - > sk ;
struct inet_sock * inet = inet_sk ( sk ) ;
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
sin - > sin6_family = AF_INET6 ;
sin - > sin6_flowinfo = 0 ;
sin - > sin6_scope_id = 0 ;
if ( peer ) {
if ( ! inet - > dport )
return - ENOTCONN ;
if ( ( ( 1 < < sk - > sk_state ) & ( TCPF_CLOSE | TCPF_SYN_SENT ) ) & &
peer = = 1 )
return - ENOTCONN ;
sin - > sin6_port = inet - > dport ;
ipv6_addr_copy ( & sin - > sin6_addr , & np - > daddr ) ;
if ( np - > sndflow )
sin - > sin6_flowinfo = np - > flow_label ;
} else {
if ( ipv6_addr_any ( & np - > rcv_saddr ) )
ipv6_addr_copy ( & sin - > sin6_addr , & np - > saddr ) ;
else
ipv6_addr_copy ( & sin - > sin6_addr , & np - > rcv_saddr ) ;
sin - > sin6_port = inet - > sport ;
}
if ( ipv6_addr_type ( & sin - > sin6_addr ) & IPV6_ADDR_LINKLOCAL )
sin - > sin6_scope_id = sk - > sk_bound_dev_if ;
* uaddr_len = sizeof ( * sin ) ;
return ( 0 ) ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( inet6_getname ) ;
2005-04-17 02:20:36 +04:00
int inet6_ioctl ( struct socket * sock , unsigned int cmd , unsigned long arg )
{
struct sock * sk = sock - > sk ;
2008-03-05 00:47:47 +03:00
struct net * net = sk - > sk_net ;
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:49 +03:00
switch ( cmd )
2005-04-17 02:20:36 +04:00
{
case SIOCGSTAMP :
return sock_get_timestamp ( sk , ( struct timeval __user * ) arg ) ;
2007-03-19 03:33:16 +03:00
case SIOCGSTAMPNS :
return sock_get_timestampns ( sk , ( struct timespec __user * ) arg ) ;
2005-04-17 02:20:36 +04:00
case SIOCADDRT :
case SIOCDELRT :
2007-02-09 17:24:49 +03:00
2008-03-05 00:47:47 +03:00
return ( ipv6_route_ioctl ( net , cmd , ( void __user * ) arg ) ) ;
2005-04-17 02:20:36 +04:00
case SIOCSIFADDR :
return addrconf_add_ifaddr ( ( void __user * ) arg ) ;
case SIOCDIFADDR :
return addrconf_del_ifaddr ( ( void __user * ) arg ) ;
case SIOCSIFDSTADDR :
return addrconf_set_dstaddr ( ( void __user * ) arg ) ;
default :
2006-01-04 01:18:33 +03:00
if ( ! sk - > sk_prot - > ioctl )
return - ENOIOCTLCMD ;
return sk - > sk_prot - > ioctl ( sk , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
}
/*NOTREACHED*/
return ( 0 ) ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( inet6_ioctl ) ;
2005-12-22 23:49:22 +03:00
const struct proto_ops inet6_stream_ops = {
2006-03-21 09:48:35 +03:00
. family = PF_INET6 ,
. owner = THIS_MODULE ,
. release = inet6_release ,
. bind = inet6_bind ,
. connect = inet_stream_connect , /* ok */
. socketpair = sock_no_socketpair , /* a do nothing */
. accept = inet_accept , /* ok */
. getname = inet6_getname ,
. poll = tcp_poll , /* ok */
. ioctl = inet6_ioctl , /* must change */
. listen = inet_listen , /* ok */
. shutdown = inet_shutdown , /* ok */
. setsockopt = sock_common_setsockopt , /* ok */
. getsockopt = sock_common_getsockopt , /* ok */
2007-08-03 06:23:56 +04:00
. sendmsg = tcp_sendmsg , /* ok */
2006-03-21 09:48:35 +03:00
. recvmsg = sock_common_recvmsg , /* ok */
. mmap = sock_no_mmap ,
. sendpage = tcp_sendpage ,
2007-11-07 10:31:58 +03:00
. splice_read = tcp_splice_read ,
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
2006-03-21 09:48:35 +03:00
. compat_setsockopt = compat_sock_common_setsockopt ,
. compat_getsockopt = compat_sock_common_getsockopt ,
2006-03-21 09:45:21 +03:00
# endif
2005-04-17 02:20:36 +04:00
} ;
2005-12-22 23:49:22 +03:00
const struct proto_ops inet6_dgram_ops = {
2006-03-21 09:48:35 +03:00
. family = PF_INET6 ,
. owner = THIS_MODULE ,
. release = inet6_release ,
. bind = inet6_bind ,
. connect = inet_dgram_connect , /* ok */
. socketpair = sock_no_socketpair , /* a do nothing */
. accept = sock_no_accept , /* a do nothing */
. getname = inet6_getname ,
. poll = udp_poll , /* ok */
. ioctl = inet6_ioctl , /* must change */
. listen = sock_no_listen , /* ok */
. shutdown = inet_shutdown , /* ok */
. setsockopt = sock_common_setsockopt , /* ok */
. getsockopt = sock_common_getsockopt , /* ok */
. sendmsg = inet_sendmsg , /* ok */
. recvmsg = sock_common_recvmsg , /* ok */
. mmap = sock_no_mmap ,
. sendpage = sock_no_sendpage ,
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
2006-03-21 09:48:35 +03:00
. compat_setsockopt = compat_sock_common_setsockopt ,
. compat_getsockopt = compat_sock_common_getsockopt ,
2006-03-21 09:45:21 +03:00
# endif
2005-04-17 02:20:36 +04:00
} ;
static struct net_proto_family inet6_family_ops = {
. family = PF_INET6 ,
. create = inet6_create ,
. owner = THIS_MODULE ,
} ;
2007-12-11 13:25:01 +03:00
int inet6_register_protosw ( struct inet_protosw * p )
2005-04-17 02:20:36 +04:00
{
struct list_head * lh ;
struct inet_protosw * answer ;
struct list_head * last_perm ;
2007-12-11 13:25:01 +03:00
int protocol = p - > protocol ;
int ret ;
2005-04-17 02:20:36 +04:00
spin_lock_bh ( & inetsw6_lock ) ;
2007-12-11 13:25:01 +03:00
ret = - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( p - > type > = SOCK_MAX )
goto out_illegal ;
/* If we are trying to override a permanent protocol, bail. */
answer = NULL ;
2007-12-11 13:25:01 +03:00
ret = - EPERM ;
2005-04-17 02:20:36 +04:00
last_perm = & inetsw6 [ p - > type ] ;
list_for_each ( lh , & inetsw6 [ p - > type ] ) {
answer = list_entry ( lh , struct inet_protosw , list ) ;
/* Check only the non-wild match. */
if ( INET_PROTOSW_PERMANENT & answer - > flags ) {
if ( protocol = = answer - > protocol )
break ;
last_perm = lh ;
}
answer = NULL ;
}
if ( answer )
goto out_permanent ;
/* Add the new entry after the last permanent entry if any, so that
* the new entry does not override a permanent entry when matched with
* a wild - card protocol . But it is allowed to override any existing
2007-02-09 17:24:49 +03:00
* non - permanent entry . This means that when we remove this entry , the
2005-04-17 02:20:36 +04:00
* system automatically returns to the old behavior .
*/
list_add_rcu ( & p - > list , last_perm ) ;
2007-12-11 13:25:01 +03:00
ret = 0 ;
2005-04-17 02:20:36 +04:00
out :
spin_unlock_bh ( & inetsw6_lock ) ;
2007-12-11 13:25:01 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
out_permanent :
printk ( KERN_ERR " Attempt to override permanent protocol %d. \n " ,
protocol ) ;
goto out ;
out_illegal :
printk ( KERN_ERR
" Ignoring attempt to register invalid socket type %d. \n " ,
p - > type ) ;
goto out ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( inet6_register_protosw ) ;
2005-04-17 02:20:36 +04:00
void
inet6_unregister_protosw ( struct inet_protosw * p )
{
if ( INET_PROTOSW_PERMANENT & p - > flags ) {
printk ( KERN_ERR
" Attempt to unregister permanent protocol %d. \n " ,
p - > protocol ) ;
} else {
spin_lock_bh ( & inetsw6_lock ) ;
list_del_rcu ( & p - > list ) ;
spin_unlock_bh ( & inetsw6_lock ) ;
synchronize_net ( ) ;
}
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( inet6_unregister_protosw ) ;
2005-12-14 10:22:54 +03:00
int inet6_sk_rebuild_header ( struct sock * sk )
{
int err ;
struct dst_entry * dst ;
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
dst = __sk_dst_check ( sk , np - > dst_cookie ) ;
if ( dst = = NULL ) {
struct inet_sock * inet = inet_sk ( sk ) ;
struct in6_addr * final_p = NULL , final ;
struct flowi fl ;
memset ( & fl , 0 , sizeof ( fl ) ) ;
fl . proto = sk - > sk_protocol ;
ipv6_addr_copy ( & fl . fl6_dst , & np - > daddr ) ;
ipv6_addr_copy ( & fl . fl6_src , & np - > saddr ) ;
fl . fl6_flowlabel = np - > flow_label ;
fl . oif = sk - > sk_bound_dev_if ;
fl . fl_ip_dport = inet - > dport ;
fl . fl_ip_sport = inet - > sport ;
2006-08-05 10:12:42 +04:00
security_sk_classify_flow ( sk , & fl ) ;
2005-12-14 10:22:54 +03:00
if ( np - > opt & & np - > opt - > srcrt ) {
struct rt0_hdr * rt0 = ( struct rt0_hdr * ) np - > opt - > srcrt ;
ipv6_addr_copy ( & final , & fl . fl6_dst ) ;
ipv6_addr_copy ( & fl . fl6_dst , rt0 - > addr ) ;
final_p = & final ;
}
err = ip6_dst_lookup ( sk , & dst , & fl ) ;
if ( err ) {
sk - > sk_route_caps = 0 ;
return err ;
}
if ( final_p )
ipv6_addr_copy ( & fl . fl6_dst , final_p ) ;
if ( ( err = xfrm_lookup ( & dst , & fl , sk , 0 ) ) < 0 ) {
sk - > sk_err_soft = - err ;
return err ;
}
2006-08-30 04:15:09 +04:00
__ip6_dst_store ( sk , dst , NULL , NULL ) ;
2005-12-14 10:22:54 +03:00
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( inet6_sk_rebuild_header ) ;
2005-12-14 10:24:28 +03:00
int ipv6_opt_accepted ( struct sock * sk , struct sk_buff * skb )
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
struct inet6_skb_parm * opt = IP6CB ( skb ) ;
if ( np - > rxopt . all ) {
if ( ( opt - > hop & & ( np - > rxopt . bits . hopopts | |
np - > rxopt . bits . ohopopts ) ) | |
2007-04-11 07:50:43 +04:00
( ( IPV6_FLOWINFO_MASK &
* ( __be32 * ) skb_network_header ( skb ) ) & &
2005-12-14 10:24:28 +03:00
np - > rxopt . bits . rxflow ) | |
( opt - > srcrt & & ( np - > rxopt . bits . srcrt | |
np - > rxopt . bits . osrcrt ) ) | |
( ( opt - > dst1 | | opt - > dst0 ) & &
( np - > rxopt . bits . dstopts | | np - > rxopt . bits . odstopts ) ) )
return 1 ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( ipv6_opt_accepted ) ;
2008-02-27 17:14:03 +03:00
static struct inet6_protocol * ipv6_gso_pull_exthdrs ( struct sk_buff * skb ,
int proto )
{
struct inet6_protocol * ops = NULL ;
for ( ; ; ) {
struct ipv6_opt_hdr * opth ;
int len ;
if ( proto ! = NEXTHDR_HOP ) {
ops = rcu_dereference ( inet6_protos [ proto ] ) ;
if ( unlikely ( ! ops ) )
break ;
if ( ! ( ops - > flags & INET6_PROTO_GSO_EXTHDR ) )
break ;
}
if ( unlikely ( ! pskb_may_pull ( skb , 8 ) ) )
break ;
opth = ( void * ) skb - > data ;
len = ipv6_optlen ( opth ) ;
if ( unlikely ( ! pskb_may_pull ( skb , len ) ) )
break ;
proto = opth - > nexthdr ;
__skb_pull ( skb , len ) ;
}
return ops ;
}
static int ipv6_gso_send_check ( struct sk_buff * skb )
{
struct ipv6hdr * ipv6h ;
struct inet6_protocol * ops ;
int err = - EINVAL ;
if ( unlikely ( ! pskb_may_pull ( skb , sizeof ( * ipv6h ) ) ) )
goto out ;
ipv6h = ipv6_hdr ( skb ) ;
__skb_pull ( skb , sizeof ( * ipv6h ) ) ;
err = - EPROTONOSUPPORT ;
rcu_read_lock ( ) ;
ops = ipv6_gso_pull_exthdrs ( skb , ipv6h - > nexthdr ) ;
if ( likely ( ops & & ops - > gso_send_check ) ) {
skb_reset_transport_header ( skb ) ;
err = ops - > gso_send_check ( skb ) ;
}
rcu_read_unlock ( ) ;
out :
return err ;
}
static struct sk_buff * ipv6_gso_segment ( struct sk_buff * skb , int features )
{
struct sk_buff * segs = ERR_PTR ( - EINVAL ) ;
struct ipv6hdr * ipv6h ;
struct inet6_protocol * ops ;
if ( ! ( features & NETIF_F_V6_CSUM ) )
features & = ~ NETIF_F_SG ;
if ( unlikely ( skb_shinfo ( skb ) - > gso_type &
~ ( SKB_GSO_UDP |
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
SKB_GSO_TCPV6 |
0 ) ) )
goto out ;
if ( unlikely ( ! pskb_may_pull ( skb , sizeof ( * ipv6h ) ) ) )
goto out ;
ipv6h = ipv6_hdr ( skb ) ;
__skb_pull ( skb , sizeof ( * ipv6h ) ) ;
segs = ERR_PTR ( - EPROTONOSUPPORT ) ;
rcu_read_lock ( ) ;
ops = ipv6_gso_pull_exthdrs ( skb , ipv6h - > nexthdr ) ;
if ( likely ( ops & & ops - > gso_segment ) ) {
skb_reset_transport_header ( skb ) ;
segs = ops - > gso_segment ( skb , features ) ;
}
rcu_read_unlock ( ) ;
if ( unlikely ( IS_ERR ( segs ) ) )
goto out ;
for ( skb = segs ; skb ; skb = skb - > next ) {
ipv6h = ipv6_hdr ( skb ) ;
ipv6h - > payload_len = htons ( skb - > len - skb - > mac_len -
sizeof ( * ipv6h ) ) ;
}
out :
return segs ;
}
static struct packet_type ipv6_packet_type = {
. type = __constant_htons ( ETH_P_IPV6 ) ,
. func = ipv6_rcv ,
. gso_send_check = ipv6_gso_send_check ,
. gso_segment = ipv6_gso_segment ,
} ;
static int __init ipv6_packet_init ( void )
{
dev_add_pack ( & ipv6_packet_type ) ;
return 0 ;
}
static void ipv6_packet_cleanup ( void )
{
dev_remove_pack ( & ipv6_packet_type ) ;
}
2005-04-17 02:20:36 +04:00
static int __init init_ipv6_mibs ( void )
{
2008-01-24 09:31:45 +03:00
if ( snmp_mib_init ( ( void * * ) ipv6_statistics ,
sizeof ( struct ipstats_mib ) ) < 0 )
2005-04-17 02:20:36 +04:00
goto err_ip_mib ;
2008-01-24 09:31:45 +03:00
if ( snmp_mib_init ( ( void * * ) icmpv6_statistics ,
sizeof ( struct icmpv6_mib ) ) < 0 )
2005-04-17 02:20:36 +04:00
goto err_icmp_mib ;
2007-09-17 03:52:35 +04:00
if ( snmp_mib_init ( ( void * * ) icmpv6msg_statistics ,
2008-01-24 09:31:45 +03:00
sizeof ( struct icmpv6msg_mib ) ) < 0 )
2007-09-17 03:52:35 +04:00
goto err_icmpmsg_mib ;
2008-01-24 09:31:45 +03:00
if ( snmp_mib_init ( ( void * * ) udp_stats_in6 , sizeof ( struct udp_mib ) ) < 0 )
2005-04-17 02:20:36 +04:00
goto err_udp_mib ;
2008-02-29 19:06:47 +03:00
# ifdef CONFIG_IP_UDPLITE
2008-01-24 09:31:45 +03:00
if ( snmp_mib_init ( ( void * * ) udplite_stats_in6 ,
sizeof ( struct udp_mib ) ) < 0 )
2006-11-27 22:10:57 +03:00
goto err_udplite_mib ;
2008-02-29 19:06:47 +03:00
# endif
2005-04-17 02:20:36 +04:00
return 0 ;
2008-02-29 19:06:47 +03:00
# ifdef CONFIG_IP_UDPLITE
2006-11-27 22:10:57 +03:00
err_udplite_mib :
2008-02-29 19:06:47 +03:00
# endif
2007-04-25 08:54:09 +04:00
snmp_mib_free ( ( void * * ) udp_stats_in6 ) ;
2005-04-17 02:20:36 +04:00
err_udp_mib :
2007-09-17 03:52:35 +04:00
snmp_mib_free ( ( void * * ) icmpv6msg_statistics ) ;
err_icmpmsg_mib :
2007-04-25 08:54:09 +04:00
snmp_mib_free ( ( void * * ) icmpv6_statistics ) ;
2005-04-17 02:20:36 +04:00
err_icmp_mib :
2007-04-25 08:54:09 +04:00
snmp_mib_free ( ( void * * ) ipv6_statistics ) ;
2005-04-17 02:20:36 +04:00
err_ip_mib :
return - ENOMEM ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
}
static void cleanup_ipv6_mibs ( void )
{
2007-04-25 08:54:09 +04:00
snmp_mib_free ( ( void * * ) ipv6_statistics ) ;
snmp_mib_free ( ( void * * ) icmpv6_statistics ) ;
2007-10-18 06:30:40 +04:00
snmp_mib_free ( ( void * * ) icmpv6msg_statistics ) ;
2007-04-25 08:54:09 +04:00
snmp_mib_free ( ( void * * ) udp_stats_in6 ) ;
2008-02-29 19:06:47 +03:00
# ifdef CONFIG_IP_UDPLITE
2007-04-25 08:54:09 +04:00
snmp_mib_free ( ( void * * ) udplite_stats_in6 ) ;
2008-02-29 19:06:47 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
2008-01-10 13:48:33 +03:00
static int inet6_net_init ( struct net * net )
{
2008-01-10 13:54:53 +03:00
net - > ipv6 . sysctl . bindv6only = 0 ;
2008-01-10 14:01:01 +03:00
net - > ipv6 . sysctl . flush_delay = 0 ;
net - > ipv6 . sysctl . ip6_rt_max_size = 4096 ;
net - > ipv6 . sysctl . ip6_rt_gc_min_interval = HZ / 2 ;
net - > ipv6 . sysctl . ip6_rt_gc_timeout = 60 * HZ ;
net - > ipv6 . sysctl . ip6_rt_gc_interval = 30 * HZ ;
net - > ipv6 . sysctl . ip6_rt_gc_elasticity = 9 ;
net - > ipv6 . sysctl . ip6_rt_mtu_expires = 10 * 60 * HZ ;
net - > ipv6 . sysctl . ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40 ;
2008-01-10 14:02:40 +03:00
net - > ipv6 . sysctl . icmpv6_time = 1 * HZ ;
2008-01-10 13:56:03 +03:00
2008-01-10 13:48:33 +03:00
return 0 ;
}
static void inet6_net_exit ( struct net * net )
{
return ;
}
static struct pernet_operations inet6_net_ops = {
. init = inet6_net_init ,
. exit = inet6_net_exit ,
} ;
2005-04-17 02:20:36 +04:00
static int __init inet6_init ( void )
{
struct sk_buff * dummy_skb ;
2007-02-09 17:24:49 +03:00
struct list_head * r ;
2005-04-17 02:20:36 +04:00
int err ;
2006-09-01 11:29:06 +04:00
BUILD_BUG_ON ( sizeof ( struct inet6_skb_parm ) > sizeof ( dummy_skb - > cb ) ) ;
2005-04-17 02:20:36 +04:00
err = proto_register ( & tcpv6_prot , 1 ) ;
if ( err )
goto out ;
err = proto_register ( & udpv6_prot , 1 ) ;
if ( err )
goto out_unregister_tcp_proto ;
2008-02-29 19:06:47 +03:00
# ifdef CONFIG_IP_UDPLITE
2006-11-27 22:10:57 +03:00
err = proto_register ( & udplitev6_prot , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto out_unregister_udp_proto ;
2008-02-29 19:06:47 +03:00
# endif
2005-04-17 02:20:36 +04:00
2006-11-27 22:10:57 +03:00
err = proto_register ( & rawv6_prot , 1 ) ;
if ( err )
goto out_unregister_udplite_proto ;
2005-04-17 02:20:36 +04:00
/* Register the socket-side information for inet6_create. */
for ( r = & inetsw6 [ 0 ] ; r < & inetsw6 [ SOCK_MAX ] ; + + r )
INIT_LIST_HEAD ( r ) ;
/* We MUST register RAW sockets before we create the ICMP6,
* IGMP6 , or NDISC control sockets .
*/
2007-12-11 13:25:35 +03:00
err = rawv6_init ( ) ;
if ( err )
goto out_unregister_raw_proto ;
2005-04-17 02:20:36 +04:00
/* Register the family here so that the init calls below will
* be able to create sockets . ( ? ? is this dangerous ? ? )
*/
2005-11-12 02:05:47 +03:00
err = sock_register ( & inet6_family_ops ) ;
if ( err )
2007-12-11 13:25:35 +03:00
goto out_sock_register_fail ;
2005-04-17 02:20:36 +04:00
/* Initialise ipv6 mibs */
err = init_ipv6_mibs ( ) ;
if ( err )
2005-11-12 02:05:47 +03:00
goto out_unregister_sock ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
/*
* ipngwg API draft makes clear that the correct semantics
* for TCP and UDP is to consider one TCP and UDP instance
* in a host availiable by both INET and INET6 APIs and
* able to communicate via both network protocols .
*/
2008-01-10 13:48:33 +03:00
err = register_pernet_subsys ( & inet6_net_ops ) ;
if ( err )
goto register_pernet_fail ;
2008-02-29 22:13:15 +03:00
err = icmpv6_init ( ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto icmp_fail ;
2008-02-29 22:13:15 +03:00
err = ndisc_init ( ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto ndisc_fail ;
2008-02-29 22:13:15 +03:00
err = igmp6_init ( ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto igmp_fail ;
2005-08-10 06:42:34 +04:00
err = ipv6_netfilter_init ( ) ;
if ( err )
goto netfilter_fail ;
2005-04-17 02:20:36 +04:00
/* Create /proc/foo6 entries. */
# ifdef CONFIG_PROC_FS
err = - ENOMEM ;
if ( raw6_proc_init ( ) )
goto proc_raw6_fail ;
if ( tcp6_proc_init ( ) )
goto proc_tcp6_fail ;
if ( udp6_proc_init ( ) )
goto proc_udp6_fail ;
2006-11-27 22:10:57 +03:00
if ( udplite6_proc_init ( ) )
goto proc_udplite6_fail ;
2005-04-17 02:20:36 +04:00
if ( ipv6_misc_proc_init ( ) )
goto proc_misc6_fail ;
if ( ac6_proc_init ( ) )
goto proc_anycast6_fail ;
if ( if6_proc_init ( ) )
goto proc_if6_fail ;
# endif
2007-12-07 11:44:29 +03:00
err = ip6_route_init ( ) ;
if ( err )
goto ip6_route_fail ;
2007-12-11 13:23:18 +03:00
err = ip6_flowlabel_init ( ) ;
if ( err )
goto ip6_flowlabel_fail ;
2005-04-17 02:20:36 +04:00
err = addrconf_init ( ) ;
if ( err )
goto addrconf_fail ;
/* Init v6 extension headers. */
2007-12-11 13:23:54 +03:00
err = ipv6_exthdrs_init ( ) ;
if ( err )
goto ipv6_exthdrs_fail ;
2007-12-11 13:24:29 +03:00
err = ipv6_frag_init ( ) ;
if ( err )
goto ipv6_frag_fail ;
2005-04-17 02:20:36 +04:00
/* Init v6 transport protocols. */
2007-12-11 13:25:35 +03:00
err = udpv6_init ( ) ;
if ( err )
goto udpv6_fail ;
2005-07-06 01:41:20 +04:00
2007-12-11 13:25:35 +03:00
err = udplitev6_init ( ) ;
if ( err )
goto udplitev6_fail ;
err = tcpv6_init ( ) ;
if ( err )
goto tcpv6_fail ;
err = ipv6_packet_init ( ) ;
if ( err )
goto ipv6_packet_fail ;
2008-03-05 21:45:36 +03:00
# ifdef CONFIG_SYSCTL
err = ipv6_sysctl_register ( ) ;
if ( err )
goto sysctl_fail ;
# endif
2005-04-17 02:20:36 +04:00
out :
return err ;
2008-03-05 21:45:36 +03:00
# ifdef CONFIG_SYSCTL
sysctl_fail :
ipv6_packet_cleanup ( ) ;
# endif
2007-12-11 13:25:35 +03:00
ipv6_packet_fail :
tcpv6_exit ( ) ;
tcpv6_fail :
udplitev6_exit ( ) ;
udplitev6_fail :
udpv6_exit ( ) ;
udpv6_fail :
ipv6_frag_exit ( ) ;
2007-12-11 13:24:29 +03:00
ipv6_frag_fail :
ipv6_exthdrs_exit ( ) ;
2007-12-11 13:23:54 +03:00
ipv6_exthdrs_fail :
addrconf_cleanup ( ) ;
2005-04-17 02:20:36 +04:00
addrconf_fail :
ip6_flowlabel_cleanup ( ) ;
2007-12-11 13:23:18 +03:00
ip6_flowlabel_fail :
2005-04-17 02:20:36 +04:00
ip6_route_cleanup ( ) ;
2007-12-07 11:44:29 +03:00
ip6_route_fail :
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PROC_FS
if6_proc_exit ( ) ;
proc_if6_fail :
ac6_proc_exit ( ) ;
proc_anycast6_fail :
ipv6_misc_proc_exit ( ) ;
proc_misc6_fail :
2006-11-27 22:10:57 +03:00
udplite6_proc_exit ( ) ;
proc_udplite6_fail :
2005-04-17 02:20:36 +04:00
udp6_proc_exit ( ) ;
proc_udp6_fail :
tcp6_proc_exit ( ) ;
proc_tcp6_fail :
raw6_proc_exit ( ) ;
proc_raw6_fail :
# endif
2005-08-10 06:42:34 +04:00
ipv6_netfilter_fini ( ) ;
netfilter_fail :
2005-04-17 02:20:36 +04:00
igmp6_cleanup ( ) ;
igmp_fail :
ndisc_cleanup ( ) ;
ndisc_fail :
icmpv6_cleanup ( ) ;
icmp_fail :
2008-01-10 13:48:33 +03:00
unregister_pernet_subsys ( & inet6_net_ops ) ;
register_pernet_fail :
2005-04-17 02:20:36 +04:00
cleanup_ipv6_mibs ( ) ;
2005-11-12 02:05:47 +03:00
out_unregister_sock :
sock_unregister ( PF_INET6 ) ;
2007-12-07 11:44:29 +03:00
rtnl_unregister_all ( PF_INET6 ) ;
2007-12-11 13:25:35 +03:00
out_sock_register_fail :
rawv6_exit ( ) ;
2005-04-17 02:20:36 +04:00
out_unregister_raw_proto :
proto_unregister ( & rawv6_prot ) ;
2006-11-27 22:10:57 +03:00
out_unregister_udplite_proto :
2008-02-29 19:06:47 +03:00
# ifdef CONFIG_IP_UDPLITE
2006-11-27 22:10:57 +03:00
proto_unregister ( & udplitev6_prot ) ;
2005-04-17 02:20:36 +04:00
out_unregister_udp_proto :
2008-02-29 19:06:47 +03:00
# endif
2005-04-17 02:20:36 +04:00
proto_unregister ( & udpv6_prot ) ;
out_unregister_tcp_proto :
proto_unregister ( & tcpv6_prot ) ;
goto out ;
}
module_init ( inet6_init ) ;
static void __exit inet6_exit ( void )
{
/* First of all disallow new sockets creation. */
sock_unregister ( PF_INET6 ) ;
2007-03-22 21:58:32 +03:00
/* Disallow any further netlink messages */
rtnl_unregister_all ( PF_INET6 ) ;
2007-02-20 12:30:15 +03:00
2008-03-05 21:45:36 +03:00
# ifdef CONFIG_SYSCTL
ipv6_sysctl_unregister ( ) ;
# endif
2007-12-11 13:25:35 +03:00
udpv6_exit ( ) ;
2008-02-29 19:06:47 +03:00
# ifdef CONFIG_IP_UDPLITE
2007-12-11 13:25:35 +03:00
udplitev6_exit ( ) ;
2008-02-29 19:06:47 +03:00
# endif
2007-12-11 13:25:35 +03:00
tcpv6_exit ( ) ;
2007-02-20 12:30:15 +03:00
/* Cleanup code parts. */
ipv6_packet_cleanup ( ) ;
2007-12-11 13:24:29 +03:00
ipv6_frag_exit ( ) ;
2007-12-11 13:23:54 +03:00
ipv6_exthdrs_exit ( ) ;
2007-02-20 12:30:15 +03:00
addrconf_cleanup ( ) ;
ip6_flowlabel_cleanup ( ) ;
ip6_route_cleanup ( ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PROC_FS
2007-02-20 12:30:15 +03:00
/* Cleanup code parts. */
2005-04-17 02:20:36 +04:00
if6_proc_exit ( ) ;
ac6_proc_exit ( ) ;
2007-02-09 17:24:49 +03:00
ipv6_misc_proc_exit ( ) ;
udplite6_proc_exit ( ) ;
2007-02-20 12:30:15 +03:00
udp6_proc_exit ( ) ;
2007-02-09 17:24:49 +03:00
tcp6_proc_exit ( ) ;
raw6_proc_exit ( ) ;
2006-08-24 07:31:11 +04:00
# endif
2005-08-10 06:42:34 +04:00
ipv6_netfilter_fini ( ) ;
2007-02-20 12:30:15 +03:00
igmp6_cleanup ( ) ;
2005-04-17 02:20:36 +04:00
ndisc_cleanup ( ) ;
icmpv6_cleanup ( ) ;
2007-12-11 13:25:35 +03:00
rawv6_exit ( ) ;
2008-03-05 21:45:36 +03:00
2008-01-10 13:48:33 +03:00
unregister_pernet_subsys ( & inet6_net_ops ) ;
2005-04-17 02:20:36 +04:00
cleanup_ipv6_mibs ( ) ;
proto_unregister ( & rawv6_prot ) ;
2008-02-29 19:06:47 +03:00
# ifdef CONFIG_IP_UDPLITE
2007-02-20 12:30:15 +03:00
proto_unregister ( & udplitev6_prot ) ;
2008-02-29 19:06:47 +03:00
# endif
2005-04-17 02:20:36 +04:00
proto_unregister ( & udpv6_prot ) ;
proto_unregister ( & tcpv6_prot ) ;
}
module_exit ( inet6_exit ) ;
MODULE_ALIAS_NETPROTO ( PF_INET6 ) ;