2005-04-17 02:20:36 +04:00
/*
* IPv6 BSD socket options interface
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
*
* Based on linux / net / ipv4 / ip_sockglue . c
*
* $ Id : ipv6_sockglue . c , v 1.41 2002 / 02 / 01 22 : 01 : 04 davem Exp $
*
* 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 .
*
* FIXME : Make the setsockopt code POSIX compliant : That is
*
* o Truncate getsockopt returns
* o Return an optlen of the truncated length if need be
*
* Changes :
* David L Stevens < dlstevens @ us . ibm . com > :
* - added multicast source filtering API for MLDv2
*/
# 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/sockios.h>
# include <linux/net.h>
# include <linux/in6.h>
2008-04-03 04:22:53 +04:00
# include <linux/mroute6.h>
2005-04-17 02:20:36 +04:00
# include <linux/netdevice.h>
# include <linux/if_arp.h>
# include <linux/init.h>
# include <linux/sysctl.h>
# include <linux/netfilter.h>
# include <net/sock.h>
# include <net/snmp.h>
# include <net/ipv6.h>
# include <net/ndisc.h>
# include <net/protocol.h>
# include <net/transp_v6.h>
# include <net/ip6_route.h>
# include <net/addrconf.h>
# include <net/inet_common.h>
# include <net/tcp.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/xfrm.h>
2008-04-27 12:06:07 +04:00
# include <net/compat.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
2005-08-26 23:05:31 +04:00
DEFINE_SNMP_STAT ( struct ipstats_mib , ipv6_statistics ) __read_mostly ;
2005-04-17 02:20:36 +04:00
struct ip6_ra_chain * ip6_ra_chain ;
DEFINE_RWLOCK ( ip6_ra_lock ) ;
int ip6_ra_control ( struct sock * sk , int sel , void ( * destructor ) ( struct sock * ) )
{
struct ip6_ra_chain * ra , * new_ra , * * rap ;
/* RA packet may be delivered ONLY to IPPROTO_RAW socket */
if ( sk - > sk_type ! = SOCK_RAW | | inet_sk ( sk ) - > num ! = IPPROTO_RAW )
2008-06-11 22:27:26 +04:00
return - ENOPROTOOPT ;
2005-04-17 02:20:36 +04:00
new_ra = ( sel > = 0 ) ? kmalloc ( sizeof ( * new_ra ) , GFP_KERNEL ) : NULL ;
write_lock_bh ( & ip6_ra_lock ) ;
for ( rap = & ip6_ra_chain ; ( ra = * rap ) ! = NULL ; rap = & ra - > next ) {
if ( ra - > sk = = sk ) {
if ( sel > = 0 ) {
write_unlock_bh ( & ip6_ra_lock ) ;
2005-11-08 20:41:34 +03:00
kfree ( new_ra ) ;
2005-04-17 02:20:36 +04:00
return - EADDRINUSE ;
}
* rap = ra - > next ;
write_unlock_bh ( & ip6_ra_lock ) ;
if ( ra - > destructor )
ra - > destructor ( sk ) ;
sock_put ( sk ) ;
kfree ( ra ) ;
return 0 ;
}
}
if ( new_ra = = NULL ) {
write_unlock_bh ( & ip6_ra_lock ) ;
return - ENOBUFS ;
}
new_ra - > sk = sk ;
new_ra - > sel = sel ;
new_ra - > destructor = destructor ;
new_ra - > next = ra ;
* rap = new_ra ;
sock_hold ( sk ) ;
write_unlock_bh ( & ip6_ra_lock ) ;
return 0 ;
}
2008-04-14 10:21:52 +04:00
static
struct ipv6_txoptions * ipv6_update_options ( struct sock * sk ,
struct ipv6_txoptions * opt )
{
if ( inet_sk ( sk ) - > is_icsk ) {
if ( opt & &
! ( ( 1 < < sk - > sk_state ) & ( TCPF_LISTEN | TCPF_CLOSE ) ) & &
inet_sk ( sk ) - > daddr ! = LOOPBACK4_IPV6 ) {
struct inet_connection_sock * icsk = inet_csk ( sk ) ;
icsk - > icsk_ext_hdr_len = opt - > opt_flen + opt - > opt_nflen ;
icsk - > icsk_sync_mss ( sk , icsk - > icsk_pmtu_cookie ) ;
}
opt = xchg ( & inet6_sk ( sk ) - > opt , opt ) ;
} else {
write_lock ( & sk - > sk_dst_lock ) ;
opt = xchg ( & inet6_sk ( sk ) - > opt , opt ) ;
write_unlock ( & sk - > sk_dst_lock ) ;
}
sk_dst_reset ( sk ) ;
return opt ;
}
2006-03-21 09:45:21 +03:00
static int do_ipv6_setsockopt ( struct sock * sk , int level , int optname ,
2005-04-17 02:20:36 +04:00
char __user * optval , int optlen )
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
2008-03-25 20:26:21 +03:00
struct net * net = sock_net ( sk ) ;
2005-04-17 02:20:36 +04:00
int val , valbool ;
int retv = - ENOPROTOOPT ;
if ( optval = = NULL )
val = 0 ;
2008-04-12 07:59:42 +04:00
else {
if ( optlen > = sizeof ( int ) ) {
if ( get_user ( val , ( int __user * ) optval ) )
return - EFAULT ;
} else
val = 0 ;
}
2005-04-17 02:20:36 +04:00
valbool = ( val ! = 0 ) ;
2008-04-03 04:22:53 +04:00
if ( ip6_mroute_opt ( optname ) )
return ip6_mroute_setsockopt ( sk , optname , optval , optlen ) ;
2005-04-17 02:20:36 +04:00
lock_sock ( sk ) ;
switch ( optname ) {
case IPV6_ADDRFORM :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
if ( val = = PF_INET ) {
struct ipv6_txoptions * opt ;
struct sk_buff * pktopt ;
2008-06-04 15:49:06 +04:00
if ( sk - > sk_type = = SOCK_RAW )
break ;
2008-06-04 15:49:08 +04:00
if ( sk - > sk_protocol = = IPPROTO_UDP | |
sk - > sk_protocol = = IPPROTO_UDPLITE ) {
struct udp_sock * up = udp_sk ( sk ) ;
if ( up - > pending = = AF_INET6 ) {
retv = - EBUSY ;
break ;
}
} else if ( sk - > sk_protocol ! = IPPROTO_TCP )
2005-04-17 02:20:36 +04:00
break ;
if ( sk - > sk_state ! = TCP_ESTABLISHED ) {
retv = - ENOTCONN ;
break ;
}
if ( ipv6_only_sock ( sk ) | |
2007-08-25 10:16:08 +04:00
! ipv6_addr_v4mapped ( & np - > daddr ) ) {
2005-04-17 02:20:36 +04:00
retv = - EADDRNOTAVAIL ;
break ;
}
fl6_free_socklist ( sk ) ;
ipv6_sock_mc_close ( sk ) ;
2005-08-10 06:45:38 +04:00
/*
* Sock is moving from IPv6 to IPv4 ( sk_prot ) , so
* remove it from the refcnt debug socks count in the
* original family . . .
*/
sk_refcnt_debug_dec ( sk ) ;
2005-04-17 02:20:36 +04:00
if ( sk - > sk_protocol = = IPPROTO_TCP ) {
2005-12-14 10:26:10 +03:00
struct inet_connection_sock * icsk = inet_csk ( sk ) ;
2005-04-17 02:20:36 +04:00
local_bh_disable ( ) ;
2008-04-01 06:41:46 +04:00
sock_prot_inuse_add ( net , sk - > sk_prot , - 1 ) ;
sock_prot_inuse_add ( net , & tcp_prot , 1 ) ;
2005-04-17 02:20:36 +04:00
local_bh_enable ( ) ;
sk - > sk_prot = & tcp_prot ;
2005-12-14 10:26:10 +03:00
icsk - > icsk_af_ops = & ipv4_specific ;
2005-04-17 02:20:36 +04:00
sk - > sk_socket - > ops = & inet_stream_ops ;
sk - > sk_family = PF_INET ;
2005-12-14 10:26:10 +03:00
tcp_sync_mss ( sk , icsk - > icsk_pmtu_cookie ) ;
2005-04-17 02:20:36 +04:00
} else {
2006-11-27 22:10:57 +03:00
struct proto * prot = & udp_prot ;
2008-03-07 03:22:02 +03:00
if ( sk - > sk_protocol = = IPPROTO_UDPLITE )
2006-11-27 22:10:57 +03:00
prot = & udplite_prot ;
2005-04-17 02:20:36 +04:00
local_bh_disable ( ) ;
2008-04-01 06:41:46 +04:00
sock_prot_inuse_add ( net , sk - > sk_prot , - 1 ) ;
sock_prot_inuse_add ( net , prot , 1 ) ;
2005-04-17 02:20:36 +04:00
local_bh_enable ( ) ;
2006-11-27 22:10:57 +03:00
sk - > sk_prot = prot ;
2005-04-17 02:20:36 +04:00
sk - > sk_socket - > ops = & inet_dgram_ops ;
sk - > sk_family = PF_INET ;
}
opt = xchg ( & np - > opt , NULL ) ;
if ( opt )
sock_kfree_s ( sk , opt , opt - > tot_len ) ;
pktopt = xchg ( & np - > pktoptions , NULL ) ;
if ( pktopt )
kfree_skb ( pktopt ) ;
sk - > sk_destruct = inet_sock_destruct ;
2005-08-10 06:45:38 +04:00
/*
* . . . and add it to the refcnt debug socks count
* in the new family . - acme
*/
sk_refcnt_debug_inc ( sk ) ;
2005-04-17 02:20:36 +04:00
module_put ( THIS_MODULE ) ;
retv = 0 ;
break ;
}
goto e_inval ;
case IPV6_V6ONLY :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) | |
inet_sk ( sk ) - > num )
2005-04-17 02:20:36 +04:00
goto e_inval ;
np - > ipv6only = valbool ;
retv = 0 ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_RECVPKTINFO :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > rxopt . bits . rxinfo = valbool ;
retv = 0 ;
break ;
2007-02-09 17:24:49 +03:00
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292PKTINFO :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
np - > rxopt . bits . rxoinfo = valbool ;
retv = 0 ;
break ;
2005-04-17 02:20:36 +04:00
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_RECVHOPLIMIT :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > rxopt . bits . rxhlim = valbool ;
retv = 0 ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292HOPLIMIT :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
np - > rxopt . bits . rxohlim = valbool ;
retv = 0 ;
break ;
case IPV6_RECVRTHDR :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2007-05-23 08:28:48 +04:00
np - > rxopt . bits . srcrt = valbool ;
2005-04-17 02:20:36 +04:00
retv = 0 ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292RTHDR :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2007-05-23 08:28:48 +04:00
np - > rxopt . bits . osrcrt = valbool ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
retv = 0 ;
break ;
case IPV6_RECVHOPOPTS :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > rxopt . bits . hopopts = valbool ;
retv = 0 ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292HOPOPTS :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
np - > rxopt . bits . ohopopts = valbool ;
retv = 0 ;
break ;
case IPV6_RECVDSTOPTS :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > rxopt . bits . dstopts = valbool ;
retv = 0 ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292DSTOPTS :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
np - > rxopt . bits . odstopts = valbool ;
retv = 0 ;
break ;
2005-09-08 05:19:03 +04:00
case IPV6_TCLASS :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2006-09-14 07:08:07 +04:00
if ( val < - 1 | | val > 0xff )
2005-09-08 05:19:03 +04:00
goto e_inval ;
np - > tclass = val ;
retv = 0 ;
break ;
2007-02-09 17:24:49 +03:00
2005-09-08 05:19:03 +04:00
case IPV6_RECVTCLASS :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-09-08 05:19:03 +04:00
np - > rxopt . bits . rxtclass = valbool ;
retv = 0 ;
break ;
2005-04-17 02:20:36 +04:00
case IPV6_FLOWINFO :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > rxopt . bits . rxflow = valbool ;
retv = 0 ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_HOPOPTS :
case IPV6_RTHDRDSTOPTS :
case IPV6_RTHDR :
case IPV6_DSTOPTS :
{
struct ipv6_txoptions * opt ;
2008-06-20 03:29:39 +04:00
/* remove any sticky options header with a zero option
* length , per RFC3542 .
*/
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
if ( optlen = = 0 )
2005-11-15 08:43:36 +03:00
optval = NULL ;
2008-06-20 03:29:39 +04:00
else if ( optlen < sizeof ( struct ipv6_opt_hdr ) | |
optlen & 0x7 | | optlen > 8 * 255 )
goto e_inval ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
/* hop-by-hop / destination options are privileged option */
retv = - EPERM ;
if ( optname ! = IPV6_RTHDR & & ! capable ( CAP_NET_RAW ) )
break ;
opt = ipv6_renew_options ( sk , np - > opt , optname ,
( struct ipv6_opt_hdr __user * ) optval ,
optlen ) ;
if ( IS_ERR ( opt ) ) {
retv = PTR_ERR ( opt ) ;
break ;
}
/* routing header option needs extra check */
2007-03-10 00:55:38 +03:00
if ( optname = = IPV6_RTHDR & & opt & & opt - > srcrt ) {
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
struct ipv6_rt_hdr * rthdr = opt - > srcrt ;
2006-08-24 06:17:12 +04:00
switch ( rthdr - > type ) {
2007-06-27 10:56:32 +04:00
# if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
2006-08-24 06:17:12 +04:00
case IPV6_SRCRT_TYPE_2 :
break ;
2007-07-11 09:55:49 +04:00
# endif
2006-08-24 06:17:12 +04:00
default :
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
goto sticky_done ;
2006-08-24 06:17:12 +04:00
}
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
if ( ( rthdr - > hdrlen & 1 ) | |
( rthdr - > hdrlen > > 1 ) ! = rthdr - > segments_left )
goto sticky_done ;
}
retv = 0 ;
2008-04-14 10:21:52 +04:00
opt = ipv6_update_options ( sk , opt ) ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
sticky_done :
if ( opt )
sock_kfree_s ( sk , opt , opt - > tot_len ) ;
break ;
}
case IPV6_2292PKTOPTIONS :
2005-04-17 02:20:36 +04:00
{
struct ipv6_txoptions * opt = NULL ;
struct msghdr msg ;
struct flowi fl ;
int junk ;
fl . fl6_flowlabel = 0 ;
fl . oif = sk - > sk_bound_dev_if ;
if ( optlen = = 0 )
goto update ;
/* 1K is probably excessive
* 1 K is surely not enough , 2 K per standard header is 16 K .
*/
retv = - EINVAL ;
if ( optlen > 64 * 1024 )
break ;
opt = sock_kmalloc ( sk , sizeof ( * opt ) + optlen , GFP_KERNEL ) ;
retv = - ENOBUFS ;
if ( opt = = NULL )
break ;
memset ( opt , 0 , sizeof ( * opt ) ) ;
opt - > tot_len = sizeof ( * opt ) + optlen ;
retv = - EFAULT ;
if ( copy_from_user ( opt + 1 , optval , optlen ) )
goto done ;
msg . msg_controllen = optlen ;
msg . msg_control = ( void * ) ( opt + 1 ) ;
2008-06-04 08:02:49 +04:00
retv = datagram_send_ctl ( net , & msg , & fl , opt , & junk , & junk ) ;
2005-04-17 02:20:36 +04:00
if ( retv )
goto done ;
update :
retv = 0 ;
2008-04-14 10:21:52 +04:00
opt = ipv6_update_options ( sk , opt ) ;
2005-04-17 02:20:36 +04:00
done :
if ( opt )
sock_kfree_s ( sk , opt , opt - > tot_len ) ;
break ;
}
case IPV6_UNICAST_HOPS :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
if ( val > 255 | | val < - 1 )
goto e_inval ;
np - > hop_limit = val ;
retv = 0 ;
break ;
case IPV6_MULTICAST_HOPS :
if ( sk - > sk_type = = SOCK_STREAM )
2008-06-11 22:27:26 +04:00
break ;
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
if ( val > 255 | | val < - 1 )
goto e_inval ;
np - > mcast_hops = val ;
retv = 0 ;
break ;
case IPV6_MULTICAST_LOOP :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2008-06-11 22:14:51 +04:00
if ( val ! = valbool )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > mc_loop = valbool ;
retv = 0 ;
break ;
case IPV6_MULTICAST_IF :
if ( sk - > sk_type = = SOCK_STREAM )
2008-06-11 22:27:26 +04:00
break ;
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
2007-10-12 01:39:29 +04:00
if ( val ) {
if ( sk - > sk_bound_dev_if & & sk - > sk_bound_dev_if ! = val )
goto e_inval ;
2008-03-07 22:14:16 +03:00
if ( __dev_get_by_index ( net , val ) = = NULL ) {
2007-10-12 01:39:29 +04:00
retv = - ENODEV ;
break ;
}
2005-04-17 02:20:36 +04:00
}
np - > mcast_oif = val ;
retv = 0 ;
break ;
case IPV6_ADD_MEMBERSHIP :
case IPV6_DROP_MEMBERSHIP :
{
struct ipv6_mreq mreq ;
2008-04-07 05:42:07 +04:00
if ( optlen < sizeof ( struct ipv6_mreq ) )
goto e_inval ;
2007-08-25 09:16:39 +04:00
retv = - EPROTO ;
if ( inet_sk ( sk ) - > is_icsk )
break ;
2005-04-17 02:20:36 +04:00
retv = - EFAULT ;
if ( copy_from_user ( & mreq , optval , sizeof ( struct ipv6_mreq ) ) )
break ;
if ( optname = = IPV6_ADD_MEMBERSHIP )
retv = ipv6_sock_mc_join ( sk , mreq . ipv6mr_ifindex , & mreq . ipv6mr_multiaddr ) ;
else
retv = ipv6_sock_mc_drop ( sk , mreq . ipv6mr_ifindex , & mreq . ipv6mr_multiaddr ) ;
break ;
}
case IPV6_JOIN_ANYCAST :
case IPV6_LEAVE_ANYCAST :
{
struct ipv6_mreq mreq ;
2008-04-07 05:42:07 +04:00
if ( optlen < sizeof ( struct ipv6_mreq ) )
2005-04-17 02:20:36 +04:00
goto e_inval ;
retv = - EFAULT ;
if ( copy_from_user ( & mreq , optval , sizeof ( struct ipv6_mreq ) ) )
break ;
if ( optname = = IPV6_JOIN_ANYCAST )
retv = ipv6_sock_ac_join ( sk , mreq . ipv6mr_ifindex , & mreq . ipv6mr_acaddr ) ;
else
retv = ipv6_sock_ac_drop ( sk , mreq . ipv6mr_ifindex , & mreq . ipv6mr_acaddr ) ;
break ;
}
case MCAST_JOIN_GROUP :
case MCAST_LEAVE_GROUP :
{
struct group_req greq ;
struct sockaddr_in6 * psin6 ;
2008-04-07 05:42:07 +04:00
if ( optlen < sizeof ( struct group_req ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
retv = - EFAULT ;
if ( copy_from_user ( & greq , optval , sizeof ( struct group_req ) ) )
break ;
if ( greq . gr_group . ss_family ! = AF_INET6 ) {
retv = - EADDRNOTAVAIL ;
break ;
}
psin6 = ( struct sockaddr_in6 * ) & greq . gr_group ;
if ( optname = = MCAST_JOIN_GROUP )
retv = ipv6_sock_mc_join ( sk , greq . gr_interface ,
& psin6 - > sin6_addr ) ;
else
retv = ipv6_sock_mc_drop ( sk , greq . gr_interface ,
& psin6 - > sin6_addr ) ;
break ;
}
case MCAST_JOIN_SOURCE_GROUP :
case MCAST_LEAVE_SOURCE_GROUP :
case MCAST_BLOCK_SOURCE :
case MCAST_UNBLOCK_SOURCE :
{
struct group_source_req greqs ;
int omode , add ;
2008-04-07 05:42:07 +04:00
if ( optlen < sizeof ( struct group_source_req ) )
2005-04-17 02:20:36 +04:00
goto e_inval ;
if ( copy_from_user ( & greqs , optval , sizeof ( greqs ) ) ) {
retv = - EFAULT ;
break ;
}
if ( greqs . gsr_group . ss_family ! = AF_INET6 | |
greqs . gsr_source . ss_family ! = AF_INET6 ) {
retv = - EADDRNOTAVAIL ;
break ;
}
if ( optname = = MCAST_BLOCK_SOURCE ) {
omode = MCAST_EXCLUDE ;
add = 1 ;
} else if ( optname = = MCAST_UNBLOCK_SOURCE ) {
omode = MCAST_EXCLUDE ;
add = 0 ;
} else if ( optname = = MCAST_JOIN_SOURCE_GROUP ) {
struct sockaddr_in6 * psin6 ;
psin6 = ( struct sockaddr_in6 * ) & greqs . gsr_group ;
retv = ipv6_sock_mc_join ( sk , greqs . gsr_interface ,
& psin6 - > sin6_addr ) ;
2005-06-22 00:58:25 +04:00
/* prior join w/ different source is ok */
if ( retv & & retv ! = - EADDRINUSE )
2005-04-17 02:20:36 +04:00
break ;
omode = MCAST_INCLUDE ;
add = 1 ;
2005-06-22 00:58:25 +04:00
} else /* MCAST_LEAVE_SOURCE_GROUP */ {
2005-04-17 02:20:36 +04:00
omode = MCAST_INCLUDE ;
add = 0 ;
}
retv = ip6_mc_source ( add , omode , sk , & greqs ) ;
break ;
}
case MCAST_MSFILTER :
{
extern int sysctl_mld_max_msf ;
struct group_filter * gsf ;
if ( optlen < GROUP_FILTER_SIZE ( 0 ) )
goto e_inval ;
if ( optlen > sysctl_optmem_max ) {
retv = - ENOBUFS ;
break ;
}
2006-01-12 02:56:43 +03:00
gsf = kmalloc ( optlen , GFP_KERNEL ) ;
2007-10-09 12:59:42 +04:00
if ( ! gsf ) {
2005-04-17 02:20:36 +04:00
retv = - ENOBUFS ;
break ;
}
retv = - EFAULT ;
if ( copy_from_user ( gsf , optval , optlen ) ) {
kfree ( gsf ) ;
break ;
}
/* numsrc >= (4G-140)/128 overflow in 32 bits */
if ( gsf - > gf_numsrc > = 0x1ffffffU | |
gsf - > gf_numsrc > sysctl_mld_max_msf ) {
kfree ( gsf ) ;
retv = - ENOBUFS ;
break ;
}
if ( GROUP_FILTER_SIZE ( gsf - > gf_numsrc ) > optlen ) {
kfree ( gsf ) ;
retv = - EINVAL ;
break ;
}
retv = ip6_mc_msfilter ( sk , gsf ) ;
kfree ( gsf ) ;
break ;
}
case IPV6_ROUTER_ALERT :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
retv = ip6_ra_control ( sk , val , NULL ) ;
break ;
case IPV6_MTU_DISCOVER :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2007-04-21 02:53:27 +04:00
if ( val < 0 | | val > 3 )
2005-04-17 02:20:36 +04:00
goto e_inval ;
np - > pmtudisc = val ;
retv = 0 ;
break ;
case IPV6_MTU :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
if ( val & & val < IPV6_MIN_MTU )
goto e_inval ;
np - > frag_size = val ;
retv = 0 ;
break ;
case IPV6_RECVERR :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > recverr = valbool ;
if ( ! val )
skb_queue_purge ( & sk - > sk_error_queue ) ;
retv = 0 ;
break ;
case IPV6_FLOWINFO_SEND :
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2005-04-17 02:20:36 +04:00
np - > sndflow = valbool ;
retv = 0 ;
break ;
case IPV6_FLOWLABEL_MGR :
retv = ipv6_flowlabel_opt ( sk , optval , optlen ) ;
break ;
case IPV6_IPSEC_POLICY :
case IPV6_XFRM_POLICY :
2005-08-06 17:33:15 +04:00
retv = - EPERM ;
if ( ! capable ( CAP_NET_ADMIN ) )
break ;
2005-04-17 02:20:36 +04:00
retv = xfrm_user_policy ( sk , optname , optval , optlen ) ;
break ;
2008-03-25 03:37:42 +03:00
case IPV6_ADDR_PREFERENCES :
{
unsigned int pref = 0 ;
unsigned int prefmask = ~ 0 ;
2008-04-12 07:59:42 +04:00
if ( optlen < sizeof ( int ) )
goto e_inval ;
2008-03-25 03:37:42 +03:00
retv = - EINVAL ;
/* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
switch ( val & ( IPV6_PREFER_SRC_PUBLIC |
IPV6_PREFER_SRC_TMP |
IPV6_PREFER_SRC_PUBTMP_DEFAULT ) ) {
case IPV6_PREFER_SRC_PUBLIC :
pref | = IPV6_PREFER_SRC_PUBLIC ;
break ;
case IPV6_PREFER_SRC_TMP :
pref | = IPV6_PREFER_SRC_TMP ;
break ;
case IPV6_PREFER_SRC_PUBTMP_DEFAULT :
break ;
case 0 :
goto pref_skip_pubtmp ;
default :
goto e_inval ;
}
prefmask & = ~ ( IPV6_PREFER_SRC_PUBLIC |
IPV6_PREFER_SRC_TMP ) ;
pref_skip_pubtmp :
/* check HOME/COA conflicts */
switch ( val & ( IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_COA ) ) {
case IPV6_PREFER_SRC_HOME :
break ;
case IPV6_PREFER_SRC_COA :
pref | = IPV6_PREFER_SRC_COA ;
case 0 :
goto pref_skip_coa ;
default :
goto e_inval ;
}
prefmask & = ~ IPV6_PREFER_SRC_COA ;
pref_skip_coa :
/* check CGA/NONCGA conflicts */
switch ( val & ( IPV6_PREFER_SRC_CGA | IPV6_PREFER_SRC_NONCGA ) ) {
case IPV6_PREFER_SRC_CGA :
case IPV6_PREFER_SRC_NONCGA :
case 0 :
break ;
default :
goto e_inval ;
}
np - > srcprefs = ( np - > srcprefs & prefmask ) | pref ;
retv = 0 ;
break ;
}
2005-04-17 02:20:36 +04:00
}
2008-03-25 03:37:42 +03:00
2005-04-17 02:20:36 +04:00
release_sock ( sk ) ;
return retv ;
e_inval :
release_sock ( sk ) ;
return - EINVAL ;
}
2006-03-21 09:45:21 +03:00
int ipv6_setsockopt ( struct sock * sk , int level , int optname ,
char __user * optval , int optlen )
{
int err ;
if ( level = = SOL_IP & & sk - > sk_type ! = SOCK_RAW )
return udp_prot . setsockopt ( sk , level , optname , optval , optlen ) ;
if ( level ! = SOL_IPV6 )
return - ENOPROTOOPT ;
err = do_ipv6_setsockopt ( sk , level , optname , optval , optlen ) ;
# ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if ( err = = - ENOPROTOOPT & & optname ! = IPV6_IPSEC_POLICY & &
optname ! = IPV6_XFRM_POLICY ) {
lock_sock ( sk ) ;
err = nf_setsockopt ( sk , PF_INET6 , optname , optval ,
optlen ) ;
release_sock ( sk ) ;
}
# endif
return err ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( ipv6_setsockopt ) ;
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
int compat_ipv6_setsockopt ( struct sock * sk , int level , int optname ,
2006-03-21 09:48:35 +03:00
char __user * optval , int optlen )
2006-03-21 09:45:21 +03:00
{
int err ;
if ( level = = SOL_IP & & sk - > sk_type ! = SOCK_RAW ) {
2006-03-21 09:48:35 +03:00
if ( udp_prot . compat_setsockopt ! = NULL )
return udp_prot . compat_setsockopt ( sk , level , optname ,
optval , optlen ) ;
return udp_prot . setsockopt ( sk , level , optname , optval , optlen ) ;
2006-03-21 09:45:21 +03:00
}
if ( level ! = SOL_IPV6 )
return - ENOPROTOOPT ;
2008-04-27 12:06:07 +04:00
if ( optname > = MCAST_JOIN_GROUP & & optname < = MCAST_MSFILTER )
return compat_mc_setsockopt ( sk , level , optname , optval , optlen ,
ipv6_setsockopt ) ;
2006-03-21 09:45:21 +03:00
err = do_ipv6_setsockopt ( sk , level , optname , optval , optlen ) ;
# ifdef CONFIG_NETFILTER
/* we need to exclude all possible ENOPROTOOPTs except default case */
if ( err = = - ENOPROTOOPT & & optname ! = IPV6_IPSEC_POLICY & &
2006-03-21 09:48:35 +03:00
optname ! = IPV6_XFRM_POLICY ) {
2006-03-21 09:45:21 +03:00
lock_sock ( sk ) ;
2006-03-21 09:48:35 +03:00
err = compat_nf_setsockopt ( sk , PF_INET6 , optname ,
optval , optlen ) ;
2006-03-21 09:45:21 +03:00
release_sock ( sk ) ;
}
# endif
return err ;
}
2006-03-21 09:48:35 +03:00
EXPORT_SYMBOL ( compat_ipv6_setsockopt ) ;
2006-03-21 09:45:21 +03:00
# endif
2007-03-07 23:50:46 +03:00
static int ipv6_getsockopt_sticky ( struct sock * sk , struct ipv6_txoptions * opt ,
2007-03-19 03:35:57 +03:00
int optname , char __user * optval , int len )
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
{
2007-03-07 23:50:46 +03:00
struct ipv6_opt_hdr * hdr ;
2007-03-19 03:35:57 +03:00
if ( ! opt )
return 0 ;
switch ( optname ) {
case IPV6_HOPOPTS :
hdr = opt - > hopopt ;
break ;
case IPV6_RTHDRDSTOPTS :
hdr = opt - > dst0opt ;
break ;
case IPV6_RTHDR :
hdr = ( struct ipv6_opt_hdr * ) opt - > srcrt ;
break ;
case IPV6_DSTOPTS :
hdr = opt - > dst1opt ;
break ;
default :
return - EINVAL ; /* should not happen */
}
if ( ! hdr )
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
return 0 ;
2007-03-07 23:50:46 +03:00
2007-03-10 03:19:17 +03:00
len = min_t ( unsigned int , len , ipv6_optlen ( hdr ) ) ;
2007-08-16 02:07:30 +04:00
if ( copy_to_user ( optval , hdr , len ) )
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
return - EFAULT ;
2008-05-28 12:27:28 +04:00
return len ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
}
2006-03-21 09:45:21 +03:00
static int do_ipv6_getsockopt ( struct sock * sk , int level , int optname ,
2005-04-17 02:20:36 +04:00
char __user * optval , int __user * optlen )
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
int len ;
int val ;
2008-04-03 04:22:53 +04:00
if ( ip6_mroute_opt ( optname ) )
return ip6_mroute_getsockopt ( sk , optname , optval , optlen ) ;
2005-04-17 02:20:36 +04:00
if ( get_user ( len , optlen ) )
return - EFAULT ;
switch ( optname ) {
case IPV6_ADDRFORM :
if ( sk - > sk_protocol ! = IPPROTO_UDP & &
2006-11-27 22:10:57 +03:00
sk - > sk_protocol ! = IPPROTO_UDPLITE & &
2005-04-17 02:20:36 +04:00
sk - > sk_protocol ! = IPPROTO_TCP )
2008-06-11 22:27:26 +04:00
return - ENOPROTOOPT ;
2005-04-17 02:20:36 +04:00
if ( sk - > sk_state ! = TCP_ESTABLISHED )
return - ENOTCONN ;
val = sk - > sk_family ;
break ;
case MCAST_MSFILTER :
{
struct group_filter gsf ;
int err ;
if ( len < GROUP_FILTER_SIZE ( 0 ) )
return - EINVAL ;
if ( copy_from_user ( & gsf , optval , GROUP_FILTER_SIZE ( 0 ) ) )
return - EFAULT ;
2008-04-28 09:40:55 +04:00
if ( gsf . gf_group . ss_family ! = AF_INET6 )
return - EADDRNOTAVAIL ;
2005-04-17 02:20:36 +04:00
lock_sock ( sk ) ;
err = ip6_mc_msfget ( sk , & gsf ,
( struct group_filter __user * ) optval , optlen ) ;
release_sock ( sk ) ;
return err ;
}
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292PKTOPTIONS :
2005-04-17 02:20:36 +04:00
{
struct msghdr msg ;
struct sk_buff * skb ;
if ( sk - > sk_type ! = SOCK_STREAM )
return - ENOPROTOOPT ;
msg . msg_control = optval ;
msg . msg_controllen = len ;
msg . msg_flags = 0 ;
lock_sock ( sk ) ;
skb = np - > pktoptions ;
if ( skb )
atomic_inc ( & skb - > users ) ;
release_sock ( sk ) ;
if ( skb ) {
int err = datagram_recv_ctl ( sk , & msg , skb ) ;
kfree_skb ( skb ) ;
if ( err )
return err ;
} else {
if ( np - > rxopt . bits . rxinfo ) {
struct in6_pktinfo src_info ;
src_info . ipi6_ifindex = np - > mcast_oif ;
ipv6_addr_copy ( & src_info . ipi6_addr , & np - > daddr ) ;
put_cmsg ( & msg , SOL_IPV6 , IPV6_PKTINFO , sizeof ( src_info ) , & src_info ) ;
}
if ( np - > rxopt . bits . rxhlim ) {
int hlim = np - > mcast_hops ;
put_cmsg ( & msg , SOL_IPV6 , IPV6_HOPLIMIT , sizeof ( hlim ) , & hlim ) ;
}
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
if ( np - > rxopt . bits . rxoinfo ) {
struct in6_pktinfo src_info ;
src_info . ipi6_ifindex = np - > mcast_oif ;
ipv6_addr_copy ( & src_info . ipi6_addr , & np - > daddr ) ;
put_cmsg ( & msg , SOL_IPV6 , IPV6_2292PKTINFO , sizeof ( src_info ) , & src_info ) ;
}
if ( np - > rxopt . bits . rxohlim ) {
int hlim = np - > mcast_hops ;
put_cmsg ( & msg , SOL_IPV6 , IPV6_2292HOPLIMIT , sizeof ( hlim ) , & hlim ) ;
}
2005-04-17 02:20:36 +04:00
}
len - = msg . msg_controllen ;
return put_user ( len , optlen ) ;
}
case IPV6_MTU :
{
struct dst_entry * dst ;
2007-02-09 17:24:49 +03:00
val = 0 ;
2005-04-17 02:20:36 +04:00
lock_sock ( sk ) ;
dst = sk_dst_get ( sk ) ;
if ( dst ) {
val = dst_mtu ( dst ) ;
dst_release ( dst ) ;
}
release_sock ( sk ) ;
if ( ! val )
return - ENOTCONN ;
break ;
}
case IPV6_V6ONLY :
val = np - > ipv6only ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_RECVPKTINFO :
2005-04-17 02:20:36 +04:00
val = np - > rxopt . bits . rxinfo ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292PKTINFO :
val = np - > rxopt . bits . rxoinfo ;
break ;
case IPV6_RECVHOPLIMIT :
2005-04-17 02:20:36 +04:00
val = np - > rxopt . bits . rxhlim ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292HOPLIMIT :
val = np - > rxopt . bits . rxohlim ;
break ;
case IPV6_RECVRTHDR :
2005-04-17 02:20:36 +04:00
val = np - > rxopt . bits . srcrt ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292RTHDR :
val = np - > rxopt . bits . osrcrt ;
break ;
2005-04-17 02:20:36 +04:00
case IPV6_HOPOPTS :
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_RTHDRDSTOPTS :
case IPV6_RTHDR :
case IPV6_DSTOPTS :
{
lock_sock ( sk ) ;
2007-03-07 23:50:46 +03:00
len = ipv6_getsockopt_sticky ( sk , np - > opt ,
2007-03-19 03:35:57 +03:00
optname , optval , len ) ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
release_sock ( sk ) ;
2008-05-28 12:23:47 +04:00
/* check if ipv6_getsockopt_sticky() returns err code */
if ( len < 0 )
return len ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
return put_user ( len , optlen ) ;
}
case IPV6_RECVHOPOPTS :
2005-04-17 02:20:36 +04:00
val = np - > rxopt . bits . hopopts ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292HOPOPTS :
val = np - > rxopt . bits . ohopopts ;
break ;
case IPV6_RECVDSTOPTS :
2005-04-17 02:20:36 +04:00
val = np - > rxopt . bits . dstopts ;
break ;
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 04:59:17 +04:00
case IPV6_2292DSTOPTS :
val = np - > rxopt . bits . odstopts ;
break ;
2005-09-08 05:19:03 +04:00
case IPV6_TCLASS :
val = np - > tclass ;
2006-09-14 07:08:07 +04:00
if ( val < 0 )
val = 0 ;
2005-09-08 05:19:03 +04:00
break ;
case IPV6_RECVTCLASS :
val = np - > rxopt . bits . rxtclass ;
break ;
2005-04-17 02:20:36 +04:00
case IPV6_FLOWINFO :
val = np - > rxopt . bits . rxflow ;
break ;
case IPV6_UNICAST_HOPS :
case IPV6_MULTICAST_HOPS :
2006-12-13 04:09:49 +03:00
{
struct dst_entry * dst ;
if ( optname = = IPV6_UNICAST_HOPS )
val = np - > hop_limit ;
else
val = np - > mcast_hops ;
dst = sk_dst_get ( sk ) ;
if ( dst ) {
if ( val < 0 )
2008-03-10 13:00:30 +03:00
val = ip6_dst_hoplimit ( dst ) ;
2006-12-13 04:09:49 +03:00
dst_release ( dst ) ;
}
if ( val < 0 )
val = ipv6_devconf . hop_limit ;
2005-04-17 02:20:36 +04:00
break ;
2006-12-13 04:09:49 +03:00
}
2005-04-17 02:20:36 +04:00
case IPV6_MULTICAST_LOOP :
val = np - > mc_loop ;
break ;
case IPV6_MULTICAST_IF :
val = np - > mcast_oif ;
break ;
case IPV6_MTU_DISCOVER :
val = np - > pmtudisc ;
break ;
case IPV6_RECVERR :
val = np - > recverr ;
break ;
case IPV6_FLOWINFO_SEND :
val = np - > sndflow ;
break ;
2008-03-25 03:37:42 +03:00
case IPV6_ADDR_PREFERENCES :
val = 0 ;
if ( np - > srcprefs & IPV6_PREFER_SRC_TMP )
val | = IPV6_PREFER_SRC_TMP ;
else if ( np - > srcprefs & IPV6_PREFER_SRC_PUBLIC )
val | = IPV6_PREFER_SRC_PUBLIC ;
else {
/* XXX: should we return system default? */
val | = IPV6_PREFER_SRC_PUBTMP_DEFAULT ;
}
if ( np - > srcprefs & IPV6_PREFER_SRC_COA )
val | = IPV6_PREFER_SRC_COA ;
else
val | = IPV6_PREFER_SRC_HOME ;
break ;
2005-04-17 02:20:36 +04:00
default :
2007-12-17 00:39:57 +03:00
return - ENOPROTOOPT ;
2005-04-17 02:20:36 +04:00
}
len = min_t ( unsigned int , sizeof ( int ) , len ) ;
if ( put_user ( len , optlen ) )
return - EFAULT ;
if ( copy_to_user ( optval , & val , len ) )
return - EFAULT ;
return 0 ;
}
2006-03-21 09:45:21 +03:00
int ipv6_getsockopt ( struct sock * sk , int level , int optname ,
char __user * optval , int __user * optlen )
{
int err ;
if ( level = = SOL_IP & & sk - > sk_type ! = SOCK_RAW )
return udp_prot . getsockopt ( sk , level , optname , optval , optlen ) ;
if ( level ! = SOL_IPV6 )
return - ENOPROTOOPT ;
err = do_ipv6_getsockopt ( sk , level , optname , optval , optlen ) ;
# ifdef CONFIG_NETFILTER
2007-12-17 00:39:57 +03:00
/* we need to exclude all possible ENOPROTOOPTs except default case */
if ( err = = - ENOPROTOOPT & & optname ! = IPV6_2292PKTOPTIONS ) {
2006-03-21 09:45:21 +03:00
int len ;
if ( get_user ( len , optlen ) )
return - EFAULT ;
lock_sock ( sk ) ;
err = nf_getsockopt ( sk , PF_INET6 , optname , optval ,
& len ) ;
release_sock ( sk ) ;
if ( err > = 0 )
err = put_user ( len , optlen ) ;
}
# endif
return err ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( ipv6_getsockopt ) ;
2006-03-21 09:45:21 +03:00
# ifdef CONFIG_COMPAT
int compat_ipv6_getsockopt ( struct sock * sk , int level , int optname ,
2006-03-21 09:48:35 +03:00
char __user * optval , int __user * optlen )
2006-03-21 09:45:21 +03:00
{
int err ;
if ( level = = SOL_IP & & sk - > sk_type ! = SOCK_RAW ) {
2006-03-21 09:48:35 +03:00
if ( udp_prot . compat_getsockopt ! = NULL )
return udp_prot . compat_getsockopt ( sk , level , optname ,
optval , optlen ) ;
return udp_prot . getsockopt ( sk , level , optname , optval , optlen ) ;
2006-03-21 09:45:21 +03:00
}
2006-03-21 09:48:35 +03:00
if ( level ! = SOL_IPV6 )
2006-03-21 09:45:21 +03:00
return - ENOPROTOOPT ;
2008-05-01 01:49:54 +04:00
if ( optname = = MCAST_MSFILTER )
return compat_mc_getsockopt ( sk , level , optname , optval , optlen ,
ipv6_getsockopt ) ;
2006-03-21 09:45:21 +03:00
err = do_ipv6_getsockopt ( sk , level , optname , optval , optlen ) ;
# ifdef CONFIG_NETFILTER
2007-12-17 00:39:57 +03:00
/* we need to exclude all possible ENOPROTOOPTs except default case */
if ( err = = - ENOPROTOOPT & & optname ! = IPV6_2292PKTOPTIONS ) {
2006-03-21 09:45:21 +03:00
int len ;
if ( get_user ( len , optlen ) )
return - EFAULT ;
lock_sock ( sk ) ;
2006-03-21 09:48:35 +03:00
err = compat_nf_getsockopt ( sk , PF_INET6 ,
optname , optval , & len ) ;
2006-03-21 09:45:21 +03:00
release_sock ( sk ) ;
if ( err > = 0 )
err = put_user ( len , optlen ) ;
}
# endif
return err ;
}
2006-03-21 09:48:35 +03:00
EXPORT_SYMBOL ( compat_ipv6_getsockopt ) ;
2006-03-21 09:45:21 +03:00
# endif