2018-04-26 20:42:22 +03:00
// SPDX-License-Identifier: GPL-2.0
# define _GNU_SOURCE
# include <stddef.h>
# include <arpa/inet.h>
# include <error.h>
# include <errno.h>
# include <net/if.h>
# include <linux/in.h>
# include <linux/netlink.h>
# include <linux/rtnetlink.h>
# include <netinet/if_ether.h>
# include <netinet/ip.h>
# include <netinet/ip6.h>
# include <netinet/udp.h>
# include <stdbool.h>
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <sys/ioctl.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
# ifndef ETH_MAX_MTU
# define ETH_MAX_MTU 0xFFFFU
# endif
# ifndef UDP_SEGMENT
# define UDP_SEGMENT 103
# endif
2018-05-31 19:14:39 +03:00
# ifndef UDP_MAX_SEGMENTS
# define UDP_MAX_SEGMENTS (1 << 6UL)
# endif
2018-04-26 20:42:22 +03:00
# define CONST_MTU_TEST 1500
# define CONST_HDRLEN_V4 (sizeof(struct iphdr) + sizeof(struct udphdr))
# define CONST_HDRLEN_V6 (sizeof(struct ip6_hdr) + sizeof(struct udphdr))
# define CONST_MSS_V4 (CONST_MTU_TEST - CONST_HDRLEN_V4)
# define CONST_MSS_V6 (CONST_MTU_TEST - CONST_HDRLEN_V6)
# define CONST_MAX_SEGS_V4 (ETH_MAX_MTU / CONST_MSS_V4)
# define CONST_MAX_SEGS_V6 (ETH_MAX_MTU / CONST_MSS_V6)
static bool cfg_do_ipv4 ;
static bool cfg_do_ipv6 ;
2018-04-26 20:42:23 +03:00
static bool cfg_do_connected ;
2018-04-26 20:42:22 +03:00
static bool cfg_do_connectionless ;
2018-04-26 20:42:24 +03:00
static bool cfg_do_msgmore ;
2018-04-26 20:42:22 +03:00
static bool cfg_do_setsockopt ;
static int cfg_specific_test_id = - 1 ;
static const char cfg_ifname [ ] = " lo " ;
static unsigned short cfg_port = 9000 ;
static char buf [ ETH_MAX_MTU ] ;
struct testcase {
int tlen ; /* send() buffer size, may exceed mss */
bool tfail ; /* send() call is expected to fail */
int gso_len ; /* mss after applying gso */
int r_num_mss ; /* recv(): number of calls of full mss */
int r_len_last ; /* recv(): size of last non-mss dgram, if any */
} ;
const struct in6_addr addr6 = IN6ADDR_LOOPBACK_INIT ;
const struct in_addr addr4 = { . s_addr = __constant_htonl ( INADDR_LOOPBACK + 2 ) } ;
struct testcase testcases_v4 [ ] = {
{
/* no GSO: send a single byte */
. tlen = 1 ,
. r_len_last = 1 ,
} ,
{
/* no GSO: send a single MSS */
. tlen = CONST_MSS_V4 ,
. r_num_mss = 1 ,
} ,
{
/* no GSO: send a single MSS + 1B: fail */
. tlen = CONST_MSS_V4 + 1 ,
. tfail = true ,
} ,
{
2019-10-02 20:29:23 +03:00
/* send a single MSS: will fall back to no GSO */
2018-04-26 20:42:22 +03:00
. tlen = CONST_MSS_V4 ,
. gso_len = CONST_MSS_V4 ,
. r_num_mss = 1 ,
} ,
{
/* send a single MSS + 1B */
. tlen = CONST_MSS_V4 + 1 ,
. gso_len = CONST_MSS_V4 ,
. r_num_mss = 1 ,
. r_len_last = 1 ,
} ,
{
/* send exactly 2 MSS */
. tlen = CONST_MSS_V4 * 2 ,
. gso_len = CONST_MSS_V4 ,
. r_num_mss = 2 ,
} ,
{
/* send 2 MSS + 1B */
. tlen = ( CONST_MSS_V4 * 2 ) + 1 ,
. gso_len = CONST_MSS_V4 ,
. r_num_mss = 2 ,
. r_len_last = 1 ,
} ,
{
/* send MAX segs */
. tlen = ( ETH_MAX_MTU / CONST_MSS_V4 ) * CONST_MSS_V4 ,
. gso_len = CONST_MSS_V4 ,
. r_num_mss = ( ETH_MAX_MTU / CONST_MSS_V4 ) ,
} ,
{
/* send MAX bytes */
. tlen = ETH_MAX_MTU - CONST_HDRLEN_V4 ,
. gso_len = CONST_MSS_V4 ,
. r_num_mss = CONST_MAX_SEGS_V4 ,
. r_len_last = ETH_MAX_MTU - CONST_HDRLEN_V4 -
( CONST_MAX_SEGS_V4 * CONST_MSS_V4 ) ,
} ,
{
/* send MAX + 1: fail */
. tlen = ETH_MAX_MTU - CONST_HDRLEN_V4 + 1 ,
. gso_len = CONST_MSS_V4 ,
. tfail = true ,
} ,
2018-05-31 19:14:39 +03:00
{
2019-10-02 20:29:23 +03:00
/* send a single 1B MSS: will fall back to no GSO */
2018-05-31 19:14:39 +03:00
. tlen = 1 ,
. gso_len = 1 ,
. r_num_mss = 1 ,
} ,
{
/* send 2 1B segments */
. tlen = 2 ,
. gso_len = 1 ,
. r_num_mss = 2 ,
} ,
{
/* send 2B + 2B + 1B segments */
. tlen = 5 ,
. gso_len = 2 ,
. r_num_mss = 2 ,
. r_len_last = 1 ,
} ,
{
/* send max number of min sized segments */
2021-12-24 01:24:41 +03:00
. tlen = UDP_MAX_SEGMENTS ,
2018-05-31 19:14:39 +03:00
. gso_len = 1 ,
2021-12-24 01:24:41 +03:00
. r_num_mss = UDP_MAX_SEGMENTS ,
2018-05-31 19:14:39 +03:00
} ,
{
/* send max number + 1 of min sized segments: fail */
2021-12-24 01:24:41 +03:00
. tlen = UDP_MAX_SEGMENTS + 1 ,
2018-05-31 19:14:39 +03:00
. gso_len = 1 ,
. tfail = true ,
} ,
2018-04-26 20:42:22 +03:00
{
/* EOL */
}
} ;
# ifndef IP6_MAX_MTU
# define IP6_MAX_MTU (ETH_MAX_MTU + sizeof(struct ip6_hdr))
# endif
struct testcase testcases_v6 [ ] = {
{
/* no GSO: send a single byte */
. tlen = 1 ,
. r_len_last = 1 ,
} ,
{
/* no GSO: send a single MSS */
. tlen = CONST_MSS_V6 ,
. r_num_mss = 1 ,
} ,
{
/* no GSO: send a single MSS + 1B: fail */
. tlen = CONST_MSS_V6 + 1 ,
. tfail = true ,
} ,
{
2019-10-02 20:29:23 +03:00
/* send a single MSS: will fall back to no GSO */
2018-04-26 20:42:22 +03:00
. tlen = CONST_MSS_V6 ,
. gso_len = CONST_MSS_V6 ,
. r_num_mss = 1 ,
} ,
{
/* send a single MSS + 1B */
. tlen = CONST_MSS_V6 + 1 ,
. gso_len = CONST_MSS_V6 ,
. r_num_mss = 1 ,
. r_len_last = 1 ,
} ,
{
/* send exactly 2 MSS */
. tlen = CONST_MSS_V6 * 2 ,
. gso_len = CONST_MSS_V6 ,
. r_num_mss = 2 ,
} ,
{
/* send 2 MSS + 1B */
. tlen = ( CONST_MSS_V6 * 2 ) + 1 ,
. gso_len = CONST_MSS_V6 ,
. r_num_mss = 2 ,
. r_len_last = 1 ,
} ,
{
/* send MAX segs */
. tlen = ( IP6_MAX_MTU / CONST_MSS_V6 ) * CONST_MSS_V6 ,
. gso_len = CONST_MSS_V6 ,
. r_num_mss = ( IP6_MAX_MTU / CONST_MSS_V6 ) ,
} ,
{
/* send MAX bytes */
. tlen = IP6_MAX_MTU - CONST_HDRLEN_V6 ,
. gso_len = CONST_MSS_V6 ,
. r_num_mss = CONST_MAX_SEGS_V6 ,
. r_len_last = IP6_MAX_MTU - CONST_HDRLEN_V6 -
( CONST_MAX_SEGS_V6 * CONST_MSS_V6 ) ,
} ,
{
/* send MAX + 1: fail */
. tlen = IP6_MAX_MTU - CONST_HDRLEN_V6 + 1 ,
. gso_len = CONST_MSS_V6 ,
. tfail = true ,
} ,
2018-05-31 19:14:39 +03:00
{
2019-10-02 20:29:23 +03:00
/* send a single 1B MSS: will fall back to no GSO */
2018-05-31 19:14:39 +03:00
. tlen = 1 ,
. gso_len = 1 ,
. r_num_mss = 1 ,
} ,
{
/* send 2 1B segments */
. tlen = 2 ,
. gso_len = 1 ,
. r_num_mss = 2 ,
} ,
{
/* send 2B + 2B + 1B segments */
. tlen = 5 ,
. gso_len = 2 ,
. r_num_mss = 2 ,
. r_len_last = 1 ,
} ,
{
/* send max number of min sized segments */
2021-12-24 01:24:41 +03:00
. tlen = UDP_MAX_SEGMENTS ,
2018-05-31 19:14:39 +03:00
. gso_len = 1 ,
2021-12-24 01:24:41 +03:00
. r_num_mss = UDP_MAX_SEGMENTS ,
2018-05-31 19:14:39 +03:00
} ,
{
/* send max number + 1 of min sized segments: fail */
2021-12-24 01:24:41 +03:00
. tlen = UDP_MAX_SEGMENTS + 1 ,
2018-05-31 19:14:39 +03:00
. gso_len = 1 ,
. tfail = true ,
} ,
2018-04-26 20:42:22 +03:00
{
/* EOL */
}
} ;
static unsigned int get_device_mtu ( int fd , const char * ifname )
{
struct ifreq ifr ;
memset ( & ifr , 0 , sizeof ( ifr ) ) ;
strcpy ( ifr . ifr_name , ifname ) ;
if ( ioctl ( fd , SIOCGIFMTU , & ifr ) )
error ( 1 , errno , " ioctl get mtu " ) ;
return ifr . ifr_mtu ;
}
static void __set_device_mtu ( int fd , const char * ifname , unsigned int mtu )
{
struct ifreq ifr ;
memset ( & ifr , 0 , sizeof ( ifr ) ) ;
ifr . ifr_mtu = mtu ;
strcpy ( ifr . ifr_name , ifname ) ;
if ( ioctl ( fd , SIOCSIFMTU , & ifr ) )
error ( 1 , errno , " ioctl set mtu " ) ;
}
static void set_device_mtu ( int fd , int mtu )
{
int val ;
val = get_device_mtu ( fd , cfg_ifname ) ;
fprintf ( stderr , " device mtu (orig): %u \n " , val ) ;
__set_device_mtu ( fd , cfg_ifname , mtu ) ;
val = get_device_mtu ( fd , cfg_ifname ) ;
if ( val ! = mtu )
error ( 1 , 0 , " unable to set device mtu to %u \n " , val ) ;
fprintf ( stderr , " device mtu (test): %u \n " , val ) ;
}
static void set_pmtu_discover ( int fd , bool is_ipv4 )
{
int level , name , val ;
if ( is_ipv4 ) {
level = SOL_IP ;
name = IP_MTU_DISCOVER ;
val = IP_PMTUDISC_DO ;
} else {
level = SOL_IPV6 ;
name = IPV6_MTU_DISCOVER ;
val = IPV6_PMTUDISC_DO ;
}
if ( setsockopt ( fd , level , name , & val , sizeof ( val ) ) )
error ( 1 , errno , " setsockopt path mtu " ) ;
}
2018-04-26 20:42:23 +03:00
static unsigned int get_path_mtu ( int fd , bool is_ipv4 )
{
socklen_t vallen ;
unsigned int mtu ;
int ret ;
vallen = sizeof ( mtu ) ;
if ( is_ipv4 )
ret = getsockopt ( fd , SOL_IP , IP_MTU , & mtu , & vallen ) ;
else
ret = getsockopt ( fd , SOL_IPV6 , IPV6_MTU , & mtu , & vallen ) ;
if ( ret )
error ( 1 , errno , " getsockopt mtu " ) ;
fprintf ( stderr , " path mtu (read): %u \n " , mtu ) ;
return mtu ;
}
/* very wordy version of system("ip route add dev lo mtu 1500 127.0.0.3/32") */
static void set_route_mtu ( int mtu , bool is_ipv4 )
{
struct sockaddr_nl nladdr = { . nl_family = AF_NETLINK } ;
struct nlmsghdr * nh ;
struct rtattr * rta ;
struct rtmsg * rt ;
char data [ NLMSG_ALIGN ( sizeof ( * nh ) ) +
NLMSG_ALIGN ( sizeof ( * rt ) ) +
NLMSG_ALIGN ( RTA_LENGTH ( sizeof ( addr6 ) ) ) +
NLMSG_ALIGN ( RTA_LENGTH ( sizeof ( int ) ) ) +
NLMSG_ALIGN ( RTA_LENGTH ( 0 ) + RTA_LENGTH ( sizeof ( int ) ) ) ] ;
int fd , ret , alen , off = 0 ;
alen = is_ipv4 ? sizeof ( addr4 ) : sizeof ( addr6 ) ;
fd = socket ( AF_NETLINK , SOCK_RAW , NETLINK_ROUTE ) ;
if ( fd = = - 1 )
error ( 1 , errno , " socket netlink " ) ;
memset ( data , 0 , sizeof ( data ) ) ;
nh = ( void * ) data ;
nh - > nlmsg_type = RTM_NEWROUTE ;
nh - > nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE ;
off + = NLMSG_ALIGN ( sizeof ( * nh ) ) ;
rt = ( void * ) ( data + off ) ;
rt - > rtm_family = is_ipv4 ? AF_INET : AF_INET6 ;
rt - > rtm_table = RT_TABLE_MAIN ;
rt - > rtm_dst_len = alen < < 3 ;
rt - > rtm_protocol = RTPROT_BOOT ;
rt - > rtm_scope = RT_SCOPE_UNIVERSE ;
rt - > rtm_type = RTN_UNICAST ;
off + = NLMSG_ALIGN ( sizeof ( * rt ) ) ;
rta = ( void * ) ( data + off ) ;
rta - > rta_type = RTA_DST ;
rta - > rta_len = RTA_LENGTH ( alen ) ;
if ( is_ipv4 )
memcpy ( RTA_DATA ( rta ) , & addr4 , alen ) ;
else
memcpy ( RTA_DATA ( rta ) , & addr6 , alen ) ;
off + = NLMSG_ALIGN ( rta - > rta_len ) ;
rta = ( void * ) ( data + off ) ;
rta - > rta_type = RTA_OIF ;
rta - > rta_len = RTA_LENGTH ( sizeof ( int ) ) ;
* ( ( int * ) ( RTA_DATA ( rta ) ) ) = 1 ; //if_nametoindex("lo");
off + = NLMSG_ALIGN ( rta - > rta_len ) ;
/* MTU is a subtype in a metrics type */
rta = ( void * ) ( data + off ) ;
rta - > rta_type = RTA_METRICS ;
rta - > rta_len = RTA_LENGTH ( 0 ) + RTA_LENGTH ( sizeof ( int ) ) ;
off + = NLMSG_ALIGN ( rta - > rta_len ) ;
/* now fill MTU subtype. Note that it fits within above rta_len */
rta = ( void * ) ( ( ( char * ) rta ) + RTA_LENGTH ( 0 ) ) ;
rta - > rta_type = RTAX_MTU ;
rta - > rta_len = RTA_LENGTH ( sizeof ( int ) ) ;
* ( ( int * ) ( RTA_DATA ( rta ) ) ) = mtu ;
nh - > nlmsg_len = off ;
ret = sendto ( fd , data , off , 0 , ( void * ) & nladdr , sizeof ( nladdr ) ) ;
if ( ret ! = off )
error ( 1 , errno , " send netlink: %uB != %uB \n " , ret , off ) ;
if ( close ( fd ) )
error ( 1 , errno , " close netlink " ) ;
fprintf ( stderr , " route mtu (test): %u \n " , mtu ) ;
}
2018-04-26 20:42:24 +03:00
static bool __send_one ( int fd , struct msghdr * msg , int flags )
{
int ret ;
ret = sendmsg ( fd , msg , flags ) ;
2018-05-31 19:14:39 +03:00
if ( ret = = - 1 & &
( errno = = EMSGSIZE | | errno = = ENOMEM | | errno = = EINVAL ) )
2018-04-26 20:42:24 +03:00
return false ;
if ( ret = = - 1 )
error ( 1 , errno , " sendmsg " ) ;
if ( ret ! = msg - > msg_iov - > iov_len )
2019-10-23 07:58:07 +03:00
error ( 1 , 0 , " sendto: %d != %llu " , ret ,
( unsigned long long ) msg - > msg_iov - > iov_len ) ;
2018-04-26 20:42:24 +03:00
if ( msg - > msg_flags )
error ( 1 , 0 , " sendmsg: return flags 0x%x \n " , msg - > msg_flags ) ;
return true ;
}
2018-04-26 20:42:22 +03:00
static bool send_one ( int fd , int len , int gso_len ,
struct sockaddr * addr , socklen_t alen )
{
char control [ CMSG_SPACE ( sizeof ( uint16_t ) ) ] = { 0 } ;
struct msghdr msg = { 0 } ;
struct iovec iov = { 0 } ;
struct cmsghdr * cm ;
iov . iov_base = buf ;
iov . iov_len = len ;
msg . msg_iov = & iov ;
msg . msg_iovlen = 1 ;
msg . msg_name = addr ;
msg . msg_namelen = alen ;
if ( gso_len & & ! cfg_do_setsockopt ) {
msg . msg_control = control ;
msg . msg_controllen = sizeof ( control ) ;
cm = CMSG_FIRSTHDR ( & msg ) ;
cm - > cmsg_level = SOL_UDP ;
cm - > cmsg_type = UDP_SEGMENT ;
cm - > cmsg_len = CMSG_LEN ( sizeof ( uint16_t ) ) ;
* ( ( uint16_t * ) CMSG_DATA ( cm ) ) = gso_len ;
}
2018-04-26 20:42:24 +03:00
/* If MSG_MORE, send 1 byte followed by remainder */
if ( cfg_do_msgmore & & len > 1 ) {
iov . iov_len = 1 ;
if ( ! __send_one ( fd , & msg , MSG_MORE ) )
error ( 1 , 0 , " send 1B failed " ) ;
2018-04-26 20:42:22 +03:00
2018-04-26 20:42:24 +03:00
iov . iov_base + + ;
iov . iov_len = len - 1 ;
}
return __send_one ( fd , & msg , 0 ) ;
2018-04-26 20:42:22 +03:00
}
static int recv_one ( int fd , int flags )
{
int ret ;
ret = recv ( fd , buf , sizeof ( buf ) , flags ) ;
if ( ret = = - 1 & & errno = = EAGAIN & & ( flags & MSG_DONTWAIT ) )
return 0 ;
if ( ret = = - 1 )
error ( 1 , errno , " recv " ) ;
return ret ;
}
static void run_one ( struct testcase * test , int fdt , int fdr ,
struct sockaddr * addr , socklen_t alen )
{
int i , ret , val , mss ;
bool sent ;
fprintf ( stderr , " ipv%d tx:%d gso:%d %s \n " ,
addr - > sa_family = = AF_INET ? 4 : 6 ,
test - > tlen , test - > gso_len ,
test - > tfail ? " (fail) " : " " ) ;
val = test - > gso_len ;
if ( cfg_do_setsockopt ) {
if ( setsockopt ( fdt , SOL_UDP , UDP_SEGMENT , & val , sizeof ( val ) ) )
error ( 1 , errno , " setsockopt udp segment " ) ;
}
sent = send_one ( fdt , test - > tlen , test - > gso_len , addr , alen ) ;
if ( sent & & test - > tfail )
error ( 1 , 0 , " send succeeded while expecting failure " ) ;
if ( ! sent & & ! test - > tfail )
error ( 1 , 0 , " send failed while expecting success " ) ;
if ( ! sent )
return ;
2018-05-31 19:14:39 +03:00
if ( test - > gso_len )
mss = test - > gso_len ;
else
mss = addr - > sa_family = = AF_INET ? CONST_MSS_V4 : CONST_MSS_V6 ;
2018-04-26 20:42:22 +03:00
/* Recv all full MSS datagrams */
for ( i = 0 ; i < test - > r_num_mss ; i + + ) {
ret = recv_one ( fdr , 0 ) ;
if ( ret ! = mss )
error ( 1 , 0 , " recv.%d: %d != %d " , i , ret , mss ) ;
}
/* Recv the non-full last datagram, if tlen was not a multiple of mss */
if ( test - > r_len_last ) {
ret = recv_one ( fdr , 0 ) ;
if ( ret ! = test - > r_len_last )
error ( 1 , 0 , " recv.%d: %d != %d (last) " ,
i , ret , test - > r_len_last ) ;
}
/* Verify received all data */
ret = recv_one ( fdr , MSG_DONTWAIT ) ;
if ( ret )
error ( 1 , 0 , " recv: unexpected datagram " ) ;
}
static void run_all ( int fdt , int fdr , struct sockaddr * addr , socklen_t alen )
{
struct testcase * tests , * test ;
tests = addr - > sa_family = = AF_INET ? testcases_v4 : testcases_v6 ;
for ( test = tests ; test - > tlen ; test + + ) {
/* if a specific test is given, then skip all others */
if ( cfg_specific_test_id = = - 1 | |
cfg_specific_test_id = = test - tests )
run_one ( test , fdt , fdr , addr , alen ) ;
}
}
static void run_test ( struct sockaddr * addr , socklen_t alen )
{
struct timeval tv = { . tv_usec = 100 * 1000 } ;
2018-04-26 20:42:23 +03:00
int fdr , fdt , val ;
2018-04-26 20:42:22 +03:00
fdr = socket ( addr - > sa_family , SOCK_DGRAM , 0 ) ;
if ( fdr = = - 1 )
error ( 1 , errno , " socket r " ) ;
if ( bind ( fdr , addr , alen ) )
error ( 1 , errno , " bind " ) ;
/* Have tests fail quickly instead of hang */
if ( setsockopt ( fdr , SOL_SOCKET , SO_RCVTIMEO , & tv , sizeof ( tv ) ) )
error ( 1 , errno , " setsockopt rcv timeout " ) ;
fdt = socket ( addr - > sa_family , SOCK_DGRAM , 0 ) ;
if ( fdt = = - 1 )
error ( 1 , errno , " socket t " ) ;
/* Do not fragment these datagrams: only succeed if GSO works */
set_pmtu_discover ( fdt , addr - > sa_family = = AF_INET ) ;
if ( cfg_do_connectionless ) {
set_device_mtu ( fdt , CONST_MTU_TEST ) ;
run_all ( fdt , fdr , addr , alen ) ;
}
2018-04-26 20:42:23 +03:00
if ( cfg_do_connected ) {
set_device_mtu ( fdt , CONST_MTU_TEST + 100 ) ;
set_route_mtu ( CONST_MTU_TEST , addr - > sa_family = = AF_INET ) ;
if ( connect ( fdt , addr , alen ) )
error ( 1 , errno , " connect " ) ;
val = get_path_mtu ( fdt , addr - > sa_family = = AF_INET ) ;
if ( val ! = CONST_MTU_TEST )
error ( 1 , 0 , " bad path mtu %u \n " , val ) ;
run_all ( fdt , fdr , addr , 0 /* use connected addr */ ) ;
}
2018-04-26 20:42:22 +03:00
if ( close ( fdt ) )
error ( 1 , errno , " close t " ) ;
if ( close ( fdr ) )
error ( 1 , errno , " close r " ) ;
}
static void run_test_v4 ( void )
{
struct sockaddr_in addr = { 0 } ;
addr . sin_family = AF_INET ;
addr . sin_port = htons ( cfg_port ) ;
addr . sin_addr = addr4 ;
run_test ( ( void * ) & addr , sizeof ( addr ) ) ;
}
static void run_test_v6 ( void )
{
struct sockaddr_in6 addr = { 0 } ;
addr . sin6_family = AF_INET6 ;
addr . sin6_port = htons ( cfg_port ) ;
addr . sin6_addr = addr6 ;
run_test ( ( void * ) & addr , sizeof ( addr ) ) ;
}
static void parse_opts ( int argc , char * * argv )
{
int c ;
2018-04-26 20:42:24 +03:00
while ( ( c = getopt ( argc , argv , " 46cCmst: " ) ) ! = - 1 ) {
2018-04-26 20:42:22 +03:00
switch ( c ) {
case ' 4 ' :
cfg_do_ipv4 = true ;
break ;
case ' 6 ' :
cfg_do_ipv6 = true ;
break ;
2018-04-26 20:42:23 +03:00
case ' c ' :
cfg_do_connected = true ;
break ;
2018-04-26 20:42:22 +03:00
case ' C ' :
cfg_do_connectionless = true ;
break ;
2018-04-26 20:42:24 +03:00
case ' m ' :
cfg_do_msgmore = true ;
break ;
2018-04-26 20:42:22 +03:00
case ' s ' :
cfg_do_setsockopt = true ;
break ;
case ' t ' :
cfg_specific_test_id = strtoul ( optarg , NULL , 0 ) ;
break ;
default :
error ( 1 , 0 , " %s: parse error " , argv [ 0 ] ) ;
}
}
}
int main ( int argc , char * * argv )
{
parse_opts ( argc , argv ) ;
if ( cfg_do_ipv4 )
run_test_v4 ( ) ;
if ( cfg_do_ipv6 )
run_test_v6 ( ) ;
fprintf ( stderr , " OK \n " ) ;
return 0 ;
}