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>
19 * [ ] refactor error messages
20 * [ ] start/stop listening
21 * [ ] remove routing job
22 * [ ] recv netlink reponse
26 #define CEGW_CMD_ADD 1
27 #define CEGW_CMD_LIST 2
28 #define CEGW_CMD_FLUSH 4
29 #define CEGW_CMD_LISTEN 8
43 struct in_addr eth_addr;
44 unsigned short eth_port;
45 struct in_addr eth_listen_addr;
46 unsigned short eth_listen_port;
52 struct rtcanmsg rtcan;
53 char buf[600]; /* enough? */
56 unsigned int cegw_errno = 0;
66 char* cegw_errlist[] =
68 [ CEGW_ERR_UNKNOWN ] = "unknown error",
69 [ CEGW_ERR_IF_UNSPEC ] = "source or destination not specified",
70 [ CEGW_ERR_IF_SAME ] = "source and destination have same interface type",
71 [ CEGW_ERR_IF_TYPE ] = "unknown interface type"
74 static void perr( char* s )
78 fprintf( stderr, "error: %s; %s\n", s,
79 cegw_errlist[ cegw_errno ] );
83 fprintf( stderr, "error: %s\n", cegw_errlist[ cegw_errno ] );
87 * Parses @in for eth address. Valid input is
88 * e.g. udp@127.0.0.1:10502 or can@vcan0.
90 * @param[out] addr ip address
91 * @param[out] port transport layer port
92 * @return 0 on success
94 int read_addr_port( char* in, struct in_addr* addr, unsigned short* port )
100 if( (delim = strchr( in, ':' )) == NULL )
102 fprintf( stderr, "error: ':'\n" );
107 addrlen = delim - in;
108 memcpy( addrstr, in, addrlen );
109 addrstr[addrlen] = '\0';
110 if( inet_aton( addrstr, addr ) == 0 )
112 fprintf( stderr, "error: aton\n" );
117 if( sscanf( delim, ":%hu", port ) != 1 ) /* todo: handle overflow */
119 fprintf( stderr, "error: port\n" );
127 * Reads @in for iftype (e.g. "can@" or "udp@").
129 * @param[in] in string to search in
130 * @param[out] iftype iftype detected
131 * @return pointer to @in after iftype on success, NULL otherwise
133 char* read_iftype( char* in, int* iftype )
137 if( strncmp( "udp@", optarg, 4 ) == 0 )
139 *iftype = IF_ETH_UDP;
143 else if( strncmp( "tcp@", optarg, 4 ) == 0 )
148 else if( strncmp( "can@", optarg, 4 ) == 0 )
154 errno = CEGW_ERR_IF_TYPE;
158 int read_if( char* in, int* iftype, struct cegw_data* d )
162 if( (optstr = read_iftype( in, iftype )) == NULL )
170 d->can_ifidx = if_nametoindex( optstr );
173 read_addr_port( optstr, &d->eth_addr, &d->eth_port );
183 inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
185 req->nh.nlmsg_type = RTM_NEWROUTE;
186 if( (d->src_if == 0 || d->dst_if == 0) )
188 cegw_errno = CEGW_ERR_IF_UNSPEC;
192 if( d->src_if == d->dst_if )
194 cegw_errno = CEGW_ERR_IF_SAME;
198 req->rtcan.gwtype = (d->src_if == IF_CAN) ? CGW_TYPE_CAN_ETH_UDP : CGW_TYPE_ETH_CAN_UDP;
199 addattr_l( &req->nh, sizeof(*req), CGW_CAN_IF, &d->can_ifidx, sizeof(d->can_ifidx) );
200 switch( req->rtcan.gwtype )
202 case CGW_TYPE_CAN_ETH_UDP:
203 addattr_l( &req->nh, sizeof(*req), CGW_ETH_IP, &d->eth_addr, sizeof(d->eth_addr) );
204 addattr_l( &req->nh, sizeof(*req), CGW_ETH_PORT, &d->eth_port, sizeof(d->eth_port) );
206 case CGW_TYPE_ETH_CAN_UDP:
215 inline static int cegw_listen( struct cegw_nlmsg* req, struct cegw_data* d )
217 req->nh.nlmsg_type = RTM_NEWROUTE;
218 req->rtcan.gwtype = CGW_TYPE_CONFIG;
219 addattr_l( &req->nh, sizeof(req), CGW_LISTEN_IP, &d->eth_listen_addr, sizeof(d->eth_listen_addr) );
220 addattr_l( &req->nh, sizeof(req), CGW_LISTEN_PORT, &d->eth_listen_port, sizeof(d->eth_listen_port) );
221 printf( "start listen: %x, %hu\n", d->eth_listen_addr, d->eth_listen_port );
226 inline static int cegw_list( struct cegw_nlmsg* req, struct cegw_data* d )
231 inline static int cegw_flush( struct cegw_nlmsg* req, struct cegw_data* d )
233 req->nh.nlmsg_type = RTM_DELROUTE;
234 req->rtcan.gwtype = CGW_TYPE_CAN_ETH_UDP;
235 req->rtcan.flags = CEGW_FLUSH;
239 int main( int argc, char* argv[] )
246 struct sockaddr_nl nladdr;
248 struct cegw_nlmsg req;
252 struct nlmsghdr* nlh;
253 struct nlmsgerr* rte;
255 memset( &d, 0, sizeof(d) );
257 struct option long_opt[] =
259 { "add" , 0, NULL, 'A' },
260 { "flush" , 0, NULL, 'F' },
261 { "list" , 0, NULL, 'L' },
262 { "listen", 1, NULL, 'l' },
268 opt = getopt_long( argc, argv, "As:d:", long_opt, NULL );
278 cmd |= CEGW_CMD_FLUSH;
281 cmd |= CEGW_CMD_FLUSH;
284 cmd |= CEGW_CMD_LISTEN;
285 if( (optstr = read_iftype( optarg, &tmp )) == NULL )
290 if( tmp != IF_ETH_UDP )
292 fprintf( stderr, "error: -l bad input format\n" );
295 read_addr_port( optstr, &d.eth_listen_addr, &d.eth_listen_port ); /*chk*/
298 if( read_if( optarg, &d.src_if, &d ) != 0 )
300 fprintf( stderr, "error: bad input format\n" );
306 if( read_if( optarg, &d.dst_if, &d ) != 0 )
308 fprintf( stderr, "error: bad input format\n" );
314 fprintf( stderr, "error: unknown option\n" );
318 fprintf( stderr, "error: unknown option\n" );
324 /* prepare netlink message */
325 req.nh.nlmsg_len = NLMSG_LENGTH( sizeof(struct rtcanmsg) );
326 req.nh.nlmsg_type = RTM_NEWROUTE;
327 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
328 req.nh.nlmsg_seq = 0;
329 req.nh.nlmsg_pid = 0; /* ? */
331 req.rtcan.can_family = AF_CAN;
337 fprintf( stderr, "error: command not specified\n" );
340 if( cegw_add( &req, &d ) != 0 )
347 cegw_flush( &req, &d );
350 cegw_list( &req, &d );
352 case CEGW_CMD_LISTEN:
353 cegw_listen( &req, &d );
356 fprintf( stderr, "error: command mismatch\n" );
360 /* send over netlink socket */
361 s = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); /* chck */
363 memset( &nladdr, 0, sizeof(nladdr) );
364 nladdr.nl_family = AF_NETLINK;
366 nladdr.nl_groups = 0;
368 err = sendto( s, &req, req.nh.nlmsg_len, 0,
369 (struct sockaddr*)&nladdr, sizeof(nladdr));
372 perror( "netlink sendto" );
377 rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
380 fprintf( stderr, "error: recv\n" );
383 nlh = (struct nlmsghdr*)rxbuf;
384 if( nlh->nlmsg_type != NLMSG_ERROR )
386 fprintf( stderr, "error: unexpected netlink answer\n" );
389 rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
392 fprintf( stderr, "error: netlink; %s\n", strerror(abs(err)) );