]> rtime.felk.cvut.cz Git - can-eth-gw.git/blobdiff - utils/cegw/cegw.c
netdevice_notifier implemented; test script; cegwbench rxtimeo
[can-eth-gw.git] / utils / cegw / cegw.c
index efbe2c775bcc4e1dd711d6535199a1fa1a4a14fc..c5c27b8c7d27a81084034f389032577d2293054b 100644 (file)
 #include <arpa/inet.h>
 #include <linux/can.h>
 #include <linux/types.h>
+#include <unistd.h>
 #include "canethgw.h"
 
 /**
- * ToDo
- * [ ] refactor error messages
+ * ToDo:
+ * [ ] print usage, on -h and plain execution
  * [ ] start/stop listening
- * [ ] remove routing job
- * [ ] recv netlink reponse
- * 
+ * [ ] split to files
  */
 
 #define CEGW_CMD_ADD     1
@@ -49,8 +48,16 @@ struct cegw_data
 struct cegw_nlmsg 
 {
        struct nlmsghdr nh;
-       struct rtcanmsg rtcan;
-       char buf[600]; /* enough? */
+       struct rtmsg rt;
+       char buf[768]; /* enough? */
+};
+
+struct list_item
+{
+       int type;
+       int can;
+       struct in_addr ip;
+       unsigned short port;
 };
 
 unsigned int cegw_errno = 0;
@@ -60,23 +67,40 @@ enum
        CEGW_ERR_UNKNOWN,
        CEGW_ERR_IF_UNSPEC,
        CEGW_ERR_IF_SAME,
-       CEGW_ERR_IF_TYPE
+       CEGW_ERR_IF_TYPE,
+       CEGW_ERR_IF_CAN,
+       CEGW_ERR_IF_ETH,
+       CEGW_ERR_COLON,
+       CEGW_ERR_ATON,
+       CEGW_ERR_PORT
 };
 
 char* cegw_errlist[] =
 {
-       [ CEGW_ERR_UNKNOWN ]   = "unknown error",
+       [ CEGW_ERR_UNKNOWN   ] = "",
        [ CEGW_ERR_IF_UNSPEC ] = "source or destination not specified",
        [ CEGW_ERR_IF_SAME   ] = "source and destination have same interface type",
-       [ CEGW_ERR_IF_TYPE    ] = "unknown interface type"
+       [ CEGW_ERR_IF_TYPE   ] = "unknown interface type",
+       [ CEGW_ERR_IF_CAN    ] = "invalid can interface",
+       [ CEGW_ERR_IF_ETH    ] = "invalid eth interface",
+       [ CEGW_ERR_COLON     ] = "expected ':' (<ip>:<port>)",
+       [ CEGW_ERR_ATON      ] = "ip address mismatch",
+       [ CEGW_ERR_PORT      ] = "port number"
 };
 
 static void perr( char* s )
 {
        if( s )
        {
-               fprintf( stderr, "error: %s; %s\n", s,
-               cegw_errlist[ cegw_errno ] );
+               if( cegw_errno == 0 )
+               {
+                       fprintf( stderr, "error: %s\n", s );
+
+               } else
+               {
+                       fprintf( stderr, "error: %s, %s\n", s,
+                                cegw_errlist[ cegw_errno ] );
+               }
                return;
        }
 
@@ -84,14 +108,15 @@ static void perr( char* s )
 }
 
 /**
- * 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. udp@127.0.0.1:10502 or can@vcan0.
  *
+ * @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];
@@ -99,7 +124,7 @@ int read_addr_port( char* in, struct in_addr* addr, unsigned short* port )
        
        if( (delim = strchr( in, ':' )) == NULL )
        {
-               fprintf( stderr, "error: ':'\n" );
+               cegw_errno = CEGW_ERR_COLON;
                return -1;
        }
 
@@ -109,14 +134,14 @@ int read_addr_port( char* in, struct in_addr* addr, unsigned short* port )
        addrstr[addrlen] = '\0';
        if( inet_aton( addrstr, addr ) == 0 )
        {
-               fprintf( stderr, "error: aton\n" );
+               cegw_errno = CEGW_ERR_ATON;
                return -1;
        }
 
        /* get port */
-       if( sscanf( delim, ":%hu", port ) != 1 ) /* todo: handle overflow */
+       if( sscanf( delim, ":%hu", port ) != 1 ) /* ToDo: handle overflow */
        {
-               fprintf( stderr, "error: port\n" );
+               cegw_errno = CEGW_ERR_PORT;
                return -1;
        }
 
