#include <linux/wait.h>
#include <linux/netdevice.h>
#include <linux/socket.h>
+#include <linux/if_arp.h>
#include <linux/net.h>
+#include <linux/netdevice.h>
#include <linux/can/core.h>
#include <linux/can.h>
#include <net/rtnetlink.h>
static int cegw_state = CEGW_STOP;
static struct socket* can_sock = NULL, * udp_sock = NULL;
static struct task_struct* eth_to_can = NULL, * can_to_eth = NULL;
+static struct notifier_block notifier;
HLIST_HEAD( cegw_rule_can_eth );
HLIST_HEAD( cegw_rule_eth_can );
vec.iov_base = cf;
vec.iov_len = sizeof( *cf );
- kernel_sendmsg( can_sock, &mh, &vec, 1, sizeof( *cf ) ); /* ToDo: handle error */
+ kernel_sendmsg( can_sock, &mh, &vec, 1, sizeof( *cf ) );
}
/**
struct can_frame cf;
struct kvec vec;
struct msghdr mh;
- struct cegw_rule* job;
+ struct cegw_rule* rule;
struct hlist_node* pos;
int can_ifidx;
int recv_size;
break;
vec.iov_base = &cf;
vec.iov_len = sizeof(cf);
- recv_size = kernel_recvmsg( udp_sock, &mh, &vec, 1, sizeof(cf), 0 ); /* ToDo: handle error, size check */
- if( recv_size == 0 )
+ recv_size = kernel_recvmsg( udp_sock, &mh, &vec, 1, sizeof(cf), 0 );
+ /* recv_size == 0 when shutting down */
+ if( recv_size != sizeof(cf) || recv_size == 0 )
{
continue;
+ } else if( recv_size < 0 )
+ {
+ return -1;
}
- hlist_for_each_entry_rcu( job, pos, &cegw_rule_eth_can, list )
+
+ hlist_for_each_entry_rcu( rule, pos, &cegw_rule_eth_can, list )
{
- rcu_read_lock(); /**/
- can_ifidx = job->can_ifindex;
+ rcu_read_lock();
+ can_ifidx = rule->can_ifindex;
rcu_read_unlock();
/* ToDo: from filter */
cegw_can_send( can_sock, &cf, can_ifidx );
while( 1 )
{
- if( cegw_state == CEGW_STOP ) /**/
+ if( cegw_state == CEGW_STOP )
break;
vec.iov_base = &cf;
vec.iov_len = sizeof( cf );
recv_size = kernel_recvmsg( can_sock, &mh, &vec, 1, sizeof( cf ), 0 );
- if( recv_size == 0 )
+ if( recv_size != sizeof(cf) || recv_size == 0 )
{
continue;
+ } else if( recv_size < 0 )
+ {
+ return -1;
}
+
hlist_for_each_entry_rcu( rule, pos, &cegw_rule_can_eth, list )
{
rcu_read_lock();
return 0;
}
-/* NetLink */
-
-static int cegw_create_job( struct sk_buff* skb, struct nlmsghdr* nlh, void* arg )
+static int cegw_newroute( struct sk_buff* skb, struct nlmsghdr* nlh, void* arg )
{
struct nlattr* tb[ CEGW_MAX+1 ];
struct cegw_rule* rule = NULL;
static void cegw_flush( void )
{
- struct hlist_node* pos,* n;
struct cegw_rule* rule;
+ struct hlist_node* pos,* n;
hlist_for_each_entry_safe( rule, pos, n, &cegw_rule_can_eth, list )
{
}
}
-static int cegw_remove_job( struct sk_buff* skb, struct nlmsghdr* nlh, void* arg )
+static int cegw_delroute( struct sk_buff* skb, struct nlmsghdr* nlh, void* arg )
{
struct rtmsg* r;
struct nlattr* tb[ CEGW_MAX+1 ];
return -EMSGSIZE;
}
-static int cegw_dump_job( struct sk_buff* skb, struct netlink_callback* cb )
+static int cegw_getroute( struct sk_buff* skb, struct netlink_callback* cb )
{
struct cegw_rule* rule;
struct hlist_node* pos;
return skb->len;
}
+static int cegw_notifier( struct notifier_block* nb, unsigned long msg, void* data )
+{
+ struct net_device* dev = (struct net_device*)data;
+ struct cegw_rule* rule;
+ struct hlist_node* pos,* n;
+
+ if( !net_eq(dev_net(dev), &init_net) )
+ return NOTIFY_DONE;
+ if( dev->type != ARPHRD_CAN )
+ return NOTIFY_DONE;
+
+ if( msg == NETDEV_UNREGISTER )
+ {
+ hlist_for_each_entry_safe( rule, pos, n, &cegw_rule_eth_can, list )
+ {
+ if( rule->can_ifindex == dev->ifindex )
+ {
+ hlist_del( &rule->list );
+ kfree( rule );
+ }
+ }
+
+ hlist_for_each_entry_safe( rule, pos, n, &cegw_rule_can_eth, list )
+ {
+ if( rule->can_ifindex == dev->ifindex )
+ {
+ hlist_del( &rule->list );
+ kfree( rule );
+ }
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
/**
* cegw_thread_start - start working threads
* Two threads are started. One is serving udp->can routing and the other
static int __init cegw_init( void )
{
+ notifier.notifier_call = cegw_notifier;
+ register_netdevice_notifier( ¬ifier );
+
/* subscribe to netlink */
- rtnl_register( PF_CAN, RTM_GETROUTE, NULL, cegw_dump_job, NULL );
- rtnl_register( PF_CAN, RTM_NEWROUTE, cegw_create_job, NULL, NULL );
- rtnl_register( PF_CAN, RTM_DELROUTE, cegw_remove_job, NULL, NULL );
+ rtnl_register( PF_CAN, RTM_GETROUTE, NULL, cegw_getroute, NULL );
+ rtnl_register( PF_CAN, RTM_NEWROUTE, cegw_newroute, NULL, NULL );
+ rtnl_register( PF_CAN, RTM_DELROUTE, cegw_delroute, NULL, NULL );
return 0;
}
cegw_state = CEGW_EXIT;
mutex_unlock( &cegw_mutex );
+ unregister_netdevice_notifier( ¬ifier );
cegw_flush();
}
addattr32( &req->nh, sizeof(*req), CEGW_CMD_INFO, CEGW_LISTEN );
addattr_l( &req->nh, sizeof(*req), CEGW_ETH_IP, &d->eth_listen_addr, sizeof(d->eth_listen_addr) );
addattr_l( &req->nh, sizeof(*req), CEGW_ETH_PORT, &d->eth_listen_port, sizeof(d->eth_listen_port) );
- printf( "listen at: %x, %hu\n", d->eth_listen_addr.s_addr, d->eth_listen_port );
return 0;
}
if( err < 0 )
{
perror( "netlink sendto" );
- return err;
+ return -1;
}
/* recv */
rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
- printf( "recv size=%d\n", rsize );
if( rsize < 0 )
{
perr( "recv" );
}
nlh = (struct nlmsghdr*)rxbuf;
- if( cmd & CEGW_CMD_LIST )
+ if( nlh->nlmsg_type == NLMSG_ERROR )
{
- printf( "recv nlmsg_type=%d\n", nlh->nlmsg_type );
- if( nlh->nlmsg_type == NLMSG_ERROR )
+ rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
+ err = rte->error;
+
+ if( err == 0 )
{
- struct nlmsgerr* nlerr = NLMSG_DATA( nlh );
- int err = nlerr->error;
- printf( "nlerror: %d,%s\n", err, strerror(abs(err)) );
+ printf( "%s\n", strerror(abs(err)) );
+ return 0;
+ } else
+ {
+ printf( "netlink error: %s\n", strerror(abs(err)) );
+ return -1;
}
+ }
+
+ if( cmd & CEGW_CMD_LIST )
+ {
/* ToDo recv while */
printf( "%10ssource%20sdestination\n", "", "" );
while( 1 )
{
if( !NLMSG_OK( nlh, rsize ) )
{
- puts( "NLMSG_OK\n" );
break;
}
if( nlh->nlmsg_type == NLMSG_DONE )
{
- puts( "NLMSG_DONE" );
break;
}
/* ToDo: NLMSG_ERR */
-
rta = NLMSG_DATA( nlh );
- //rta = (struct rtattr*)( ((char *)rtm) + NLMSG_ALIGN(sizeof(struct rtmsg)) );
len = NLMSG_PAYLOAD( nlh, 0 );
for( ;RTA_OK(rta, len); rta = RTA_NEXT(rta,len) )
{
nlh = NLMSG_NEXT( nlh, rsize );
}
- } else
- {
- if( nlh->nlmsg_type != NLMSG_ERROR )
- {
- fprintf( stderr, "error: unexpected netlink answer=%d\n", nlh->nlmsg_type );
- return -1;
- }
- rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
- err = rte->error;
- if( err < 0 )
- fprintf( stderr, "error: netlink(%d); %s\n", err, strerror(abs(err)) );
}
-
+
return 0;
}
#include "cegwerr.h"
#include "readif.h"
-/**
- * ToDo:
- * [ ] consider can timestamp
- */
-
//#define BENCH_DEBUG
#ifdef BENCH_DEBUG
#define printdbg(...) printf( __VA_ARGS__ )
#define BENCH_FLAG_N 1
#define BENCH_FLAG_SRC 2
#define BENCH_FLAG_DST 4
+#define BENCH_FLAG_TIMEO 8
enum {
BENCH_MODE_UNDEF,
int optflag;
int mode;
int n;
+ struct timeval timeo;
struct cegw_if ceif[2]; /* 0 -> src, 1 -> dst */
};
struct can_frame cf;
int i;
int seq;
+ int ret;
if( d.ceif[1].type == IF_CAN )
sock = can_sock_create( d.ceif[1].can.ifindex );
sock = udp_sock_create( d.ceif[1].eth.ip, d.ceif[1].eth.port );
if( sock == -1 )
{
- /* ToDo: handle */
- return NULL;
+ /* ToDo: handle */
+ return (void*)1;
}
+ ret = setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, &d.timeo, sizeof(d.timeo) );
+ if( ret != 0 )
+ return (void*)1;
+
pthread_barrier_wait( &barrier );
/* recv */
for( i=0; i<d.n; i++ )
{
- recvfrom( sock, &cf, sizeof(cf), 0, NULL, 0 );
+ ret = recvfrom( sock, &cf, sizeof(cf), 0, NULL, 0 );
+ /* ToDo: rework */
+ if( ret == -1 )
+ {
+ puts( "cegwbench: recv timed out\n" );
+ return (void*)1;
+ }
seq = *((int*)cf.data);
clock_gettime( CLOCK_REALTIME, &rx_time[ seq ] );
printdbg( "recv: (id=%d)%d\n", cf.can_id, seq );
}
close( sock );
- return NULL;
+ return (void*)0;
}
int timespec_subtract( struct timespec *result, struct timespec *x, struct timespec *yy )
}
/**
- * can_sock_create()
+ * can_sock_create
* @return can socket fd, or -1 on failure
*/
int can_sock_create( int ifindex )
int sock;
pthread_t thr;
float time;
+ void* thr_ret;
int ret;
struct timespec res;
int i, tmp;
d.optflag = 0;
d.mode = BENCH_MODE_ASAP;
+ d.timeo.tv_sec = 0;
+ d.timeo.tv_usec = 0;
struct option longopt[] =
{
while( 1 )
{
- opt = getopt( argc, argv, "s:d:m:n:" );
+ opt = getopt( argc, argv, "s:d:m:n:t:" );
if( opt == -1 )
break;
switch( opt )
d.optflag |= BENCH_FLAG_N;
d.n = atoi( optarg );
break;
+ case 't':
+ d.optflag |= BENCH_FLAG_TIMEO;
+ if( sscanf( optarg, "%ld", &d.timeo.tv_sec ) != 1 )
+ {
+ perr( "timeout mismatch" );
+ return -1;
+ }
+ d.timeo.tv_usec = 0;
+ break;
case '?':
return -1;
break;
rx_time = malloc( d.n*sizeof(struct timespec) );
pthread_barrier_init( &barrier, NULL, 2 );
- pthread_create( &thr, NULL, thr_recv, NULL );
+ pthread_create( &thr, NULL, thr_recv, &d );
/**/
struct sockaddr* addr;
}
close( sock ); /* ToDo: shutdown? */
- pthread_join( thr, NULL );
+ pthread_join( thr, &thr_ret );
+
+ if( thr_ret != NULL )
+ {
+ return 1;
+ }
/* results */
dump_arg( argc, argv );