]> rtime.felk.cvut.cz Git - can-eth-gw.git/commitdiff
canethgw micsdevice
authorRadek Matějka <radek.matejka@gmail.com>
Thu, 29 Nov 2012 00:01:10 +0000 (18:01 -0600)
committerRadek Matějka <radek.matejka@gmail.com>
Thu, 29 Nov 2012 00:01:10 +0000 (18:01 -0600)
This commit introduces the new miscdevice for canethgw. The code of
kernel module and cegw utility is modified to test open, release and
ioctl callbacks on this device.

kernel/canethgw.c
kernel/canethgw.h
utils/cegw/cegw.c

index 601ea7771bd77a46e9477756c1d85d163c71743e..6fd1630f870178d2c30b9b9b993b2820f429f229 100644 (file)
@@ -18,6 +18,7 @@
 #include "canethgw.h"
 #include <linux/completion.h>
 #include <linux/mutex.h>
+#include <linux/miscdevice.h>
 #include <net/inet_common.h>
 
 MODULE_LICENSE("GPL");
@@ -31,9 +32,6 @@ static void cegw_can_send(struct socket *can_sock, struct can_frame *cf,
 static int cegw_thread_start(void *data);
 static int cegw_thread_stop(void);
 
-static int cegw_thread_recv_udp(void *data);
-static int cegw_thread_recv_can(void *data);
-
 enum __cegw_state {
        CEGW_RUN,
        CEGW_STOP,
@@ -55,7 +53,6 @@ struct cegw_setting {
 
 static int cegw_state = CEGW_STOP;
 static struct socket *can_sock = NULL, *udp_sock = NULL;
-static struct socket *s_can = NULL, *s_eth = NULL; 
 static struct task_struct *eth_to_can = NULL, *can_to_eth = NULL;
 static struct notifier_block notifier;
 
@@ -209,7 +206,6 @@ static int cegw_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        unsigned short port;
        struct cegw_setting* set;
        int err = 0;
-       int fd_eth, fd_can;
 
        if (nlmsg_len(nlh) < sizeof(*r))
                return -EINVAL;
@@ -225,36 +221,11 @@ static int cegw_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                return err;
        }
 
-       fd_eth = *(int*)nla_data(tb[CEGW_ETH_SOCK]);
-       fd_can = *(int*)nla_data(tb[CEGW_CAN_SOCK]);
-
-       printk( "cegw: sock=%i,%i\n", fd_eth, fd_can );
-
-       s_can = sockfd_lookup( fd_can, &err );
-       s_eth = sockfd_lookup( fd_eth, &err );
-
-       if( s_can == NULL || s_eth == NULL )
-       {
-               printk( "error: socket look-up failed\n" );
-               return 0;
-       }
-
-       struct can_frame greet;
-       greet.can_id = 0;
-       greet.can_dlc = 4;
-       greet.data[0] = 7;
-
-       //cegw_can_send( s_can, &greet, *(int*)nla_data(tb[CEGW_CAN_IFINDEX]) );
-       kthread_run(cegw_thread_recv_udp, NULL, "canethgw");
-       kthread_run(cegw_thread_recv_can, NULL, "canethgw");
-       /*
        if (tb[CEGW_CMD_INFO] == NULL) {
                pr_devel("canethgw: CEGW_CMD_INFO is missing in rtmsg\n");
                return -EINVAL;
        }
-       */
 
-       return 0;
        switch (*(int*)nla_data(tb[CEGW_CMD_INFO])) {
        case CEGW_LISTEN:
                if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT]) {
@@ -272,7 +243,7 @@ static int cegw_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                kthread_run(cegw_thread_start, set, "canethgw");
                break;
        case CEGW_RULE_ETH_CAN:
-               if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT] || 
+               if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT] ||
                    !tb[CEGW_CAN_IFINDEX]) {
                        pr_devel("canethgw: missing attribute for"
                                 "CEGW_RULE_ETH_CAN\n");
@@ -298,7 +269,7 @@ static int cegw_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                mutex_unlock(&rule_eth_can_mutex);
                break;
        case CEGW_RULE_CAN_ETH:
-               if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT] || 
+               if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT] ||
                    !tb[CEGW_CAN_IFINDEX]) {
                        pr_devel("canethgw: missing attribute for "
                                 "CEGW_RULE_CAN_ETH\n");
@@ -496,68 +467,97 @@ static int cegw_notifier(struct notifier_block *nb, unsigned long msg, void *dat
        return NOTIFY_DONE;
 }
 
-static int cegw_thread_recv_udp(void *data)
+/**
+ * cegw_thread_start - start working threads
+ * @data: (struct cegw_setting *) with new listening address
+ *
+ * Two threads are started. One is serving udp->can routing and the other
+ * can->udp.
+ */
+static int cegw_thread_start(void *data)
 {
-       struct can_frame cf;
-       struct kvec vec;
-       struct msghdr mh;
-       struct sockaddr_in sa;
-       struct hlist_node* pos;
-       int can_ifidx;
-       int recv_size;
+       struct sockaddr_in udp_addr;
+       struct sockaddr_can can_addr;
+       struct cegw_setting *set;
 
-       memset(&mh, 0, sizeof(mh));
-       mh.msg_name = &sa;
-       mh.msg_namelen = sizeof(sa);
-       mh.msg_control = NULL;
-       mh.msg_controllen = 0;
-       mh.msg_flags = 0;
+       set = (struct cegw_setting *)data;
 
-       printk( "udp receiving\n" );
+       can_addr.can_family = AF_CAN;
+       can_addr.can_ifindex = 0;
 
-       while( 1 )
-       {
-               vec.iov_base = &cf;
-               vec.iov_len = sizeof(cf);
+       udp_addr.sin_family = AF_INET;
+       udp_addr.sin_port = htons(set->eth_port);
+       udp_addr.sin_addr = set->eth_ip;
 
-               recv_size = kernel_recvmsg(s_eth, &mh, &vec, 1,
-                               sizeof(cf), 0);
-               printk("udp status=%i\n", recv_size);
-       }
+       kfree(data);
+       mutex_lock(&cegw_mutex);
+       if (cegw_state == CEGW_EXIT)
+               goto out_err;
 
-       return 0;
-}
+       /* stops threads if exist */
+       cegw_thread_stop();
 
-static int cegw_thread_recv_can(void *data)
-{
-       struct can_frame cf;
-       struct kvec vec;
-       struct msghdr mh;
-       struct sockaddr_can ca;
-       struct hlist_node* pos;
-       int can_ifidx;
-       int recv_size;
+       /* create and bind sockets */
+       if (sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &udp_sock)
+           != 0) {
+               printk(KERN_ERR "canethgw: udp socket creation failed\n");
+               goto out_err;
+       }
 
-       memset(&mh, 0, sizeof(mh));
-       mh.msg_name = &ca;
-       mh.msg_namelen = sizeof(ca);
-       mh.msg_control = NULL;
-       mh.msg_controllen = 0;
-       mh.msg_flags = 0;
+       if (sock_create_kern(PF_CAN, SOCK_RAW, CAN_RAW, &can_sock) != 0) {
+               printk(KERN_ERR "canethgw: can socket creation failed\n");
+               sock_release(udp_sock);
+               goto out_err;
+       }
 
-       printk( "can receiving\n" );
+       if (kernel_bind(udp_sock, (struct sockaddr*)&udp_addr,
+                       sizeof(udp_addr)) != 0) {
+               printk(KERN_ERR "canethgw: udp socket binding failed\n");
+               sock_release(udp_sock);
+               sock_release(can_sock);
+               goto out_err;
+       }
 
-       while( 1 )
-       {
-               vec.iov_base = &cf;
-               vec.iov_len = sizeof(cf);
+       if (kernel_bind(can_sock, (struct sockaddr*) &can_addr,
+                       sizeof(can_addr)) != 0) {
+               printk(KERN_ERR "canethgw: can socket binding failed\n");
+               kernel_sock_shutdown(udp_sock, SHUT_RDWR);
+               sock_release(udp_sock);
+               sock_release(can_sock);
+               goto out_err;
+       }
 
-               recv_size = kernel_recvmsg(s_can, &mh, &vec, 1,
-                               sizeof(cf), 0);
-               printk("can status=%i\n", recv_size);
+       /* start threads */
+       cegw_state = CEGW_RUN;
+
+       eth_to_can = kthread_create(cegw_udp2can, NULL, "canethgw");
+       if (IS_ERR(eth_to_can)) {
+               cegw_state = CEGW_STOP;
+               sock_release(udp_sock);
+               sock_release(can_sock);
+               goto out_err;
        }
+       get_task_struct(eth_to_can);
+       wake_up_process(eth_to_can);
+
+       can_to_eth = kthread_create(cegw_can2udp, NULL, "canethgw");
+       if (IS_ERR(can_to_eth)) {
+               cegw_state = CEGW_STOP;
+               kernel_sock_shutdown(udp_sock, SHUT_RDWR);
+               kthread_stop(eth_to_can);
+               sock_release(udp_sock);
+               sock_release(can_sock);
+               goto out_err;
+       }
+       get_task_struct(can_to_eth);
+       wake_up_process(can_to_eth);
 
+       mutex_unlock(&cegw_mutex);
+       pr_devel("threads are running\n");
        return 0;
+out_err:
+       mutex_unlock(&cegw_mutex);
+       return -1;
 }
 
 /**
@@ -595,8 +595,55 @@ static int cegw_thread_stop(void)
        return 0;
 }
 
+static int cegw_open(struct inode *inode, struct file *file)
+{
+       file->private_data = "greetings";
+
+       printk("cegw device opened\n");
+       return 0;
+}
+
+static int cegw_release(struct inode *inode, struct file *file)
+{
+       printk("cegw device released, data=%s\n", file->private_data);
+       return 0;
+}
+
+static long cegw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+               case CEGW_IOCTL_CAN_SOCK:
+                       printk("ioctl can sock\n");
+                       break;
+               case CEGW_IOCTL_UDP_SOCK:
+                       printk("ioctl udp sock\n");
+                       break;
+               default:
+                       printk("undefined ioctl command\n");
+                       break;
+       }
+
+       return 0;
+}
+
+static const struct file_operations cegw_fops = {
+       .owner = THIS_MODULE,
+       .open = cegw_open,
+       .release = cegw_release,
+       .unlocked_ioctl = cegw_ioctl
+};
+
+static struct miscdevice cegw_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "cegw",
+       .fops = &cegw_fops
+};
+
 static int __init cegw_init(void)
 {
+       misc_register(&cegw_device);
+
+       return 0;
        notifier.notifier_call = cegw_notifier;
        register_netdevice_notifier(&notifier);
 
@@ -610,6 +657,9 @@ static int __init cegw_init(void)
 
 static void __exit cegw_exit(void)
 {
+       misc_deregister(&cegw_device);
+
+       return;
        /* ToDo: effect on cangw? */
        rtnl_unregister_all(PF_CAN);
 
index 10f745a8392da509688c3d9b59e36971b5433a5f..4381bda47e68faf2f460666a9b62c19f487a1f62 100644 (file)
@@ -4,6 +4,10 @@
 #include <linux/types.h>
 #include <linux/can.h>
 
+#define CEGW_IOCTL_BASE 'c'
+#define CEGW_IOCTL_CAN_SOCK _IOW(CEGW_IOCTL_BASE, 0, int)
+#define CEGW_IOCTL_UDP_SOCK _IOW(CEGW_IOCTL_BASE, 1, int)
+
 /* these are from gw.h */
 struct rtcanmsg {
        __u8  can_family;
@@ -28,8 +32,6 @@ enum {
        CEGW_ETH_IP,
        CEGW_ETH_PORT,
        CEGW_CMD_INFO,
-       CEGW_CAN_SOCK,
-       CEGW_ETH_SOCK,
        __CEGW_MAX
 };
 #define CEGW_MAX (__CEGW_MAX - 1)
index fc91a9ce48a9672b38f2001ba9be5bf7eefe205c..73726346e291b69096afb0dcedd4c5a606dd9e18 100644 (file)
 #include <linux/can.h>
 #include <linux/types.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
 #include "canethgw.h"
 
 /**
@@ -45,7 +49,7 @@ struct cegw_data
        unsigned short eth_listen_port;
 };
 
-struct cegw_nlmsg
+struct cegw_nlmsg 
 {
        struct nlmsghdr nh;
        struct rtmsg rt;
@@ -62,7 +66,7 @@ struct list_item
 
 unsigned int cegw_errno = 0;
 
-enum
+enum 
 {
        CEGW_ERR_UNKNOWN,
        CEGW_ERR_IF_UNSPEC,
@@ -108,7 +112,7 @@ static void perr( char* s )
 }
 
 /**
- * read_addrport - parses @in for eth address. 
+ * 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
@@ -266,7 +270,7 @@ inline static int cegw_list( struct cegw_nlmsg* req, struct cegw_data* d )
 {
        req->nh.nlmsg_type = RTM_GETROUTE;
        req->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-
+       
        return 0;
 }
 
@@ -323,40 +327,94 @@ int main( int argc, char* argv[] )
        struct rtattr* rta;
        int len;
        struct list_item li;
-       int gw_can_sock = 0;
-       int gw_eth_sock = 0;
-       struct sockaddr_in gw_eth_addr;
-       struct sockaddr_can gw_can_addr;
-       struct in_addr addr;
-
-       memset( &d, 0, sizeof(d) );
 
-       /* create sockets for gateway  */
-       gw_eth_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
-       gw_can_sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
+       /**/
+       int fd;
 
-       if( gw_eth_sock == -1 )
-               fprintf( stderr, "error: udp socket for gw creation failed\n" );
-       if( gw_can_sock == -1 )
-               fprintf( stderr, "error: can socket for gw creation failed\n" );
+       fd = open( "/dev/cegw", O_RDONLY );
+       if( fd == -1 )
+       {
+               fprintf( stderr, "error: could not open device file\n" );
+               return -1;
+       }
 
-       gw_eth_addr.sin_family = AF_INET;
-       gw_eth_addr.sin_port = htons( 10501 );
-       gw_eth_addr.sin_addr.s_addr = INADDR_ANY;
+       ioctl( fd, CEGW_IOCTL_CAN_SOCK, 8 );
+       close( fd );
+       return 0;
 
-       gw_can_addr.can_family = AF_CAN;
-       gw_can_addr.can_ifindex = 0;
+       memset( &d, 0, sizeof(d) );
 
-       if( bind( gw_eth_sock, (struct sockaddr*)&gw_eth_addr, sizeof(gw_eth_addr) ) != 0 )
-               fprintf( stderr, "error: eth binding\n" );
-       if( bind( gw_can_sock, (struct sockaddr*)&gw_can_addr, sizeof(gw_can_addr) ) )
-               fprintf( stderr, "error: can binding\n" );
+       struct option long_opt[] =
+       {
+               { "add"   , 0, NULL, 'A' },
+               { "flush" , 0, NULL, 'F' },
+               { "list"  , 0, NULL, 'L' },
+               { "listen", 1, NULL, 'l' },
+               { 0, 0, 0, 0 }
+       };
+
+       while( 1 )
+       {
+               opt = getopt_long( argc, argv, "AFLl:s:d:", long_opt, NULL );
+               if( opt == -1 )
+                       break;
 
-       printf( "sockets created (%i,%i)\n", gw_can_sock, gw_eth_sock );
+               switch( opt )
+               {
+                       case 'A':
+                               cmd |= CEGW_CMD_ADD;
+                               break;
+                       case 'F':
+                               cmd |= CEGW_CMD_FLUSH;
+                               break;
+                       case 'L':
+                               cmd |= CEGW_CMD_LIST;
+                               break;
+                       case 'l':
+                               cmd |= CEGW_CMD_LISTEN;
+                               if( (optstr = read_iftype( optarg, &tmp )) == NULL )
+                               {
+                                       perr( "'--listen'" );
+                                       return -1;
+                               }       
+                               if( tmp != IF_ETH_UDP )
+                               {
+                                       perr( "'--listen' expects udp interface" );
+                                       return -1;
+                               }       
+                               if( read_addrport( optstr, &d.eth_listen_addr, &d.eth_listen_port ) )
+                               {
+                                       perr( "'--listen'" );
+                                       return -1;
+                               }
+                               break;
+                       case 's':
+                               if( read_if( optarg, &d.src_if, &d ) != 0 )
+                               {
+                                       perr( "'-s'" );
+                                       return -1;
+                               }
+                               break;
+                       case 'd':
+                               if( read_if( optarg, &d.dst_if, &d ) != 0 )
+                               {
+                                       perr( "'-d'" );
+                                       return -1;
+                               }
+                               break;
+                       case '?':
+                               return -1;
+                               break;
+                       default:
+                               perr( "unknown option" );
+                               return -1;
+                               break;
+               }                               
+       }
 
        /* prepare netlink message */
        req.nh.nlmsg_len   = NLMSG_LENGTH( sizeof(struct rtmsg) );
-       req.nh.nlmsg_type  = RTM_NEWROUTE;
+       //req.nh.nlmsg_type;
        req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
        req.nh.nlmsg_seq   = 0;
        req.nh.nlmsg_pid   = 0; /* ? */
@@ -364,27 +422,50 @@ int main( int argc, char* argv[] )
        memset( &req.rt, 0, sizeof(req.rt) );
        req.rt.rtm_family = AF_CAN;
 
-       addattr32( &req.nh, sizeof(req), CEGW_CAN_SOCK, gw_can_sock );
-       addattr32( &req.nh, sizeof(req), CEGW_ETH_SOCK, gw_eth_sock );
-       addattr32( &req.nh, sizeof(req), CEGW_CAN_IFINDEX, if_nametoindex("vcan0") );
+       switch( cmd )
+       {
+               case 0:
+                       perr( "command not specified" );
+                       return -1;
+                       break;
+               case CEGW_CMD_ADD:
+                       if( cegw_add( &req, &d ) != 0 )
+                       {
+                               perr( "'--add'" );
+                               return -1;
+                       }
+                       break;
+               case CEGW_CMD_FLUSH:
+                       cegw_flush( &req, &d );
+                       break;
+               case CEGW_CMD_LIST:
+                       cegw_list( &req, &d );
+                       break;
+               case CEGW_CMD_LISTEN:
+                       cegw_listen( &req, &d );
+                       break;
+               default:
+                       perr( "command mismatch" );
+                       break;
+       }
 
        /* send over netlink socket */
        s = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); /* chck */
-
+       
        memset( &nladdr, 0, sizeof(nladdr) );
        nladdr.nl_family = AF_NETLINK;
        nladdr.nl_pad = 0;
        nladdr.nl_pid = 0;
        nladdr.nl_groups = 0;
 
-       err = sendto( s, &req, req.nh.nlmsg_len, 0,
+       err = sendto( s, &req, req.nh.nlmsg_len, 0, 
                      (struct sockaddr*)&nladdr, sizeof(nladdr) );
        if( err < 0 )
        {
                perror( "netlink sendto" );
                return -1;
        }
-
+       
        /* recv */
        rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
        if( rsize < 0 )
@@ -394,14 +475,6 @@ int main( int argc, char* argv[] )
        }
        nlh = (struct nlmsghdr*)rxbuf;
 
-       /* REMOVE */
-       sleep( 2 );
-       //printf( "closing sockets\n" );
-       //shutdown( gw_eth_sock, SHUT_RDWR );
-       shutdown( gw_can_sock, SHUT_RDWR );
-       //close( gw_can_sock );
-       //close( gw_eth_sock );
-
        if( nlh->nlmsg_type == NLMSG_ERROR )
        {
                rte = (struct nlmsgerr*)NLMSG_DATA( nlh );