]> rtime.felk.cvut.cz Git - can-utils.git/commitdiff
Add CAN-Ethernet gateway - user space part
authorMichal Sojka <sojkam1@fel.cvut.cz>
Thu, 20 Dec 2012 19:11:48 +0000 (20:11 +0100)
committerMichal Sojka <sojkam1@fel.cvut.cz>
Thu, 20 Dec 2012 19:11:48 +0000 (20:11 +0100)
Signed-off-by: Radek Matějka <radek.matejka@gmail.com>
Signed-off-by: Michal Sojka <sojkam1@fel.cvut.cz>
Makefile
cegw.c [new file with mode: 0644]
include/linux/can/canethgw.h [new file with mode: 0644]

index c3e6d9c443aae718b9fa2de9303b105e5098fee4..f4cdb2c6fb4fe92ab6051c18e979a2c5a6f9b87b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -58,7 +58,7 @@ PROGRAMS = can-calc-bit-timing candump cansniffer cansend canplayer cangen canbu
           $(PROGRAMS_ISOTP)\
           $(PROGRAMS_CANGW)\
           $(PROGRAMS_SLCAN)\
-          slcanpty canfdtest
+          slcanpty canfdtest cegw
 
 all: $(PROGRAMS)
 
@@ -90,3 +90,4 @@ canlogserver: canlogserver.o  lib.o
 log2long:      log2long.o      lib.o
 log2asc:       log2asc.o       lib.o
 asc2log:       asc2log.o       lib.o
