]> rtime.felk.cvut.cz Git - can-eth-gw.git/blob - utils/cegw/cegw.c
Sending socket fd over netlink
[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 /* ToDo: move to common */
185
186 /**
187  * read_if - reads interface from @in
188  * Function analyzes @in for interface specification in format
189  * <if>@<ip>:<port>, where <if> is can or udp, <ip> is address in dotted
190  * format
191  *
192  * @param[in]  in string to search in
193  * @param[out] iftype interface type (IF_CAN or IF_ETH_UDP)
194  * @param[out] d ip and port is stored to @d
195  */
196 int read_if( char* in, int* iftype, struct cegw_data* d )
197 {
198         char* optstr = NULL;
199
200         if( (optstr = read_iftype( in, iftype )) == NULL )
201         {
202                 return -1;
203         }
204
205         switch( *iftype )
206         {
207                 case IF_CAN:
208                         d->can_ifidx = if_nametoindex( optstr );
209                         if( d->can_ifidx == 0 )
210                         {
211                                 cegw_errno = CEGW_ERR_IF_CAN;
212                                 return -1;
213                         }
214                         break;
215                 case IF_ETH_UDP:
216                         if( read_addrport( optstr, &d->eth_addr, &d->eth_port ) != 0 )
217                         {
218                                 return -1;
219                         }
220                         break;
221                 default:
222                         return -1;
223                         break;
224         }
225
226         return 0;
227 }
228
229 inline static int cegw_add( struct cegw_nlmsg* req, struct cegw_data* d )
230 {
231         int gwtype = 0;
232
233         req->nh.nlmsg_type  = RTM_NEWROUTE;
234         if( (d->src_if == 0 || d->dst_if == 0) )
235         {
236                 cegw_errno = CEGW_ERR_IF_UNSPEC;
237                 return -cegw_errno;
238         }
239
240         if( d->src_if == d->dst_if )
241         {
242                 cegw_errno = CEGW_ERR_IF_SAME;
243                 return -cegw_errno;
244         }
245
246         gwtype = (d->src_if == IF_CAN) ? CEGW_RULE_CAN_ETH : CEGW_RULE_ETH_CAN;
247         addattr_l( &req->nh, sizeof(*req), CEGW_CAN_IFINDEX, &d->can_ifidx, sizeof(d->can_ifidx) );
248         addattr_l( &req->nh, sizeof(*req), CEGW_ETH_IP, &d->eth_addr, sizeof(d->eth_addr) );
249         addattr_l( &req->nh, sizeof(*req), CEGW_ETH_PORT, &d->eth_port, sizeof(d->eth_port) );
250         addattr32( &req->nh, sizeof(*req), CEGW_CMD_INFO, gwtype );
251
252         return 0;
253 }
254
255 inline static int cegw_listen( struct cegw_nlmsg* req, struct cegw_data* d )
256 {
257         req->nh.nlmsg_type = RTM_NEWROUTE;
258         addattr32( &req->nh, sizeof(*req), CEGW_CMD_INFO, CEGW_LISTEN );
259         addattr_l( &req->nh, sizeof(*req), CEGW_ETH_IP, &d->eth_listen_addr, sizeof(d->eth_listen_addr) );
260         addattr_l( &req->nh, sizeof(*req), CEGW_ETH_PORT, &d->eth_listen_port, sizeof(d->eth_listen_port) );
261
262         return 0;
263 }
264
265 inline static int cegw_list( struct cegw_nlmsg* req, struct cegw_data* d )
266 {
267         req->nh.nlmsg_type = RTM_GETROUTE;
268         req->nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
269
270         return 0;
271 }
272
273 inline static int cegw_flush( struct cegw_nlmsg* req, struct cegw_data* d )
274 {
275         addattr32( &req->nh, sizeof(*req), CEGW_CMD_INFO, CEGW_FLUSH );
276         req->nh.nlmsg_type  = RTM_DELROUTE;
277         return 0;
278 }
279
280 void print_list_item( struct list_item* li )
281 {
282         char ifname[IF_NAMESIZE];
283         const char src_width = 21;
284         char* dotaddr;
285         int tmp;
286
287         if( if_indextoname( li->can, ifname ) == NULL )
288         {
289                 strncpy( ifname, "unknown", IF_NAMESIZE );
290         }
291
292         /* ToDo listening at */
293         switch( li->type )
294         {
295                 case CEGW_RULE_CAN_ETH:
296                         printf( "can@%-*s -> udp@%s:%hu\n", src_width, ifname, \
297                                  inet_ntoa(li->ip), li->port );
298                         break;
299                 case CEGW_RULE_ETH_CAN:
300                         dotaddr = inet_ntoa(li->ip);
301                         tmp = src_width - strlen(dotaddr) - 1;
302                         printf( "udp@%s:%-*hu -> can@%s\n", inet_ntoa(li->ip), \
303                                  tmp, li->port, ifname );
304                         break;
305         }
306 }
307
308 int main( int argc, char* argv[] )
309 {
310         int s;
311         int tmp = 0;
312         int cmd = 0;
313         char* optstr;
314         char opt;
315         struct sockaddr_nl nladdr;
316         int err = 0;
317         struct cegw_nlmsg req;
318         struct cegw_data d;
319         char rxbuf[8192]; /* ToDo: /linux/netlink.h? */
320         int rsize = 0;
321         struct nlmsghdr* nlh;
322         struct nlmsgerr* rte;
323         struct rtattr* rta;
324         int len;
325         struct list_item li;
326         int gw_can_sock = 0;
327         int gw_eth_sock = 0;
328         struct sockaddr_in gw_eth_addr;
329         struct sockaddr_can gw_can_addr;
330         struct in_addr addr;
331
332         memset( &d, 0, sizeof(d) );
333
334         /* create sockets for gateway  */
335         gw_eth_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
336         gw_can_sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
337
338         if( gw_eth_sock == -1 )
339                 fprintf( stderr, "error: udp socket for gw creation failed\n" );
340         if( gw_can_sock == -1 )
341                 fprintf( stderr, "error: can socket for gw creation failed\n" );
342
343         gw_eth_addr.sin_family = AF_INET;
344         gw_eth_addr.sin_port = htons( 10501 );
345         gw_eth_addr.sin_addr.s_addr = INADDR_ANY;
346
347         gw_can_addr.can_family = AF_CAN;
348         gw_can_addr.can_ifindex = 0;
349
350         if( bind( gw_eth_sock, (struct sockaddr*)&gw_eth_addr, sizeof(gw_eth_addr) ) != 0 )
351                 fprintf( stderr, "error: eth binding\n" );
352         if( bind( gw_can_sock, (struct sockaddr*)&gw_can_addr, sizeof(gw_can_addr) ) )
353                 fprintf( stderr, "error: can binding\n" );
354
355         printf( "sockets created (%i,%i)\n", gw_can_sock, gw_eth_sock );
356
357         /* prepare netlink message */
358         req.nh.nlmsg_len   = NLMSG_LENGTH( sizeof(struct rtmsg) );
359         req.nh.nlmsg_type  = RTM_NEWROUTE;
360         req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
361         req.nh.nlmsg_seq   = 0;
362         req.nh.nlmsg_pid   = 0; /* ? */
363
364         memset( &req.rt, 0, sizeof(req.rt) );
365         req.rt.rtm_family = AF_CAN;
366
367         addattr32( &req.nh, sizeof(req), CEGW_CAN_SOCK, gw_can_sock );
368         addattr32( &req.nh, sizeof(req), CEGW_ETH_SOCK, gw_eth_sock );
369         addattr32( &req.nh, sizeof(req), CEGW_CAN_IFINDEX, if_nametoindex("vcan0") );
370
371         /* send over netlink socket */
372         s = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); /* chck */
373
374         memset( &nladdr, 0, sizeof(nladdr) );
375         nladdr.nl_family = AF_NETLINK;
376         nladdr.nl_pad = 0;
377         nladdr.nl_pid = 0;
378         nladdr.nl_groups = 0;
379
380         err = sendto( s, &req, req.nh.nlmsg_len, 0,
381                       (struct sockaddr*)&nladdr, sizeof(nladdr) );
382         if( err < 0 )
383         {
384                 perror( "netlink sendto" );
385                 return -1;
386         }
387
388         /* recv */
389         rsize = recv( s, &rxbuf, sizeof(rxbuf), 0 );
390         if( rsize < 0 )
391         {
392                 perr( "recv" );
393                 return -1;
394         }
395         nlh = (struct nlmsghdr*)rxbuf;
396
397         /* REMOVE */
398         sleep( 2 );
399         //printf( "closing sockets\n" );
400         //shutdown( gw_eth_sock, SHUT_RDWR );
401         shutdown( gw_can_sock, SHUT_RDWR );
402         //close( gw_can_sock );
403         //close( gw_eth_sock );
404
405         if( nlh->nlmsg_type == NLMSG_ERROR )
406         {
407                 rte = (struct nlmsgerr*)NLMSG_DATA( nlh );
408                 err = rte->error;
409
410                 if( err == 0  )
411                 {
412                         printf( "%s\n", strerror(abs(err)) );
413                         return 0;
414                 } else
415                 {
416                         printf( "netlink error: %s\n", strerror(abs(err)) );
417                         return -1;
418                 }
419         }
420
421         if( cmd & CEGW_CMD_LIST )
422         {
423                 /* ToDo recv while */
424                 printf( "%10ssource%20sdestination\n", "", "" );
425                 while( 1 )
426                 {
427                         if( !NLMSG_OK( nlh, rsize ) )
428                         {
429                                 break;
430                         }
431                         if( nlh->nlmsg_type == NLMSG_DONE )
432                         {
433                                 break;
434                         }
435                         /* ToDo: NLMSG_ERR */
436                         rta = NLMSG_DATA( nlh );
437                         len = NLMSG_PAYLOAD( nlh, 0 );
438                         for( ;RTA_OK(rta, len); rta = RTA_NEXT(rta,len) )
439                         {
440                                 switch( rta->rta_type )
441                                 {
442                                         case CEGW_TYPE:
443                                                 li.type = *(int*)RTA_DATA(rta);
444                                                 break;
445                                         case CEGW_CAN_IFINDEX:
446                                                 li.can = *(int*)RTA_DATA(rta);
447                                                 break;
448                                         case CEGW_ETH_IP:
449                                                 li.ip = *(struct in_addr*)RTA_DATA(rta);
450                                                 break;
451                                         case CEGW_ETH_PORT:
452                                                 li.port = *(unsigned short*)RTA_DATA(rta);
453                                                 break;
454                                         /* case CGW_ETH_PROTO */
455                                 }
456                         }
457
458                         print_list_item( &li );
459
460                         nlh = NLMSG_NEXT( nlh, rsize );
461                 }
462         }
463
464         return 0;
465 }
466