@@ -124,7 +149,8 @@ int read_addr_port( char* in, struct in_addr* addr, unsigned short* port )
 }
 
 /**
- * Reads @in for iftype (e.g. "can@" or "udp@").
+ * read_iftype - reads @in for iftype
+ * Iftype type is e.g. "can@" or "udp@".
  *
  * @param[in] in string to search in
  * @param[out] iftype iftype detected
@@ -140,21 +166,33 @@ char* read_iftype( char* in, int* iftype )
                return ret;
        }
        /*
-       else if( strncmp( "tcp@", optarg, 4 ) == 0 )
+       if( strncmp( "tcp@", optarg, 4 ) == 0 )
        {
                return NULL;
        }
        */
-       else if( strncmp( "can@", optarg, 4 ) == 0 )
+       if( strncmp( "can@", optarg, 4 ) == 0 )
        {
                *iftype = IF_CAN;
                return ret;
        }
        
-       errno = CEGW_ERR_IF_TYPE;
+       cegw_errno = CEGW_ERR_IF_TYPE;
        return NULL;
 }
 
+/* ToDo: move to common */
+
+/**
+ * read_if - reads interface from @in
+ * Function analyzes @in for interface specification in format
+ * <if>@<ip>:<port>, where <if> is can or udp, <ip> is address in dotted
+ * format
+ *
+ * @param[in]  in string to search in
+ * @param[out] iftype interface type (IF_CAN or IF_ETH_UDP)
+ * @param[out] d ip and port is stored to @d
+ */
 int read_if( char* in, int* iftype, struct cegw_data* d )
 {
        char* optstr = NULL;
@@ -168,9 +206,17 @@ int read_if( char* in, int* iftype, struct cegw_data* d )
        {
                case IF_CAN:
                        d->can_ifidx = if_nametoindex( optstr );
+                       if( d->can_ifidx == 0 )
+                       {
+                               cegw_errno = CEGW_ERR_IF_CAN;
+                               return -1;
+                       }
                        break;
                case IF_ETH_UDP:
-                       read_addr_port( optstr, &d->eth_addr, &d->eth_port );
+                       if( read_addrport( optstr, &d->eth_addr, &d->eth_port ) != 0 )
+                       {
+                               return -1;
+                       }
                        break;
                default:
                        return -1;
@@ -182,6 +228,8 @@ int read_if( char* in, int* iftype, struct cegw_data* d )
 
 inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
 {
+       int gwtype = 0;
+
        req->nh.nlmsg_type  = RTM_NEWROUTE;
        if( (d->src_if == 0 || d->dst_if == 0) )
        {
@@ -195,47 +243,68 @@ inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
                return -cegw_errno;
        }
 
-       req->rtcan.gwtype = (d->src_if == IF_CAN) ? CGW_TYPE_CAN_ETH_UDP : CGW_TYPE_ETH_CAN_UDP;
-       addattr_l( &req->nh, sizeof(*req), CGW_CAN_IF, &d->can_ifidx, sizeof(d->can_ifidx) );
-       switch( req->rtcan.gwtype )
-       {
-               case CGW_TYPE_CAN_ETH_UDP:
-                       addattr_l( &req->nh, sizeof(*req), CGW_ETH_IP, &d->eth_addr, sizeof(d->eth_addr) );
-                       addattr_l( &req->nh, sizeof(*req), CGW_ETH_PORT, &d->eth_port, sizeof(d->eth_port) );
-                       break;
-               case CGW_TYPE_ETH_CAN_UDP:
-                       break;
-               default:
-                       break;
-       }
+       gwtype = (d->src_if == IF_CAN) ? CEGW_RULE_CAN_ETH : CEGW_RULE_ETH_CAN;
+       addattr_l( &req->nh, sizeof(*req), CEGW_CAN_IFINDEX, &d->can_ifidx, sizeof(d->can_ifidx) );
+       addattr_l( &req->nh, sizeof(*req), CEGW_ETH_IP, &d->eth_addr, sizeof(d->eth_addr) );
+       addattr_l( &req->nh, sizeof(*req), CEGW_ETH_PORT, &d->eth_port, sizeof(d->eth_port) );
+       addattr32( &req->nh, sizeof(*req), CEGW_CMD_INFO, gwtype );
 
        return 0;
 }
 
 inline static int cegw_listen( struct cegw_nlmsg* req, struct cegw_data* d )
 {
-       req->nh.nlmsg_type  = RTM_NEWROUTE;
-       req->rtcan.gwtype = CGW_TYPE_CONFIG;
-       addattr_l( &req->nh, sizeof(req), CGW_LISTEN_IP, &d->eth_listen_addr, sizeof(d->eth_listen_addr) );
-       addattr_l( &req->nh, sizeof(req), CGW_LISTEN_PORT, &d->eth_listen_port, sizeof(d->eth_listen_port) );
-       printf( "start listen: %x, %hu\n", d->eth_listen_addr, d->eth_listen_port );
+       req->nh.nlmsg_type = RTM_NEWROUTE;
+       addattr32( &req->nh, sizeof(*req), CEGW_CMD_INFO, CEGW_LISTEN );
+       addattr_l( &req->nh, sizeof(*req), CEGW_ETH_IP, &d->eth_listen_addr, sizeof(d->eth_listen_addr) );
+       addattr_l( &req->nh, sizeof(*req), CEGW_ETH_PORT, &d->eth_listen_port, sizeof(d->eth_listen_port) );
 
        return 0;
 }
 
 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;
 }
 
 inline static int cegw_flush( struct cegw_nlmsg* req, struct cegw_data* d )
 {
+       addattr32( &req->nh, sizeof(*req), CEGW_CMD_INFO, CEGW_FLUSH );
        req->nh.nlmsg_type  = RTM_DELROUTE;
-       req->rtcan.gwtype = CGW_TYPE_CAN_ETH_UDP;
-       req->rtcan.flags = CEGW_FLUSH; 
        return 0;
 }
 
+void print_list_item( struct list_item* li )
+{
+       char ifname[IF_NAMESIZE];
+       const char src_width = 21;
+       char* dotaddr;
+       int tmp;
+
+       if( if_indextoname( li->can, ifname ) == NULL )
+       {
+               strncpy( ifname, "unknown", IF_NAMESIZE );
+       }
+
+       /* ToDo listening at */
+       switch( li->type )
+       {
+               case CEGW_RULE_CAN_ETH:
+                       printf( "can@%-*s -> udp@%s:%hu\n", src_width, ifname, \
+                                inet_ntoa(li->ip), li->port );
+                       break;
+               case CEGW_RULE_ETH_CAN:
+                       dotaddr = inet_ntoa(li->ip);
+                       tmp = src_width - strlen(dotaddr) - 1;
+                       printf( "udp@%s:%-*hu -> can@%s\n", inet_ntoa(li->ip), \
+                                tmp, li->port, ifname );
+                       break;
+       }
+}
+
 int main( int argc, char* argv[] )
 {
        int s;
@@ -247,10 +316,13 @@ int main( int argc, char* argv[] )
        int err = 0;
        struct cegw_nlmsg req;
        struct cegw_data d;
-       char rxbuf[256];
+       char rxbuf[8192]; /* ToDo: /linux/netlink.h? */
        int rsize = 0;
        struct nlmsghdr* nlh;
        struct nlmsgerr* rte;
+       struct rtattr* rta;
+       int len;
+       struct list_item li;
 
        memset( &d, 0, sizeof(d) );
 
@@ -265,7 +337,7 @@ int main( int argc, char* argv[] )
 
        while( 1 )
        {
-               opt = getopt_long( argc, argv, "As:d:", long_opt, NULL );
+               opt = getopt_long( argc, argv, "AFLl:s:d:", long_opt, NULL );
                if( opt == -1 )
                        break;
 
@@ -278,68 +350,70 @@ int main( int argc, char* argv[] )
                                cmd |= CEGW_CMD_FLUSH;
                                break;
                        case 'L':
-                               cmd |= CEGW_CMD_FLUSH;
+                               cmd |= CEGW_CMD_LIST;
                                break;
                        case 'l':
                                cmd |= CEGW_CMD_LISTEN;
                                if( (optstr = read_iftype( optarg, &tmp )) == NULL )
                                {
-                                       perr( "listen" );
+                                       perr( "'--listen'" );
                                        return -1;
                                }       
                                if( tmp != IF_ETH_UDP )
                                {
-                                       fprintf( stderr, "error: -l bad input format\n" );
+                                       perr( "'--listen' expects udp interface" );
                                        return -1;
                                }       
-                               read_addr_port( optstr, &d.eth_listen_addr, &d.eth_listen_port ); /*chk*/
+                               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 )
                                {
-                                       fprintf( stderr, "error: bad input format\n" );
-                                       goto syntax_error;
-                                       break;
+                                       perr( "'-s'" );
+                                       return -1;
                                }
                                break;
                        case 'd':
                                if( read_if( optarg, &d.dst_if, &d ) != 0 )
                                {
-                                       fprintf( stderr, "error: bad input format\n" );
-                                       goto syntax_error;
-                                       break;
+                                       perr( "'-d'" );
+                                       return -1;
                                }
                                break;
                        case '?':
-                               fprintf( stderr, "error: unknown option\n" );
                                return -1;
                                break;
                        default:
-                               fprintf( stderr, "error: unknown option\n" );
+                               perr( "unknown option" );
                                return -1;
                                break;
                }                               
        }
 
        /* prepare netlink message */
