#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
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;
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;
}
+/* 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;
{
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_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) )
{
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;
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) );
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 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;
cegw_listen( &req, &d );
break;
default:
- fprintf( stderr, "error: command mismatch\n" );
+ perr( "command mismatch" );
break;
}
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;
}