From: Radek Matejka Date: Mon, 30 Jul 2012 16:09:44 +0000 (+0200) Subject: list routing rules X-Git-Url: http://rtime.felk.cvut.cz/gitweb/can-eth-gw.git/commitdiff_plain/7662155d7a07a0c89bb0e0a2c48706c37c4d370c list routing rules Listing command is implemented in cegw and corresponding dump_job function was added to canethgw module. --- diff --git a/kernel/canethgw.c b/kernel/canethgw.c index 02aa60a..d5a928a 100644 --- a/kernel/canethgw.c +++ b/kernel/canethgw.c @@ -13,9 +13,14 @@ #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, ð_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, ) diff --git a/kernel/canethgw.h b/kernel/canethgw.h index d8743d6..3da136a 100644 --- a/kernel/canethgw.h +++ b/kernel/canethgw.h @@ -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 }; diff --git a/utils/cegw/cegw.c b/utils/cegw/cegw.c index f47418b..dceb5a0 100644 --- a/utils/cegw/cegw.c +++ b/utils/cegw/cegw.c @@ -16,12 +16,10 @@ #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 ':' (:)", + [ 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 + * @:, where is can or udp, 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; }