]> rtime.felk.cvut.cz Git - can-eth-gw.git/blob - utils/cegw/cegw.c
list routing rules
[can-eth-gw.git] / utils / cegw / cegw.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <libgen.h>
4 #include <string.h>
5 #include <getopt.h>
6 #include <errno.h>
7 #include <sys/socket.h>
8 #include <net/if.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>
15 #include <unistd.h>
16 #include "canethgw.h"
17
18 /**
19  * ToDo:
20  * [ ] print usage, on -h and plain execution
21  * [ ] start/stop listening
22  * [ ] split to files
23  */
24
25 #define CEGW_CMD_ADD     1
26 #define CEGW_CMD_LIST    2
27 #define CEGW_CMD_FLUSH   4
28 #define CEGW_CMD_LISTEN  8
29
30 enum
31 {
32         IF_UNDEF,
33         IF_CAN,
34         IF_ETH_UDP
35 };
36
37 struct cegw_data
38 {
39         int content;
40         int src_if, dst_if;
41         int can_ifidx;
42         struct in_addr eth_addr;
43         unsigned short eth_port;
44         struct in_addr eth_listen_addr;
45         unsigned short eth_listen_port;
46 };
47
48 struct cegw_nlmsg 
49 {
50         struct nlmsghdr nh;
51         struct rtmsg rt;
52         char buf[768]; /* enough? */
53 };
54
55 struct list_item
56 {
57         int type;
58         int can;
59         struct in_addr ip;
60         unsigned short port;
61 };
62
63 unsigned int cegw_errno = 0;
64
65 enum 
66 {
67         CEGW_ERR_UNKNOWN,
68         CEGW_ERR_IF_UNSPEC,
69         CEGW_ERR_IF_SAME,
70         CEGW_ERR_IF_TYPE,
71         CEGW_ERR_IF_CAN,
72         CEGW_ERR_IF_ETH,
73         CEGW_ERR_COLON,
74         CEGW_ERR_ATON,
75         CEGW_ERR_PORT
76 };
77
78 char* cegw_errlist[] =
79 {
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"
89 };
90
91 static void perr( char* s )
92 {
93         if( s )
94         {
95                 if( cegw_errno == 0 )
96                 {
97                         fprintf( stderr, "error: %s\n", s );
98
99                 } else
100                 {
101                         fprintf( stderr, "error: %s, %s\n", s,
102                                  cegw_errlist[ cegw_errno ] );
103                 }
104                 return;
105         }
106
107         fprintf( stderr, "error: %s\n", cegw_errlist[ cegw_errno ] );
108 }
109
110 /**
111  * read_addrport - parses @in for eth address. 
112  * Valid input is e.g. udp@127.0.0.1:10502 or can@vcan0.
113  *
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
118  */
119 int read_addrport( char* in, struct in_addr* addr, unsigned short* port )
120 {
121         char* delim = NULL;
122         char addrstr[16];
123         int addrlen;
124         
125         if( (delim = strchr( in, ':' )) == NULL )
126         {
127                 cegw_errno = CEGW_ERR_COLON;
128                 return -1;
129         }
130
131         /* get address */
132         addrlen = delim - in;
133         memcpy( addrstr, in, addrlen );
134         addrstr[addrlen] = '\0';
135         if( inet_aton( addrstr, addr ) == 0 )
136         {
137                 cegw_errno = CEGW_ERR_ATON;
138                 return -1;
139         }
140
141         /* get port */
142         if( sscanf( delim, ":%hu", port ) != 1 ) /* ToDo: handle overflow */
143         {
144                 cegw_errno = CEGW_ERR_PORT;
145                 return -1;
146         }
147
148         return 0;
149 }
150
151 /**
152  * read_iftype - reads @in for iftype
153  * Iftype type is e.g. "can@" or "udp@".
154  *
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
158  */
159 char* read_iftype( char* in, int* iftype )
160 {
161         char* ret = in+4;
162         
163         if( strncmp( "udp@", optarg, 4 ) == 0 )
164         {
165                 *iftype = IF_ETH_UDP;
166                 return ret;
167         }
168         /*
169         if( strncmp( "tcp@", optarg, 4 ) == 0 )
170         {
171                 return NULL;
172         }
173         */
174         if( strncmp( "can@", optarg, 4 ) == 0 )
175         {
176                 *iftype = IF_CAN;
177                 return ret;
178         }
179         
180         cegw_errno = CEGW_ERR_IF_TYPE;
181         return NULL;
182 }
183
184 /**
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
188  * format
189  *
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
193  */
194 int read_if( char* in, int* iftype, struct cegw_data* d )
195 {
196         char* optstr = NULL;
197
198         if( (optstr = read_iftype( in, iftype )) == NULL )
199         {
200                 return -1;
201         }
202
203         switch( *iftype )
204         {
205                 case IF_CAN:
206                         d->can_ifidx = if_nametoindex( optstr );
207                         if( d->can_ifidx == 0 )
208                         {
209                                 cegw_errno = CEGW_ERR_IF_CAN;
210                                 return -1;
211                         }
212                         break;
213                 case IF_ETH_UDP:
214                         if( read_addrport( optstr, &d->eth_addr, &d->eth_port ) != 0 )
215                         {
216                                 return -1;
217                         }
218                         break;
219                 default:
220                         return -1;
221                         break;
222         }
223
224         return 0;
225 }
226
227 inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
228 {
229         int gwtype = 0;
230
231         req->nh.nlmsg_type  = RTM_NEWROUTE;
232         if( (d->src_if == 0 || d->dst_if == 0) )
233         {
234                 cegw_errno = CEGW_ERR_IF_UNSPEC;
235                 return -cegw_errno;
236         }
237
238         if( d->src_if == d->dst_if )
239         {
240                 cegw_errno = CEGW_ERR_IF_SAME;
241                 return -cegw_errno;
242         }
243
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) );
246
247         switch( gwtype )
248         {
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) );
252                         break;
253                 case CGW_TYPE_ETH_CAN_UDP:
254                         break;
255                 default:
256                         break;
257         }
258
259         addattr32( &req->nh, sizeof(*req), CGW_CMD_INFO, gwtype );
260
261         return 0;
262 }
263
264 inline static int cegw_listen( struct cegw_nlmsg* req, struct cegw_data* d )
265 {
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 );
271
272         return 0;
273 }
274
275 inline static int cegw_list( struct cegw_nlmsg* req, struct cegw_data* d )
276 {
277         req->nh.nlmsg_type = RTM_GETROUTE;
278         req->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
279         
280         return 0;
281 }
282
283 inline static int cegw_flush( struct cegw_nlmsg* req, struct cegw_data* d )
284 {
285         addattr32( &req->nh, sizeof(*req), CGW_CMD_INFO, CEGW_FLUSH );
286         req->nh.nlmsg_type  = RTM_DELROUTE;
287         return 0;
288 }
289
290 void print_list_item( struct list_item* li )
291 {
292         char ifname[IF_NAMESIZE];
293         const char src_width = 21;
294
295         if( if_indextoname( li->can, ifname ) == NULL )
296         {
297                 strncpy( ifname, "unknown", IF_NAMESIZE );
298         }
299
300         /* ToDo listening at */
301         switch( li->type )
302         {
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 );
306                         break;
307                 case CGW_TYPE_ETH_CAN_UDP:
308                         printf( "udp@%-*s -> can@%s\n", src_width, "*:*", ifname );
309                         break;
310         }
311 }
312
313 int main( int argc, char* argv[] )
314 {
315         int s;
316         int tmp = 0;
317         int cmd = 0;
318         char* optstr;
319         char opt;
320         struct sockaddr_nl nladdr;
321         int err = 0;
322         struct cegw_nlmsg req;
323         struct cegw_data d;
324         char rxbuf[8192]; /* ToDo: /linux/netlink.h? */
325         int rsize = 0;
326         struct nlmsghdr* nlh;
327         struct nlmsgerr* rte;
328         struct rtattr* rta;
329         int len;
330         struct list_item li;
331
332         memset( &d, 0, sizeof(d) );
333
334         struct option long_opt[] =
335         {
336                 { "add"   , 0, NULL, 'A' },
337                 { "flush" , 0, NULL, 'F' },
338                 { "list"  , 0, NULL, 'L' },
339                 { "listen", 1, NULL, 'l' },
340                 { 0, 0, 0, 0 }
341         };
342
343         while( 1 )
344         {
345                 opt = getopt_long( argc, argv, "AFLl:s:d:", long_opt, NULL );
346                 if( opt == -1 )
347                         break;
348
349                 switch( opt )
350                 {
351                         case 'A':
352                                 cmd |= CEGW_CMD_ADD;
353                                 break;
354                         case 'F':
355                                 cmd |= CEGW_CMD_FLUSH;
356                                 break;
357                         case 'L':
358                                 cmd |= CEGW_CMD_LIST;
359                                 break;
360                         case 'l':
361                                 cmd |= CEGW_CMD_LISTEN;
362                                 if( (optstr = read_iftype( optarg, &tmp )) == NULL )
363                                 {
364                                         perr( "'--listen'" );
365                                         return -1;
366                                 }       
367                                 if( tmp != IF_ETH_UDP )
368                                 {
369                                         perr( "'--listen' expects udp interface" );
370                                         return -1;
371                                 }       
372                                 if( read_addrport( optstr, &d.eth_listen_addr, &d.eth_listen_port ) )
373                                 {
374                                         perr( "'--listen'" );
375                                         return -1;
376                                 }
377                                 break;
378                         case 's':
379                                 if( read_if( optarg, &d.src_if, &d ) != 0 )
380                                 {
381                                         perr( "'-s'" );
382                                         return -1;
383                                 }
384                                 break;
385                         case 'd':
386                                 if( read_if( optarg, &d.dst_if, &d ) != 0 )
387                                 {
388                                         perr( "'-d'" );
389                                         return -1;
390                                 }
391                                 break;
392                         case '?':
393                                 return -1;
394                                 break;
395                         default:
396                                 perr( "unknown option" );
397                                 return -1;
398                                 break;
399                 }                               
400         }
401
402         /* prepare netlink message */
403         req.nh.nlmsg_len   = NLMSG_LENGTH( sizeof(struct rtmsg) );
404         //req.nh.nlmsg_type;
405         req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
406         req.nh.nlmsg_seq   = 0;
407         req.nh.nlmsg_pid   = 0; /* ? */
408
409         memset( &req.rt, 0, sizeof(req.rt) );
410         req.rt.rtm_family = AF_CAN;
411
412         switch( cmd )
413         {
414                 case 0:
415                         perr( "command not specified" );
416                         return -1;
417                         break;
418                 case CEGW_CMD_ADD:
419                         if( cegw_add( &req, &d ) != 0 )
420                         {
421                                 perr( "'--add'" );
422                                 return -1;
423                         }
424                         break;
425                 case CEGW_CMD_FLUSH:
426                         cegw_flush( &req, &d );
427                         break;
428                 case CEGW_CMD_LIST:
429                         cegw_list( &req, &d );
430                         break;
431                 case CEGW_CMD_LISTEN:
432                         cegw_listen( &req, &d );
433                         break;
434                 default:
435                         perr( "command mismatch" );
436                         break;
437         }
438
439         /* send over netlink socket */
440         s = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); /* chck */
441         
442         memset( &nladdr, 0, sizeof(nladdr) );
443         nladdr.nl_family = AF_NETLINK;
444         nladdr.nl_pad = 0;
445         nladdr.nl_pid = 0;
446         nladdr.nl_groups = 0;
447
448         err = sendto( s, &req, req.nh.nlmsg_len, 0, 
449                       (struct sockaddr*)&nladdr, sizeof(nladdr) );
450         if( err < 0 )
451         {
452                 perror( "netlink sendto" );
453                 return err;
454         }
455         
456         /* recv */
457         rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
458         printf( "recv size=%d\n", rsize );
459         if( rsize < 0 )
460         {
461                 perr( "recv" );
462                 return -1;
463         }
464         nlh = (struct nlmsghdr*)rxbuf;
465
466         if( cmd & CEGW_CMD_LIST )
467         {
468                 printf( "recv nlmsg_type=%d\n", nlh->nlmsg_type );
469                 if( nlh->nlmsg_type == NLMSG_ERROR )
470                 {
471                         struct nlmsgerr* nlerr = NLMSG_DATA( nlh );
472                         int err = nlerr->error;
473                         printf( "nlerror: %d,%s\n", err, strerror(abs(err)) );
474                 }
475                 /* ToDo recv while */
476                 printf( "%10ssource%20sdestination\n", "", "" );
477                 while( 1 )
478                 {
479                         if( !NLMSG_OK( nlh, rsize ) )
480                         {
481                                 puts( "NLMSG_OK\n" );
482                                 break;
483                         }
484                         if( nlh->nlmsg_type == NLMSG_DONE )
485                         {
486                                 puts( "NLMSG_DONE" );
487                                 break;
488                         }
489                         /* ToDo: NLMSG_ERR */
490
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) )
495                         {
496                                 switch( rta->rta_type )
497                                 {
498                                         case CGW_TYPE:
499                                                 li.type = *(int*)RTA_DATA(rta);
500                                                 break;
501                                         case CGW_CAN_IF:
502                                                 li.can = *(int*)RTA_DATA(rta);
503                                                 break;
504                                         case CGW_ETH_IP:
505                                                 li.ip = *(struct in_addr*)RTA_DATA(rta);
506                                                 break;
507                                         case CGW_ETH_PORT:
508                                                 li.port = *(unsigned short*)RTA_DATA(rta);
509                                                 break;
510                                         /* case CGW_ETH_PROTO */
511                                 }
512                         }
513
514                         print_list_item( &li );
515
516                         nlh = NLMSG_NEXT( nlh, rsize );
517                 }
518         } else
519         {
520                 if( nlh->nlmsg_type != NLMSG_ERROR )
521                 {
522                         fprintf( stderr, "error: unexpected netlink answer=%d\n", nlh->nlmsg_type );
523                         return -1;
524                 }
525                 rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
526                 err = rte->error;
527                 if( err < 0 )
528                         fprintf( stderr, "error: netlink(%d); %s\n", err, strerror(abs(err)) );
529         }
530         
531         return 0;
532 }
533