+cegw:          cegw.o
diff --git a/cegw.c b/cegw.c
new file mode 100644 (file)
index 0000000..3e0e9d5
--- /dev/null
+++ b/cegw.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright: (c) 2012 Czech Technical University in Prague
+ *
+ * Authors:
+ *     Radek Matějka <radek.matejka@gmail.com>
+ *     Michal Sojka  <sojkam1@fel.cvut.cz>
+ *
+ * Funded by: Volkswagen Group Research
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <linux/can.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/can/canethgw.h>
+
+unsigned int cegw_errno = 0;
+
+enum
+{
+       CEGW_ERR_UNKNOWN,
+       CEGW_ERR_COLON,
+       CEGW_ERR_UNEXLEN,
+       CEGW_ERR_ATON,
+       CEGW_ERR_PORT
+};
+
+char* cegw_errlist[] =
+{
+       [CEGW_ERR_UNKNOWN] = "",
+       [CEGW_ERR_COLON  ] = "expected ':' (<ip>:<port>)",
+       [CEGW_ERR_UNEXLEN] = "unexpected ip address length, please use dot notation"
+                            " (eg. 127.0.0.1)",
+       [CEGW_ERR_ATON   ] = "ip address mismatch",
+       [CEGW_ERR_PORT   ] = "port number"
+};
+
+static const char help_msg[] = "usage:\n"
+                              "        %s <can_if> <udp_listen_addr>:<port> <udp_dest_addr>:<port>\n"
+                              "                [list of additional udp recipients <addr:port>]\n"
+                              "example:\n"
+                              "        %s can0 192.168.0.1:10501 192.168.0.4:980 192.168.0.7:1160\n\n"
+                              "        Executing this command will set the gateway so that it will\n"
+                              "        listen for udp messages on 192.168.0.1:10501 and send them\n"
+                              "        to can0. Simultaneously, it will send all messages from can0\n"
+                              "        to 192.168.0.4:980 and 192.168.0.7:1160 via udp. The message is\n"
+                              "        therefore cloned. Notice that there can be any number\n"
+                              "        of udp recipients.\n";
+
+static void perr(char* s)
+{
+       if (s) {
+               if (cegw_errno == 0) {
+                       fprintf(stderr, "error: %s\n", s);
+
+               } else {
+                       fprintf(stderr, "error: %s, %s\n", s, cegw_errlist[cegw_errno]);
+               }
+               return;
+       }
+
+       fprintf(stderr, "error: %s\n", cegw_errlist[cegw_errno]);
+}
+
+/**
+ * read_addrport - parses @in for eth address.
+ * Valid input is e.g. 127.0.0.1:10502. If parsing fails
+ * the cause is stored in cegw_errno.
+ *
+ * @param[in]  in   string to search in
+ * @param[out] addr ip address
+ * @param[out] port transport layer port
+ * @return 0 on success, -1 otherwise
+ */
+int read_addrport(char* in, struct in_addr* addr, unsigned short* port)
+{
+       char* delim = NULL;
+       const int addrstr_len = 16;
+       char addrstr[addrstr_len];
+       int addrlen;
+
+       if ((delim = strchr(in, ':')) == NULL) {
+               cegw_errno = CEGW_ERR_COLON;
+               return -1;
+       }
+
+       /* get address */
+       addrlen = delim - in;
+       if (addrlen > addrstr_len) {
+               cegw_errno = CEGW_ERR_UNEXLEN;
+               return -1;
+       }
+
+       memcpy(addrstr, in, addrlen);
+       addrstr[addrlen] = '\0';
+       if (inet_aton(addrstr, addr) == 0) {
+               cegw_errno = CEGW_ERR_ATON;
+               return -1;
+       }
+
+       /* get port */
+       /* ToDo: handle overflow */
+       if (sscanf(delim, ":%hu", port) != 1) {
+               cegw_errno = CEGW_ERR_PORT;
+               return -1;
+       }
+
+       return 0;
+}
+
+int main(int argc, char* argv[])
+{
+       int i;
+       int fd;
+       int tmpi;
+       int dstcnt;
+       unsigned short port;
+       int udp_sock, can_sock;
+       struct sockaddr_in udp_addr;
+       struct sockaddr_can can_addr;
+       struct sockaddr_in* dst = NULL;
+       struct cegw_ioctl* gwctl = NULL;
+
+       if (argc == 1)
+       {
+               printf(help_msg, argv[0], argv[0]);
+               return 0;
+       }
+
+       if (argc < 4) {
+               perr("not enough arguments");
+               printf(help_msg, argv[0], argv[0]);
+               /* ToDo: print usage */
+               return 1;
+       }
+
+       dstcnt = argc-3;
+       gwctl = (struct cegw_ioctl*)malloc(sizeof(*gwctl) + (dstcnt)*sizeof(struct sockaddr_in));
+
+       for (i=1; i<argc; i++) {
+               switch (i) {
+                       case 1: /* can ifindex */
+                               can_addr.can_family = AF_CAN;
+                               tmpi = if_nametoindex(argv[i]);
+                               if (tmpi == 0) {
+                                       perr("given can interface not found");
+                                       free(gwctl);
+                                       return 1;
+                               }
+                               can_addr.can_ifindex = tmpi;
+                               break;
+                       case 2: /* listen addr */
+                               udp_addr.sin_family = AF_INET;
+                               if (read_addrport(argv[i], &udp_addr.sin_addr, &port) != 0) {
+                                       perr("listening address mismatch");
+                                       free(gwctl);
+                                       return 1;
+                               }
+                               udp_addr.sin_port = htons(port);
+                               break;
+                       default: /* udp destination */
+                               dst = &gwctl->udp_dst[i-3];
+                               dst->sin_family = AF_INET;
+                               if (read_addrport(argv[i], &dst->sin_addr, &port) != 0) {
+                                       perr("udp destination mismatch");
+                                       free(gwctl);
+                                       return 1;
+                               }
+                               dst->sin_port = htons(port);
+                               break;
+               }
+       }
+
+       /* prepare udp socket */
+       udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (udp_sock == -1) {
+               perror("udp socket()");
+               free(gwctl);
+               return 1;
+       }
+
+       if (bind(udp_sock, (struct sockaddr *)&udp_addr, sizeof(struct sockaddr_in)) != 0) {
+               perror("bind(udp)");
+               free(gwctl);
+               return 1;
+       }
+
+       /* prepare can socket */
+       can_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+       if (can_sock == -1) {
+               perror("can socket()");
+               free(gwctl);
+               return 1;
+       }
+
+       if (bind(can_sock, (struct sockaddr *)&can_addr, sizeof(struct sockaddr_can)) != 0) {
+               perror("bind(can)");
+               free(gwctl);
+               return 1;
+       }
+
+       /* send it to kernel gateway */
+       fd = open("/dev/canethgw", O_RDONLY);
+       if (fd == -1) {
+               perror("/dev/canethgw");
+               free(gwctl);
+               return 1;
+       }
+
+       gwctl->can_sock = can_sock;
+       gwctl->udp_sock = udp_sock;
+       gwctl->udp_dstcnt = dstcnt;
+       gwctl->udp_addrlen = sizeof(struct sockaddr_in);
+
+       if (ioctl(fd, CEGW_IOCTL_START, gwctl) != 0) {
+               perror("ioctl");
+               free(gwctl);
+               return 1;
+       }
+       printf("gateway successfully set and running\n");
+       free(gwctl);
+
+       /* sleep until someone kills me */
+       pause();
+
+       return 0;
+}
diff --git a/include/linux/can/canethgw.h b/include/linux/can/canethgw.h
new file mode 100644 (file)
index 0000000..f12e054
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef CANETHGW_H
+#define CANETHGW_H
+
+#include <linux/types.h>
+
+struct cegw_ioctl
+{
+       __u32 can_sock;
+       __u32 udp_sock;
+       __u32 udp_dstcnt;
+       __u32 udp_addrlen;
+       struct sockaddr_in udp_dst[0];
+};
+
+#define CEGW_IOCTL_BASE 'c'
+#define CEGW_IOCTL_START _IOW(CEGW_IOCTL_BASE, 0, struct cegw_ioctl)
+
+#endif /* CANETHGW_H */