]> rtime.felk.cvut.cz Git - can-eth-gw.git/commitdiff
netdevice_notifier implemented; test script; cegwbench rxtimeo
authorRadek Matejka <radek.matejka@gmail.com>
Wed, 15 Aug 2012 14:58:24 +0000 (16:58 +0200)
committerRadek Matejka <radek.matejka@gmail.com>
Wed, 15 Aug 2012 14:58:24 +0000 (16:58 +0200)
netdevice_notifier was implemented to avoid sending can frames to detached
devices or device index mismatch. The test script was written to test basic
functionality of canethgw. cegwbench has now ability to set timeout for
receiving function with its '-t' option.

kernel/canethgw.c
test/boot
test/test [new file with mode: 0755]
utils/cegw/cegw.c
utils/cegwbench/cegwbench.c

index 76860397bd7b3b847730ab2841f9d5c7ca56df93..1b4bb439e7be0e16b72ff34cff4196ff0bdf46f5 100644 (file)
@@ -8,7 +8,9 @@
 #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>
@@ -51,6 +53,7 @@ struct cegw_setting
 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 );
@@ -96,7 +99,7 @@ inline static void cegw_can_send( struct socket* can_sock, struct can_frame* cf,
        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 ) );
 }
 
 /** 
@@ -108,7 +111,7 @@ static int cegw_udp_can( void* data )
        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;
@@ -127,15 +130,20 @@ static int cegw_udp_can( void* data )
                        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 );
@@ -168,16 +176,20 @@ static int cegw_can_udp( void* data )
 
        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();
@@ -192,9 +204,7 @@ static int cegw_can_udp( void* data )
        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;
@@ -299,8 +309,8 @@ static int cegw_create_job( struct sk_buff* skb, struct nlmsghdr* nlh, void* arg
 
 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 )
        {
@@ -314,7 +324,7 @@ static void cegw_flush( void )
        }       
 }
 
-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 ];
@@ -394,7 +404,7 @@ cancel:
        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;
@@ -436,6 +446,41 @@ brk:
        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
@@ -566,10 +611,13 @@ static int cegw_thread_stop( void )
 
 static int __init cegw_init( void )
 {
+       notifier.notifier_call = cegw_notifier;
+       register_netdevice_notifier( &notifier );
+
        /* 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;
 }
@@ -588,6 +636,7 @@ static void __exit cegw_exit( void )
        cegw_state = CEGW_EXIT;
        mutex_unlock( &cegw_mutex );
 
+       unregister_netdevice_notifier( &notifier );
        cegw_flush();
 }
 
index 29a273b2fb8d03eb21e7092f613ef33f8264e4d6..cf19abc98a084e2e72bc8e9b56c05d3029834726 100755 (executable)
--- a/test/boot
+++ b/test/boot
@@ -1,4 +1,4 @@
 #!/usr/bin/env novaboot
-KERNEL=qemuKernel console=ttyS0,115200
+KERNEL=kernel/arch/x86/boot/bzImage console=ttyS0,115200
 ramdisk < ( ( cd ~/workdir/busybox/_install; find . | cpio -o -H newc ); ( cd $SRCDIR/myroot; find . | cpio -o --dereference -H newc ) ) | gzip
 
diff --git a/test/test b/test/test
new file mode 100755 (executable)
index 0000000..44b0ecd
--- /dev/null
+++ b/test/test
@@ -0,0 +1,19 @@
+#!/bin/sh
+evalmsg()
+{
+       if test $1 -eq 0; then
+               echo -e "\033[32mOK"
+       else
+               echo -e "\033[31mFAIL"
+       fi;
+               echo -ne "\033[0m"
+}
+
+echo -n "Testing eth->can: "
+cegwbench -s udp@127.0.0.1:10501 -d can@vcan0 -n 10 -t 1 &> /dev/zero
+evalmsg $?
+
+echo -n "Testing can->eth: "
+cegwbench -s can@vcan0 -d udp@127.0.0.1:10502 -n 10 -t 1 &> /dev/zero
+evalmsg $?
+
index dcaecc390f17b0eef9c8db30d13b537a66e6a906..c5c27b8c7d27a81084034f389032577d2293054b 100644 (file)
@@ -258,7 +258,6 @@ inline static int cegw_listen( struct cegw_nlmsg* req, struct cegw_data* d )
        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;
 }
@@ -446,12 +445,11 @@ int main( int argc, char* argv[] )
        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" );
@@ -459,33 +457,38 @@ int main( int argc, char* argv[] )
        }
        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) )
                        {
@@ -511,19 +514,8 @@ int main( int argc, char* argv[] )
 
                        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;
 }
 
index 00663d0db7bed9796dd499bde0bea95c6452cdf2..c1438c40ab858a7e25c89e24fb1666982d92ea51 100644 (file)
 #include "cegwerr.h"
 #include "readif.h"
 
-/**
- * ToDo:
- * [ ] consider can timestamp
- */
-
 //#define BENCH_DEBUG
 #ifdef BENCH_DEBUG
 #define printdbg(...) printf( __VA_ARGS__ )
@@ -29,6 +24,7 @@
 #define BENCH_FLAG_N 1
 #define BENCH_FLAG_SRC 2
 #define BENCH_FLAG_DST 4
+#define BENCH_FLAG_TIMEO 8
 
 enum {
        BENCH_MODE_UNDEF,
@@ -45,6 +41,7 @@ struct optdata
        int optflag;
        int mode;
        int n;
+       struct timeval timeo;
        struct cegw_if ceif[2]; /* 0 -> src, 1 -> dst */
 };
 
@@ -58,6 +55,7 @@ void* thr_recv( void* arg )
        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 );
@@ -65,16 +63,26 @@ void* thr_recv( void* arg )
                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 );
@@ -85,7 +93,7 @@ void* thr_recv( void* arg )
        }
 
        close( sock );
-       return NULL;
+       return (void*)0;
 }
 
 int timespec_subtract( struct timespec *result, struct timespec *x, struct timespec *yy )
@@ -139,7 +147,7 @@ int udp_sock_create( struct in_addr ip, unsigned int port  )
 }
 
 /**
- * can_sock_create()
+ * can_sock_create
  * @return can socket fd, or -1 on failure
  */
 int can_sock_create( int ifindex )
@@ -197,12 +205,15 @@ int main( int argc, char* argv[] )
        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[] =
        {
@@ -212,7 +223,7 @@ int main( int argc, char* argv[] )
 
        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 )
@@ -246,6 +257,15 @@ int main( int argc, char* argv[] )
                                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;
@@ -285,7 +305,7 @@ int main( int argc, char* argv[] )
        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;
@@ -332,7 +352,12 @@ int main( int argc, char* argv[] )
        }
 
        close( sock ); /* ToDo: shutdown? */
-       pthread_join( thr, NULL );
+       pthread_join( thr, &thr_ret );
+
+       if( thr_ret != NULL )
+       {
+               return 1;
+       }
 
        /* results */
        dump_arg( argc, argv );