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