2005-04-17 02:20:36 +04:00
/*
* common UDP / RAW code
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
*
* $ Id : datagram . c , v 1.24 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 .
*/
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/kernel.h>
# include <linux/interrupt.h>
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/in6.h>
# include <linux/ipv6.h>
# include <linux/route.h>
# include <net/ipv6.h>
# include <net/ndisc.h>
# include <net/addrconf.h>
# include <net/transp_v6.h>
# include <net/ip6_route.h>
2005-08-10 07:08:28 +04:00
# include <net/tcp_states.h>
2005-04-17 02:20:36 +04:00
# include <linux/errqueue.h>
# include <asm/uaccess.h>
int ip6_datagram_connect ( struct sock * sk , struct sockaddr * uaddr , int addr_len )
{
struct sockaddr_in6 * usin = ( struct sockaddr_in6 * ) uaddr ;
struct inet_sock * inet = inet_sk ( sk ) ;
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
struct in6_addr * daddr , * final_p = NULL , final ;
struct dst_entry * dst ;
struct flowi fl ;
struct ip6_flowlabel * flowlabel = NULL ;
int addr_type ;
int err ;
if ( usin - > sin6_family = = AF_INET ) {
if ( __ipv6_only_sock ( sk ) )
return - EAFNOSUPPORT ;
err = ip4_datagram_connect ( sk , uaddr , addr_len ) ;
goto ipv4_connected ;
}
if ( addr_len < SIN6_LEN_RFC2133 )
2007-02-09 17:24:49 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:49 +03:00
if ( usin - > sin6_family ! = AF_INET6 )
return - EAFNOSUPPORT ;
2005-04-17 02:20:36 +04:00
memset ( & fl , 0 , sizeof ( fl ) ) ;
if ( np - > sndflow ) {
fl . fl6_flowlabel = usin - > sin6_flowinfo & IPV6_FLOWINFO_MASK ;
if ( fl . fl6_flowlabel & IPV6_FLOWLABEL_MASK ) {
flowlabel = fl6_sock_lookup ( sk , fl . fl6_flowlabel ) ;
if ( flowlabel = = NULL )
return - EINVAL ;
ipv6_addr_copy ( & usin - > sin6_addr , & flowlabel - > dst ) ;
}
}
addr_type = ipv6_addr_type ( & usin - > sin6_addr ) ;
if ( addr_type = = IPV6_ADDR_ANY ) {
/*
* connect to self
*/
usin - > sin6_addr . s6_addr [ 15 ] = 0x01 ;
}
daddr = & usin - > sin6_addr ;
if ( addr_type = = IPV6_ADDR_MAPPED ) {
struct sockaddr_in sin ;
if ( __ipv6_only_sock ( sk ) ) {
err = - ENETUNREACH ;
goto out ;
}
sin . sin_family = AF_INET ;
sin . sin_addr . s_addr = daddr - > s6_addr32 [ 3 ] ;
sin . sin_port = usin - > sin6_port ;
2007-02-09 17:24:49 +03:00
err = ip4_datagram_connect ( sk ,
( struct sockaddr * ) & sin ,
2005-04-17 02:20:36 +04:00
sizeof ( sin ) ) ;
ipv4_connected :
if ( err )
goto out ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
ipv6_addr_set ( & np - > daddr , 0 , 0 , htonl ( 0x0000ffff ) , inet - > daddr ) ;
if ( ipv6_addr_any ( & np - > saddr ) ) {
ipv6_addr_set ( & np - > saddr , 0 , 0 , htonl ( 0x0000ffff ) ,
inet - > saddr ) ;
}
if ( ipv6_addr_any ( & np - > rcv_saddr ) ) {
ipv6_addr_set ( & np - > rcv_saddr , 0 , 0 , htonl ( 0x0000ffff ) ,
inet - > rcv_saddr ) ;
}
goto out ;
}
if ( addr_type & IPV6_ADDR_LINKLOCAL ) {
if ( addr_len > = sizeof ( struct sockaddr_in6 ) & &
usin - > sin6_scope_id ) {
if ( sk - > sk_bound_dev_if & &
sk - > sk_bound_dev_if ! = usin - > sin6_scope_id ) {
err = - EINVAL ;
goto out ;
}
sk - > sk_bound_dev_if = usin - > sin6_scope_id ;
if ( ! sk - > sk_bound_dev_if & &
( addr_type & IPV6_ADDR_MULTICAST ) )
fl . oif = np - > mcast_oif ;
}
/* Connect to link-local address requires an interface */
if ( ! sk - > sk_bound_dev_if ) {
err = - EINVAL ;
goto out ;
}
}
ipv6_addr_copy ( & np - > daddr , daddr ) ;
np - > flow_label = fl . fl6_flowlabel ;
inet - > dport = usin - > sin6_port ;
/*
* Check for a route to destination an obtain the
* destination cache for it .
*/
fl . proto = sk - > sk_protocol ;
ipv6_addr_copy ( & fl . fl6_dst , & np - > daddr ) ;
ipv6_addr_copy ( & fl . fl6_src , & np - > saddr ) ;
fl . oif = sk - > sk_bound_dev_if ;
fl . fl_ip_dport = inet - > dport ;
fl . fl_ip_sport = inet - > sport ;
if ( ! fl . oif & & ( addr_type & IPV6_ADDR_MULTICAST ) )
fl . oif = np - > mcast_oif ;
2006-08-05 10:12:42 +04:00
security_sk_classify_flow ( sk , & fl ) ;
2005-04-17 02:20:36 +04:00
if ( flowlabel ) {
if ( flowlabel - > opt & & flowlabel - > opt - > srcrt ) {
struct rt0_hdr * rt0 = ( struct rt0_hdr * ) flowlabel - > opt - > srcrt ;
ipv6_addr_copy ( & final , & fl . fl6_dst ) ;
ipv6_addr_copy ( & fl . fl6_dst , rt0 - > addr ) ;
final_p = & final ;
}
} else 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 )
goto out ;
if ( final_p )
ipv6_addr_copy ( & fl . fl6_dst , final_p ) ;
2007-05-25 05:17:54 +04:00
if ( ( err = __xfrm_lookup ( & dst , & fl , sk , 1 ) ) < 0 ) {
if ( err = = - EREMOTE )
err = ip6_dst_blackhole ( sk , & dst , & fl ) ;
if ( err < 0 )
goto out ;
}
2005-04-17 02:20:36 +04:00
/* source address lookup done in ip6_dst_lookup */
if ( ipv6_addr_any ( & np - > saddr ) )
ipv6_addr_copy ( & np - > saddr , & fl . fl6_src ) ;
if ( ipv6_addr_any ( & np - > rcv_saddr ) ) {
ipv6_addr_copy ( & np - > rcv_saddr , & fl . fl6_src ) ;
inet - > rcv_saddr = LOOPBACK4_IPV6 ;
}
ip6_dst_store ( sk , dst ,
ipv6_addr_equal ( & fl . fl6_dst , & np - > daddr ) ?
2006-08-30 04:15:09 +04:00
& np - > daddr : NULL ,
# ifdef CONFIG_IPV6_SUBTREES
ipv6_addr_equal ( & fl . fl6_src , & np - > saddr ) ?
& np - > saddr :
# endif
NULL ) ;
2005-04-17 02:20:36 +04:00
sk - > sk_state = TCP_ESTABLISHED ;
out :
fl6_sock_release ( flowlabel ) ;
return err ;
}
2007-02-09 17:24:49 +03:00
void ipv6_icmp_error ( struct sock * sk , struct sk_buff * skb , int err ,
2006-11-15 07:56:00 +03:00
__be16 port , u32 info , u8 * payload )
2005-04-17 02:20:36 +04:00
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
2007-03-13 20:03:22 +03:00
struct icmp6hdr * icmph = icmp6_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
struct sock_exterr_skb * serr ;
if ( ! np - > recverr )
return ;
skb = skb_clone ( skb , GFP_ATOMIC ) ;
if ( ! skb )
return ;
serr = SKB_EXT_ERR ( skb ) ;
serr - > ee . ee_errno = err ;
serr - > ee . ee_origin = SO_EE_ORIGIN_ICMP6 ;
2007-02-09 17:24:49 +03:00
serr - > ee . ee_type = icmph - > icmp6_type ;
2005-04-17 02:20:36 +04:00
serr - > ee . ee_code = icmph - > icmp6_code ;
serr - > ee . ee_pad = 0 ;
serr - > ee . ee_info = info ;
serr - > ee . ee_data = 0 ;
2007-04-11 07:50:43 +04:00
serr - > addr_offset = ( u8 * ) & ( ( ( struct ipv6hdr * ) ( icmph + 1 ) ) - > daddr ) -
skb_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
serr - > port = port ;
__skb_pull ( skb , payload - skb - > data ) ;
2007-03-13 23:10:43 +03:00
skb_reset_transport_header ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( sock_queue_err_skb ( sk , skb ) )
kfree_skb ( skb ) ;
}
void ipv6_local_error ( struct sock * sk , int err , struct flowi * fl , u32 info )
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
struct sock_exterr_skb * serr ;
struct ipv6hdr * iph ;
struct sk_buff * skb ;
if ( ! np - > recverr )
return ;
skb = alloc_skb ( sizeof ( struct ipv6hdr ) , GFP_ATOMIC ) ;
if ( ! skb )
return ;
2007-03-11 01:57:15 +03:00
skb_put ( skb , sizeof ( struct ipv6hdr ) ) ;
skb_reset_network_header ( skb ) ;
2007-04-26 04:54:47 +04:00
iph = ipv6_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
ipv6_addr_copy ( & iph - > daddr , & fl - > fl6_dst ) ;
serr = SKB_EXT_ERR ( skb ) ;
serr - > ee . ee_errno = err ;
serr - > ee . ee_origin = SO_EE_ORIGIN_LOCAL ;
2007-02-09 17:24:49 +03:00
serr - > ee . ee_type = 0 ;
2005-04-17 02:20:36 +04:00
serr - > ee . ee_code = 0 ;
serr - > ee . ee_pad = 0 ;
serr - > ee . ee_info = info ;
serr - > ee . ee_data = 0 ;
2007-04-11 07:50:43 +04:00
serr - > addr_offset = ( u8 * ) & iph - > daddr - skb_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
serr - > port = fl - > fl_ip_dport ;
2007-04-20 07:29:13 +04:00
__skb_pull ( skb , skb_tail_pointer ( skb ) - skb - > data ) ;
2007-03-13 23:10:43 +03:00
skb_reset_transport_header ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( sock_queue_err_skb ( sk , skb ) )
kfree_skb ( skb ) ;
}
2007-02-09 17:24:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* Handle MSG_ERRQUEUE
*/
int ipv6_recv_error ( struct sock * sk , struct msghdr * msg , int len )
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
struct sock_exterr_skb * serr ;
struct sk_buff * skb , * skb2 ;
struct sockaddr_in6 * sin ;
struct {
struct sock_extended_err ee ;
struct sockaddr_in6 offender ;
} errhdr ;
int err ;
int copied ;
err = - EAGAIN ;
skb = skb_dequeue ( & sk - > sk_error_queue ) ;
if ( skb = = NULL )
goto out ;
copied = skb - > len ;
if ( copied > len ) {
msg - > msg_flags | = MSG_TRUNC ;
copied = len ;
}
err = skb_copy_datagram_iovec ( skb , 0 , msg - > msg_iov , copied ) ;
if ( err )
goto out_free_skb ;
sock_recv_timestamp ( msg , sk , skb ) ;
serr = SKB_EXT_ERR ( skb ) ;
sin = ( struct sockaddr_in6 * ) msg - > msg_name ;
if ( sin ) {
2007-04-11 07:50:43 +04:00
const unsigned char * nh = skb_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
sin - > sin6_family = AF_INET6 ;
sin - > sin6_flowinfo = 0 ;
2007-02-09 17:24:49 +03:00
sin - > sin6_port = serr - > port ;
2005-04-17 02:20:36 +04:00
sin - > sin6_scope_id = 0 ;
if ( serr - > ee . ee_origin = = SO_EE_ORIGIN_ICMP6 ) {
ipv6_addr_copy ( & sin - > sin6_addr ,
2007-04-11 07:50:43 +04:00
( struct in6_addr * ) ( nh + serr - > addr_offset ) ) ;
2005-04-17 02:20:36 +04:00
if ( np - > sndflow )
2007-04-11 07:50:43 +04:00
sin - > sin6_flowinfo =
( * ( __be32 * ) ( nh + serr - > addr_offset - 24 ) &
IPV6_FLOWINFO_MASK ) ;
2005-04-17 02:20:36 +04:00
if ( ipv6_addr_type ( & sin - > sin6_addr ) & IPV6_ADDR_LINKLOCAL )
sin - > sin6_scope_id = IP6CB ( skb ) - > iif ;
} else {
ipv6_addr_set ( & sin - > sin6_addr , 0 , 0 ,
htonl ( 0xffff ) ,
2007-04-11 07:50:43 +04:00
* ( __be32 * ) ( nh + serr - > addr_offset ) ) ;
2005-04-17 02:20:36 +04:00
}
}
memcpy ( & errhdr . ee , & serr - > ee , sizeof ( struct sock_extended_err ) ) ;
sin = & errhdr . offender ;
sin - > sin6_family = AF_UNSPEC ;
if ( serr - > ee . ee_origin ! = SO_EE_ORIGIN_LOCAL ) {
sin - > sin6_family = AF_INET6 ;
sin - > sin6_flowinfo = 0 ;
sin - > sin6_scope_id = 0 ;
if ( serr - > ee . ee_origin = = SO_EE_ORIGIN_ICMP6 ) {
2007-04-26 04:54:47 +04:00
ipv6_addr_copy ( & sin - > sin6_addr , & ipv6_hdr ( skb ) - > saddr ) ;
2005-04-17 02:20:36 +04:00
if ( np - > rxopt . all )
datagram_recv_ctl ( sk , msg , skb ) ;
if ( ipv6_addr_type ( & sin - > sin6_addr ) & IPV6_ADDR_LINKLOCAL )
sin - > sin6_scope_id = IP6CB ( skb ) - > iif ;
} else {
struct inet_sock * inet = inet_sk ( sk ) ;
ipv6_addr_set ( & sin - > sin6_addr , 0 , 0 ,
2007-04-21 09:47:35 +04:00
htonl ( 0xffff ) , ip_hdr ( skb ) - > saddr ) ;
2005-04-17 02:20:36 +04:00
if ( inet - > cmsg_flags )
ip_cmsg_recv ( msg , skb ) ;
}
}
put_cmsg ( msg , SOL_IPV6 , IPV6_RECVERR , sizeof ( errhdr ) , & errhdr ) ;
/* Now we could try to dump offended packet options */
msg - > msg_flags | = MSG_ERRQUEUE ;
err = copied ;
/* Reset and regenerate socket error */
2005-06-19 09:56:18 +04:00
spin_lock_bh ( & sk - > sk_error_queue . lock ) ;
2005-04-17 02:20:36 +04:00
sk - > sk_err = 0 ;
if ( ( skb2 = skb_peek ( & sk - > sk_error_queue ) ) ! = NULL ) {
sk - > sk_err = SKB_EXT_ERR ( skb2 ) - > ee . ee_errno ;
2005-06-19 09:56:18 +04:00
spin_unlock_bh ( & sk - > sk_error_queue . lock ) ;
2005-04-17 02:20:36 +04:00
sk - > sk_error_report ( sk ) ;
} else {
2005-06-19 09:56:18 +04:00
spin_unlock_bh ( & sk - > sk_error_queue . lock ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 17:24:49 +03:00
out_free_skb :
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
out :
return err ;
}
int datagram_recv_ctl ( struct sock * sk , struct msghdr * msg , struct sk_buff * skb )
{
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
struct inet6_skb_parm * opt = IP6CB ( skb ) ;
2007-04-11 07:50:43 +04:00
unsigned char * nh = skb_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( np - > rxopt . bits . rxinfo ) {
struct in6_pktinfo src_info ;
src_info . ipi6_ifindex = opt - > iif ;
2007-04-26 04:54:47 +04:00
ipv6_addr_copy ( & src_info . ipi6_addr , & ipv6_hdr ( skb ) - > daddr ) ;
2005-04-17 02:20:36 +04:00
put_cmsg ( msg , SOL_IPV6 , IPV6_PKTINFO , sizeof ( src_info ) , & src_info ) ;
}
if ( np - > rxopt . bits . rxhlim ) {
2007-04-26 04:54:47 +04:00
int hlim = ipv6_hdr ( skb ) - > hop_limit ;
2005-04-17 02:20:36 +04:00
put_cmsg ( msg , SOL_IPV6 , IPV6_HOPLIMIT , sizeof ( hlim ) , & hlim ) ;
}
2005-09-08 05:19:03 +04:00
if ( np - > rxopt . bits . rxtclass ) {
2007-04-26 04:54:47 +04:00
int tclass = ( ntohl ( * ( __be32 * ) ipv6_hdr ( skb ) ) > > 20 ) & 0xff ;
2005-09-08 05:19:03 +04:00
put_cmsg ( msg , SOL_IPV6 , IPV6_TCLASS , sizeof ( tclass ) , & tclass ) ;
}
2007-04-11 07:50:43 +04:00
if ( np - > rxopt . bits . rxflow & & ( * ( __be32 * ) nh & IPV6_FLOWINFO_MASK ) ) {
__be32 flowinfo = * ( __be32 * ) nh & IPV6_FLOWINFO_MASK ;
2005-04-17 02:20:36 +04:00
put_cmsg ( msg , SOL_IPV6 , IPV6_FLOWINFO , sizeof ( flowinfo ) , & flowinfo ) ;
}
[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
/* HbH is allowed only once */
2005-04-17 02:20:36 +04:00
if ( np - > rxopt . bits . hopopts & & opt - > hop ) {
2007-04-11 07:50:43 +04:00
u8 * ptr = nh + opt - > hop ;
2005-04-17 02:20:36 +04:00
put_cmsg ( msg , SOL_IPV6 , IPV6_HOPOPTS , ( ptr [ 1 ] + 1 ) < < 3 , ptr ) ;
}
[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 ( opt - > lastopt & &
( np - > rxopt . bits . dstopts | | np - > rxopt . bits . srcrt ) ) {
/*
* Silly enough , but we need to reparse in order to
* report extension headers ( except for HbH )
* in order .
*
2007-02-09 17:24:49 +03:00
* Also note that IPV6_RECVRTHDRDSTOPTS is NOT
[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
* ( and WILL NOT be ) defined because
* IPV6_RECVDSTOPTS is more generic . - - yoshfuji
*/
unsigned int off = sizeof ( struct ipv6hdr ) ;
2007-04-26 04:54:47 +04:00
u8 nexthdr = ipv6_hdr ( skb ) - > nexthdr ;
[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
while ( off < = opt - > lastopt ) {
unsigned len ;
2007-04-11 07:50:43 +04:00
u8 * ptr = nh + off ;
[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
switch ( nexthdr ) {
case IPPROTO_DSTOPTS :
nexthdr = ptr [ 0 ] ;
len = ( ptr [ 1 ] + 1 ) < < 3 ;
if ( np - > rxopt . bits . dstopts )
put_cmsg ( msg , SOL_IPV6 , IPV6_DSTOPTS , len , ptr ) ;
break ;
case IPPROTO_ROUTING :
nexthdr = ptr [ 0 ] ;
len = ( ptr [ 1 ] + 1 ) < < 3 ;
if ( np - > rxopt . bits . srcrt )
put_cmsg ( msg , SOL_IPV6 , IPV6_RTHDR , len , ptr ) ;
break ;
case IPPROTO_AH :
nexthdr = ptr [ 0 ] ;
2005-11-20 06:21:59 +03:00
len = ( ptr [ 1 ] + 2 ) < < 2 ;
[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
break ;
default :
nexthdr = ptr [ 0 ] ;
len = ( ptr [ 1 ] + 1 ) < < 3 ;
break ;
}
off + = len ;
}
}
/* socket options in old style */
if ( np - > rxopt . bits . rxoinfo ) {
struct in6_pktinfo src_info ;
src_info . ipi6_ifindex = opt - > iif ;
2007-04-26 04:54:47 +04:00
ipv6_addr_copy ( & src_info . ipi6_addr , & ipv6_hdr ( skb ) - > daddr ) ;
[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
put_cmsg ( msg , SOL_IPV6 , IPV6_2292PKTINFO , sizeof ( src_info ) , & src_info ) ;
}
if ( np - > rxopt . bits . rxohlim ) {
2007-04-26 04:54:47 +04:00
int hlim = ipv6_hdr ( skb ) - > hop_limit ;
[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
put_cmsg ( msg , SOL_IPV6 , IPV6_2292HOPLIMIT , sizeof ( hlim ) , & hlim ) ;
}
if ( np - > rxopt . bits . ohopopts & & opt - > hop ) {
2007-04-11 07:50:43 +04:00
u8 * ptr = nh + opt - > hop ;
[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
put_cmsg ( msg , SOL_IPV6 , IPV6_2292HOPOPTS , ( ptr [ 1 ] + 1 ) < < 3 , ptr ) ;
}
if ( np - > rxopt . bits . odstopts & & opt - > dst0 ) {
2007-04-11 07:50:43 +04:00
u8 * ptr = nh + opt - > dst0 ;
[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
put_cmsg ( msg , SOL_IPV6 , IPV6_2292DSTOPTS , ( ptr [ 1 ] + 1 ) < < 3 , ptr ) ;
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
if ( np - > rxopt . bits . osrcrt & & opt - > srcrt ) {
2007-04-11 07:50:43 +04:00
struct ipv6_rt_hdr * rthdr = ( struct ipv6_rt_hdr * ) ( nh + 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
put_cmsg ( msg , SOL_IPV6 , IPV6_2292RTHDR , ( rthdr - > hdrlen + 1 ) < < 3 , rthdr ) ;
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
if ( np - > rxopt . bits . odstopts & & opt - > dst1 ) {
2007-04-11 07:50:43 +04:00
u8 * ptr = nh + opt - > dst1 ;
[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
put_cmsg ( msg , SOL_IPV6 , IPV6_2292DSTOPTS , ( ptr [ 1 ] + 1 ) < < 3 , ptr ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
int datagram_send_ctl ( struct msghdr * msg , struct flowi * fl ,
struct ipv6_txoptions * opt ,
2005-09-08 05:19:03 +04:00
int * hlimit , int * tclass )
2005-04-17 02:20:36 +04:00
{
struct in6_pktinfo * src_info ;
struct cmsghdr * cmsg ;
struct ipv6_rt_hdr * rthdr ;
struct ipv6_opt_hdr * hdr ;
int len ;
int err = 0 ;
for ( cmsg = CMSG_FIRSTHDR ( msg ) ; cmsg ; cmsg = CMSG_NXTHDR ( msg , cmsg ) ) {
int addr_type ;
struct net_device * dev = NULL ;
if ( ! CMSG_OK ( msg , cmsg ) ) {
err = - EINVAL ;
goto exit_f ;
}
if ( cmsg - > cmsg_level ! = SOL_IPV6 )
continue ;
switch ( cmsg - > cmsg_type ) {
2007-02-09 17:24:49 +03:00
case IPV6_PKTINFO :
case IPV6_2292PKTINFO :
if ( cmsg - > cmsg_len < CMSG_LEN ( sizeof ( struct in6_pktinfo ) ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
src_info = ( struct in6_pktinfo * ) CMSG_DATA ( cmsg ) ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
if ( src_info - > ipi6_ifindex ) {
if ( fl - > oif & & src_info - > ipi6_ifindex ! = fl - > oif )
return - EINVAL ;
fl - > oif = src_info - > ipi6_ifindex ;
}
addr_type = ipv6_addr_type ( & src_info - > ipi6_addr ) ;
if ( addr_type = = IPV6_ADDR_ANY )
break ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
if ( addr_type & IPV6_ADDR_LINKLOCAL ) {
if ( ! src_info - > ipi6_ifindex )
return - EINVAL ;
else {
dev = dev_get_by_index ( src_info - > ipi6_ifindex ) ;
if ( ! dev )
return - ENODEV ;
}
}
if ( ! ipv6_chk_addr ( & src_info - > ipi6_addr , dev , 0 ) ) {
if ( dev )
dev_put ( dev ) ;
err = - EINVAL ;
goto exit_f ;
}
if ( dev )
dev_put ( dev ) ;
ipv6_addr_copy ( & fl - > fl6_src , & src_info - > ipi6_addr ) ;
break ;
case IPV6_FLOWINFO :
2007-02-09 17:24:49 +03:00
if ( cmsg - > cmsg_len < CMSG_LEN ( 4 ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
if ( fl - > fl6_flowlabel & IPV6_FLOWINFO_MASK ) {
2006-11-08 11:25:17 +03:00
if ( ( fl - > fl6_flowlabel ^ * ( __be32 * ) CMSG_DATA ( cmsg ) ) & ~ IPV6_FLOWINFO_MASK ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
}
2006-11-08 11:25:17 +03:00
fl - > fl6_flowlabel = IPV6_FLOWINFO_MASK & * ( __be32 * ) CMSG_DATA ( cmsg ) ;
2005-04-17 02:20:36 +04:00
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 :
2005-04-17 02:20:36 +04:00
case IPV6_HOPOPTS :
2007-02-09 17:24:49 +03:00
if ( opt - > hopopt | | cmsg - > cmsg_len < CMSG_LEN ( sizeof ( struct ipv6_opt_hdr ) ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
hdr = ( struct ipv6_opt_hdr * ) CMSG_DATA ( cmsg ) ;
len = ( ( hdr - > hdrlen + 1 ) < < 3 ) ;
if ( cmsg - > cmsg_len < CMSG_LEN ( len ) ) {
err = - EINVAL ;
goto exit_f ;
}
if ( ! capable ( CAP_NET_RAW ) ) {
err = - EPERM ;
goto exit_f ;
}
opt - > opt_nflen + = len ;
opt - > hopopt = hdr ;
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 :
2007-02-09 17:24:49 +03:00
if ( cmsg - > cmsg_len < CMSG_LEN ( sizeof ( struct ipv6_opt_hdr ) ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
hdr = ( struct ipv6_opt_hdr * ) CMSG_DATA ( cmsg ) ;
len = ( ( hdr - > hdrlen + 1 ) < < 3 ) ;
if ( cmsg - > cmsg_len < CMSG_LEN ( len ) ) {
err = - EINVAL ;
goto exit_f ;
}
if ( ! capable ( CAP_NET_RAW ) ) {
err = - EPERM ;
goto exit_f ;
}
if ( opt - > dst1opt ) {
err = - EINVAL ;
goto exit_f ;
}
opt - > opt_flen + = len ;
opt - > dst1opt = hdr ;
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_DSTOPTS :
case IPV6_RTHDRDSTOPTS :
if ( cmsg - > cmsg_len < CMSG_LEN ( sizeof ( struct ipv6_opt_hdr ) ) ) {
err = - EINVAL ;
goto exit_f ;
}
hdr = ( struct ipv6_opt_hdr * ) CMSG_DATA ( cmsg ) ;
len = ( ( hdr - > hdrlen + 1 ) < < 3 ) ;
if ( cmsg - > cmsg_len < CMSG_LEN ( len ) ) {
err = - EINVAL ;
goto exit_f ;
}
if ( ! capable ( CAP_NET_RAW ) ) {
err = - EPERM ;
goto exit_f ;
}
if ( cmsg - > cmsg_type = = IPV6_DSTOPTS ) {
opt - > opt_flen + = len ;
opt - > dst1opt = hdr ;
} else {
opt - > opt_nflen + = len ;
opt - > dst0opt = hdr ;
}
break ;
case IPV6_2292RTHDR :
2005-04-17 02:20:36 +04:00
case IPV6_RTHDR :
2007-02-09 17:24:49 +03:00
if ( cmsg - > cmsg_len < CMSG_LEN ( sizeof ( struct ipv6_rt_hdr ) ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
rthdr = ( struct ipv6_rt_hdr * ) CMSG_DATA ( cmsg ) ;
2006-08-24 06:17:12 +04:00
switch ( rthdr - > type ) {
case IPV6_SRCRT_TYPE_0 :
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 :
# endif
break ;
default :
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
len = ( ( rthdr - > hdrlen + 1 ) < < 3 ) ;
2007-02-09 17:24:49 +03:00
if ( cmsg - > cmsg_len < CMSG_LEN ( len ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto exit_f ;
}
/* segments left must also match */
if ( ( rthdr - > hdrlen > > 1 ) ! = rthdr - > segments_left ) {
err = - EINVAL ;
goto exit_f ;
}
opt - > opt_nflen + = len ;
opt - > srcrt = rthdr ;
[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 ( cmsg - > cmsg_type = = IPV6_2292RTHDR & & opt - > dst1opt ) {
2005-04-17 02:20:36 +04:00
int dsthdrlen = ( ( opt - > dst1opt - > hdrlen + 1 ) < < 3 ) ;
opt - > opt_nflen + = dsthdrlen ;
opt - > dst0opt = opt - > dst1opt ;
opt - > dst1opt = NULL ;
opt - > opt_flen - = dsthdrlen ;
}
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 :
2005-04-17 02:20:36 +04:00
case IPV6_HOPLIMIT :
if ( cmsg - > cmsg_len ! = CMSG_LEN ( sizeof ( int ) ) ) {
err = - EINVAL ;
goto exit_f ;
}
* hlimit = * ( int * ) CMSG_DATA ( cmsg ) ;
break ;
2005-09-08 05:19:03 +04:00
case IPV6_TCLASS :
{
int tc ;
err = - EINVAL ;
if ( cmsg - > cmsg_len ! = CMSG_LEN ( sizeof ( int ) ) ) {
goto exit_f ;
}
tc = * ( int * ) CMSG_DATA ( cmsg ) ;
2006-09-14 07:08:07 +04:00
if ( tc < - 1 | | tc > 0xff )
2005-09-08 05:19:03 +04:00
goto exit_f ;
err = 0 ;
* tclass = tc ;
break ;
}
2005-04-17 02:20:36 +04:00
default :
2005-08-10 07:50:53 +04:00
LIMIT_NETDEBUG ( KERN_DEBUG " invalid cmsg type: %d \n " ,
2007-02-09 17:24:49 +03:00
cmsg - > cmsg_type ) ;
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
break ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
}
exit_f :
return err ;
}