-       req.nh.nlmsg_len   = NLMSG_LENGTH( sizeof(struct rtcanmsg) );
-       req.nh.nlmsg_type  = RTM_NEWROUTE;
+       req.nh.nlmsg_len   = NLMSG_LENGTH( sizeof(struct rtmsg) );
+       //req.nh.nlmsg_type;
        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;
+
+       memset( &req.rt, 0, sizeof(req.rt) );
+       req.rt.rtm_family = AF_CAN;
 
        switch( cmd )
        {
                case 0:
-                       fprintf( stderr, "error: command not specified\n" );
+                       perr( "command not specified" );
+                       return -1;
                        break;
                case CEGW_CMD_ADD:
                        if( cegw_add( &req, &d ) != 0 )
                        {
-                               perr( "add" );
+                               perr( "'--add'" );
                                return -1;
                        }
                        break;
@@ -353,7 +427,7 @@ int main( int argc, char* argv[] )
                        cegw_listen( &req, &d );
                        break;
                default:
-                       fprintf( stderr, "error: command mismatch\n" );
+                       perr( "command mismatch" );
                        break;
        }
 
@@ -362,37 +436,86 @@ int main( int argc, char* argv[] )
        
        memset( &nladdr, 0, sizeof(nladdr) );
        nladdr.nl_family = AF_NETLINK;
