]> rtime.felk.cvut.cz Git - can-utils.git/blob - isotptun.c
candump: Enable HW timestamping before using it
[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 /* stay on 4095 bytes for the max. PDU length which is still much more than the standard ethernet MTU */
72 #define MAX_PDU_LENGTH 4095
73 #define BUF_LEN (MAX_PDU_LENGTH + 1)
74
75 static volatile int running = 1;
76
77 void print_usage(char *prg)
78 {
79         fprintf(stderr, "\nUsage: %s [options] <CAN interface>\n\n", prg);
80         fprintf(stderr, "This program creates a Linux tunnel netdevice 'ctunX' and transfers the\n");
81         fprintf(stderr, "ethernet frames inside ISO15765-2 (unreliable) datagrams on CAN.\n\n");
82         fprintf(stderr, "Options: -s <can_id>  (source can_id. Use 8 digits for extended IDs)\n");
83         fprintf(stderr, "         -d <can_id>  (destination can_id. Use 8 digits for extended IDs)\n");
84         fprintf(stderr, "         -n <name>    (name of created IP netdevice. Default: '%s')\n", DEFAULT_NAME);
85         fprintf(stderr, "         -x <addr>[:<rxaddr>] (extended addressing / opt. separate rxaddr)\n");
86         fprintf(stderr, "         -L <mtu>:<tx_dl>:<tx_flags> (link layer options for CAN FD)\n");
87         fprintf(stderr, "         -p [tx]:[rx] (set and enable tx/rx padding bytes)\n");
88         fprintf(stderr, "         -P <mode>    (check rx padding for (l)ength (c)ontent (a)ll)\n");
89         fprintf(stderr, "         -t <time ns> (transmit time in nanosecs)\n");
90         fprintf(stderr, "         -b <bs>      (blocksize. 0 = off)\n");
91         fprintf(stderr, "         -m <val>     (STmin in ms/ns. See spec.)\n");
92         fprintf(stderr, "         -w <num>     (max. wait frame transmissions.)\n");
93         fprintf(stderr, "         -h           (half duplex mode.)\n");
94         fprintf(stderr, "         -v           (verbose mode. Print symbols for tunneled msgs.)\n");
95         fprintf(stderr, "\nCAN IDs and addresses are given and expected in hexadecimal values.\n");
96         fprintf(stderr, "Use e.g. 'ifconfig ctun0 123.123.123.1 pointopoint 123.123.123.2 up'\n");
97         fprintf(stderr, "to create a point-to-point IP connection on CAN.\n");
98         fprintf(stderr, "\n");
99 }
100
101 void sigterm(int signo)
102 {
103         running = 0;
104 }
105
106 int main(int argc, char **argv)
107 {
108         fd_set rdfs;
109         int s, t;
110         struct sockaddr_can addr;
111         struct ifreq ifr;
112         static struct can_isotp_options opts;
113         static struct can_isotp_fc_options fcopts;
114         static struct can_isotp_ll_options llopts;
115         int opt, ret;
116         extern int optind, opterr, optopt;
117         static int verbose;
118         unsigned char buffer[BUF_LEN];
119         static char name[IFNAMSIZ] = DEFAULT_NAME;
120         int nbytes;
121
122         signal(SIGTERM, sigterm);
123         signal(SIGHUP, sigterm);
124         signal(SIGINT, sigterm);
125
126         addr.can_addr.tp.tx_id = addr.can_addr.tp.rx_id = NO_CAN_ID;
127
128         while ((opt = getopt(argc, argv, "s:d:n:x:p:P:t:b:m:whL:v?")) != -1) {
129                 switch (opt) {
130                 case 's':
131                         addr.can_addr.tp.tx_id = strtoul(optarg, (char **)NULL, 16);
132                         if (strlen(optarg) > 7)
133                                 addr.can_addr.tp.tx_id |= CAN_EFF_FLAG;
134                         break;
135
136                 case 'd':
137                         addr.can_addr.tp.rx_id = strtoul(optarg, (char **)NULL, 16);
138                         if (strlen(optarg) > 7)
139                                 addr.can_addr.tp.rx_id |= CAN_EFF_FLAG;
140                         break;
141
142                 case 'n':
143                         strncpy(name, optarg, IFNAMSIZ-1);
144                         break;
145
146                 case 'x':
147                 {
148                         int elements = sscanf(optarg, "%hhx:%hhx",
149                                               &opts.ext_address,
150                                               &opts.rx_ext_address);
151
152                         if (elements == 1)
153                                 opts.flags |= CAN_ISOTP_EXTEND_ADDR;
154                         else if (elements == 2)
155                                 opts.flags |= (CAN_ISOTP_EXTEND_ADDR | CAN_ISOTP_RX_EXT_ADDR);
156                         else {
157                                 printf("incorrect extended addr values '%s'.\n", optarg);
158                                 print_usage(basename(argv[0]));
159                                 exit(0);
160                         }
161                         break;
162                 }
163
164                 case 'p':
165                 {
166                         int elements = sscanf(optarg, "%hhx:%hhx",
167                                               &opts.txpad_content,
168                                               &opts.rxpad_content);
169
170                         if (elements == 1)
171                                 opts.flags |= CAN_ISOTP_TX_PADDING;
172                         else if (elements == 2)
173                                 opts.flags |= (CAN_ISOTP_TX_PADDING | CAN_ISOTP_RX_PADDING);
174                         else if (sscanf(optarg, ":%hhx", &opts.rxpad_content) == 1)
175                                 opts.flags |= CAN_ISOTP_RX_PADDING;
176                         else {
177                                 printf("incorrect padding values '%s'.\n", optarg);
178                                 print_usage(basename(argv[0]));
179                                 exit(0);
180                         }
181                         break;
182                 }
183
184                 case 'P':
185                         if (optarg[0] == 'l')
186                                 opts.flags |= CAN_ISOTP_CHK_PAD_LEN;
187                         else if (optarg[0] == 'c')
188                                 opts.flags |= CAN_ISOTP_CHK_PAD_DATA;
189                         else if (optarg[0] == 'a')
190                                 opts.flags |= (CAN_ISOTP_CHK_PAD_LEN | CAN_ISOTP_CHK_PAD_DATA);
191                         else {
192                                 printf("unknown padding check option '%c'.\n", optarg[0]);
193                                 print_usage(basename(argv[0]));
194                                 exit(0);
195                         }
196                         break;
197
198                 case 't':
199                         opts.frame_txtime = strtoul(optarg, (char **)NULL, 10);
200                         break;
201
202                 case 'b':
203                         fcopts.bs = strtoul(optarg, (char **)NULL, 16) & 0xFF;
204                         break;
205
206                 case 'm':
207                         fcopts.stmin = strtoul(optarg, (char **)NULL, 16) & 0xFF;
208                         break;
209
210                 case 'w':
211                         fcopts.wftmax = strtoul(optarg, (char **)NULL, 16) & 0xFF;
212                         break;
213
214                 case 'h':
215                         opts.flags |= CAN_ISOTP_HALF_DUPLEX;
216                         break;
217
218                 case 'L':
219                         if (sscanf(optarg, "%hhu:%hhu:%hhu",
220                                    &llopts.mtu,
221                                    &llopts.tx_dl,
222                                    &llopts.tx_flags) != 3) {
223                                 printf("unknown link layer options '%s'.\n", optarg);
224                                 print_usage(basename(argv[0]));
225                                 exit(0);
226                         }
227                         break;
228
229                 case 'v':
230                         verbose = 1;
231                         break;
232
233                 case '?':
234                         print_usage(basename(argv[0]));
235                         exit(0);
236                         break;
237
238                 default:
239                         fprintf(stderr, "Unknown option %c\n", opt);
240                         print_usage(basename(argv[0]));
241                         exit(1);
242                         break;
243                 }
244         }
245
246         if ((argc - optind != 1) ||
247             (addr.can_addr.tp.tx_id == NO_CAN_ID) ||
248             (addr.can_addr.tp.rx_id == NO_CAN_ID)) {
249                 print_usage(basename(argv[0]));
250                 exit(1);
251         }
252   
253         if ((s = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP)) < 0) {
254                 perror("socket");
255                 exit(1);
256         }
257
258         setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts));
259         setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fcopts, sizeof(fcopts));
260
261         if (llopts.tx_dl) {
262                 if (setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, &llopts, sizeof(llopts)) < 0) {
263                         perror("link layer sockopt");
264                         exit(1);
265                 }
266         }
267
268         strncpy(ifr.ifr_name, argv[optind], IFNAMSIZ);
269         ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name);
270         if (!ifr.ifr_ifindex) {
271                 perror("if_nametoindex");
272                 close(s);
273                 exit(1);
274         }
275
276         addr.can_family = AF_CAN;
277         addr.can_ifindex = ifr.ifr_ifindex;
278
279         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
280                 perror("bind");
281                 close(s);
282                 exit(1);
283         }
284
285         if ((t = open("/dev/net/tun", O_RDWR)) < 0) {
286                 perror("open tunfd");
287                 close(s);
288                 close(t);
289                 exit(1);
290         }
291
292         memset(&ifr, 0, sizeof(ifr));
293         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
294         strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
295         ifr.ifr_name[IFNAMSIZ - 1] = '\0';
296
297         if (ioctl(t, TUNSETIFF, (void *) &ifr) < 0) {
298                 perror("ioctl tunfd");
299                 close(s);
300                 close(t);
301                 exit(1);
302         }
303
304         while (running) {
305
306                 FD_ZERO(&rdfs);
307                 FD_SET(s, &rdfs);
308                 FD_SET(t, &rdfs);
309
310                 if ((ret = select(t+1, &rdfs, NULL, NULL, NULL)) < 0) {
311                         perror("select");
312                         continue;
313                 }
314
315                 if (FD_ISSET(s, &rdfs)) {
316                         nbytes = read(s, buffer, BUF_LEN);
317                         if (nbytes < 0) {
318                                 perror("read isotp socket");
319                                 return -1;
320                         }
321                         if (nbytes > MAX_PDU_LENGTH)
322                                 return -1;
323                         ret = write(t, buffer, nbytes);
324                         if (verbose) {
325                                 if (ret < 0 && errno == EAGAIN)
326                                         printf(";");
327                                 else
328                                         printf(",");
329                                 fflush(stdout);
330                         }
331                 }
332
333                 if (FD_ISSET(t, &rdfs)) {
334                         nbytes = read(t, buffer, BUF_LEN);
335                         if (nbytes < 0) {
336                                 perror("read tunfd");
337                                 return -1;
338                         }
339                         if (nbytes > MAX_PDU_LENGTH)
340                                 return -1;
341                         ret = write(s, buffer, nbytes);
342                         if (verbose) {
343                                 if (ret < 0 && errno == EAGAIN)
344                                         printf(":");
345                                 else
346                                         printf(".");
347                                 fflush(stdout);
348                         }
349                 }
350         }
351
352         close(s);
353         close(t);
354         return 0;
355 }