]> rtime.felk.cvut.cz Git - can-eth-gw.git/commitdiff
list routing rules
authorRadek Matejka <radek.matejka@gmail.com>
Mon, 30 Jul 2012 16:09:44 +0000 (18:09 +0200)
committerRadek Matejka <radek.matejka@gmail.com>
Mon, 30 Jul 2012 16:09:44 +0000 (18:09 +0200)
Listing command is implemented in cegw and corresponding dump_job function
was added to canethgw module.

kernel/canethgw.c
kernel/canethgw.h
utils/cegw/cegw.c

index 02aa60afe6cea7b0abce0a15f4d641b749a47494..d5a928ac57995393399f7ecb52fcb2020830798c 100644 (file)
 #include "canethgw.h"
 
 /**
- * ToDo
- *  [ ] check every input
- *  [ ] refactor
+ * ToDo:
+ * [ ] encapsule module - check inputs
+ * [ ] refactor - chc .h
+ * [ ] dump callback
+ * [ ] rtnl vs nl functions
+ * [ ] stop threads
+ * [ ] change listening
+ * [ ] clean exit - threads, jobs
  */
 
 MODULE_LICENSE( "GPL" );
@@ -161,7 +166,7 @@ static int gw_can_recv( void* data )
                        eth_addr = job->dst_addr;
                        eth_port = job->dst_port;
                        rcu_read_unlock();
-                       printk( KERN_INFO "%x\n", eth_addr );
+                       printk( KERN_INFO "%x\n", eth_addr.s_addr );
                        if( job->src_if_idx == ca.can_ifindex )
                                gw_udp_send( &cf, eth_addr, eth_port );
                }
@@ -197,11 +202,9 @@ inline static void gw_can_send( struct can_frame* cf, int ifidx )
 static int cegw_create_job( struct sk_buff* skb, struct nlmsghdr* nlh, void* arg )
 {
        struct nlattr* tb[ CGW_MAX+1 ];
-       struct rtmsg *r;
        struct can_eth_gw* cethgw = NULL;
        struct eth_can_gw* ecangw = NULL;
        int err = 0;
-       int type = 0;
        
        /* ToDo: size check
        if (nlmsg_len(nlh) < sizeof(*r))
@@ -311,6 +314,76 @@ static int cegw_remove_job( struct sk_buff* skb, struct nlmsghdr* nlh, void* arg
        return 0;
 }
 
+static int cegw_dump_job( struct sk_buff* skb, struct netlink_callback* cb )
+{
+       struct can_eth_gw* ceth;
+       struct eth_can_gw* ecan;
+       struct hlist_node* pos;
+       struct nlmsghdr* nlh;
+       int idx = 0;
+       int s_idx = cb->args[0];
+       int ifidx, type;
+       struct in_addr dst_ip; 
+       unsigned short dst_port;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu( ecan, pos, &eth_can_job, list )
+       {
+
+       //      if( idx < s_idx )
+       //              goto cont1;
+
+               nlh = nlmsg_put( skb, 0, 0, 0, 0, 0 );
+
+               ifidx = ecan->dst_if_idx;
+               type = CGW_TYPE_ETH_CAN_UDP;
+               nla_put( skb, CGW_TYPE, sizeof(type), &type );
+               nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN( sizeof(type) );
+
+               nla_put( skb, CGW_CAN_IF, sizeof(ifidx), &ifidx ); /* ToDo return */
+               nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN( sizeof(ifidx) );
+cont1:
+               idx++;
+       }
+       rcu_read_unlock();
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu( ceth, pos, &can_eth_job, list )
+       {
+       //      if( idx < s_idx )
+       //              goto cont2;
+       
+               nlh = nlmsg_put( skb, 0, 0, 0, 0, 0 );
+
+               ifidx = ceth->src_if_idx;
+               type = CGW_TYPE_CAN_ETH_UDP;
+               dst_ip = ceth->dst_addr;
+               dst_port = ceth->dst_port;
+
+               nla_put( skb, CGW_TYPE, sizeof(type), &type );
+               nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN( sizeof(type) );
+
+               nla_put( skb, CGW_CAN_IF, sizeof(ifidx), &ifidx ); /* ToDo return */
+               nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN( sizeof(ifidx) );
+
+               nla_put( skb, CGW_ETH_IP, sizeof(dst_ip), &dst_ip ); /* ToDo return */
+               nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN( sizeof(dst_ip) );
+
+               nla_put( skb, CGW_ETH_PORT, sizeof(dst_port), &dst_port ); /* ToDo return */
+               nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN( sizeof(dst_port) );
+       
+               //nla_put( skb, CGW_ETH_IP, sizeof() IP_ADDR  )
+cont2:
+               idx++;
+       }
+       rcu_read_unlock();
+
+       /* ToDo nlmsg_cancel */
+       cb->args[0] = idx;
+
+       return skb->len;
+}
+
 static int listen( int can_ifidx, struct in_addr eth_addr, u16 eth_port )
 {
        struct sockaddr_in udp_addr;
@@ -382,7 +455,13 @@ static int __init cangw_init( void )
        }
        
        /* subscribe to netlink */