-       nladdr.nl_pid    = 0;
+       nladdr.nl_pad = 0;
+       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)
+                     (struct sockaddr*)&nladdr, sizeof(nladdr) );
+       if( err < 0 )
        {
                perror( "netlink sendto" );
-               return err;
+               return -1;
        }
        
        /* recv */
        rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
        if( rsize < 0 )
        {
-               fprintf( stderr, "error: recv\n" );
+               perr( "recv" );
                return -1;
        }
        nlh = (struct nlmsghdr*)rxbuf;
-       if( nlh->nlmsg_type != NLMSG_ERROR )
+
+       if( nlh->nlmsg_type == NLMSG_ERROR )
        {
-               fprintf( stderr, "error: unexpected netlink answer\n" );
-               return -1;
+               rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
+               err = rte->error;
+
+               if( err == 0  )
+               {
+                       printf( "%s\n", strerror(abs(err)) );
+                       return 0;
+               } else
+               {
+                       printf( "netlink error: %s\n", strerror(abs(err)) );
+                       return -1;
+               }
        }
-       rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
-       err = rte->error;
-       if( err < 0 )
-               fprintf( stderr, "error: netlink; %s\n", strerror(abs(err)) );
-       
+
+       if( cmd & CEGW_CMD_LIST )
+       {
+               /* ToDo recv while */
+               printf( "%10ssource%20sdestination\n", "", "" );
+               while( 1 )
+               {
+                       if( !NLMSG_OK( nlh, rsize ) )
+                       {
+                               break;
+                       }
+                       if( nlh->nlmsg_type == NLMSG_DONE )
+                       {
+                               break;
+                       }
+                       /* ToDo: NLMSG_ERR */
+                       rta = NLMSG_DATA( nlh );
+                       len = NLMSG_PAYLOAD( nlh, 0 );
+                       for( ;RTA_OK(rta, len); rta = RTA_NEXT(rta,len) )
+                       {
+                               switch( rta->rta_type )
+                               {
+                                       case CEGW_TYPE:
+                                               li.type = *(int*)RTA_DATA(rta);
+                                               break;
+                                       case CEGW_CAN_IFINDEX:
+                                               li.can = *(int*)RTA_DATA(rta);
+                                               break;
+                                       case CEGW_ETH_IP:
+                                               li.ip = *(struct in_addr*)RTA_DATA(rta);
+                                               break;
+                                       case CEGW_ETH_PORT:
+                                               li.port = *(unsigned short*)RTA_DATA(rta);
+                                               break;
+                                       /* case CGW_ETH_PROTO */
+                               }
+                       }
+
+                       print_list_item( &li );
+
+                       nlh = NLMSG_NEXT( nlh, rsize );
+               }
+       }
+
        return 0;
-syntax_error:
-       return -1;
 }