X-Git-Url: http://rtime.felk.cvut.cz/gitweb/can-eth-gw.git/blobdiff_plain/a5d61b569fe49e2ca9f8f38f0af5a0b147f2b806..5f30138c72a8c4893b03b63664e3b6b1ba82e8df:/utils/cegw/cegw.c diff --git a/utils/cegw/cegw.c b/utils/cegw/cegw.c index 275d659..4787f55 100644 --- a/utils/cegw/cegw.c +++ b/utils/cegw/cegw.c @@ -1,281 +1,241 @@ +/* + * Copyright: (c) 2012 Czech Technical University in Prague + * + * Authors: + * Radek Matějka + * Michal Sojka + * + * 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 #include -#include +#include +#include +#include +#include #include "canethgw.h" -/** - * ToDo - * [ ] refactor error messages - * [ ] start/stop listening - * [ ] remove routing job - * [ ] recv netlink reponse - * - */ +unsigned int cegw_errno = 0; -#define CMD_STOP_LISTEN 1 -#define CMD_START_LISTEN 2 -#define CMD_ADD_RULE 4 +enum +{ + CEGW_ERR_UNKNOWN, + CEGW_ERR_COLON, + CEGW_ERR_UNEXLEN, + CEGW_ERR_ATON, + CEGW_ERR_PORT +}; -enum { - IF_UNDEF, - IF_CAN, - IF_ETH_UDP +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]); +} /** - * parses @in for eth address, valid input is - * e.g. udp@127.0.0.1:10502 or can@vcan0 + * 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 + * @return 0 on success, -1 otherwise */ -int read_addr_port( char* in, struct in_addr* addr, unsigned short* port ) +int read_addrport(char* in, struct in_addr* addr, unsigned short* port) { char* delim = NULL; - char addrstr[16]; + const int addrstr_len = 16; + char addrstr[addrstr_len]; int addrlen; - - if( (delim = strchr( in, ':' )) == NULL ) - { - fprintf( stderr, "error: ':'\n" ); + + if ((delim = strchr(in, ':')) == NULL) { + cegw_errno = CEGW_ERR_COLON; return -1; } /* get address */ addrlen = delim - in; - memcpy( addrstr, in, addrlen ); + if (addrlen > addrstr_len) { + cegw_errno = CEGW_ERR_UNEXLEN; + return -1; + } + + memcpy(addrstr, in, addrlen); addrstr[addrlen] = '\0'; - if( inet_aton( addrstr, addr ) == 0 ) - { - fprintf( stderr, "error: aton\n" ); + if (inet_aton(addrstr, addr) == 0) { + cegw_errno = CEGW_ERR_ATON; return -1; } /* get port */ - if( sscanf( delim, ":%hu", port ) != 1 ) /* todo: handle overflow */ - { - fprintf( stderr, "error: port\n" ); + /* ToDo: handle overflow */ + if (sscanf(delim, ":%hu", port) != 1) { + cegw_errno = CEGW_ERR_PORT; return -1; } return 0; } -char* read_iftype( char* in, int* iftype ) +int main(int argc, char* argv[]) { - char* ret = in+4; - - if( strncmp( "udp@", optarg, 4 ) == 0 ) - { - *iftype = IF_ETH_UDP; - return ret; - } - else if( strncmp( "tcp@", optarg, 4 ) == 0 ) - { - return NULL; - } - else if( strncmp( "can@", optarg, 4 ) == 0 ) + 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) { - *iftype = IF_CAN; - return ret; + printf(help_msg, argv[0], argv[0]); + return 0; } - - return NULL; -} -int main( int argc, char* argv[] ) -{ - int s; - int src_if = 0, dst_if = 0; - int can_ifidx = 0; - int tmp = 0; - int cmd = 0; - struct in_addr eth_addr; - unsigned short eth_port; - struct in_addr eth_listen_addr; - unsigned short eth_listen_port; - char* optstr; - char opt; - struct sockaddr_nl nladdr; - int err = 0; - - struct option long_opt[] = - { - { "start-listen", 1, NULL, 'l' }, - { "stop-listen" , 1, NULL, 'k' }, - { 0, 0, 0, 0 } - }; - - struct { - struct nlmsghdr nh; - struct rtcanmsg rtcan; - char buf[600]; /* enough? */ - } req; - - while( 1 ) - { - opt = getopt_long( argc, argv, "As:d:", long_opt, NULL ); - if( opt == -1 ) - break; + if (argc < 4) { + perr("not enought arguments"); + printf(help_msg, argv[0], argv[0]); + /* ToDo: print usage */ + return -1; + } - switch( opt ) - { - case 's': - cmd |= CMD_ADD_RULE; - if( (optstr = read_iftype( optarg, &src_if )) == NULL ) - { - fprintf( stderr, "error: bad input format\n" ); - goto syntax_error; - break; - } - - switch( src_if ) - { - case IF_CAN: - can_ifidx = if_nametoindex( optstr ); - break; - case IF_ETH_UDP: - read_addr_port( optstr, ð_addr, ð_port ); - break; - default: - fprintf( stderr, "error: unrecognized interface" ); - goto syntax_error; - break; + 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; } - - if( tmp != IF_ETH_UDP ) - { - fprintf( stderr, "error: -l bad input format\n" ); - goto syntax_error; - break; - } - - read_addr_port( optstr, ð_listen_addr, ð_listen_port ); /*chk*/ - case 'k': - cmd |= CMD_STOP_LISTEN; - break; - case '?': + dst->sin_port = htons(port); break; - default: - fprintf( stderr, "error: unknown option\n" ); - break; - } + } } - /* 2. do check on arguments */ - if( cmd & CMD_ADD_RULE ) - { - if( (src_if == 0 || dst_if == 0) || (src_if == dst_if) ) - { - fprintf( stderr, "error: source or destination not specified\n" ); - return -1; - } - - if( src_if == dst_if ) - { - fprintf( stderr, "error: source and destination same interface type\n" ); - } + /* prepare udp socket */ + udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (udp_sock == -1) { + fprintf(stderr, "error: udp socket(..) failed\n"); + free(gwctl); + return -1; } - - /* 3. prepare netlink message */ - req.nh.nlmsg_len = NLMSG_LENGTH( sizeof(struct rtcanmsg) ); - req.nh.nlmsg_type = RTM_NEWROUTE; - req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - req.nh.nlmsg_seq = 0; - req.nh.nlmsg_pid = 0; /* ? */ - - req.rtcan.can_family = AF_CAN; - req.rtcan.flags = 0; - if( cmd & CMD_ADD_RULE ) - { - req.rtcan.gwtype = (src_if == IF_CAN) ? CGW_TYPE_CAN_ETH_UDP : CGW_TYPE_ETH_CAN_UDP; - addattr_l( &req.nh, sizeof(req), CGW_CAN_IF, &can_ifidx, sizeof(can_ifidx) ); - switch( req.rtcan.gwtype ) - { - case CGW_TYPE_CAN_ETH_UDP: - addattr_l( &req.nh, sizeof(req), CGW_ETH_IP, ð_addr, sizeof(eth_addr) ); - addattr_l( &req.nh, sizeof(req), CGW_ETH_PORT, ð_port, sizeof(eth_port) ); - break; - case CGW_TYPE_ETH_CAN_UDP: - break; - default: - break; - } + if (bind(udp_sock, (struct sockaddr *)&udp_addr, sizeof(struct sockaddr_in)) != 0) { + fprintf(stderr, "error: udp bind(..) failed\n"); + free(gwctl); + return -1; } - - if( cmd & CMD_START_LISTEN ) - { - req.rtcan.gwtype = CGW_TYPE_CONFIG; - addattr_l( &req.nh, sizeof(req), CGW_LISTEN_IP, ð_listen_addr, sizeof(eth_listen_addr) ); - addattr_l( &req.nh, sizeof(req), CGW_LISTEN_PORT, ð_listen_port, sizeof(eth_listen_port) ); - printf( "start listen: %x, %hu\n", eth_listen_addr, eth_listen_port ); + + /* prepare can socket */ + can_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (can_sock == -1) { + fprintf(stderr, "error: can socket(..) failed\n"); + free(gwctl); + return -1; } - - /* 4. 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_pid = 0; - nladdr.nl_groups = 0; - err = sendto( s, &req, req.nh.nlmsg_len, 0, - (struct sockaddr*)&nladdr, sizeof(nladdr)); - if (err < 0) - { - perror( "netlink sendto" ); - return err; + if (bind(can_sock, (struct sockaddr *)&can_addr, sizeof(struct sockaddr_can)) != 0) { + fprintf(stderr, "error: can bind(..) failed\n"); + free(gwctl); + return -1; + } + + /* send it to kernel gateway */ + fd = open("/dev/cegw", O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error: could not open device file\n"); + 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(NULL); + free(gwctl); + return -1; + } + printf("gateway successfully set and running\n"); + free(gwctl); + + /* sleep until someone kills me */ + while (1) { + sleep(UINT_MAX); } - - /* ack? */ - + return 0; -syntax_error: - return -1; }