2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-05-23 00:17:31 +04:00
/*
* INET An implementation of the TCP / IP protocol suite for the LINUX
* operating system . INET is implemented using the BSD Socket
* interface as the means of communication with the user level .
*
* " Ping " sockets
*
* Based on ipv4 / ping . c code .
*
* Authors : Lorenzo Colitti ( IPv6 support )
* Vasiliy Kulikov / Openwall ( IPv4 implementation , for Linux 2.6 ) ,
* Pavel Kankovsky ( IPv4 implementation , for Linux 2.4 .32 )
*/
# include <net/addrconf.h>
# include <net/ipv6.h>
# include <net/ip6_route.h>
# include <net/protocol.h>
# include <net/udp.h>
# include <net/transp_v6.h>
2018-04-10 21:04:20 +03:00
# include <linux/proc_fs.h>
2013-05-23 00:17:31 +04:00
# include <net/ping.h>
/* Compatibility glue so we can support IPv6 when it's compiled as a module */
2013-11-23 03:46:12 +04:00
static int dummy_ipv6_recv_error ( struct sock * sk , struct msghdr * msg , int len ,
int * addr_len )
2013-05-23 00:17:31 +04:00
{
return - EAFNOSUPPORT ;
}
2014-01-20 06:43:08 +04:00
static void dummy_ip6_datagram_recv_ctl ( struct sock * sk , struct msghdr * msg ,
2013-06-12 17:04:16 +04:00
struct sk_buff * skb )
2013-05-23 00:17:31 +04:00
{
}
2013-06-12 17:04:16 +04:00
static int dummy_icmpv6_err_convert ( u8 type , u8 code , int * err )
2013-05-23 00:17:31 +04:00
{
return - EAFNOSUPPORT ;
}
2013-06-12 17:04:16 +04:00
static void dummy_ipv6_icmp_error ( struct sock * sk , struct sk_buff * skb , int err ,
__be16 port , u32 info , u8 * payload ) { }
static int dummy_ipv6_chk_addr ( struct net * net , const struct in6_addr * addr ,
const struct net_device * dev , int strict )
2013-05-23 00:17:31 +04:00
{
return 0 ;
}
2016-03-23 12:59:51 +03:00
static int ping_v6_sendmsg ( struct sock * sk , struct msghdr * msg , size_t len )
2013-05-23 00:17:31 +04:00
{
struct inet_sock * inet = inet_sk ( sk ) ;
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
struct icmp6hdr user_icmph ;
int addr_type ;
struct in6_addr * daddr ;
2016-08-12 19:13:38 +03:00
int oif = 0 ;
2013-05-23 00:17:31 +04:00
struct flowi6 fl6 ;
int err ;
struct dst_entry * dst ;
struct rt6_info * rt ;
struct pingfakehdr pfh ;
2016-05-03 07:40:07 +03:00
struct ipcm6_cookie ipc6 ;
2013-05-23 00:17:31 +04:00
pr_debug ( " ping_v6_sendmsg(sk=%p,sk->num=%u) \n " , inet , inet - > inet_num ) ;
err = ping_common_sendmsg ( AF_INET6 , msg , len , & user_icmph ,
sizeof ( user_icmph ) ) ;
if ( err )
return err ;
if ( msg - > msg_name ) {
2014-01-18 01:53:15 +04:00
DECLARE_SOCKADDR ( struct sockaddr_in6 * , u , msg - > msg_name ) ;
net: ping: Return EAFNOSUPPORT when appropriate.
1. For an IPv4 ping socket, ping_check_bind_addr does not check
the family of the socket address that's passed in. Instead,
make it behave like inet_bind, which enforces either that the
address family is AF_INET, or that the family is AF_UNSPEC and
the address is 0.0.0.0.
2. For an IPv6 ping socket, ping_check_bind_addr returns EINVAL
if the socket family is not AF_INET6. Return EAFNOSUPPORT
instead, for consistency with inet6_bind.
3. Make ping_v4_sendmsg and ping_v6_sendmsg return EAFNOSUPPORT
instead of EINVAL if an incorrect socket address structure is
passed in.
4. Make IPv6 ping sockets be IPv6-only. The code does not support
IPv4, and it cannot easily be made to support IPv4 because
the protocol numbers for ICMP and ICMPv6 are different. This
makes connect(::ffff:192.0.2.1) fail with EAFNOSUPPORT instead
of making the socket unusable.
Among other things, this fixes an oops that can be triggered by:
int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
.sin6_addr = in6addr_any,
};
bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
Change-Id: If06ca86d9f1e4593c0d6df174caca3487c57a241
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-03 17:16:16 +03:00
if ( msg - > msg_namelen < sizeof ( * u ) )
2013-05-23 00:17:31 +04:00
return - EINVAL ;
net: ping: Return EAFNOSUPPORT when appropriate.
1. For an IPv4 ping socket, ping_check_bind_addr does not check
the family of the socket address that's passed in. Instead,
make it behave like inet_bind, which enforces either that the
address family is AF_INET, or that the family is AF_UNSPEC and
the address is 0.0.0.0.
2. For an IPv6 ping socket, ping_check_bind_addr returns EINVAL
if the socket family is not AF_INET6. Return EAFNOSUPPORT
instead, for consistency with inet6_bind.
3. Make ping_v4_sendmsg and ping_v6_sendmsg return EAFNOSUPPORT
instead of EINVAL if an incorrect socket address structure is
passed in.
4. Make IPv6 ping sockets be IPv6-only. The code does not support
IPv4, and it cannot easily be made to support IPv4 because
the protocol numbers for ICMP and ICMPv6 are different. This
makes connect(::ffff:192.0.2.1) fail with EAFNOSUPPORT instead
of making the socket unusable.
Among other things, this fixes an oops that can be triggered by:
int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
.sin6_addr = in6addr_any,
};
bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
Change-Id: If06ca86d9f1e4593c0d6df174caca3487c57a241
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-03-03 17:16:16 +03:00
if ( u - > sin6_family ! = AF_INET6 ) {
return - EAFNOSUPPORT ;
2013-05-23 00:17:31 +04:00
}
daddr = & ( u - > sin6_addr ) ;
2016-08-12 19:13:38 +03:00
if ( __ipv6_addr_needs_scope_id ( ipv6_addr_type ( daddr ) ) )
oif = u - > sin6_scope_id ;
2013-05-23 00:17:31 +04:00
} else {
if ( sk - > sk_state ! = TCP_ESTABLISHED )
return - EDESTADDRREQ ;
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 02:42:29 +04:00
daddr = & sk - > sk_v6_daddr ;
2013-05-23 00:17:31 +04:00
}
2016-08-12 19:13:38 +03:00
if ( ! oif )
oif = sk - > sk_bound_dev_if ;
if ( ! oif )
oif = np - > sticky_pktinfo . ipi6_ifindex ;
if ( ! oif & & ipv6_addr_is_multicast ( daddr ) )
oif = np - > mcast_oif ;
else if ( ! oif )
oif = np - > ucast_oif ;
2013-05-23 00:17:31 +04:00
addr_type = ipv6_addr_type ( daddr ) ;
2016-08-12 19:13:38 +03:00
if ( ( __ipv6_addr_needs_scope_id ( addr_type ) & & ! oif ) | |
( addr_type & IPV6_ADDR_MAPPED ) | |
( oif & & sk - > sk_bound_dev_if & & oif ! = sk - > sk_bound_dev_if ) )
2013-05-23 00:17:31 +04:00
return - EINVAL ;
/* TODO: use ip6_datagram_send_ctl to get options from cmsg */
memset ( & fl6 , 0 , sizeof ( fl6 ) ) ;
fl6 . flowi6_proto = IPPROTO_ICMPV6 ;
fl6 . saddr = np - > saddr ;
fl6 . daddr = * daddr ;
2016-08-12 19:13:38 +03:00
fl6 . flowi6_oif = oif ;
2014-02-27 08:38:26 +04:00
fl6 . flowi6_mark = sk - > sk_mark ;
2016-11-03 20:23:43 +03:00
fl6 . flowi6_uid = sk - > sk_uid ;
2013-05-23 00:17:31 +04:00
fl6 . fl6_icmp_type = user_icmph . icmp6_type ;
fl6 . fl6_icmp_code = user_icmph . icmp6_code ;
security_sk_classify_flow ( sk , flowi6_to_flowi ( & fl6 ) ) ;
2018-07-06 17:12:55 +03:00
ipcm6_init_sk ( & ipc6 , np ) ;
2016-06-11 21:08:19 +03:00
fl6 . flowlabel = ip6_make_flowinfo ( ipc6 . tclass , fl6 . flowlabel ) ;
2018-04-03 15:00:08 +03:00
dst = ip6_sk_dst_lookup_flow ( sk , & fl6 , daddr , false ) ;
2013-05-23 00:17:31 +04:00
if ( IS_ERR ( dst ) )
return PTR_ERR ( dst ) ;
rt = ( struct rt6_info * ) dst ;
if ( ! fl6 . flowi6_oif & & ipv6_addr_is_multicast ( & fl6 . daddr ) )
fl6 . flowi6_oif = np - > mcast_oif ;
else if ( ! fl6 . flowi6_oif )
fl6 . flowi6_oif = np - > ucast_oif ;
pfh . icmph . type = user_icmph . icmp6_type ;
pfh . icmph . code = user_icmph . icmp6_code ;
pfh . icmph . checksum = 0 ;
pfh . icmph . un . echo . id = inet - > inet_sport ;
pfh . icmph . un . echo . sequence = user_icmph . icmp6_sequence ;
2014-11-28 04:34:16 +03:00
pfh . msg = msg ;
2013-05-23 00:17:31 +04:00
pfh . wcheck = 0 ;
pfh . family = AF_INET6 ;
2016-05-03 07:40:07 +03:00
ipc6 . hlimit = ip6_sk_dst_hoplimit ( np , & fl6 , dst ) ;
2013-05-23 00:17:31 +04:00
2013-07-03 19:52:49 +04:00
lock_sock ( sk ) ;
2013-05-23 00:17:31 +04:00
err = ip6_append_data ( sk , ping_getfrag , & pfh , len ,
2016-05-03 07:40:07 +03:00
0 , & ipc6 , & fl6 , rt ,
2018-07-06 17:12:57 +03:00
MSG_DONTWAIT ) ;
2013-05-23 00:17:31 +04:00
if ( err ) {
2014-03-31 22:14:10 +04:00
ICMP6_INC_STATS ( sock_net ( sk ) , rt - > rt6i_idev ,
ICMP6_MIB_OUTERRORS ) ;
2013-05-23 00:17:31 +04:00
ip6_flush_pending_frames ( sk ) ;
} else {
2017-10-06 09:46:14 +03:00
icmpv6_push_pending_frames ( sk , & fl6 ,
( struct icmp6hdr * ) & pfh . icmph , len ) ;
2013-05-23 00:17:31 +04:00
}
2013-07-03 19:52:49 +04:00
release_sock ( sk ) ;
2013-05-23 00:17:31 +04:00
2016-09-02 21:39:50 +03:00
dst_release ( dst ) ;
2013-07-03 19:12:40 +04:00
if ( err )
return err ;
return len ;
2013-05-23 00:17:31 +04:00
}
2013-05-31 19:05:50 +04:00
2016-03-23 12:59:51 +03:00
struct proto pingv6_prot = {
. name = " PINGv6 " ,
. owner = THIS_MODULE ,
. init = ping_init_sock ,
. close = ping_close ,
. connect = ip6_datagram_connect_v6_only ,
2016-10-20 19:39:40 +03:00
. disconnect = __udp_disconnect ,
2016-03-23 12:59:51 +03:00
. setsockopt = ipv6_setsockopt ,
. getsockopt = ipv6_getsockopt ,
. sendmsg = ping_v6_sendmsg ,
. recvmsg = ping_recvmsg ,
. bind = ping_bind ,
. backlog_rcv = ping_queue_rcv_skb ,
. hash = ping_hash ,
. unhash = ping_unhash ,
. get_port = ping_get_port ,
. obj_size = sizeof ( struct raw6_sock ) ,
} ;
EXPORT_SYMBOL_GPL ( pingv6_prot ) ;
static struct inet_protosw pingv6_protosw = {
. type = SOCK_DGRAM ,
. protocol = IPPROTO_ICMPV6 ,
. prot = & pingv6_prot ,
2017-06-03 19:29:25 +03:00
. ops = & inet6_sockraw_ops ,
2016-03-23 12:59:51 +03:00
. flags = INET_PROTOSW_REUSE ,
} ;
2013-05-31 19:05:50 +04:00
# ifdef CONFIG_PROC_FS
static void * ping_v6_seq_start ( struct seq_file * seq , loff_t * pos )
{
return ping_seq_start ( seq , pos , AF_INET6 ) ;
}
2013-06-12 17:04:16 +04:00
static int ping_v6_seq_show ( struct seq_file * seq , void * v )
2013-05-31 19:05:50 +04:00
{
if ( v = = SEQ_START_TOKEN ) {
seq_puts ( seq , IPV6_SEQ_DGRAM_HEADER ) ;
} else {
int bucket = ( ( struct ping_iter_state * ) seq - > private ) - > bucket ;
struct inet_sock * inet = inet_sk ( v ) ;
__u16 srcp = ntohs ( inet - > inet_sport ) ;
__u16 destp = ntohs ( inet - > inet_dport ) ;
ip6_dgram_sock_seq_show ( seq , v , srcp , destp , bucket ) ;
}
return 0 ;
}
2018-04-10 21:04:20 +03:00
static const struct seq_operations ping_v6_seq_ops = {
. start = ping_v6_seq_start ,
. show = ping_v6_seq_show ,
. next = ping_seq_next ,
. stop = ping_seq_stop ,
} ;
2013-05-31 19:05:50 +04:00
static int __net_init ping_v6_proc_init_net ( struct net * net )
{
2018-04-10 20:42:55 +03:00
if ( ! proc_create_net ( " icmp6 " , 0444 , net - > proc_net , & ping_v6_seq_ops ,
sizeof ( struct ping_iter_state ) ) )
2018-04-10 21:04:20 +03:00
return - ENOMEM ;
return 0 ;
2013-05-31 19:05:50 +04:00
}
static void __net_init ping_v6_proc_exit_net ( struct net * net )
{
2018-04-10 21:04:20 +03:00
remove_proc_entry ( " icmp6 " , net - > proc_net ) ;
2013-05-31 19:05:50 +04:00
}
static struct pernet_operations ping_v6_net_ops = {
. init = ping_v6_proc_init_net ,
. exit = ping_v6_proc_exit_net ,
} ;
# endif
int __init pingv6_init ( void )
{
# ifdef CONFIG_PROC_FS
int ret = register_pernet_subsys ( & ping_v6_net_ops ) ;
if ( ret )
return ret ;
# endif
pingv6_ops . ipv6_recv_error = ipv6_recv_error ;
2014-01-20 06:43:08 +04:00
pingv6_ops . ip6_datagram_recv_common_ctl = ip6_datagram_recv_common_ctl ;
pingv6_ops . ip6_datagram_recv_specific_ctl =
ip6_datagram_recv_specific_ctl ;
2013-05-31 19:05:50 +04:00
pingv6_ops . icmpv6_err_convert = icmpv6_err_convert ;
pingv6_ops . ipv6_icmp_error = ipv6_icmp_error ;
pingv6_ops . ipv6_chk_addr = ipv6_chk_addr ;
return inet6_register_protosw ( & pingv6_protosw ) ;
}
/* This never gets called because it's not possible to unload the ipv6 module,
* but just in case .
*/
void pingv6_exit ( void )
{
pingv6_ops . ipv6_recv_error = dummy_ipv6_recv_error ;
2014-01-20 06:43:08 +04:00
pingv6_ops . ip6_datagram_recv_common_ctl = dummy_ip6_datagram_recv_ctl ;
pingv6_ops . ip6_datagram_recv_specific_ctl = dummy_ip6_datagram_recv_ctl ;
2013-05-31 19:05:50 +04:00
pingv6_ops . icmpv6_err_convert = dummy_icmpv6_err_convert ;
pingv6_ops . ipv6_icmp_error = dummy_ipv6_icmp_error ;
pingv6_ops . ipv6_chk_addr = dummy_ipv6_chk_addr ;
# ifdef CONFIG_PROC_FS
unregister_pernet_subsys ( & ping_v6_net_ops ) ;
# endif
inet6_unregister_protosw ( & pingv6_protosw ) ;
}