#include "canethgw.h"
#include <linux/completion.h>
#include <linux/mutex.h>
+#include <linux/miscdevice.h>
#include <net/inet_common.h>
MODULE_LICENSE("GPL");
static int cegw_udp2can(void *data);
static void cegw_udp_send(struct socket *udp_sock, struct can_frame *cf,
- struct in_addr ipaddr, u16 port);
+ struct sockaddr_in* addr);
static int cegw_can2udp(void *data);
-static void cegw_can_send(struct socket *can_sock, struct can_frame *cf,
- int ifindex);
+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 DEFINE_MUTEX(rule_can_eth_mutex);
static DEFINE_MUTEX(cegw_mutex);
-static void cegw_udp_send(struct socket *udp_sock, struct can_frame *cf,
- struct in_addr ipaddr, u16 port)
+static void cegw_udp_send(struct socket *udp_sock, struct can_frame *cf, struct sockaddr_in* addr)
{
struct msghdr mh;
- struct sockaddr_in addr;
struct kvec vec;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr = ipaddr;
-
- mh.msg_name = &addr;
- mh.msg_namelen = sizeof(addr);
+ mh.msg_name = addr;
+ mh.msg_namelen = sizeof(*addr);
mh.msg_control = NULL;
mh.msg_controllen = 0;
mh.msg_flags = 0;
kernel_sendmsg(udp_sock, &mh, &vec, 1, sizeof(*cf));
}
-static void cegw_can_send(struct socket* can_sock, struct can_frame* cf,
- int ifindex)
+static void cegw_can_send(struct socket* can_sock, struct can_frame* cf)
{
struct msghdr mh;
struct kvec vec;
- struct sockaddr_can addr;
- addr.can_family = AF_CAN;
- addr.can_ifindex = ifindex;
-
- mh.msg_name = &addr;
- mh.msg_namelen = sizeof(addr);
+ mh.msg_name = NULL;
+ mh.msg_namelen = 0;
mh.msg_control = NULL;
mh.msg_controllen = 0;
mh.msg_flags = 0;
struct can_frame cf;
struct kvec vec;
struct msghdr mh;
- struct cegw_rule* rule;
- struct hlist_node* pos;
- int can_ifidx;
+ struct cegw_job *job = (struct cegw_job *)data;
+ struct socket *udp_sock = NULL, *can_sock = NULL;
int recv_size;
memset(&mh, 0, sizeof(mh));
+ udp_sock = job->udp_sock;
+ can_sock = job->can_sock;
- while (cegw_state != CEGW_STOP) {
+ while (1) {
+ printk( "recv\n" );
vec.iov_base = &cf;
vec.iov_len = sizeof(cf);
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;
+ /* if(recv_size != sizeof(cf) */
+ if (recv_size < 1) /* ToDo: split 0 and else */
+ {
+ printk("udp2can error\n");
+ break;
+ }
/* FIXME: Convert endianing of cf.can_id */
- mutex_lock(&rule_eth_can_mutex);
- hlist_for_each_entry(rule, pos, &rule_eth_can, list) {
- can_ifidx = rule->can_ifindex;
- /* ToDo: from filter */
- cegw_can_send(can_sock, &cf, can_ifidx);
- }
- mutex_unlock(&rule_eth_can_mutex);
+ printk( "sendit to can\n" );
+ cegw_can_send(can_sock, &cf);
}
return 0;
struct msghdr mh;
struct kvec vec;
struct can_frame cf;
- struct sockaddr_can ca;
- struct cegw_rule* rule;
- struct hlist_node* pos;
- struct in_addr eth_ip;
- u16 eth_port;
+ struct cegw_job* job = (struct cegw_job*)data;
int recv_size;
+ struct socket* udp_sock = job->udp_sock;
+ struct socket* can_sock = job->can_sock;
+ int i;
- mh.msg_name = &ca;
- mh.msg_namelen = sizeof(ca);
- mh.msg_control = NULL;
- mh.msg_controllen = 0;
- mh.msg_flags = 0;
+ memset(&mh, 0, sizeof(mh));
- while (cegw_state != CEGW_STOP) {
+ while (1) {
vec.iov_base = &cf;
vec.iov_len = sizeof(cf);
recv_size = kernel_recvmsg(can_sock, &mh, &vec, 1,
sizeof(cf), 0);
- if (recv_size != sizeof(cf) || recv_size == 0)
- continue;
- else if (recv_size < 0)
- return -1;
-
- mutex_lock(&rule_can_eth_mutex);
- hlist_for_each_entry(rule, pos, &rule_can_eth, list) {
- eth_ip = rule->eth_ip;
- eth_port = rule->eth_port;
- if (rule->can_ifindex == ca.can_ifindex)
- cegw_udp_send(udp_sock, &cf, eth_ip, eth_port);
- }
- mutex_unlock(&rule_can_eth_mutex);
- }
-
- return 0;
-}
-
-static int cegw_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
-{
- struct nlattr* tb[ CEGW_MAX+1 ];
- struct cegw_rule* rule = NULL;
- int ifindex;
- struct rtmsg* r;
- struct in_addr ip;
- unsigned short port;
- struct cegw_setting* set;
- int err = 0;
-
- if (nlmsg_len(nlh) < sizeof(*r))
- return -EINVAL;
-
- r = nlmsg_data(nlh);
-
- if (r->rtm_family != AF_CAN)
- return -EPFNOSUPPORT;
-
- err = nlmsg_parse(nlh, sizeof(*r), tb, CEGW_MAX, NULL);
- if (err < 0) {
- pr_devel("canethgw: nlmsg_parse error\n");
- return err;
- }
- if (tb[CEGW_CMD_INFO] == NULL) {
- pr_devel("canethgw: CEGW_CMD_INFO is missing in rtmsg\n");
- return -EINVAL;
- }
-
- switch (*(int*)nla_data(tb[CEGW_CMD_INFO])) {
- case CEGW_LISTEN:
- if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT]) {
- pr_devel("canethgw: missing attribute for CEGW_LISTEN\n");
- return -EINVAL;
- }
-
- /* ToDo: valid listen address */
- set = kmalloc(sizeof(*set), GFP_KERNEL);
- if (set == NULL)
- return -ENOMEM;
- set->eth_ip = *(struct in_addr*)nla_data(tb[CEGW_ETH_IP]);
- set->eth_port = *(unsigned short*)nla_data(tb[CEGW_ETH_PORT]);
- /* MS: It would be better to use workqueues here. */
- kthread_run(cegw_thread_start, set, "canethgw");
- break;
- case CEGW_RULE_ETH_CAN:
- if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT] ||
- !tb[CEGW_CAN_IFINDEX]) {
- pr_devel("canethgw: missing attribute for"
- "CEGW_RULE_ETH_CAN\n");
- return -EINVAL;
- }
+ if (recv_size < 1)
+ break;
- ifindex = *(int*)nla_data(tb[CEGW_CAN_IFINDEX]);
- ip = *(struct in_addr*)nla_data(tb[CEGW_ETH_IP]);
- port = *(unsigned short*)nla_data(tb[CEGW_ETH_PORT]);
- pr_devel("canethgw: new eth->can rule - (%x:%hu)->(%d)\n",
- ip.s_addr, port, ifindex);
-
- rule = kmalloc(sizeof(struct cegw_rule), GFP_KERNEL);
- if (rule == NULL)
- return -ENOMEM;
-
- rule->can_ifindex = ifindex;
- rule->eth_ip = ip;
- rule->eth_port = port;
-
- mutex_lock(&rule_eth_can_mutex);
- hlist_add_head(&rule->list, &rule_eth_can);
- mutex_unlock(&rule_eth_can_mutex);
- break;
- case CEGW_RULE_CAN_ETH:
- if (!tb[CEGW_ETH_IP] || !tb[CEGW_ETH_PORT] ||
- !tb[CEGW_CAN_IFINDEX]) {
- pr_devel("canethgw: missing attribute for "
- "CEGW_RULE_CAN_ETH\n");
- return -EINVAL;
+ for( i=0; i<job->udp_dstcnt; i++ )
+ {
+ cegw_udp_send(udp_sock, &cf, &job->udp_dst[i]);
}
-
- ifindex = *(int*)nla_data(tb[CEGW_CAN_IFINDEX]);
- ip = *(struct in_addr*)nla_data(tb[CEGW_ETH_IP]);
- port = *(unsigned short*)nla_data(tb[CEGW_ETH_PORT]);
- pr_devel("canethgw: new can->eth rule - (%d)->(%x:%hu)\n",
- ifindex, ip.s_addr, port);
-
- rule = kmalloc(sizeof(struct cegw_rule), GFP_KERNEL);
- if (rule == NULL)
- return -ENOMEM;
-
- rule->can_ifindex = ifindex;
- rule->eth_ip = ip;
- rule->eth_port = port;
-
- mutex_lock(&rule_can_eth_mutex);
- hlist_add_head(&rule->list, &rule_can_eth);
- mutex_unlock(&rule_can_eth_mutex);
- break;
- default:
- pr_devel("canethgw: unknown CEGW_CMD_INFO\n");
- break;
- }
-
- return 0;
-}
-
-static void cegw_flush(void)
-{
- struct cegw_rule *rule;
- struct hlist_node *pos, *n;
-
- mutex_lock(&rule_eth_can_mutex);
- hlist_for_each_entry_safe(rule, pos, n, &rule_eth_can, list) {
- hlist_del(&rule->list);
- kfree(rule);
- }
- mutex_unlock(&rule_eth_can_mutex);
-
- mutex_lock(&rule_can_eth_mutex);
- hlist_for_each_entry_safe(rule, pos, n, &rule_can_eth, list) {
- hlist_del(&rule->list);
- kfree(rule);
- }
- mutex_unlock(&rule_can_eth_mutex);
-}
-
-static int cegw_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
-{
- struct rtmsg *r;
- struct nlattr *tb[CEGW_MAX+1];
- int err = 0;
-
- if (nlmsg_len(nlh) < sizeof(*r))
- return -EINVAL;
-
- r = nlmsg_data(nlh);
-
- if (r->rtm_family != AF_CAN)
- return -EPFNOSUPPORT;
-
- err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, CEGW_MAX, NULL);
- if (err != 0)
- return -EINVAL;
-
- if (tb[CEGW_CMD_INFO] == NULL) {
- pr_devel("canethgw: CEGW_CMD_INFO is missing in rtmsg\n");
- return -EINVAL;
}
- if (*(int*)nla_data(tb[CEGW_CMD_INFO]) != CEGW_FLUSH) {
- return -EINVAL;
- }
-
- cegw_flush();
-
return 0;
}
-static int cegw_put_rule(struct sk_buff *skb, int type, struct cegw_rule *rule)
-{
- int ifindex;
- struct in_addr ip;
- unsigned short port;
- struct nlmsghdr *nlh;
-
- ifindex = rule->can_ifindex;
- ip = rule->eth_ip;
- port = rule->eth_port;
-
- nlh = nlmsg_put(skb, 0, 0, 0, 0, 0);
- if (nlh == NULL)
- return -EMSGSIZE;
-
- /* type */
- if (nla_put(skb, CEGW_TYPE, sizeof(type), &type) < 0)
- goto cancel;
- else
- nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(type));
-
- /* can ifindex */
- if (nla_put(skb, CEGW_CAN_IFINDEX, sizeof(ifindex), &ifindex) < 0)
- goto cancel;
- else
- nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(ifindex));
-
- /* ip adress */
- if (nla_put(skb, CEGW_ETH_IP, sizeof(ip), &ip) < 0)
- goto cancel;
- else
- nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(ip));
-
- /* port */
- if (nla_put(skb, CEGW_ETH_PORT, sizeof(port), &port) < 0)
- goto cancel;
- else
- nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(port));
-
- return skb->len;
-
-cancel:
- nlmsg_cancel(skb, nlh);
- return -EMSGSIZE;
-}
-
-static int cegw_getroute(struct sk_buff *skb, struct netlink_callback *cb)
-{
- struct cegw_rule *rule;
- struct hlist_node *pos;
- int idx = 0;
- int s_idx = cb->args[0];
-
- mutex_lock(&rule_eth_can_mutex);
- mutex_lock(&rule_can_eth_mutex);
- hlist_for_each_entry(rule, pos, &rule_eth_can, list) {
- if (idx < s_idx)
- goto cont1;
-
- if (cegw_put_rule(skb, CEGW_RULE_ETH_CAN, rule) < 0)
- goto brk;
-cont1:
- idx++;
- }
-
- hlist_for_each_entry(rule, pos, &rule_can_eth, list) {
- if (idx < s_idx)
- goto cont2;
-
- if (cegw_put_rule(skb, CEGW_RULE_CAN_ETH, rule) < 0)
- goto brk;
-cont2:
- idx++;
- }
-
-brk:
- mutex_unlock(&rule_eth_can_mutex);
- mutex_unlock(&rule_can_eth_mutex);
- cb->args[0] = idx;
-
- return skb->len;
-}
-
static int cegw_notifier(struct notifier_block *nb, unsigned long msg, void *data)
{
struct net_device *dev = (struct net_device *)data;
*/
static int cegw_thread_start(void *data)
{
- struct sockaddr_in udp_addr;
- struct sockaddr_can can_addr;
- struct cegw_setting *set;
-
- set = (struct cegw_setting *)data;
-
- can_addr.can_family = AF_CAN;
- can_addr.can_ifindex = 0;
-
- udp_addr.sin_family = AF_INET;
- udp_addr.sin_port = htons(set->eth_port);
- udp_addr.sin_addr = set->eth_ip;
-
- kfree(data);
- mutex_lock(&cegw_mutex);
- if (cegw_state == CEGW_EXIT)
- goto out_err;
-
- /* stops threads if exist */
- cegw_thread_stop();
-
- /* 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;
- }
-
- 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;
- }
-
- 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;
- }
+ struct task_struct *task = NULL;
- 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);
+ task = kthread_run(cegw_udp2can, data, "canethgw_udp2can");
+ if (IS_ERR(task)) {
goto out_err;
}
- /* 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);
+ task = kthread_run(cegw_can2udp, data, "canethgw_can2udp");
+ if (IS_ERR(task)) {
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;
+ return -ENOMEM;
}
/**
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)
+{
+ struct cegw_job *job = (struct cegw_job *)file->private_data;
+
+ sock_release( job->udp_sock );
+ sock_release( job->can_sock );
+ printk("cegw device released, data=%s\n", (char *)file->private_data);
+ return 0;
+}
+
+static long cegw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ int memsz = 0;
+ __u32 dstcnt = 0;
+ __u32 addrlen = 0;
+ struct cegw_ioctl *gwctl = NULL;
+ struct cegw_job *job = NULL;
+
+ /* ToDo: verify access and authorization */
+ if( get_user(dstcnt, &((struct cegw_ioctl __user *)arg)->udp_dstcnt) != 0 )
+ return -EFAULT;
+ if( get_user(addrlen, &((struct cegw_ioctl __user *)arg)->udp_addrlen) != 0 )
+ return -EFAULT;
+
+ memsz = sizeof(*gwctl) + dstcnt * addrlen;
+ /* ToDo: memory limit */
+ gwctl = kmalloc(GFP_KERNEL, memsz);
+ if (gwctl==NULL)
+ return -ENOMEM;
+
+ err = copy_from_user(gwctl, (void __user *)arg, memsz);
+ if (err != 0)
+ {
+ return -EFAULT;
+ }
+ /**/
+ job = kmalloc(GFP_KERNEL, sizeof(*job));
+ if (job == NULL)
+ return -ENOMEM;
+
+ job->udp_dst = kmalloc(GFP_KERNEL, dstcnt * addrlen); /* ToDo: limit */
+ if (job->udp_dst == NULL)
+ return -ENOMEM;
+
+ err = copy_from_user(job->udp_dst, (void __user *)(arg + sizeof(struct cegw_ioctl)), dstcnt*addrlen);
+ if (err != 0)
+ return -EFAULT;
+
+ job->udp_sock = sockfd_lookup( gwctl->udp_sock, &err );
+ if (job->udp_sock == NULL)
+ return err;
+
+ job->can_sock = sockfd_lookup( gwctl->can_sock, &err );
+ if (job->can_sock == NULL)
+ return err;
+
+ job->udp_dstcnt = dstcnt;
+
+ /* ToDo: sin_family? */
+
+ file->private_data = job;
+ cegw_thread_start(job);
+
+ /* process udp destinations */
+ switch (cmd) {
+ case CEGW_IOCTL_START:
+ printk("udp_dstcnt=%i\n", gwctl->udp_dstcnt);
+ printk("udp_dst[0] family=%i\n", job->udp_dst[0].sin_family);
+ printk("udp_dst[1] family=%i\n", job->udp_dst[1].sin_family);
+ 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);
- /* subscribe to netlink */
- 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;
}
static void __exit cegw_exit(void)
{
+ misc_deregister(&cegw_device);
+
+ return;
/* ToDo: effect on cangw? */
rtnl_unregister_all(PF_CAN);
mutex_unlock(&cegw_mutex);
unregister_netdevice_notifier(¬ifier);
- cegw_flush();
+ //cegw_flush();
}
module_init(cegw_init);