*
*/
-#define CMD_STOP_LISTEN 1
-#define CMD_START_LISTEN 2
-#define CMD_ADD_RULE 4
+#define CEGW_CMD_ADD 1
+#define CEGW_CMD_LIST 2
+#define CEGW_CMD_FLUSH 4
+#define CEGW_CMD_LISTEN 8
-enum {
+enum
+{
IF_UNDEF,
IF_CAN,
IF_ETH_UDP
};
+struct cegw_data
+{
+ int content;
+ int src_if, dst_if;
+ int can_ifidx;
+ struct in_addr eth_addr;
+ unsigned short eth_port;
+ struct in_addr eth_listen_addr;
+ unsigned short eth_listen_port;
+};
+
+struct cegw_nlmsg
+{
+ struct nlmsghdr nh;
+ struct rtcanmsg rtcan;
+ char buf[600]; /* enough? */
+};
+
+unsigned int cegw_errno = 0;
+
+enum
+{
+ CEGW_ERR_UNKNOWN,
+ CEGW_ERR_IF_UNSPEC,
+ CEGW_ERR_IF_SAME,
+ CEGW_ERR_IF_TYPE
+};
+
+char* cegw_errlist[] =
+{
+ [ CEGW_ERR_UNKNOWN ] = "unknown error",
+ [ 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"
+};
+
+static void perr( char* s )
+{
+ if( s )
+ {
+ fprintf( stderr, "error: %s; %s\n", s,
+ cegw_errlist[ cegw_errno ] );
+ return;
+ }
+
+ fprintf( stderr, "error: %s\n", cegw_errlist[ cegw_errno ] );
+}
/**
- * parses @in for eth address, valid input is
- * e.g. udp@127.0.0.1:10502 or can@vcan0
+ * Parses @in for eth address. Valid input is
+ * e.g. udp@127.0.0.1:10502 or can@vcan0.
*
* @param[out] addr ip address
* @param[out] port transport layer port
return 0;
}
+/**
+ * Reads @in for iftype (e.g. "can@" or "udp@").
+ *
+ * @param[in] in string to search in
+ * @param[out] iftype iftype detected
+ * @return pointer to @in after iftype on success, NULL otherwise
+ */
char* read_iftype( char* in, int* iftype )
{
char* ret = in+4;
*iftype = IF_ETH_UDP;
return ret;
}
+ /*
else if( strncmp( "tcp@", optarg, 4 ) == 0 )
{
return NULL;
}
+ */
else if( strncmp( "can@", optarg, 4 ) == 0 )
{
*iftype = IF_CAN;
return ret;
}
+ errno = CEGW_ERR_IF_TYPE;
return NULL;
}
+int read_if( char* in, int* iftype, struct cegw_data* d )
+{
+ char* optstr = NULL;
+
+ if( (optstr = read_iftype( in, iftype )) == NULL )
+ {
+ return -1;
+ }
+
+ switch( *iftype )
+ {
+ case IF_CAN:
+ d->can_ifidx = if_nametoindex( optstr );
+ break;
+ case IF_ETH_UDP:
+ read_addr_port( optstr, &d->eth_addr, &d->eth_port );
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
+{
+ req->nh.nlmsg_type = RTM_NEWROUTE;
+ if( (d->src_if == 0 || d->dst_if == 0) )
+ {
+ cegw_errno = CEGW_ERR_IF_UNSPEC;
+ return -cegw_errno;
+ }
+
+ if( d->src_if == d->dst_if )
+ {
+ cegw_errno = CEGW_ERR_IF_SAME;
+ 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;
+ }
+
+ 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 );
+
+ return 0;
+}
+
+inline static int cegw_list( struct cegw_nlmsg* req, struct cegw_data* d )
+{
+ return 0;
+}
+
+inline static int cegw_flush( struct cegw_nlmsg* req, struct cegw_data* d )
+{
+ req->nh.nlmsg_type = RTM_DELROUTE;
+ req->rtcan.gwtype = CGW_TYPE_CAN_ETH_UDP;
+ req->rtcan.flags = CEGW_FLUSH;
+ return 0;
+}
+
int main( int argc, char* argv[] )
{
int s;
- int src_if = 0, dst_if = 0;
- int can_ifidx = 0;
int tmp = 0;
int cmd = 0;
- struct in_addr eth_addr;
- unsigned short eth_port;
- struct in_addr eth_listen_addr;
- unsigned short eth_listen_port;
char* optstr;
char opt;
struct sockaddr_nl nladdr;
int err = 0;
+ struct cegw_nlmsg req;
+ struct cegw_data d;
+ char rxbuf[256];
+ int rsize = 0;
+ struct nlmsghdr* nlh;
+ struct nlmsgerr* rte;
+
+ memset( &d, 0, sizeof(d) );
struct option long_opt[] =
{
- { "start-listen", 1, NULL, 'l' },
- { "stop-listen" , 1, NULL, 'k' },
+ { "add" , 0, NULL, 'A' },
+ { "flush" , 0, NULL, 'F' },
+ { "list" , 0, NULL, 'L' },
+ { "listen", 1, NULL, 'l' },
{ 0, 0, 0, 0 }
};
-
- struct {
- struct nlmsghdr nh;
- struct rtcanmsg rtcan;
- char buf[600]; /* enough? */
- } req;
-
+
while( 1 )
{
opt = getopt_long( argc, argv, "As:d:", long_opt, NULL );
switch( opt )
{
- case 's':
- cmd |= CMD_ADD_RULE;
- if( (optstr = read_iftype( optarg, &src_if )) == NULL )
- {
- fprintf( stderr, "error: bad input format\n" );
- goto syntax_error;
- break;
- }
-
- switch( src_if )
- {
- case IF_CAN:
- can_ifidx = if_nametoindex( optstr );
- break;
- case IF_ETH_UDP:
- read_addr_port( optstr, ð_addr, ð_port );
- break;
- default:
- fprintf( stderr, "error: unrecognized interface" );
- goto syntax_error;
- break;
- }
+ case 'A':
+ cmd |= CEGW_CMD_ADD;
break;
- case 'd':
- cmd |= CMD_ADD_RULE;
- if( (optstr = read_iftype( optarg, &dst_if )) == NULL )
- {
- fprintf( stderr, "error: bad input format\n" );
- goto syntax_error;
- break;
- }
-
- switch( dst_if )
- {
- case IF_CAN:
- can_ifidx = if_nametoindex( optstr ); /*chk*/
- break;
- case IF_ETH_UDP:
- read_addr_port( optstr, ð_addr, ð_port ); /*chk*/
- break;
- default:
- fprintf( stderr, "error: unrecognized interface" );
- break;
- }
+ case 'F':
+ cmd |= CEGW_CMD_FLUSH;
+ break;
+ case 'L':
+ cmd |= CEGW_CMD_FLUSH;
break;
case 'l':
- cmd |= CMD_START_LISTEN;
+ cmd |= CEGW_CMD_LISTEN;
if( (optstr = read_iftype( optarg, &tmp )) == NULL )
+ {
+ perr( "listen" );
+ return -1;
+ }
+ if( tmp != IF_ETH_UDP )
{
fprintf( stderr, "error: -l bad input format\n" );
+ return -1;
+ }
+ read_addr_port( optstr, &d.eth_listen_addr, &d.eth_listen_port ); /*chk*/
+ break;
+ case 's':
+ if( read_if( optarg, &d.src_if, &d ) != 0 )
+ {
+ fprintf( stderr, "error: bad input format\n" );
goto syntax_error;
break;
}
-
- if( tmp != IF_ETH_UDP )
+ break;
+ case 'd':
+ if( read_if( optarg, &d.dst_if, &d ) != 0 )
{
- fprintf( stderr, "error: -l bad input format\n" );
+ fprintf( stderr, "error: bad input format\n" );
goto syntax_error;
break;
}
-
- read_addr_port( optstr, ð_listen_addr, ð_listen_port ); /*chk*/
- case 'k':
- cmd |= CMD_STOP_LISTEN;
break;
case '?':
+ fprintf( stderr, "error: unknown option\n" );
+ return -1;
break;
default:
fprintf( stderr, "error: unknown option\n" );
- break;
+ return -1;
+ break;
}
}
- /* 2. do check on arguments */
- if( cmd & CMD_ADD_RULE )
- {
- if( (src_if == 0 || dst_if == 0) || (src_if == dst_if) )
- {
- fprintf( stderr, "error: source or destination not specified\n" );
- return -1;
- }
-
- if( src_if == dst_if )
- {
- fprintf( stderr, "error: source and destination same interface type\n" );
- }
- }
-
- /* 3. prepare netlink message */
+ /* prepare netlink message */
req.nh.nlmsg_len = NLMSG_LENGTH( sizeof(struct rtcanmsg) );
req.nh.nlmsg_type = RTM_NEWROUTE;
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.rtcan.can_family = AF_CAN;
req.rtcan.flags = 0;
- if( cmd & CMD_ADD_RULE )
- {
- req.rtcan.gwtype = (src_if == IF_CAN) ? CGW_TYPE_CAN_ETH_UDP : CGW_TYPE_ETH_CAN_UDP;
- addattr_l( &req.nh, sizeof(req), CGW_CAN_IF, &can_ifidx, sizeof(can_ifidx) );
- switch( req.rtcan.gwtype )
- {
- case CGW_TYPE_CAN_ETH_UDP:
- addattr_l( &req.nh, sizeof(req), CGW_ETH_IP, ð_addr, sizeof(eth_addr) );
- addattr_l( &req.nh, sizeof(req), CGW_ETH_PORT, ð_port, sizeof(eth_port) );
- break;
- case CGW_TYPE_ETH_CAN_UDP:
- break;
- default:
- break;
- }
- }
-
- if( cmd & CMD_START_LISTEN )
+ switch( cmd )
{
- req.rtcan.gwtype = CGW_TYPE_CONFIG;
- addattr_l( &req.nh, sizeof(req), CGW_LISTEN_IP, ð_listen_addr, sizeof(eth_listen_addr) );
- addattr_l( &req.nh, sizeof(req), CGW_LISTEN_PORT, ð_listen_port, sizeof(eth_listen_port) );
- printf( "start listen: %x, %hu\n", eth_listen_addr, eth_listen_port );
+ case 0:
+ fprintf( stderr, "error: command not specified\n" );
+ break;
+ case CEGW_CMD_ADD:
+ if( cegw_add( &req, &d ) != 0 )
+ {
+ perr( "add" );
+ return -1;
+ }
+ break;
+ case CEGW_CMD_FLUSH:
+ cegw_flush( &req, &d );
+ break;
+ case CEGW_CMD_LIST:
+ cegw_list( &req, &d );
+ break;
+ case CEGW_CMD_LISTEN:
+ cegw_listen( &req, &d );
+ break;
+ default:
+ fprintf( stderr, "error: command mismatch\n" );
+ break;
}
+
+ /* send over netlink socket */
+ s = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); /* chck */
- /* 4. send over netlink socket */
- s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); /* chck */
-
- memset(&nladdr, 0, sizeof(nladdr));
+ memset( &nladdr, 0, sizeof(nladdr) );
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
return err;
}
- /* ack? */
+ /* recv */
+ rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
+ if( rsize < 0 )
+ {
+ fprintf( stderr, "error: recv\n" );
+ return -1;
+ }
+ nlh = (struct nlmsghdr*)rxbuf;
+ 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 )
+ fprintf( stderr, "error: netlink; %s\n", strerror(abs(err)) );
return 0;
syntax_error: