From 865c5c312215747448f7edf9318d2c6bf0c16d6d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Radek=20Mat=C4=9Bjka?= Date: Wed, 28 Nov 2012 18:01:10 -0600 Subject: [PATCH] canethgw micsdevice 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 | 210 ++++++++++++++++++++++++++++------------------ kernel/canethgw.h | 6 +- utils/cegw/cegw.c | 159 +++++++++++++++++++++++++---------- 3 files changed, 250 insertions(+), 125 deletions(-) diff --git a/kernel/canethgw.c b/kernel/canethgw.c index 601ea77..6fd1630 100644 --- a/kernel/canethgw.c +++ b/kernel/canethgw.c @@ -18,6 +18,7 @@ #include "canethgw.h" #include #include +#include #include 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(¬ifier); @@ -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); diff --git a/kernel/canethgw.h b/kernel/canethgw.h index 10f745a..4381bda 100644 --- a/kernel/canethgw.h +++ b/kernel/canethgw.h @@ -4,6 +4,10 @@ #include #include +#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) diff --git a/utils/cegw/cegw.c b/utils/cegw/cegw.c index fc91a9c..7372634 100644 --- a/utils/cegw/cegw.c +++ b/utils/cegw/cegw.c @@ -13,6 +13,10 @@ #include #include #include +#include +#include +#include +#include #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 ); -- 2.39.2