-       //if( __rtnl_register( PF_CAN, RTM_GETROUTE,  ) != 0 )
+       if( __rtnl_register( PF_CAN, RTM_GETROUTE, NULL, cegw_dump_job, NULL ) != 0 )
+       {
+               printk( KERN_ERR "error: rtnl_register fail\n" );
+               sock_release( udp_sock );
+               sock_release( can_sock );
+               return -1;
+       }
        __rtnl_register( PF_CAN, RTM_NEWROUTE, cegw_create_job, NULL, NULL );
        __rtnl_register( PF_CAN, RTM_DELROUTE, cegw_remove_job, NULL, NULL );
        //__rtnl_register( PF_CAN, RTM_DELROUTE,  )     
index d8743d67b09efb86c26e16e3928bae5500a58ac1..3da136abb3da0c388938155372937794a0c8faa2 100644 (file)
@@ -29,20 +29,21 @@ enum {
 enum {
        CGW_UNSPEC,
        CGW_MOD_AND,    /* CAN frame modification binary AND */
-       CGW_MOD_OR,     /* CAN frame modification binary OR */
+       CGW_MOD_OR,         /* CAN frame modification binary OR */
        CGW_MOD_XOR,    /* CAN frame modification binary XOR */
        CGW_MOD_SET,    /* CAN frame modification set alternate values */
-       CGW_CS_XOR,     /* set data[] XOR checksum into data[index] */
+       CGW_CS_XOR,         /* set data[] XOR checksum into data[index] */
        CGW_CS_CRC8,    /* set data[] CRC8 checksum into data[index] */
        CGW_HANDLED,    /* number of handled CAN frames */
        CGW_DROPPED,    /* number of dropped CAN frames */
-       CGW_CAN_IF,     /* ifindex of source network interface */
+       CGW_CAN_IF,         /* ifindex of source network interface */
        CGW_LISTEN_IP,
        CGW_LISTEN_PORT,
+       CGW_TYPE,
        CGW_CMD_INFO,
        CGW_ETH_IP,
        CGW_ETH_PORT,
-       CGW_ETH_PROTO,  /* ifindex of destination network interface */
+       CGW_ETH_PROTO,
        CGW_FILTER,     /* specify struct can_filter on source CAN device */
        __CGW_MAX
 };
index f47418b559f80e33a1c0b91bcf1d533dc4e6fc5c..dceb5a04e8f6c57ed06c478005a973147dbaf1bd 100644 (file)
 #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
@@ -54,6 +52,14 @@ struct cegw_nlmsg
        char buf[768]; /* enough? */
 };
 
+struct list_item
+{
+       int type;
+       int can;
+       struct in_addr ip;
+       unsigned short port;
+};
+
 unsigned int cegw_errno = 0;
 
 enum 
@@ -61,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;
        }
 
@@ -85,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];
@@ -100,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;
        }
 
@@ -110,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;
        }
 
@@ -125,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
@@ -141,21 +166,31 @@ 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;
 }
 
+/**
+ * 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;
@@ -169,9 +204,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;
@@ -220,32 +263,53 @@ inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
 
 inline static int cegw_listen( struct cegw_nlmsg* req, struct cegw_data* d )
 {
-       req->nh.nlmsg_type  = RTM_NEWROUTE;
+       req->nh.nlmsg_type = RTM_NEWROUTE;
        addattr32( &req->nh, sizeof(*req), CGW_CMD_INFO, CEGW_LISTEN );
        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 );
+       printf( "listen at: %x, %hu\n", d->eth_listen_addr.s_addr, 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 )
 {
-//     char foo[2];
        addattr32( &req->nh, sizeof(*req), CGW_CMD_INFO, CEGW_FLUSH );
        req->nh.nlmsg_type  = RTM_DELROUTE;
-//     addattr_l( &req->nh, sizeof(*req), 0, &foo, sizeof(foo) );
-       //addattr_l( &req->nh, sizeof(*req), CGW_CAN_IF, &d->can_ifidx, sizeof(d->can_ifidx) );
-//     req->rtcan.gwtype = CGW_TYPE_CAN_ETH_UDP;
-//     req->rtcan.flags = 5;//CEGW_FLUSH; 
        return 0;
 }
 
+void print_list_item( struct list_item* li )
+{
+       char ifname[IF_NAMESIZE];
+       const char src_width = 21;
+
+       if( if_indextoname( li->can, ifname ) == NULL )
+       {
+               strncpy( ifname, "unknown", IF_NAMESIZE );
+       }
+
+       /* ToDo listening at */
+       switch( li->type )
+       {
+               case CGW_TYPE_CAN_ETH_UDP:
+                       printf( "can@%-*s -> udp@%s:%hu\n", src_width, ifname, \
+                                inet_ntoa(li->ip), li->port );
+                       break;
+               case CGW_TYPE_ETH_CAN_UDP:
+                       printf( "udp@%-*s -> can@%s\n", src_width, "*:*", ifname );
+                       break;
+       }
+}
+
 int main( int argc, char* argv[] )
 {
        int s;
@@ -257,12 +321,13 @@ int main( int argc, char* argv[] )
        int err = 0;
        struct cegw_nlmsg req;
        struct cegw_data d;
-       char rxbuf[256]; /* 8129+ ? */
+       char rxbuf[8192]; /* ToDo: /linux/netlink.h? */
        int rsize = 0;
        struct nlmsghdr* nlh;
        struct nlmsgerr* rte;
-
-       printf( "size=%d\n", NLMSG_LENGTH(sizeof(struct rtmsg)) );
+       struct rtattr* rta;
+       int len;
+       struct list_item li;
 
        memset( &d, 0, sizeof(d) );
 
@@ -277,7 +342,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;
 
@@ -290,44 +355,45 @@ 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;
                }                               
