7 #include <sys/socket.h>
9 #include <libnetlink.h>
10 #include <linux/netlink.h>
11 #include <linux/rtnetlink.h>
12 #include <arpa/inet.h>
13 #include <linux/can.h>
14 #include <linux/types.h>
20 * [ ] print usage, on -h and plain execution
21 * [ ] start/stop listening
25 #define CEGW_CMD_ADD 1
26 #define CEGW_CMD_LIST 2
27 #define CEGW_CMD_FLUSH 4
28 #define CEGW_CMD_LISTEN 8
42 struct in_addr eth_addr;
43 unsigned short eth_port;
44 struct in_addr eth_listen_addr;
45 unsigned short eth_listen_port;
52 char buf[768]; /* enough? */
63 unsigned int cegw_errno = 0;
78 char* cegw_errlist[] =
80 [ CEGW_ERR_UNKNOWN ] = "",
81 [ CEGW_ERR_IF_UNSPEC ] = "source or destination not specified",
82 [ CEGW_ERR_IF_SAME ] = "source and destination have same interface type",
83 [ CEGW_ERR_IF_TYPE ] = "unknown interface type",
84 [ CEGW_ERR_IF_CAN ] = "invalid can interface",
85 [ CEGW_ERR_IF_ETH ] = "invalid eth interface",
86 [ CEGW_ERR_COLON ] = "expected ':' (<ip>:<port>)",
87 [ CEGW_ERR_ATON ] = "ip address mismatch",
88 [ CEGW_ERR_PORT ] = "port number"
91 static void perr( char* s )
97 fprintf( stderr, "error: %s\n", s );
101 fprintf( stderr, "error: %s, %s\n", s,
102 cegw_errlist[ cegw_errno ] );
107 fprintf( stderr, "error: %s\n", cegw_errlist[ cegw_errno ] );
111 * read_addrport - parses @in for eth address.
112 * Valid input is e.g. udp@127.0.0.1:10502 or can@vcan0.
114 * @param[in] in string to search in
115 * @param[out] addr ip address
116 * @param[out] port transport layer port
117 * @return 0 on success, -1 otherwise
119 int read_addrport( char* in, struct in_addr* addr, unsigned short* port )
125 if( (delim = strchr( in, ':' )) == NULL )
127 cegw_errno = CEGW_ERR_COLON;
132 addrlen = delim - in;
133 memcpy( addrstr, in, addrlen );
134 addrstr[addrlen] = '\0';
135 if( inet_aton( addrstr, addr ) == 0 )
137 cegw_errno = CEGW_ERR_ATON;
142 if( sscanf( delim, ":%hu", port ) != 1 ) /* ToDo: handle overflow */
144 cegw_errno = CEGW_ERR_PORT;
152 * read_iftype - reads @in for iftype
153 * Iftype type is e.g. "can@" or "udp@".
155 * @param[in] in string to search in
156 * @param[out] iftype iftype detected
157 * @return pointer to @in after iftype on success, NULL otherwise
159 char* read_iftype( char* in, int* iftype )
163 if( strncmp( "udp@", optarg, 4 ) == 0 )
165 *iftype = IF_ETH_UDP;
169 if( strncmp( "tcp@", optarg, 4 ) == 0 )
174 if( strncmp( "can@", optarg, 4 ) == 0 )
180 cegw_errno = CEGW_ERR_IF_TYPE;
185 * read_if - reads interface from @in
186 * Function analyzes @in for interface specification in format
187 * <if>@<ip>:<port>, where <if> is can or udp, <ip> is address in dotted
190 * @param[in] in string to search in
191 * @param[out] iftype interface type (IF_CAN or IF_ETH_UDP)
192 * @param[out] d ip and port is stored to @d
194 int read_if( char* in, int* iftype, struct cegw_data* d )
198 if( (optstr = read_iftype( in, iftype )) == NULL )
206 d->can_ifidx = if_nametoindex( optstr );
207 if( d->can_ifidx == 0 )
209 cegw_errno = CEGW_ERR_IF_CAN;
214 if( read_addrport( optstr, &d->eth_addr, &d->eth_port ) != 0 )
227 inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
231 req->nh.nlmsg_type = RTM_NEWROUTE;
232 if( (d->src_if == 0 || d->dst_if == 0) )
234 cegw_errno = CEGW_ERR_IF_UNSPEC;
238 if( d->src_if == d->dst_if )
240 cegw_errno = CEGW_ERR_IF_SAME;
244 gwtype = (d->src_if == IF_CAN) ? CGW_TYPE_CAN_ETH_UDP : CGW_TYPE_ETH_CAN_UDP;
245 addattr_l( &req->nh, sizeof(*req), CGW_CAN_IF, &d->can_ifidx, sizeof(d->can_ifidx) );
249 case CGW_TYPE_CAN_ETH_UDP:
250 addattr_l( &req->nh, sizeof(*req), CGW_ETH_IP, &d->eth_addr, sizeof(d->eth_addr) );
251 addattr_l( &req->nh, sizeof(*req), CGW_ETH_PORT, &d->eth_port, sizeof(d->eth_port) );
253 case CGW_TYPE_ETH_CAN_UDP:
259 addattr32( &req->nh, sizeof(*req), CGW_CMD_INFO, gwtype );
264 inline static int cegw_listen( struct cegw_nlmsg* req, struct cegw_data* d )
266 req->nh.nlmsg_type = RTM_NEWROUTE;
267 addattr32( &req->nh, sizeof(*req), CGW_CMD_INFO, CEGW_LISTEN );
268 addattr_l( &req->nh, sizeof(*req), CGW_LISTEN_IP, &d->eth_listen_addr, sizeof(d->eth_listen_addr) );
269 addattr_l( &req->nh, sizeof(*req), CGW_LISTEN_PORT, &d->eth_listen_port, sizeof(d->eth_listen_port) );
270 printf( "listen at: %x, %hu\n", d->eth_listen_addr.s_addr, d->eth_listen_port );
275 inline static int cegw_list( struct cegw_nlmsg* req, struct cegw_data* d )
277 req->nh.nlmsg_type = RTM_GETROUTE;
278 req->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
283 inline static int cegw_flush( struct cegw_nlmsg* req, struct cegw_data* d )
285 addattr32( &req->nh, sizeof(*req), CGW_CMD_INFO, CEGW_FLUSH );
286 req->nh.nlmsg_type = RTM_DELROUTE;
290 void print_list_item( struct list_item* li )
292 char ifname[IF_NAMESIZE];
293 const char src_width = 21;
295 if( if_indextoname( li->can, ifname ) == NULL )
297 strncpy( ifname, "unknown", IF_NAMESIZE );
300 /* ToDo listening at */
303 case CGW_TYPE_CAN_ETH_UDP:
304 printf( "can@%-*s -> udp@%s:%hu\n", src_width, ifname, \
305 inet_ntoa(li->ip), li->port );
307 case CGW_TYPE_ETH_CAN_UDP:
308 printf( "udp@%-*s -> can@%s\n", src_width, "*:*", ifname );
313 int main( int argc, char* argv[] )
320 struct sockaddr_nl nladdr;
322 struct cegw_nlmsg req;
324 char rxbuf[8192]; /* ToDo: /linux/netlink.h? */
326 struct nlmsghdr* nlh;
327 struct nlmsgerr* rte;
332 memset( &d, 0, sizeof(d) );
334 struct option long_opt[] =
336 { "add" , 0, NULL, 'A' },
337 { "flush" , 0, NULL, 'F' },
338 { "list" , 0, NULL, 'L' },
339 { "listen", 1, NULL, 'l' },
345 opt = getopt_long( argc, argv, "AFLl:s:d:", long_opt, NULL );
355 cmd |= CEGW_CMD_FLUSH;
358 cmd |= CEGW_CMD_LIST;
361 cmd |= CEGW_CMD_LISTEN;
362 if( (optstr = read_iftype( optarg, &tmp )) == NULL )
364 perr( "'--listen'" );
367 if( tmp != IF_ETH_UDP )
369 perr( "'--listen' expects udp interface" );
372 if( read_addrport( optstr, &d.eth_listen_addr, &d.eth_listen_port ) )
374 perr( "'--listen'" );
379 if( read_if( optarg, &d.src_if, &d ) != 0 )
386 if( read_if( optarg, &d.dst_if, &d ) != 0 )
396 perr( "unknown option" );
402 /* prepare netlink message */
403 req.nh.nlmsg_len = NLMSG_LENGTH( sizeof(struct rtmsg) );
405 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
406 req.nh.nlmsg_seq = 0;
407 req.nh.nlmsg_pid = 0; /* ? */
409 memset( &req.rt, 0, sizeof(req.rt) );
410 req.rt.rtm_family = AF_CAN;
415 perr( "command not specified" );
419 if( cegw_add( &req, &d ) != 0 )
426 cegw_flush( &req, &d );
429 cegw_list( &req, &d );
431 case CEGW_CMD_LISTEN:
432 cegw_listen( &req, &d );
435 perr( "command mismatch" );
439 /* send over netlink socket */
440 s = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); /* chck */
442 memset( &nladdr, 0, sizeof(nladdr) );
443 nladdr.nl_family = AF_NETLINK;
446 nladdr.nl_groups = 0;
448 err = sendto( s, &req, req.nh.nlmsg_len, 0,
449 (struct sockaddr*)&nladdr, sizeof(nladdr) );
452 perror( "netlink sendto" );
457 rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
458 printf( "recv size=%d\n", rsize );
464 nlh = (struct nlmsghdr*)rxbuf;
466 if( cmd & CEGW_CMD_LIST )
468 printf( "recv nlmsg_type=%d\n", nlh->nlmsg_type );
469 if( nlh->nlmsg_type == NLMSG_ERROR )
471 struct nlmsgerr* nlerr = NLMSG_DATA( nlh );
472 int err = nlerr->error;
473 printf( "nlerror: %d,%s\n", err, strerror(abs(err)) );
475 /* ToDo recv while */
476 printf( "%10ssource%20sdestination\n", "", "" );
479 if( !NLMSG_OK( nlh, rsize ) )
481 puts( "NLMSG_OK\n" );
484 if( nlh->nlmsg_type == NLMSG_DONE )
486 puts( "NLMSG_DONE" );
489 /* ToDo: NLMSG_ERR */
491 rta = NLMSG_DATA( nlh );
492 //rta = (struct rtattr*)( ((char *)rtm) + NLMSG_ALIGN(sizeof(struct rtmsg)) );
493 len = NLMSG_PAYLOAD( nlh, 0 );
494 for( ;RTA_OK(rta, len); rta = RTA_NEXT(rta,len) )
496 switch( rta->rta_type )
499 li.type = *(int*)RTA_DATA(rta);
502 li.can = *(int*)RTA_DATA(rta);
505 li.ip = *(struct in_addr*)RTA_DATA(rta);
508 li.port = *(unsigned short*)RTA_DATA(rta);
510 /* case CGW_ETH_PROTO */
514 print_list_item( &li );
516 nlh = NLMSG_NEXT( nlh, rsize );
520 if( nlh->nlmsg_type != NLMSG_ERROR )
522 fprintf( stderr, "error: unexpected netlink answer=%d\n", nlh->nlmsg_type );
525 rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
528 fprintf( stderr, "error: netlink(%d); %s\n", err, strerror(abs(err)) );