From: Michal Sojka Date: Thu, 20 Dec 2012 19:11:48 +0000 (+0100) Subject: Add CAN-Ethernet gateway - user space part X-Git-Url: http://rtime.felk.cvut.cz/gitweb/can-utils.git/commitdiff_plain/b5c645f83c7fc395b36918f79634ea553eae4e43 Add CAN-Ethernet gateway - user space part Signed-off-by: Radek Matějka Signed-off-by: Michal Sojka --- diff --git a/Makefile b/Makefile index c3e6d9c..f4cdb2c 100644 --- 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 index 0000000..3e0e9d5 --- /dev/null +++ b/cegw.c @@ -0,0 +1,240 @@ +/* + * Copyright: (c) 2012 Czech Technical University in Prague + * + * Authors: + * Radek Matějka + * Michal Sojka + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ':' (:)", + [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 : :\n" + " [list of additional udp recipients ]\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; iudp_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 index 0000000..f12e054 --- /dev/null +++ b/include/linux/can/canethgw.h @@ -0,0 +1,18 @@ +#ifndef CANETHGW_H +#define CANETHGW_H + +#include + +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 */