#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" );
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 );
}
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))
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;
}
/* 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, )
#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
char buf[768]; /* enough? */
};
+struct list_item
+{
+ int type;
+ int can;
+ struct in_addr ip;
+ unsigned short port;
+};
+
unsigned int cegw_errno = 0;
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;
}
}
/**
- * 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];
if( (delim = strchr( in, ':' )) == NULL )
{
- fprintf( stderr, "error: ':'\n" );
+ cegw_errno = CEGW_ERR_COLON;
return -1;
}
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;
}
}
/**
- * 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
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;
{
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;
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;
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) );
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;
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 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; /* ? */
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;
cegw_listen( &req, &d );
break;
default:
- fprintf( stderr, "error: command mismatch\n" );
+ perr( "command mismatch" );
break;
}
/* 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;
}