2018-12-12 13:15:37 -08:00
// SPDX-License-Identifier: GPL-2.0
/* Test that sockets listening on a specific address are preferred
* over sockets listening on addr_any .
*/
# define _GNU_SOURCE
# include <arpa/inet.h>
# include <errno.h>
# include <error.h>
2018-12-15 14:27:24 -08:00
# include <linux/dccp.h>
2018-12-12 13:15:37 -08:00
# include <linux/in.h>
# include <linux/unistd.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/epoll.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <unistd.h>
2020-03-18 18:53:21 +00:00
# ifndef SOL_DCCP
# define SOL_DCCP 269
# endif
2018-12-12 13:15:37 -08:00
static const char * IP4_ADDR = " 127.0.0.1 " ;
static const char * IP6_ADDR = " ::1 " ;
static const char * IP4_MAPPED6 = " ::ffff:127.0.0.1 " ;
static const int PORT = 8888 ;
static void build_rcv_fd ( int family , int proto , int * rcv_fds , int count ,
const char * addr_str )
{
struct sockaddr_in addr4 = { 0 } ;
struct sockaddr_in6 addr6 = { 0 } ;
struct sockaddr * addr ;
int opt , i , sz ;
memset ( & addr , 0 , sizeof ( addr ) ) ;
switch ( family ) {
case AF_INET :
addr4 . sin_family = family ;
if ( ! addr_str )
addr4 . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
else if ( ! inet_pton ( family , addr_str , & addr4 . sin_addr . s_addr ) )
error ( 1 , errno , " inet_pton failed: %s " , addr_str ) ;
addr4 . sin_port = htons ( PORT ) ;
sz = sizeof ( addr4 ) ;
addr = ( struct sockaddr * ) & addr4 ;
break ;
case AF_INET6 :
addr6 . sin6_family = AF_INET6 ;
if ( ! addr_str )
addr6 . sin6_addr = in6addr_any ;
else if ( ! inet_pton ( family , addr_str , & addr6 . sin6_addr ) )
error ( 1 , errno , " inet_pton failed: %s " , addr_str ) ;
addr6 . sin6_port = htons ( PORT ) ;
sz = sizeof ( addr6 ) ;
addr = ( struct sockaddr * ) & addr6 ;
break ;
default :
error ( 1 , 0 , " Unsupported family %d " , family ) ;
2018-12-20 16:41:52 -08:00
/* clang does not recognize error() above as terminating
* the program , so it complains that saddr , sz are
* not initialized when this code path is taken . Silence it .
*/
return ;
2018-12-12 13:15:37 -08:00
}
for ( i = 0 ; i < count ; + + i ) {
rcv_fds [ i ] = socket ( family , proto , 0 ) ;
if ( rcv_fds [ i ] < 0 )
error ( 1 , errno , " failed to create receive socket " ) ;
opt = 1 ;
if ( setsockopt ( rcv_fds [ i ] , SOL_SOCKET , SO_REUSEPORT , & opt ,
sizeof ( opt ) ) )
error ( 1 , errno , " failed to set SO_REUSEPORT " ) ;
if ( bind ( rcv_fds [ i ] , addr , sz ) )
error ( 1 , errno , " failed to bind receive socket " ) ;
if ( proto = = SOCK_STREAM & & listen ( rcv_fds [ i ] , 10 ) )
2018-12-15 14:27:24 -08:00
error ( 1 , errno , " tcp: failed to listen on receive port " ) ;
else if ( proto = = SOCK_DCCP ) {
if ( setsockopt ( rcv_fds [ i ] , SOL_DCCP ,
DCCP_SOCKOPT_SERVICE ,
& ( int ) { htonl ( 42 ) } , sizeof ( int ) ) )
error ( 1 , errno , " failed to setsockopt " ) ;
if ( listen ( rcv_fds [ i ] , 10 ) )
error ( 1 , errno , " dccp: failed to listen on receive port " ) ;
}
2018-12-12 13:15:37 -08:00
}
}
static int connect_and_send ( int family , int proto )
{
struct sockaddr_in saddr4 = { 0 } ;
struct sockaddr_in daddr4 = { 0 } ;
struct sockaddr_in6 saddr6 = { 0 } ;
struct sockaddr_in6 daddr6 = { 0 } ;
struct sockaddr * saddr , * daddr ;
int fd , sz ;
switch ( family ) {
case AF_INET :
saddr4 . sin_family = AF_INET ;
saddr4 . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
saddr4 . sin_port = 0 ;
daddr4 . sin_family = AF_INET ;
if ( ! inet_pton ( family , IP4_ADDR , & daddr4 . sin_addr . s_addr ) )
error ( 1 , errno , " inet_pton failed: %s " , IP4_ADDR ) ;
daddr4 . sin_port = htons ( PORT ) ;
sz = sizeof ( saddr4 ) ;
saddr = ( struct sockaddr * ) & saddr4 ;
daddr = ( struct sockaddr * ) & daddr4 ;
break ;
case AF_INET6 :
saddr6 . sin6_family = AF_INET6 ;
saddr6 . sin6_addr = in6addr_any ;
daddr6 . sin6_family = AF_INET6 ;
if ( ! inet_pton ( family , IP6_ADDR , & daddr6 . sin6_addr ) )
error ( 1 , errno , " inet_pton failed: %s " , IP6_ADDR ) ;
daddr6 . sin6_port = htons ( PORT ) ;
sz = sizeof ( saddr6 ) ;
saddr = ( struct sockaddr * ) & saddr6 ;
daddr = ( struct sockaddr * ) & daddr6 ;
break ;
default :
error ( 1 , 0 , " Unsupported family %d " , family ) ;
2018-12-20 16:41:52 -08:00
/* clang does not recognize error() above as terminating
* the program , so it complains that saddr , daddr , sz are
* not initialized when this code path is taken . Silence it .
*/
return - 1 ;
2018-12-12 13:15:37 -08:00
}
fd = socket ( family , proto , 0 ) ;
if ( fd < 0 )
error ( 1 , errno , " failed to create send socket " ) ;
2018-12-15 14:27:24 -08:00
if ( proto = = SOCK_DCCP & &
setsockopt ( fd , SOL_DCCP , DCCP_SOCKOPT_SERVICE ,
& ( int ) { htonl ( 42 ) } , sizeof ( int ) ) )
error ( 1 , errno , " failed to setsockopt " ) ;
2018-12-12 13:15:37 -08:00
if ( bind ( fd , saddr , sz ) )
error ( 1 , errno , " failed to bind send socket " ) ;
if ( connect ( fd , daddr , sz ) )
error ( 1 , errno , " failed to connect send socket " ) ;
if ( send ( fd , " a " , 1 , 0 ) < 0 )
error ( 1 , errno , " failed to send message " ) ;
return fd ;
}
static int receive_once ( int epfd , int proto )
{
struct epoll_event ev ;
int i , fd ;
char buf [ 8 ] ;
i = epoll_wait ( epfd , & ev , 1 , 3 ) ;
if ( i < 0 )
error ( 1 , errno , " epoll_wait failed " ) ;
2018-12-15 14:27:24 -08:00
if ( proto = = SOCK_STREAM | | proto = = SOCK_DCCP ) {
2018-12-12 13:15:37 -08:00
fd = accept ( ev . data . fd , NULL , NULL ) ;
if ( fd < 0 )
error ( 1 , errno , " failed to accept " ) ;
i = recv ( fd , buf , sizeof ( buf ) , 0 ) ;
close ( fd ) ;
} else {
i = recv ( ev . data . fd , buf , sizeof ( buf ) , 0 ) ;
}
if ( i < 0 )
error ( 1 , errno , " failed to recv " ) ;
return ev . data . fd ;
}
static void test ( int * rcv_fds , int count , int family , int proto , int fd )
{
struct epoll_event ev ;
int epfd , i , send_fd , recv_fd ;
epfd = epoll_create ( 1 ) ;
if ( epfd < 0 )
error ( 1 , errno , " failed to create epoll " ) ;
ev . events = EPOLLIN ;
for ( i = 0 ; i < count ; + + i ) {
ev . data . fd = rcv_fds [ i ] ;
if ( epoll_ctl ( epfd , EPOLL_CTL_ADD , rcv_fds [ i ] , & ev ) )
error ( 1 , errno , " failed to register sock epoll " ) ;
}
send_fd = connect_and_send ( family , proto ) ;
recv_fd = receive_once ( epfd , proto ) ;
if ( recv_fd ! = fd )
error ( 1 , 0 , " received on an unexpected socket " ) ;
close ( send_fd ) ;
close ( epfd ) ;
}
2018-12-19 10:20:09 -08:00
static void run_one_test ( int fam_send , int fam_rcv , int proto ,
const char * addr_str )
2018-12-12 13:15:37 -08:00
{
/* Below we test that a socket listening on a specific address
* is always selected in preference over a socket listening
* on addr_any . Bugs where this is not the case often result
* in sockets created first or last to get picked . So below
* we make sure that there are always addr_any sockets created
* before and after a specific socket is created .
*/
int rcv_fds [ 10 ] , i ;
2018-12-19 10:20:09 -08:00
build_rcv_fd ( AF_INET , proto , rcv_fds , 2 , NULL ) ;
build_rcv_fd ( AF_INET6 , proto , rcv_fds + 2 , 2 , NULL ) ;
build_rcv_fd ( fam_rcv , proto , rcv_fds + 4 , 1 , addr_str ) ;
build_rcv_fd ( AF_INET , proto , rcv_fds + 5 , 2 , NULL ) ;
build_rcv_fd ( AF_INET6 , proto , rcv_fds + 7 , 2 , NULL ) ;
test ( rcv_fds , 9 , fam_send , proto , rcv_fds [ 4 ] ) ;
2018-12-12 13:15:37 -08:00
for ( i = 0 ; i < 9 ; + + i )
close ( rcv_fds [ i ] ) ;
2018-12-19 10:20:09 -08:00
fprintf ( stderr , " pass \n " ) ;
}
2018-12-12 13:15:37 -08:00
2018-12-19 10:20:09 -08:00
static void test_proto ( int proto , const char * proto_str )
{
if ( proto = = SOCK_DCCP ) {
int test_fd ;
test_fd = socket ( AF_INET , proto , 0 ) ;
if ( test_fd < 0 ) {
if ( errno = = ESOCKTNOSUPPORT ) {
fprintf ( stderr , " DCCP not supported: skipping DCCP tests \n " ) ;
return ;
} else
error ( 1 , errno , " failed to create a DCCP socket " ) ;
}
close ( test_fd ) ;
}
2018-12-12 13:15:37 -08:00
2018-12-19 10:20:09 -08:00
fprintf ( stderr , " %s IPv4 ... " , proto_str ) ;
run_one_test ( AF_INET , AF_INET , proto , IP4_ADDR ) ;
2018-12-12 13:15:37 -08:00
2018-12-19 10:20:09 -08:00
fprintf ( stderr , " %s IPv6 ... " , proto_str ) ;
run_one_test ( AF_INET6 , AF_INET6 , proto , IP6_ADDR ) ;
2018-12-15 14:27:24 -08:00
2018-12-19 10:20:09 -08:00
fprintf ( stderr , " %s IPv4 mapped to IPv6 ... " , proto_str ) ;
run_one_test ( AF_INET , AF_INET6 , proto , IP4_MAPPED6 ) ;
}
2018-12-15 14:27:24 -08:00
2018-12-19 10:20:09 -08:00
int main ( void )
{
test_proto ( SOCK_DGRAM , " UDP " ) ;
test_proto ( SOCK_STREAM , " TCP " ) ;
test_proto ( SOCK_DCCP , " DCCP " ) ;
2018-12-15 14:27:24 -08:00
2018-12-12 13:15:37 -08:00
fprintf ( stderr , " SUCCESS \n " ) ;
return 0 ;
}