2017-08-15 22:33:32 -07:00
/* Copyright (c) 2017 Covalent IO, Inc. http://covalent.io
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*/
# include <stdio.h>
# include <stdlib.h>
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <sys/select.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>
# include <sys/ioctl.h>
# include <stdbool.h>
# include <signal.h>
# include <fcntl.h>
2018-01-22 10:36:02 -08:00
# include <sys/wait.h>
2018-01-22 10:36:19 -08:00
# include <time.h>
2017-08-15 22:33:32 -07:00
# include <sys/time.h>
2018-01-22 10:37:11 -08:00
# include <sys/resource.h>
2017-08-15 22:33:32 -07:00
# include <sys/types.h>
2018-03-18 12:57:46 -07:00
# include <sys/sendfile.h>
2017-08-15 22:33:32 -07:00
# include <linux/netlink.h>
# include <linux/socket.h>
# include <linux/sock_diag.h>
# include <linux/bpf.h>
# include <linux/if_link.h>
# include <assert.h>
# include <libgen.h>
2018-01-22 10:35:27 -08:00
# include <getopt.h>
2017-08-15 22:33:32 -07:00
# include "../bpf/bpf_load.h"
# include "../bpf/bpf_util.h"
# include "../bpf/libbpf.h"
int running ;
void running_handler ( int a ) ;
/* randomly selected ports for testing on lo */
# define S1_PORT 10000
# define S2_PORT 10001
2018-01-22 10:35:27 -08:00
/* global sockets */
int s1 , s2 , c1 , c2 , p1 , p2 ;
2018-03-18 12:57:41 -07:00
int txmsg_pass ;
int txmsg_noisy ;
int txmsg_redir ;
int txmsg_redir_noisy ;
2018-03-18 12:58:07 -07:00
int txmsg_drop ;
2018-03-18 12:57:56 -07:00
int txmsg_apply ;
2018-03-18 12:58:02 -07:00
int txmsg_cork ;
2018-03-18 12:58:12 -07:00
int txmsg_start ;
int txmsg_end ;
2018-03-28 12:49:20 -07:00
int txmsg_ingress ;
2018-03-28 12:49:30 -07:00
int txmsg_skb ;
2018-03-18 12:57:41 -07:00
2018-01-22 10:35:27 -08:00
static const struct option long_options [ ] = {
{ " help " , no_argument , NULL , ' h ' } ,
{ " cgroup " , required_argument , NULL , ' c ' } ,
{ " rate " , required_argument , NULL , ' r ' } ,
{ " verbose " , no_argument , NULL , ' v ' } ,
2018-01-22 10:35:45 -08:00
{ " iov_count " , required_argument , NULL , ' i ' } ,
{ " length " , required_argument , NULL , ' l ' } ,
{ " test " , required_argument , NULL , ' t ' } ,
2018-03-18 12:57:51 -07:00
{ " data_test " , no_argument , NULL , ' d ' } ,
2018-03-18 12:57:46 -07:00
{ " txmsg " , no_argument , & txmsg_pass , 1 } ,
{ " txmsg_noisy " , no_argument , & txmsg_noisy , 1 } ,
{ " txmsg_redir " , no_argument , & txmsg_redir , 1 } ,
{ " txmsg_redir_noisy " , no_argument , & txmsg_redir_noisy , 1 } ,
2018-03-18 12:58:07 -07:00
{ " txmsg_drop " , no_argument , & txmsg_drop , 1 } ,
2018-03-18 12:57:56 -07:00
{ " txmsg_apply " , required_argument , NULL , ' a ' } ,
2018-03-18 12:58:02 -07:00
{ " txmsg_cork " , required_argument , NULL , ' k ' } ,
2018-03-18 12:58:12 -07:00
{ " txmsg_start " , required_argument , NULL , ' s ' } ,
{ " txmsg_end " , required_argument , NULL , ' e ' } ,
2018-03-28 12:49:20 -07:00
{ " txmsg_ingress " , no_argument , & txmsg_ingress , 1 } ,
2018-03-28 12:49:30 -07:00
{ " txmsg_skb " , no_argument , & txmsg_skb , 1 } ,
2018-01-22 10:35:27 -08:00
{ 0 , 0 , NULL , 0 }
} ;
static void usage ( char * argv [ ] )
2017-08-15 22:33:32 -07:00
{
2018-01-22 10:35:27 -08:00
int i ;
printf ( " Usage: %s --cgroup <cgroup_path> \n " , argv [ 0 ] ) ;
printf ( " options: \n " ) ;
for ( i = 0 ; long_options [ i ] . name ! = 0 ; i + + ) {
printf ( " --%-12s " , long_options [ i ] . name ) ;
if ( long_options [ i ] . flag ! = NULL )
printf ( " flag (internal value:%d) \n " ,
* long_options [ i ] . flag ) ;
else
printf ( " -%c \n " , long_options [ i ] . val ) ;
}
printf ( " \n " ) ;
}
static int sockmap_init_sockets ( void )
{
int i , err , one = 1 ;
2017-08-15 22:33:32 -07:00
struct sockaddr_in addr ;
int * fds [ 4 ] = { & s1 , & s2 , & c1 , & c2 } ;
s1 = s2 = p1 = p2 = c1 = c2 = 0 ;
/* Init sockets */
for ( i = 0 ; i < 4 ; i + + ) {
* fds [ i ] = socket ( AF_INET , SOCK_STREAM , 0 ) ;
if ( * fds [ i ] < 0 ) {
perror ( " socket s1 failed() " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
}
/* Allow reuse */
for ( i = 0 ; i < 2 ; i + + ) {
err = setsockopt ( * fds [ i ] , SOL_SOCKET , SO_REUSEADDR ,
( char * ) & one , sizeof ( one ) ) ;
if ( err ) {
perror ( " setsockopt failed() " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
}
/* Non-blocking sockets */
2018-01-22 10:36:53 -08:00
for ( i = 0 ; i < 2 ; i + + ) {
2017-08-15 22:33:32 -07:00
err = ioctl ( * fds [ i ] , FIONBIO , ( char * ) & one ) ;
if ( err < 0 ) {
perror ( " ioctl s1 failed() " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
}
/* Bind server sockets */
memset ( & addr , 0 , sizeof ( struct sockaddr_in ) ) ;
addr . sin_family = AF_INET ;
addr . sin_addr . s_addr = inet_addr ( " 127.0.0.1 " ) ;
addr . sin_port = htons ( S1_PORT ) ;
err = bind ( s1 , ( struct sockaddr * ) & addr , sizeof ( addr ) ) ;
if ( err < 0 ) {
perror ( " bind s1 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
addr . sin_port = htons ( S2_PORT ) ;
err = bind ( s2 , ( struct sockaddr * ) & addr , sizeof ( addr ) ) ;
if ( err < 0 ) {
perror ( " bind s2 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
/* Listen server sockets */
addr . sin_port = htons ( S1_PORT ) ;
err = listen ( s1 , 32 ) ;
if ( err < 0 ) {
perror ( " listen s1 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
addr . sin_port = htons ( S2_PORT ) ;
err = listen ( s2 , 32 ) ;
if ( err < 0 ) {
perror ( " listen s1 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
/* Initiate Connect */
addr . sin_port = htons ( S1_PORT ) ;
err = connect ( c1 , ( struct sockaddr * ) & addr , sizeof ( addr ) ) ;
if ( err < 0 & & errno ! = EINPROGRESS ) {
perror ( " connect c1 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
addr . sin_port = htons ( S2_PORT ) ;
err = connect ( c2 , ( struct sockaddr * ) & addr , sizeof ( addr ) ) ;
if ( err < 0 & & errno ! = EINPROGRESS ) {
perror ( " connect c2 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
} else if ( err < 0 ) {
err = 0 ;
2017-08-15 22:33:32 -07:00
}
/* Accept Connecrtions */
p1 = accept ( s1 , NULL , NULL ) ;
if ( p1 < 0 ) {
perror ( " accept s1 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
p2 = accept ( s2 , NULL , NULL ) ;
if ( p2 < 0 ) {
perror ( " accept s1 failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return errno ;
2017-08-15 22:33:32 -07:00
}
printf ( " connected sockets: c1 <-> p1, c2 <-> p2 \n " ) ;
printf ( " cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i) \n " ,
c1 , s1 , c2 , s2 ) ;
2018-01-22 10:35:27 -08:00
return 0 ;
}
2018-01-22 10:35:45 -08:00
struct msg_stats {
size_t bytes_sent ;
size_t bytes_recvd ;
2018-01-22 10:36:19 -08:00
struct timespec start ;
struct timespec end ;
2018-01-22 10:35:45 -08:00
} ;
2018-03-18 12:58:07 -07:00
struct sockmap_options {
int verbose ;
bool base ;
bool sendpage ;
bool data_test ;
bool drop_expected ;
} ;
2018-03-18 12:57:46 -07:00
static int msg_loop_sendpage ( int fd , int iov_length , int cnt ,
2018-03-18 12:58:07 -07:00
struct msg_stats * s ,
struct sockmap_options * opt )
2018-03-18 12:57:46 -07:00
{
2018-03-18 12:58:07 -07:00
bool drop = opt - > drop_expected ;
2018-03-18 12:57:51 -07:00
unsigned char k = 0 ;
2018-03-18 12:57:46 -07:00
FILE * file ;
int i , fp ;
file = fopen ( " .sendpage_tst.tmp " , " w+ " ) ;
2018-03-18 12:57:51 -07:00
for ( i = 0 ; i < iov_length * cnt ; i + + , k + + )
fwrite ( & k , sizeof ( char ) , 1 , file ) ;
fflush ( file ) ;
2018-03-18 12:57:46 -07:00
fseek ( file , 0 , SEEK_SET ) ;
2018-03-18 12:57:51 -07:00
fclose ( file ) ;
2018-03-18 12:57:46 -07:00
2018-03-18 12:57:51 -07:00
fp = open ( " .sendpage_tst.tmp " , O_RDONLY ) ;
2018-03-18 12:57:46 -07:00
clock_gettime ( CLOCK_MONOTONIC , & s - > start ) ;
for ( i = 0 ; i < cnt ; i + + ) {
2018-03-18 12:57:51 -07:00
int sent = sendfile ( fd , fp , NULL , iov_length ) ;
2018-03-18 12:57:46 -07:00
2018-03-18 12:58:07 -07:00
if ( ! drop & & sent < 0 ) {
2018-03-18 12:57:46 -07:00
perror ( " send loop error: " ) ;
2018-03-18 12:57:51 -07:00
close ( fp ) ;
2018-03-18 12:57:46 -07:00
return sent ;
2018-03-18 12:58:07 -07:00
} else if ( drop & & sent > = 0 ) {
printf ( " sendpage loop error expected: %i \n " , sent ) ;
close ( fp ) ;
return - EIO ;
2018-03-18 12:57:46 -07:00
}
2018-03-18 12:58:07 -07:00
if ( sent > 0 )
s - > bytes_sent + = sent ;
2018-03-18 12:57:46 -07:00
}
clock_gettime ( CLOCK_MONOTONIC , & s - > end ) ;
2018-03-18 12:57:51 -07:00
close ( fp ) ;
2018-03-18 12:57:46 -07:00
return 0 ;
}
2018-01-22 10:35:45 -08:00
static int msg_loop ( int fd , int iov_count , int iov_length , int cnt ,
2018-03-18 12:58:07 -07:00
struct msg_stats * s , bool tx ,
struct sockmap_options * opt )
2018-01-22 10:35:45 -08:00
{
struct msghdr msg = { 0 } ;
2018-01-22 10:36:19 -08:00
int err , i , flags = MSG_NOSIGNAL ;
2018-01-22 10:35:45 -08:00
struct iovec * iov ;
2018-03-18 12:57:51 -07:00
unsigned char k ;
2018-03-18 12:58:07 -07:00
bool data_test = opt - > data_test ;
bool drop = opt - > drop_expected ;
2018-01-22 10:35:45 -08:00
iov = calloc ( iov_count , sizeof ( struct iovec ) ) ;
if ( ! iov )
return errno ;
2018-03-18 12:57:51 -07:00
k = 0 ;
2018-01-22 10:35:45 -08:00
for ( i = 0 ; i < iov_count ; i + + ) {
2018-03-18 12:57:51 -07:00
unsigned char * d = calloc ( iov_length , sizeof ( char ) ) ;
2018-01-22 10:35:45 -08:00
if ( ! d ) {
fprintf ( stderr , " iov_count %i/%i OOM \n " , i , iov_count ) ;
goto out_errno ;
}
iov [ i ] . iov_base = d ;
iov [ i ] . iov_len = iov_length ;
2018-03-18 12:57:51 -07:00
if ( data_test & & tx ) {
int j ;
for ( j = 0 ; j < iov_length ; j + + )
d [ j ] = k + + ;
}
2018-01-22 10:35:45 -08:00
}
msg . msg_iov = iov ;
msg . msg_iovlen = iov_count ;
2018-03-18 12:57:51 -07:00
k = 0 ;
2018-01-22 10:35:45 -08:00
if ( tx ) {
2018-01-22 10:36:19 -08:00
clock_gettime ( CLOCK_MONOTONIC , & s - > start ) ;
2018-01-22 10:35:45 -08:00
for ( i = 0 ; i < cnt ; i + + ) {
int sent = sendmsg ( fd , & msg , flags ) ;
2018-03-18 12:58:07 -07:00
if ( ! drop & & sent < 0 ) {
2018-01-22 10:35:45 -08:00
perror ( " send loop error: " ) ;
goto out_errno ;
2018-03-18 12:58:07 -07:00
} else if ( drop & & sent > = 0 ) {
printf ( " send loop error expected: %i \n " , sent ) ;
errno = - EIO ;
goto out_errno ;
2018-01-22 10:35:45 -08:00
}
2018-03-18 12:58:07 -07:00
if ( sent > 0 )
s - > bytes_sent + = sent ;
2018-01-22 10:35:45 -08:00
}
2018-01-22 10:36:19 -08:00
clock_gettime ( CLOCK_MONOTONIC , & s - > end ) ;
2018-01-22 10:35:45 -08:00
} else {
int slct , recv , max_fd = fd ;
struct timeval timeout ;
float total_bytes ;
fd_set w ;
total_bytes = ( float ) iov_count * ( float ) iov_length * ( float ) cnt ;
2018-01-22 10:36:19 -08:00
err = clock_gettime ( CLOCK_MONOTONIC , & s - > start ) ;
if ( err < 0 )
perror ( " recv start time: " ) ;
2018-01-22 10:35:45 -08:00
while ( s - > bytes_recvd < total_bytes ) {
timeout . tv_sec = 1 ;
timeout . tv_usec = 0 ;
/* FD sets */
FD_ZERO ( & w ) ;
FD_SET ( fd , & w ) ;
slct = select ( max_fd + 1 , & w , NULL , NULL , & timeout ) ;
if ( slct = = - 1 ) {
perror ( " select() " ) ;
2018-01-22 10:36:19 -08:00
clock_gettime ( CLOCK_MONOTONIC , & s - > end ) ;
2018-01-22 10:35:45 -08:00
goto out_errno ;
} else if ( ! slct ) {
fprintf ( stderr , " unexpected timeout \n " ) ;
errno = - EIO ;
2018-01-22 10:36:19 -08:00
clock_gettime ( CLOCK_MONOTONIC , & s - > end ) ;
2018-01-22 10:35:45 -08:00
goto out_errno ;
}
recv = recvmsg ( fd , & msg , flags ) ;
if ( recv < 0 ) {
if ( errno ! = EWOULDBLOCK ) {
2018-01-22 10:36:19 -08:00
clock_gettime ( CLOCK_MONOTONIC , & s - > end ) ;
2018-01-22 10:35:45 -08:00
perror ( " recv failed() \n " ) ;
goto out_errno ;
}
}
s - > bytes_recvd + = recv ;
2018-03-18 12:57:51 -07:00
if ( data_test ) {
int j ;
for ( i = 0 ; i < msg . msg_iovlen ; i + + ) {
unsigned char * d = iov [ i ] . iov_base ;
for ( j = 0 ;
j < iov [ i ] . iov_len & & recv ; j + + ) {
if ( d [ j ] ! = k + + ) {
errno = - EIO ;
fprintf ( stderr ,
" detected data corruption @iov[%i]:%i %02x != %02x, %02x ?= %02x \n " ,
i , j , d [ j ] , k - 1 , d [ j + 1 ] , k + 1 ) ;
goto out_errno ;
}
recv - - ;
}
}
}
2018-01-22 10:35:45 -08:00
}
2018-01-22 10:36:19 -08:00
clock_gettime ( CLOCK_MONOTONIC , & s - > end ) ;
2018-01-22 10:35:45 -08:00
}
for ( i = 0 ; i < iov_count ; i + + )
free ( iov [ i ] . iov_base ) ;
free ( iov ) ;
return 0 ;
out_errno :
for ( i = 0 ; i < iov_count ; i + + )
free ( iov [ i ] . iov_base ) ;
free ( iov ) ;
return errno ;
}
2018-01-22 10:36:19 -08:00
static float giga = 1000000000 ;
static inline float sentBps ( struct msg_stats s )
{
return s . bytes_sent / ( s . end . tv_sec - s . start . tv_sec ) ;
}
static inline float recvdBps ( struct msg_stats s )
{
return s . bytes_recvd / ( s . end . tv_sec - s . start . tv_sec ) ;
}
2018-01-22 10:36:36 -08:00
static int sendmsg_test ( int iov_count , int iov_buf , int cnt ,
2018-03-18 12:57:51 -07:00
struct sockmap_options * opt )
2018-01-22 10:35:45 -08:00
{
2018-01-22 10:36:36 -08:00
float sent_Bps = 0 , recvd_Bps = 0 ;
int rx_fd , txpid , rxpid , err = 0 ;
2018-01-22 10:35:45 -08:00
struct msg_stats s = { 0 } ;
2018-01-22 10:36:02 -08:00
int status ;
errno = 0 ;
2018-03-18 12:57:51 -07:00
if ( opt - > base )
2018-01-22 10:36:36 -08:00
rx_fd = p1 ;
else
rx_fd = p2 ;
2018-01-22 10:36:02 -08:00
rxpid = fork ( ) ;
if ( rxpid = = 0 ) {
2018-03-18 12:58:07 -07:00
if ( opt - > drop_expected )
exit ( 1 ) ;
2018-03-18 12:57:51 -07:00
if ( opt - > sendpage )
2018-03-18 12:57:46 -07:00
iov_count = 1 ;
2018-03-18 12:57:51 -07:00
err = msg_loop ( rx_fd , iov_count , iov_buf ,
2018-03-18 12:58:07 -07:00
cnt , & s , false , opt ) ;
2018-01-22 10:36:02 -08:00
if ( err )
fprintf ( stderr ,
" msg_loop_rx: iov_count %i iov_buf %i cnt %i err %i \n " ,
iov_count , iov_buf , cnt , err ) ;
shutdown ( p2 , SHUT_RDWR ) ;
shutdown ( p1 , SHUT_RDWR ) ;
2018-01-22 10:36:19 -08:00
if ( s . end . tv_sec - s . start . tv_sec ) {
sent_Bps = sentBps ( s ) ;
recvd_Bps = recvdBps ( s ) ;
}
fprintf ( stdout ,
" rx_sendmsg: TX: %zuB %fB/s %fGB/s RX: %zuB %fB/s %fGB/s \n " ,
s . bytes_sent , sent_Bps , sent_Bps / giga ,
s . bytes_recvd , recvd_Bps , recvd_Bps / giga ) ;
2018-01-22 10:36:02 -08:00
exit ( 1 ) ;
} else if ( rxpid = = - 1 ) {
perror ( " msg_loop_rx: " ) ;
return errno ;
2018-01-22 10:35:45 -08:00
}
2018-01-22 10:36:02 -08:00
txpid = fork ( ) ;
if ( txpid = = 0 ) {
2018-03-18 12:57:51 -07:00
if ( opt - > sendpage )
2018-03-18 12:58:07 -07:00
err = msg_loop_sendpage ( c1 , iov_buf , cnt , & s , opt ) ;
2018-03-18 12:57:46 -07:00
else
2018-03-18 12:57:51 -07:00
err = msg_loop ( c1 , iov_count , iov_buf ,
2018-03-18 12:58:07 -07:00
cnt , & s , true , opt ) ;
2018-03-18 12:57:46 -07:00
2018-01-22 10:36:02 -08:00
if ( err )
fprintf ( stderr ,
" msg_loop_tx: iov_count %i iov_buf %i cnt %i err %i \n " ,
iov_count , iov_buf , cnt , err ) ;
shutdown ( c1 , SHUT_RDWR ) ;
2018-01-22 10:36:19 -08:00
if ( s . end . tv_sec - s . start . tv_sec ) {
sent_Bps = sentBps ( s ) ;
recvd_Bps = recvdBps ( s ) ;
}
fprintf ( stdout ,
" tx_sendmsg: TX: %zuB %fB/s %f GB/s RX: %zuB %fB/s %fGB/s \n " ,
s . bytes_sent , sent_Bps , sent_Bps / giga ,
s . bytes_recvd , recvd_Bps , recvd_Bps / giga ) ;
2018-01-22 10:36:02 -08:00
exit ( 1 ) ;
} else if ( txpid = = - 1 ) {
perror ( " msg_loop_tx: " ) ;
return errno ;
}
2018-01-22 10:35:45 -08:00
2018-01-22 10:36:02 -08:00
assert ( waitpid ( rxpid , & status , 0 ) = = rxpid ) ;
assert ( waitpid ( txpid , & status , 0 ) = = txpid ) ;
2018-01-22 10:35:45 -08:00
return err ;
}
2018-03-18 12:57:51 -07:00
static int forever_ping_pong ( int rate , struct sockmap_options * opt )
2018-01-22 10:35:27 -08:00
{
struct timeval timeout ;
char buf [ 1024 ] = { 0 } ;
int sc ;
timeout . tv_sec = 10 ;
timeout . tv_usec = 0 ;
2017-08-15 22:33:32 -07:00
/* Ping/Pong data from client to server */
sc = send ( c1 , buf , sizeof ( buf ) , 0 ) ;
if ( sc < 0 ) {
perror ( " send failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return sc ;
2017-08-15 22:33:32 -07:00
}
do {
2018-01-22 10:35:27 -08:00
int s , rc , i , max_fd = p2 ;
fd_set w ;
2017-08-15 22:33:32 -07:00
/* FD sets */
FD_ZERO ( & w ) ;
FD_SET ( c1 , & w ) ;
FD_SET ( c2 , & w ) ;
FD_SET ( p1 , & w ) ;
FD_SET ( p2 , & w ) ;
s = select ( max_fd + 1 , & w , NULL , NULL , & timeout ) ;
if ( s = = - 1 ) {
perror ( " select() " ) ;
break ;
} else if ( ! s ) {
fprintf ( stderr , " unexpected timeout \n " ) ;
break ;
}
for ( i = 0 ; i < = max_fd & & s > 0 ; + + i ) {
if ( ! FD_ISSET ( i , & w ) )
continue ;
s - - ;
rc = recv ( i , buf , sizeof ( buf ) , 0 ) ;
if ( rc < 0 ) {
if ( errno ! = EWOULDBLOCK ) {
perror ( " recv failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return rc ;
2017-08-15 22:33:32 -07:00
}
}
if ( rc = = 0 ) {
close ( i ) ;
break ;
}
sc = send ( i , buf , rc , 0 ) ;
if ( sc < 0 ) {
perror ( " send failed() \n " ) ;
2018-01-22 10:35:27 -08:00
return sc ;
2017-08-15 22:33:32 -07:00
}
}
2018-01-22 10:35:27 -08:00
if ( rate )
sleep ( rate ) ;
2018-03-18 12:57:51 -07:00
if ( opt - > verbose ) {
2017-08-15 22:33:32 -07:00
printf ( " . " ) ;
fflush ( stdout ) ;
}
} while ( running ) ;
2018-01-22 10:35:27 -08:00
return 0 ;
2017-08-15 22:33:32 -07:00
}
2018-01-22 10:35:45 -08:00
enum {
PING_PONG ,
SENDMSG ,
2018-01-22 10:36:36 -08:00
BASE ,
2018-03-18 12:57:46 -07:00
BASE_SENDPAGE ,
SENDPAGE ,
2018-01-22 10:35:45 -08:00
} ;
2017-08-15 22:33:32 -07:00
int main ( int argc , char * * argv )
{
2018-03-18 12:57:51 -07:00
int iov_count = 1 , length = 1024 , rate = 1 , tx_prog_fd ;
2018-01-22 10:37:11 -08:00
struct rlimit r = { 10 * 1024 * 1024 , RLIM_INFINITY } ;
2018-01-22 10:35:27 -08:00
int opt , longindex , err , cg_fd = 0 ;
2018-03-18 12:57:51 -07:00
struct sockmap_options options = { 0 } ;
2018-01-22 10:35:45 -08:00
int test = PING_PONG ;
2017-08-15 22:33:32 -07:00
char filename [ 256 ] ;
2018-03-18 12:57:51 -07:00
while ( ( opt = getopt_long ( argc , argv , " :dhvc:r:i:l:t: " ,
2018-01-22 10:35:27 -08:00
long_options , & longindex ) ) ! = - 1 ) {
switch ( opt ) {
2018-03-18 12:58:12 -07:00
case ' s ' :
txmsg_start = atoi ( optarg ) ;
break ;
case ' e ' :
txmsg_end = atoi ( optarg ) ;
break ;
2018-03-18 12:57:56 -07:00
case ' a ' :
txmsg_apply = atoi ( optarg ) ;
break ;
2018-03-18 12:58:02 -07:00
case ' k ' :
txmsg_cork = atoi ( optarg ) ;
break ;
2018-01-22 10:35:27 -08:00
case ' c ' :
cg_fd = open ( optarg , O_DIRECTORY , O_RDONLY ) ;
if ( cg_fd < 0 ) {
fprintf ( stderr ,
" ERROR: (%i) open cg path failed: %s \n " ,
cg_fd , optarg ) ;
return cg_fd ;
}
break ;
case ' r ' :
rate = atoi ( optarg ) ;
break ;
case ' v ' :
2018-03-18 12:57:51 -07:00
options . verbose = 1 ;
2018-01-22 10:35:27 -08:00
break ;
2018-01-22 10:35:45 -08:00
case ' i ' :
iov_count = atoi ( optarg ) ;
break ;
case ' l ' :
length = atoi ( optarg ) ;
break ;
2018-03-18 12:57:51 -07:00
case ' d ' :
options . data_test = true ;
break ;
2018-01-22 10:35:45 -08:00
case ' t ' :
if ( strcmp ( optarg , " ping " ) = = 0 ) {
test = PING_PONG ;
} else if ( strcmp ( optarg , " sendmsg " ) = = 0 ) {
test = SENDMSG ;
2018-01-22 10:36:36 -08:00
} else if ( strcmp ( optarg , " base " ) = = 0 ) {
test = BASE ;
2018-03-18 12:57:46 -07:00
} else if ( strcmp ( optarg , " base_sendpage " ) = = 0 ) {
test = BASE_SENDPAGE ;
} else if ( strcmp ( optarg , " sendpage " ) = = 0 ) {
test = SENDPAGE ;
2018-01-22 10:35:45 -08:00
} else {
usage ( argv ) ;
return - 1 ;
}
break ;
2018-03-18 12:57:41 -07:00
case 0 :
break ;
2018-01-22 10:35:27 -08:00
case ' h ' :
default :
usage ( argv ) ;
return - 1 ;
}
}
if ( ! cg_fd ) {
fprintf ( stderr , " %s requires cgroup option: --cgroup <path> \n " ,
argv [ 0 ] ) ;
return - 1 ;
}
2018-01-22 10:37:11 -08:00
if ( setrlimit ( RLIMIT_MEMLOCK , & r ) ) {
perror ( " setrlimit(RLIMIT_MEMLOCK) " ) ;
return 1 ;
}
2017-08-15 22:33:32 -07:00
snprintf ( filename , sizeof ( filename ) , " %s_kern.o " , argv [ 0 ] ) ;
running = 1 ;
/* catch SIGINT */
signal ( SIGINT , running_handler ) ;
if ( load_bpf_file ( filename ) ) {
fprintf ( stderr , " load_bpf_file: (%s) %s \n " ,
filename , strerror ( errno ) ) ;
return 1 ;
}
2018-03-18 12:57:41 -07:00
/* If base test skip BPF setup */
2018-03-18 12:57:46 -07:00
if ( test = = BASE | | test = = BASE_SENDPAGE )
2018-03-18 12:57:41 -07:00
goto run ;
2017-08-15 22:33:32 -07:00
/* Attach programs to sockmap */
2017-08-28 07:10:04 -07:00
err = bpf_prog_attach ( prog_fd [ 0 ] , map_fd [ 0 ] ,
BPF_SK_SKB_STREAM_PARSER , 0 ) ;
if ( err ) {
fprintf ( stderr , " ERROR: bpf_prog_attach (sockmap): %d (%s) \n " ,
err , strerror ( errno ) ) ;
return err ;
}
err = bpf_prog_attach ( prog_fd [ 1 ] , map_fd [ 0 ] ,
BPF_SK_SKB_STREAM_VERDICT , 0 ) ;
2017-08-15 22:33:32 -07:00
if ( err ) {
fprintf ( stderr , " ERROR: bpf_prog_attach (sockmap): %d (%s) \n " ,
err , strerror ( errno ) ) ;
return err ;
}
/* Attach to cgroups */
err = bpf_prog_attach ( prog_fd [ 2 ] , cg_fd , BPF_CGROUP_SOCK_OPS , 0 ) ;
if ( err ) {
fprintf ( stderr , " ERROR: bpf_prog_attach (groups): %d (%s) \n " ,
err , strerror ( errno ) ) ;
return err ;
}
2018-01-22 10:36:36 -08:00
run :
2018-01-22 10:35:27 -08:00
err = sockmap_init_sockets ( ) ;
2017-08-15 22:33:32 -07:00
if ( err ) {
fprintf ( stderr , " ERROR: test socket failed: %d \n " , err ) ;
2018-01-22 10:35:27 -08:00
goto out ;
2017-08-15 22:33:32 -07:00
}
2018-01-22 10:35:27 -08:00
2018-03-18 12:57:41 -07:00
/* Attach txmsg program to sockmap */
if ( txmsg_pass )
tx_prog_fd = prog_fd [ 3 ] ;
else if ( txmsg_noisy )
tx_prog_fd = prog_fd [ 4 ] ;
else if ( txmsg_redir )
tx_prog_fd = prog_fd [ 5 ] ;
else if ( txmsg_redir_noisy )
tx_prog_fd = prog_fd [ 6 ] ;
2018-03-18 12:58:07 -07:00
else if ( txmsg_drop )
tx_prog_fd = prog_fd [ 9 ] ;
/* apply and cork must be last */
2018-03-18 12:57:56 -07:00
else if ( txmsg_apply )
tx_prog_fd = prog_fd [ 7 ] ;
2018-03-18 12:58:02 -07:00
else if ( txmsg_cork )
tx_prog_fd = prog_fd [ 8 ] ;
2018-03-18 12:57:41 -07:00
else
tx_prog_fd = 0 ;
if ( tx_prog_fd ) {
int redir_fd , i = 0 ;
err = bpf_prog_attach ( tx_prog_fd ,
map_fd [ 1 ] , BPF_SK_MSG_VERDICT , 0 ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_prog_attach (txmsg): %d (%s) \n " ,
err , strerror ( errno ) ) ;
return err ;
}
err = bpf_map_update_elem ( map_fd [ 1 ] , & i , & c1 , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (txmsg): %d (%s \n " ,
err , strerror ( errno ) ) ;
return err ;
}
2018-03-18 12:58:07 -07:00
2018-03-18 12:57:46 -07:00
if ( txmsg_redir | | txmsg_redir_noisy )
2018-03-18 12:57:41 -07:00
redir_fd = c2 ;
else
redir_fd = c1 ;
err = bpf_map_update_elem ( map_fd [ 2 ] , & i , & redir_fd , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (txmsg): %d (%s \n " ,
err , strerror ( errno ) ) ;
return err ;
}
2018-03-18 12:57:56 -07:00
if ( txmsg_apply ) {
err = bpf_map_update_elem ( map_fd [ 3 ] ,
& i , & txmsg_apply , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (apply_bytes): %d (%s \n " ,
err , strerror ( errno ) ) ;
return err ;
}
}
2018-03-18 12:58:02 -07:00
if ( txmsg_cork ) {
err = bpf_map_update_elem ( map_fd [ 4 ] ,
& i , & txmsg_cork , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (cork_bytes): %d (%s \n " ,
err , strerror ( errno ) ) ;
return err ;
}
}
2018-03-18 12:58:12 -07:00
if ( txmsg_start ) {
err = bpf_map_update_elem ( map_fd [ 5 ] ,
& i , & txmsg_start , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (txmsg_start): %d (%s) \n " ,
err , strerror ( errno ) ) ;
return err ;
}
}
if ( txmsg_end ) {
i = 1 ;
err = bpf_map_update_elem ( map_fd [ 5 ] ,
& i , & txmsg_end , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (txmsg_end): %d (%s) \n " ,
err , strerror ( errno ) ) ;
return err ;
}
}
2018-03-28 12:49:20 -07:00
if ( txmsg_ingress ) {
int in = BPF_F_INGRESS ;
i = 0 ;
err = bpf_map_update_elem ( map_fd [ 6 ] , & i , & in , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (txmsg_ingress): %d (%s) \n " ,
err , strerror ( errno ) ) ;
}
i = 1 ;
err = bpf_map_update_elem ( map_fd [ 1 ] , & i , & p1 , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (p1 txmsg): %d (%s) \n " ,
err , strerror ( errno ) ) ;
}
err = bpf_map_update_elem ( map_fd [ 2 ] , & i , & p1 , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (p1 redir): %d (%s) \n " ,
err , strerror ( errno ) ) ;
}
i = 2 ;
err = bpf_map_update_elem ( map_fd [ 2 ] , & i , & p2 , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (p2 txmsg): %d (%s) \n " ,
err , strerror ( errno ) ) ;
}
}
2018-03-28 12:49:30 -07:00
if ( txmsg_skb ) {
int skb_fd = ( test = = SENDMSG | | test = = SENDPAGE ) ? p2 : p1 ;
int ingress = BPF_F_INGRESS ;
i = 0 ;
err = bpf_map_update_elem ( map_fd [ 7 ] , & i , & ingress , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (txmsg_ingress): %d (%s) \n " ,
err , strerror ( errno ) ) ;
}
i = 3 ;
err = bpf_map_update_elem ( map_fd [ 0 ] , & i , & skb_fd , BPF_ANY ) ;
if ( err ) {
fprintf ( stderr ,
" ERROR: bpf_map_update_elem (c1 sockmap): %d (%s) \n " ,
err , strerror ( errno ) ) ;
}
}
2018-03-18 12:57:41 -07:00
}
2018-03-18 12:58:07 -07:00
if ( txmsg_drop )
options . drop_expected = true ;
2018-01-22 10:35:45 -08:00
if ( test = = PING_PONG )
2018-03-18 12:57:51 -07:00
err = forever_ping_pong ( rate , & options ) ;
else if ( test = = SENDMSG ) {
options . base = false ;
options . sendpage = false ;
err = sendmsg_test ( iov_count , length , rate , & options ) ;
} else if ( test = = SENDPAGE ) {
options . base = false ;
options . sendpage = true ;
err = sendmsg_test ( iov_count , length , rate , & options ) ;
} else if ( test = = BASE ) {
options . base = true ;
options . sendpage = false ;
err = sendmsg_test ( iov_count , length , rate , & options ) ;
} else if ( test = = BASE_SENDPAGE ) {
options . base = true ;
options . sendpage = true ;
err = sendmsg_test ( iov_count , length , rate , & options ) ;
} else
2018-01-22 10:35:45 -08:00
fprintf ( stderr , " unknown test \n " ) ;
2018-01-22 10:35:27 -08:00
out :
2018-02-13 13:44:22 +09:00
bpf_prog_detach2 ( prog_fd [ 2 ] , cg_fd , BPF_CGROUP_SOCK_OPS ) ;
2018-01-22 10:35:27 -08:00
close ( s1 ) ;
close ( s2 ) ;
close ( p1 ) ;
close ( p2 ) ;
close ( c1 ) ;
close ( c2 ) ;
close ( cg_fd ) ;
return err ;
2017-08-15 22:33:32 -07:00
}
void running_handler ( int a )
{
running = 0 ;
}