@@ -335,7 +401,7 @@ int main( int argc, char* argv[] )
 
        /* prepare netlink message */
        req.nh.nlmsg_len   = NLMSG_LENGTH( sizeof(struct rtmsg) );
-       req.nh.nlmsg_type  = RTM_DELROUTE;
+       //req.nh.nlmsg_type;
        req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
        req.nh.nlmsg_seq   = 0;
        req.nh.nlmsg_pid   = 0; /* ? */
@@ -343,18 +409,16 @@ int main( int argc, char* argv[] )
        memset( &req.rt, 0, sizeof(req.rt) );
        req.rt.rtm_family = AF_CAN;
 
-       //req.rt.gwtype = CGW_TYPE_CAN_CAN;
-       //req.rt.flags = 0;
-
        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;
@@ -368,7 +432,7 @@ int main( int argc, char* argv[] )
                        cegw_listen( &req, &d );
                        break;
                default:
-                       fprintf( stderr, "error: command mismatch\n" );
+                       perr( "command mismatch" );
                        break;
        }
 
@@ -391,24 +455,79 @@ int main( int argc, char* argv[] )
        
        /* recv */
        rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
+       printf( "recv size=%d\n", rsize );
        if( rsize < 0 )
        {
-               fprintf( stderr, "error: recv\n" );
+               perr( "recv" );
                return -1;
        }
        nlh = (struct nlmsghdr*)rxbuf;
-       if( nlh->nlmsg_type != NLMSG_ERROR )
+
+       if( cmd & CEGW_CMD_LIST )
        {
-               fprintf( stderr, "error: unexpected netlink answer=%d\n", nlh->nlmsg_type );
-               return -1;
+               printf( "recv nlmsg_type=%d\n", nlh->nlmsg_type );
+               if( nlh->nlmsg_type == NLMSG_ERROR )
+               {
+                       struct nlmsgerr* nlerr = NLMSG_DATA( nlh );
+                       int err = nlerr->error;
+                       printf( "nlerror: %d,%s\n", err, strerror(abs(err)) );
+               }
+               /* ToDo recv while */
+               printf( "%10ssource%20sdestination\n", "", "" );
+               while( 1 )
+               {
+                       if( !NLMSG_OK( nlh, rsize ) )
+                       {
+                               puts( "NLMSG_OK\n" );
+                               break;
+                       }
+                       if( nlh->nlmsg_type == NLMSG_DONE )
+                       {
+                               puts( "NLMSG_DONE" );
+                               break;
+                       }
+                       /* ToDo: NLMSG_ERR */
+
+                       rta = NLMSG_DATA( nlh );
+                       //rta = (struct rtattr*)( ((char *)rtm) + NLMSG_ALIGN(sizeof(struct rtmsg)) );
+                       len = NLMSG_PAYLOAD( nlh, 0 );
+                       for( ;RTA_OK(rta, len); rta = RTA_NEXT(rta,len) )
+                       {
+                               switch( rta->rta_type )
+                               {
+                                       case CGW_TYPE:
+                                               li.type = *(int*)RTA_DATA(rta);
+                                               break;
+                                       case CGW_CAN_IF:
+                                               li.can = *(int*)RTA_DATA(rta);
+                                               break;
+                                       case CGW_ETH_IP:
+                                               li.ip = *(struct in_addr*)RTA_DATA(rta);
+                                               break;
+                                       case CGW_ETH_PORT:
+                                               li.port = *(unsigned short*)RTA_DATA(rta);
+                                               break;
+                                       /* case CGW_ETH_PROTO */
+                               }
+                       }
+
+                       print_list_item( &li );
+
+                       nlh = NLMSG_NEXT( nlh, rsize );
+               }
+       } else
+       {
+               if( nlh->nlmsg_type != NLMSG_ERROR )
+               {
+                       fprintf( stderr, "error: unexpected netlink answer=%d\n", nlh->nlmsg_type );
+                       return -1;
+               }
+               rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
+               err = rte->error;
+               if( err < 0 )
+                       fprintf( stderr, "error: netlink(%d); %s\n", err, strerror(abs(err)) );
        }
-       rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
-       err = rte->error;
-       if( err < 0 )
-               fprintf( stderr, "error: netlink(%d); %s\n", err, strerror(abs(err)) );
        
        return 0;
-syntax_error:
-       return -1;
 }