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