]> rtime.felk.cvut.cz Git - sojka/can-utils.git/blob - isotptun.c
can-utils: AOSP build clean up
[sojka/can-utils.git] / isotptun.c
1 /*
2  * isotptun.c - IP over CAN ISO-TP (ISO15765-2) tunnel / proof-of-concept
3  *
4  * This program creates a Linux tunnel netdevice 'ctunX' and transfers the
5  * ethernet frames inside ISO15765-2 (unreliable) datagrams on CAN.
6  *
7  * Use e.g. "ifconfig ctun0 123.123.123.1 pointopoint 123.123.123.2 up"
8  * to create a point-to-point IP connection on CAN.
9  *
10  * Copyright (c) 2008 Volkswagen Group Electronic Research
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of Volkswagen nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * Alternatively, provided that this notice is retained in full, this
26  * software may be distributed under the terms of the GNU General
27  * Public License ("GPL") version 2, in which case the provisions of the
28  * GPL apply INSTEAD OF those given above.
29  *
30  * The provided data structures and external interfaces from this code
31  * are not restricted to be used by modules with a GPL compatible license.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
34  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
35  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
36  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
37  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
40  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
41  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
43  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
44  * DAMAGE.
45  *
46  * Send feedback to <linux-can@vger.kernel.org>
47  *
48  */
49
50 #include <stdio.h>
51 #include <fcntl.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <libgen.h>
56 #include <errno.h>
57 #include <signal.h>
58
59 #include <net/if.h>
60 #include <sys/types.h>
61 #include <sys/socket.h>
62 #include <sys/ioctl.h>
63
64 #include <linux/can.h>
65 #include <linux/can/isotp.h>
66 #include <linux/if_tun.h>
67
68 #define NO_CAN_ID 0xFFFFFFFFU
69 #define DEFAULT_NAME "ctun%d"
70
71 static volatile int running = 1;
72
73 void print_usage(char *prg)
74 {
75         fprintf(stderr, "\nUsage: %s [options] <CAN interface>\n\n", prg);
76         fprintf(stderr, "This program creates a Linux tunnel netdevice 'ctunX' and transfers the\n");
77         fprintf(stderr, "ethernet frames inside ISO15765-2 (unreliable) datagrams on CAN.\n\n");
78         fprintf(stderr, "Options: -s <can_id>  (source can_id. Use 8 digits for extended IDs)\n");
79         fprintf(stderr, "         -d <can_id>  (destination can_id. Use 8 digits for extended IDs)\n");
80         fprintf(stderr, "         -n <name>    (name of created IP netdevice. Default: '%s')\n", DEFAULT_NAME);
81         fprintf(stderr, "         -x <addr>    (extended addressing mode.)\n");
82         fprintf(stderr, "         -p <byte>    (padding byte rx path)\n");
83         fprintf(stderr, "         -q <byte>    (padding byte tx path)\n");
84         fprintf(stderr, "         -P <mode>    (check padding. (l)ength (c)ontent (a)ll)\n");
85         fprintf(stderr, "         -t <time ns> (transmit time in nanosecs)\n");
86         fprintf(stderr, "         -b <bs>      (blocksize. 0 = off)\n");
87         fprintf(stderr, "         -m <val>     (STmin in ms/ns. See spec.)\n");
88         fprintf(stderr, "         -w <num>     (max. wait frame transmissions.)\n");
89         fprintf(stderr, "         -h           (half duplex mode.)\n");
90         fprintf(stderr, "         -v           (verbose mode. Print symbols for tunneled msgs.)\n");
91         fprintf(stderr, "\nCAN IDs and addresses are given and expected in hexadecimal values.\n");
92         fprintf(stderr, "Use e.g. 'ifconfig ctun0 123.123.123.1 pointopoint 123.123.123.2 up'\n");
93         fprintf(stderr, "to create a point-to-point IP connection on CAN.\n");
94         fprintf(stderr, "\n");
95 }
96
97 void sigterm(int signo)
98 {
99         running = 0;
100 }
101
102 int main(int argc, char **argv)
103 {
104         fd_set rdfs;
105         int s, t;
106         struct sockaddr_can addr;
107         struct ifreq ifr;
108         static struct can_isotp_options opts;
109         static struct can_isotp_fc_options fcopts;
110         int opt, ret;
111         extern int optind, opterr, optopt;
112         static int verbose;
113         unsigned char buffer[4096];
114         static char name[IFNAMSIZ] = DEFAULT_NAME;
115         int nbytes;
116
117         signal(SIGTERM, sigterm);
118         signal(SIGHUP, sigterm);
119         signal(SIGINT, sigterm);
120
121         addr.can_addr.tp.tx_id = addr.can_addr.tp.rx_id = NO_CAN_ID;
122
123         while ((opt = getopt(argc, argv, "s:d:n:x:p:q:P:t:b:m:whv?")) != -1) {
124                 switch (opt) {
125                 case 's':
126                         addr.can_addr.tp.tx_id = strtoul(optarg, (char **)NULL, 16);
127                         if (strlen(optarg) > 7)
128                                 addr.can_addr.tp.tx_id |= CAN_EFF_FLAG;
129                         break;
130
131                 case 'd':
132                         addr.can_addr.tp.rx_id = strtoul(optarg, (char **)NULL, 16);
133                         if (strlen(optarg) > 7)
134                                 addr.can_addr.tp.rx_id |= CAN_EFF_FLAG;
135                         break;
136
137                 case 'n':
138                         strncpy(name, optarg, IFNAMSIZ-1);
139                         break;
140
141                 case 'x':
142                         opts.flags |= CAN_ISOTP_EXTEND_ADDR;
143                         opts.ext_address = strtoul(optarg, (char **)NULL, 16) & 0xFF;
144                         break;
145
146                 case 'p':
147                         opts.flags |= CAN_ISOTP_RX_PADDING;
148                         opts.rxpad_content = strtoul(optarg, (char **)NULL, 16) & 0xFF;
149                         break;
150
151                 case 'q':
152                         opts.flags |= CAN_ISOTP_TX_PADDING;
153                         opts.txpad_content = strtoul(optarg, (char **)NULL, 16) & 0xFF;
154                         break;
155
156                 case 'P':
157                         if (optarg[0] == 'l')
158                                 opts.flags |= CAN_ISOTP_CHK_PAD_LEN;
159                         else if (optarg[0] == 'c')
160                                 opts.flags |= CAN_ISOTP_CHK_PAD_DATA;
161                         else if (optarg[0] == 'a')
162                                 opts.flags |= (CAN_ISOTP_CHK_PAD_DATA | CAN_ISOTP_CHK_PAD_DATA);
163                         else {
164                                 printf("unknown padding check option '%c'.\n", optarg[0]);
165                                 print_usage(basename(argv[0]));
166                                 exit(0);
167                         }
168                         break;
169
170                 case 't':
171                         opts.frame_txtime = strtoul(optarg, (char **)NULL, 10);
172                         break;
173
174                 case 'b':
175                         fcopts.bs = strtoul(optarg, (char **)NULL, 16) & 0xFF;
176                         break;
177
178                 case 'm':
179                         fcopts.stmin = strtoul(optarg, (char **)NULL, 16) & 0xFF;
180                         break;
181
182                 case 'w':
183                         fcopts.wftmax = strtoul(optarg, (char **)NULL, 16) & 0xFF;
184                         break;
185
186                 case 'h':
187                         opts.flags |= CAN_ISOTP_HALF_DUPLEX;
188                         break;
189
190                 case 'v':
191                         verbose = 1;
192                         break;
193
194                 case '?':
195                         print_usage(basename(argv[0]));
196                         exit(0);
197                         break;
198
199                 default:
200                         fprintf(stderr, "Unknown option %c\n", opt);
201                         print_usage(basename(argv[0]));
202                         exit(1);
203                         break;
204                 }
205         }
206
207         if ((argc - optind != 1) ||
208             (addr.can_addr.tp.tx_id == NO_CAN_ID) ||
209             (addr.can_addr.tp.rx_id == NO_CAN_ID)) {
210                 print_usage(basename(argv[0]));
211                 exit(1);
212         }
213   
214         if ((s = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP)) < 0) {
215                 perror("socket");
216                 exit(1);
217         }
218
219         setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts));
220         setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fcopts, sizeof(fcopts));
221
222         addr.can_family = AF_CAN;
223         strcpy(ifr.ifr_name, argv[optind]);
224         ioctl(s, SIOCGIFINDEX, &ifr);
225         addr.can_ifindex = ifr.ifr_ifindex;
226
227         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
228                 perror("bind");
229                 close(s);
230                 exit(1);
231         }
232
233         if ((t = open("/dev/net/tun", O_RDWR)) < 0) {
234                 perror("open tunfd");
235                 close(s);
236                 close(t);
237                 exit(1);
238         }
239
240         memset(&ifr, 0, sizeof(ifr));
241         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
242         strncpy(ifr.ifr_name, name, IFNAMSIZ);
243
244         if (ioctl(t, TUNSETIFF, (void *) &ifr) < 0) {
245                 perror("ioctl tunfd");
246                 close(s);
247                 close(t);
248                 exit(1);
249         }
250
251         while (running) {
252
253                 FD_ZERO(&rdfs);
254                 FD_SET(s, &rdfs);
255                 FD_SET(t, &rdfs);
256
257                 if ((ret = select(t+1, &rdfs, NULL, NULL, NULL)) < 0) {
258                         perror("select");
259                         continue;
260                 }
261
262                 if (FD_ISSET(s, &rdfs)) {
263                         nbytes = read(s, buffer, 4096);
264                         if (nbytes < 0) {
265                                 perror("read isotp socket");
266                                 return -1;
267                         }
268                         if (nbytes > 4095)
269                                 return -1;
270                         ret = write(t, buffer, nbytes);
271                         if (verbose) {
272                                 if (ret < 0 && errno == EAGAIN)
273                                         printf(";");
274                                 else
275                                         printf(",");
276                                 fflush(stdout);
277                         }
278                 }
279
280                 if (FD_ISSET(t, &rdfs)) {
281                         nbytes = read(t, buffer, 4096);
282                         if (nbytes < 0) {
283                                 perror("read tunfd");
284                                 return -1;
285                         }
286                         if (nbytes > 4095)
287                                 return -1;
288                         ret = write(s, buffer, nbytes);
289                         if (verbose) {
290                                 if (ret < 0 && errno == EAGAIN)
291                                         printf(":");
292                                 else
293                                         printf(".");
294                                 fflush(stdout);
295                         }
296                 }
297         }
298
299         close(s);
300         close(t);
301         return 0;
302 }