From: Radek Matejka Date: Wed, 8 Aug 2012 16:17:00 +0000 (+0200) Subject: new options for cegwbench, first version of cesend X-Git-Url: http://rtime.felk.cvut.cz/gitweb/can-eth-gw.git/commitdiff_plain/92859ddf64775de6efb2329989289fd0223d871c new options for cegwbench, first version of cesend Cegwbench has now options to specify interfaces(can,udp) on which the benchmark will be performed. The benchmark can be done in either (can->eth,eth->can) direction. The benchmark modes are being introduced (oneattime). Cesend is a fork od cansend from Socket-CAN project. It sends a canframe over udp. --- diff --git a/utils/cegwbench/Makefile b/utils/cegwbench/Makefile index 68ca59e..07a7030 100644 --- a/utils/cegwbench/Makefile +++ b/utils/cegwbench/Makefile @@ -1,3 +1,3 @@ all: - gcc -Wall -ocegwbench -lrt cegwbench.c + gcc -Wall -ocegwbench -I../common/include -lrt cegwbench.c ../common/readif.c ../common/cegwerr.c diff --git a/utils/cegwbench/cegwbench.c b/utils/cegwbench/cegwbench.c index dba10dd..7a93708 100644 --- a/utils/cegwbench/cegwbench.c +++ b/utils/cegwbench/cegwbench.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -10,56 +11,68 @@ #include #include #include +#include "cegwerr.h" +#include "readif.h" /** * ToDo: * [ ] consider can timestamp */ +#define BENCH_FLAG_N 1 +#define BENCH_FLAG_SRC 2 +#define BENCH_FLAG_DST 4 + +enum { + BENCH_MODE_UNDEF, + BENCH_MODE_ASAP, + BENCH_MODE_ONEATTIME, + BENCH_MODE_PERIOD +}; + +int can_sock_create( int ifindex ); +int udp_sock_create( struct in_addr ip, unsigned int port ); + struct optdata { + int optflag; + int mode; int n; + struct cegw_if ceif[2]; /* 0 -> src, 1 -> dst */ }; struct optdata d; pthread_barrier_t barrier; struct timespec* tx_time, * rx_time; -void* udp_recv( void* arg ) +void* thr_recv( void* arg ) { - struct sockaddr_in sa_udp; - int udp_sock; + int sock; struct can_frame cf; int i; - sa_udp.sin_family = AF_INET; - sa_udp.sin_port = htons( 10502 ); - inet_aton( "127.0.0.1", &sa_udp.sin_addr ); - - udp_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); - if( udp_sock < 0 ) - { - fprintf( stderr, "error: udp socket creation failed\n" ); + if( d.ceif[1].type == IF_CAN ) + sock = can_sock_create( d.ceif[1].can.ifindex ); + else + sock = udp_sock_create( d.ceif[1].eth.ip, d.ceif[1].eth.port ); + if( sock == -1 ) return NULL; - } - - if( bind(udp_sock, (struct sockaddr*)&sa_udp, sizeof(sa_udp)) != 0 ) - { - fprintf( stderr, "error: udp socket binding failed\n" ); - return NULL; - } pthread_barrier_wait( &barrier ); /* recv */ for( i=0; itv_nsec - x->tv_nsec) / 1000000000 + 1; y->tv_nsec -= 1000000000 * nsec; y->tv_sec += nsec; - } - if (x->tv_nsec - y->tv_nsec > 1000000000) { + } + if (x->tv_nsec - y->tv_nsec > 1000000000) + { int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000; y->tv_nsec += 1000000000 * nsec; y->tv_sec -= nsec; @@ -87,26 +101,126 @@ int timespec_subtract( struct timespec *result, struct timespec *x, struct times return x->tv_sec < y->tv_sec; } +int udp_sock_create( struct in_addr ip, unsigned int port ) +{ + struct sockaddr_in addr_udp; + int udp_sock; + + addr_udp.sin_family = AF_INET; + addr_udp.sin_port = htons( port ); + addr_udp.sin_addr.s_addr = INADDR_ANY; + + udp_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if( udp_sock < 0 ) + { + fprintf( stderr, "error: udp socket creation failed\n" ); + return -1; + } + + if( bind(udp_sock, (struct sockaddr*)&addr_udp, sizeof(addr_udp)) != 0 ) + { + fprintf( stderr, "error: udp socket binding failed\n" ); + return -1; + } + + return udp_sock; +} + +/** + * can_sock_create() + * @return can socket fd, or -1 on failure + */ +int can_sock_create( int ifindex ) +{ + int can_sock; + struct sockaddr_can addr_can; + + addr_can.can_family = AF_CAN; + addr_can.can_ifindex = ifindex; + + can_sock = socket( PF_CAN, SOCK_RAW, CAN_RAW ); + if( can_sock < 0 ) + { + fprintf( stderr, "error: can socket creation failed\n" ); + return -1; + } + + if( bind(can_sock, (struct sockaddr*)&addr_can, sizeof(addr_can)) != 0 ) + { + fprintf( stderr, "error: can socket binding failed\n" ); + return -1; + } + + return can_sock; +} + +int read_mode( char* in ) +{ + if( strcmp( in, "asap" ) == 0 ) + return BENCH_MODE_ASAP; + if( strcmp( in, "oneattime") == 0 ) + return BENCH_MODE_ONEATTIME; + if( strcmp( in, "period" ) == 0 ) + return BENCH_MODE_PERIOD; + + return BENCH_MODE_UNDEF; +} + int main( int argc, char* argv[] ) { char opt; - struct sockaddr_can sa_can; struct can_frame cf; - int can_sock; + int sock; pthread_t thr; float mean = 0; float time; + int ret; struct timespec res; - int i; + int i, tmp; + + d.optflag = 0; + d.mode = BENCH_MODE_ASAP; + + struct option longopt[] = + { + { "mode", 0, NULL, 'm' }, + { 0, 0, 0, 0 } + }; while( 1 ) { - opt = getopt( argc, argv, "n:" ); + opt = getopt( argc, argv, "s:d:m:n:" ); if( opt == -1 ) break; switch( opt ) { + case 's': + d.optflag |= BENCH_FLAG_SRC; + if( read_if( optarg, &d.ceif[0] ) != 0 ) + { + perr( "'-s'" ); + return -1; + } + break; + case 'd': + d.optflag |= BENCH_FLAG_DST; + if( read_if( optarg, &d.ceif[1] ) ) + { + perr( "-d" ); + return -1; + } + break; + case 'm': + tmp = read_mode( optarg ); + if( tmp == BENCH_MODE_UNDEF ) + { + perr( "benchmarking mode unknown" ); + return -1; + } + d.mode = tmp; + break; case 'n': + d.optflag |= BENCH_FLAG_N; d.n = atoi( optarg ); break; case '?': @@ -115,64 +229,109 @@ int main( int argc, char* argv[] ) } } + /* check opt */ + /* '-n' option is mandatory and its value is >0 */ + if( d.optflag & BENCH_FLAG_N ) + { + if( d.n < 1 ) + { + perr( "'-n' should be at least 1." ); + } + } else + { + perr( "'-n' is mandatory." ); + return -1; + } + + /* '-s' and '-d' should be defined and different iftype */ + if( (d.optflag & BENCH_FLAG_SRC) && (d.optflag & BENCH_FLAG_DST) ) + { + if( !(d.ceif[0].type == IF_CAN && d.ceif[1].type == IF_ETH_UDP) && + !(d.ceif[0].type == IF_ETH_UDP && d.ceif[1].type == IF_CAN ) ) + { + perr( "'-s' and '-d' should be different interface type." ); + return -1; + } + } else + { + perr( "'-s' and '-d' should be defined." ); + return -1; + } + tx_time = malloc( d.n*sizeof(struct timespec) ); rx_time = malloc( d.n*sizeof(struct timespec) ); pthread_barrier_init( &barrier, NULL, 2 ); - pthread_create( &thr, NULL, udp_recv, NULL ); + pthread_create( &thr, NULL, thr_recv, NULL ); - sa_can.can_family = AF_CAN; - sa_can.can_ifindex = if_nametoindex( "vcan0" ); - - can_sock = socket( PF_CAN, SOCK_RAW, CAN_RAW ); - if( can_sock < 0 ) + /**/ + struct sockaddr* addr; + struct sockaddr_can addr_can; + struct sockaddr_in addr_udp; + int addr_size; + + if( d.ceif[0].type == IF_CAN ) { - fprintf( stderr, "error: can socket creation failed\n" ); - return -1; + sock = socket( PF_CAN, SOCK_RAW, CAN_RAW ); + addr_can.can_family = AF_CAN; + addr_can.can_ifindex = d.ceif[0].can.ifindex; + addr = (struct sockaddr*)&addr_can; + addr_size = sizeof( addr_can ); } - - if( bind(can_sock, (struct sockaddr*)&sa_can, sizeof(sa_can)) != 0 ) + else { - fprintf( stderr, "error: can socket binding failed\n" ); - return -1; + sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); + addr_udp.sin_family = AF_INET; + addr_udp.sin_addr = d.ceif[0].eth.ip; + addr_udp.sin_port = htons( d.ceif[0].eth.port ); + addr = (struct sockaddr*)&addr_udp; + addr_size = sizeof( addr_udp ); } + if( sock < 0 ) /* ToDo: ? */ + return -1; pthread_barrier_wait( &barrier ); /* send */ for( i=0; i +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define CANID_DELIM '#' +#define DATA_SEPERATOR '.' + +unsigned char asc2nibble(char c) { + + if ((c >= '0') && (c <= '9')) + return c - '0'; + + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + + return 16; /* error */ +} + +int parse_canframe(char *cs, struct can_frame *cf) { + /* documentation see lib.h */ + + int i, idx, dlc, len; + unsigned char tmp; + + len = strlen(cs); + //printf("'%s' len %d\n", cs, len); + + memset(cf, 0, sizeof(*cf)); /* init CAN frame, e.g. DLC = 0 */ + + if (len < 4) + return 1; + + if (cs[3] == CANID_DELIM) { /* 3 digits */ + + idx = 4; + for (i=0; i<3; i++){ + if ((tmp = asc2nibble(cs[i])) > 0x0F) + return 1; + cf->can_id |= (tmp << (2-i)*4); + } + + } else if (cs[8] == CANID_DELIM) { /* 8 digits */ + + idx = 9; + for (i=0; i<8; i++){ + if ((tmp = asc2nibble(cs[i])) > 0x0F) + return 1; + cf->can_id |= (tmp << (7-i)*4); + } + if (!(cf->can_id & CAN_ERR_FLAG)) /* 8 digits but no errorframe? */ + cf->can_id |= CAN_EFF_FLAG; /* then it is an extended frame */ + + } else + return 1; + + if((cs[idx] == 'R') || (cs[idx] == 'r')){ /* RTR frame */ + cf->can_id |= CAN_RTR_FLAG; + return 0; + } + + for (i=0, dlc=0; i<8; i++){ + + if(cs[idx] == DATA_SEPERATOR) /* skip (optional) seperator */ + idx++; + + if(idx >= len) /* end of string => end of data */ + break; + + if ((tmp = asc2nibble(cs[idx++])) > 0x0F) + return 1; + cf->data[i] = (tmp << 4); + if ((tmp = asc2nibble(cs[idx++])) > 0x0F) + return 1; + cf->data[i] |= tmp; + dlc++; + } + + cf->can_dlc = dlc; + + return 0; +} + +int main(int argc, char **argv) +{ + int s; /* can raw socket */ + int nbytes; + struct sockaddr_in addr; + struct can_frame frame; + + /* check command line options */ + if (argc != 2) { + fprintf(stderr, "Usage: %s .\n", argv[0]); + return 1; + } + + /* parse CAN frame */ + if (parse_canframe(argv[1], &frame)){ + fprintf(stderr, "\nWrong CAN-frame format!\n\n"); + fprintf(stderr, "Try: #{R|data}\n"); + fprintf(stderr, "can_id can have 3 (SFF) or 8 (EFF) hex chars\n"); + fprintf(stderr, "data has 0 to 8 hex-values that can (optionally)"); + fprintf(stderr, " be seperated by '.'\n\n"); + fprintf(stderr, "e.g. 5A1#11.2233.44556677.88 / 123#DEADBEEF / "); + fprintf(stderr, "5AA# /\n 1F334455#1122334455667788 / 123#R "); + fprintf(stderr, "for remote transmission request.\n\n"); + return 1; + } + + /* open socket */ + if(( s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) { + perror("socket"); + return 1; + } + + addr.sin_family = AF_INET; + inet_aton( "127.0.0.1", &addr.sin_addr ); + addr.sin_port = htons( 10501 ); + + /* send frame */ + if ((nbytes = sendto(s, &frame, sizeof(frame), 0, (struct sockaddr*)&addr, sizeof(addr))) != sizeof(frame)) { + perror("write"); + return 1; + } + + close(s); + + return 0; +} + diff --git a/utils/common/cegwerr.c b/utils/common/cegwerr.c new file mode 100644 index 0000000..0a67978 --- /dev/null +++ b/utils/common/cegwerr.c @@ -0,0 +1,36 @@ +#include +#include "cegwerr.h" + +unsigned int cegw_errno = 0; + +char* cegw_errlist[] = +{ + [ CEGW_ERR_UNKNOWN ] = "", + [ CEGW_ERR_IF_UNSPEC ] = "source or destination not specified", + [ CEGW_ERR_IF_SAME ] = "source and destination have same interface type", + [ CEGW_ERR_IF_TYPE ] = "unknown interface type", + [ CEGW_ERR_IF_CAN ] = "invalid can interface", + [ CEGW_ERR_IF_ETH ] = "invalid eth interface", + [ CEGW_ERR_COLON ] = "expected ':' (:)", + [ CEGW_ERR_ATON ] = "ip address mismatch", + [ CEGW_ERR_PORT ] = "port number" +}; + +void perr( char* s ) +{ + if( s ) + { + if( cegw_errno == 0 ) + { + fprintf( stderr, "error: %s\n", s ); + + } else + { + fprintf( stderr, "error: %s, %s\n", s, + cegw_errlist[ cegw_errno ] ); + } + return; + } + + fprintf( stderr, "error: %s\n", cegw_errlist[ cegw_errno ] ); +} diff --git a/utils/common/include/cegwerr.h b/utils/common/include/cegwerr.h new file mode 100644 index 0000000..99efc0e --- /dev/null +++ b/utils/common/include/cegwerr.h @@ -0,0 +1,22 @@ +#ifndef CEGWERR_H +#define CEGWERR_H + +extern unsigned int cegw_errno; +extern char* cegw_errlist[]; + +enum +{ + CEGW_ERR_UNKNOWN, + CEGW_ERR_IF_UNSPEC, + CEGW_ERR_IF_SAME, + CEGW_ERR_IF_TYPE, + CEGW_ERR_IF_CAN, + CEGW_ERR_IF_ETH, + CEGW_ERR_COLON, + CEGW_ERR_ATON, + CEGW_ERR_PORT +}; + +void perr( char* s ); + +#endif /* CEGWERR_H */ diff --git a/utils/common/include/readif.h b/utils/common/include/readif.h new file mode 100644 index 0000000..c3f9dce --- /dev/null +++ b/utils/common/include/readif.h @@ -0,0 +1,31 @@ +#ifndef READIF_H +#define READIF_H + +struct in_addr; + +enum +{ + IF_UNDEF, + IF_CAN, + IF_ETH_UDP +}; + +struct cegw_if +{ + int type; + union { + struct { + struct in_addr ip; + unsigned short port; + } eth; + struct { + int ifindex; + } can; + }; +}; + +int read_addrport( char* in, struct in_addr* addr, unsigned short* port ); +char* read_iftype( char* in, int* iftype ); +int read_if( char* in, struct cegw_if* ceif ); + +#endif /* READIF_H */ diff --git a/utils/common/readif.c b/utils/common/readif.c new file mode 100644 index 0000000..5ae5439 --- /dev/null +++ b/utils/common/readif.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include +#include "cegwerr.h" +#include "readif.h" + +/** + * read_addrport - parses @in for eth address. + * Valid input is e.g. udp@127.0.0.1:10502 or can@vcan0. + * + * @param[in] in string to search in + * @param[out] addr ip address + * @param[out] port transport layer port + * @return 0 on success, -1 otherwise + */ +int read_addrport( char* in, struct in_addr* addr, unsigned short* port ) +{ + char* delim = NULL; + char addrstr[16]; + int addrlen; + + if( (delim = strchr( in, ':' )) == NULL ) + { + cegw_errno = CEGW_ERR_COLON; + return -1; + } + + /* get address */ + addrlen = delim - in; + memcpy( addrstr, in, addrlen ); + addrstr[addrlen] = '\0'; + if( inet_aton( addrstr, addr ) == 0 ) + { + cegw_errno = CEGW_ERR_ATON; + return -1; + } + + /* get port */ + if( sscanf( delim, ":%hu", port ) != 1 ) /* ToDo: handle overflow */ + { + cegw_errno = CEGW_ERR_PORT; + return -1; + } + + return 0; +} + +/** + * read_iftype - reads @in for iftype + * Iftype type is e.g. "can@" or "udp@". + * + * @param[in] in string to search in + * @param[out] iftype iftype detected + * @return pointer to @in after iftype on success, NULL otherwise + */ +char* read_iftype( char* in, int* iftype ) +{ + char* ret = in+4; + + if( strncmp( "udp@", optarg, 4 ) == 0 ) + { + *iftype = IF_ETH_UDP; + return ret; + } + /* + if( strncmp( "tcp@", optarg, 4 ) == 0 ) + { + return NULL; + } + */ + if( strncmp( "can@", optarg, 4 ) == 0 ) + { + *iftype = IF_CAN; + return ret; + } + + cegw_errno = CEGW_ERR_IF_TYPE; + return NULL; +} + +/** + * read_if - reads interface from @in + * Function analyzes @in for interface specification in format + * @:, where is can or udp, is address in dotted + * format. + * + * @param[in] in string to search in + * @param[out] iftype interface type (IF_CAN or IF_ETH_UDP) + * @param[out] d ip and port is stored to @d + */ + +/* ToDo: generalize +int read_if( char* in, int* iftype, struct cegw_data* d ) +{ + char* optstr = NULL; + + if( (optstr = read_iftype( in, iftype )) == NULL ) + { + return -1; + } + + switch( *iftype ) + { + case IF_CAN: + d->can_ifidx = if_nametoindex( optstr ); + if( d->can_ifidx == 0 ) + { + cegw_errno = CEGW_ERR_IF_CAN; + return -1; + } + break; + case IF_ETH_UDP: + if( read_addrport( optstr, &d->eth_addr, &d->eth_port ) != 0 ) + { + return -1; + } + break; + default: + return -1; + break; + } + + return 0; +} +*/ + +int read_if( char* in, struct cegw_if* ceif ) +{ + char* optstr = NULL; + + if( (optstr = read_iftype( in, &ceif->type )) == NULL ) + { + return -1; + } + + switch( ceif->type ) + { + case IF_CAN: + ceif->can.ifindex = if_nametoindex( optstr ); + if( ceif->can.ifindex == 0 ) + { + cegw_errno = CEGW_ERR_IF_CAN; + return -1; + } + break; + case IF_ETH_UDP: + if( read_addrport( optstr, &ceif->eth.ip, &ceif->eth.port ) != 0 ) + { + return -1; + } + break; + default: + return -1; + break; + } + + return 0; +}