From f1d4ebbbbaf543f2e1ac3c01dd0b56fb950762d2 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 5 Nov 2008 11:34:23 +0000 Subject: [PATCH] Added ISO 15765-2 CAN transport protocol for protocol family CAN. Including some tools and coarse documentation README.isotp --- Makefile | 3 +- isotpdump.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ isotprecv.c | 196 +++++++++++++++++++++++++++++++++ isotpsend.c | 178 ++++++++++++++++++++++++++++++ isotpsniffer.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ isotptun.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1339 insertions(+), 1 deletion(-) create mode 100644 isotpdump.c create mode 100644 isotprecv.c create mode 100644 isotpsend.c create mode 100644 isotpsniffer.c create mode 100644 isotptun.c diff --git a/Makefile b/Makefile index 727f760..b46d78e 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,8 @@ CFLAGS = -O2 -Wall -Wno-parentheses -I../kernel/2.6/include \ -DAF_CAN=PF_CAN PROGRAMS = candump cansniffer cansend canplayer canlogserver cangen\ - canbusload log2long log2asc asc2log vcan slcan_attach + canbusload log2long log2asc asc2log vcan slcan_attach\ + isotpdump isotprecv isotpsend isotpsniffer isotptun all: $(PROGRAMS) diff --git a/isotpdump.c b/isotpdump.c new file mode 100644 index 0000000..5507fae --- /dev/null +++ b/isotpdump.c @@ -0,0 +1,338 @@ +/* + * $Id: isotpdump.c 824 2008-09-02 07:01:51Z hartko $ + */ + +/* + * isotpdump.c - dump and explain ISO15765-2 protocol CAN frames + * + * Copyright (c) 2008 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include "terminal.h" + +#define NO_CAN_ID 0xFFFFFFFFU + +const char fc_info [4][9] = { "CTS", "WT", "OVFLW", "reserved" }; + +void print_usage(char *prg) +{ + fprintf(stderr, "\nUsage: %s [options] \n", prg); + fprintf(stderr, "Options: -s (source can_id. Use 8 digits for extended IDs)\n"); + fprintf(stderr, " -d (destination can_id. Use 8 digits for extended IDs)\n"); + fprintf(stderr, " -x (extended addressing mode. Use 'any' for all addresses)\n"); + fprintf(stderr, " -c (color mode)\n"); + fprintf(stderr, " -a (print data also in ASCII-chars)\n"); + fprintf(stderr, " -t (timestamp: (a)bsolute/(d)elta/(z)ero/(A)bsolute w date)\n"); + fprintf(stderr, "\nCAN IDs and addresses are given and expected in hexadecimal values.\n"); + fprintf(stderr, "\n"); +} + +int main(int argc, char **argv) +{ + int s; + struct sockaddr_can addr; + struct can_filter rfilter[2]; + struct can_frame frame; + int nbytes, i; + canid_t src = NO_CAN_ID; + canid_t dst = NO_CAN_ID; + int ext = 0; + int extaddr = 0; + int extany = 0; + int asc = 0; + int color = 0; + int timestamp = 0; + int datidx = 0; + struct ifreq ifr; + int ifindex; + struct timeval tv, last_tv; + unsigned int n_pci; + int opt; + + last_tv.tv_sec = 0; + last_tv.tv_usec = 0; + + while ((opt = getopt(argc, argv, "s:d:ax:ct:")) != -1) { + switch (opt) { + case 's': + src = strtoul(optarg, (char **)NULL, 16); + if (strlen(optarg) > 7) + src |= CAN_EFF_FLAG; + break; + + case 'd': + dst = strtoul(optarg, (char **)NULL, 16); + if (strlen(optarg) > 7) + dst |= CAN_EFF_FLAG; + break; + + case 'c': + color = 1; + break; + + case 'a': + asc = 1; + break; + + case 'x': + ext = 1; + if (!strncmp(optarg, "any", 3)) + extany = 1; + else + extaddr = strtoul(optarg, (char **)NULL, 16) & 0xFF; + + break; + + case 't': + timestamp = optarg[0]; + if ((timestamp != 'a') && (timestamp != 'A') && + (timestamp != 'd') && (timestamp != 'z')) { + printf("%s: unknown timestamp mode '%c' - ignored\n", + basename(argv[0]), optarg[0]); + timestamp = 0; + } + break; + + default: + fprintf(stderr, "Unknown option %c\n", opt); + print_usage(basename(argv[0])); + exit(0); + break; + } + } + + if ((argc - optind) != 1 || src == NO_CAN_ID || dst == NO_CAN_ID) { + print_usage(basename(argv[0])); + exit(0); + } + + if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("socket"); + return 1; + } + + rfilter[0].can_id = src; + if (src & CAN_EFF_FLAG) + rfilter[0].can_mask = CAN_EFF_MASK | CAN_EFF_FLAG; + else + rfilter[0].can_mask = CAN_SFF_MASK; + + rfilter[1].can_id = dst; + if (dst & CAN_EFF_FLAG) + rfilter[1].can_mask = CAN_EFF_MASK | CAN_EFF_FLAG; + else + rfilter[1].can_mask = CAN_SFF_MASK; + + setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); + + strcpy(ifr.ifr_name, argv[optind]); + ioctl(s, SIOCGIFINDEX, &ifr); + ifindex = ifr.ifr_ifindex; + + addr.can_family = AF_CAN; + addr.can_ifindex = ifindex; + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + return 1; + } + + while (1) { + + if ((nbytes = read(s, &frame, sizeof(struct can_frame))) < 0) { + perror("read"); + return 1; + } else if (nbytes < sizeof(struct can_frame)) { + fprintf(stderr, "read: incomplete CAN frame\n"); + return 1; + } else { + + if (ext && !extany && extaddr != frame.data[0]) + continue; + + if (color) + printf("%s", (frame.can_id == src)? FGRED:FGBLUE); + + if (timestamp) { + ioctl(s, SIOCGSTAMP, &tv); + + + switch (timestamp) { + + case 'a': /* absolute with timestamp */ + printf("(%ld.%06ld) ", tv.tv_sec, tv.tv_usec); + break; + + case 'A': /* absolute with date */ + { + struct tm tm; + char timestring[25]; + + tm = *localtime(&tv.tv_sec); + strftime(timestring, 24, "%Y-%m-%d %H:%M:%S", &tm); + printf("(%s.%06ld) ", timestring, tv.tv_usec); + } + break; + + case 'd': /* delta */ + case 'z': /* starting with zero */ + { + struct timeval diff; + + if (last_tv.tv_sec == 0) /* first init */ + last_tv = tv; + diff.tv_sec = tv.tv_sec - last_tv.tv_sec; + diff.tv_usec = tv.tv_usec - last_tv.tv_usec; + if (diff.tv_usec < 0) + diff.tv_sec--, diff.tv_usec += 1000000; + if (diff.tv_sec < 0) + diff.tv_sec = diff.tv_usec = 0; + printf("(%ld.%06ld) ", diff.tv_sec, diff.tv_usec); + + if (timestamp == 'd') + last_tv = tv; /* update for delta calculation */ + } + break; + + default: /* no timestamp output */ + break; + } + } + + if (frame.can_id & CAN_EFF_FLAG) + printf(" %s %8X", argv[optind], frame.can_id & CAN_EFF_MASK); + else + printf(" %s %3X", argv[optind], frame.can_id & CAN_SFF_MASK); + + if (ext) + printf("{%02X}", frame.data[0]); + + printf(" [%d] ", frame.can_dlc); + + datidx = 0; + n_pci = frame.data[ext]; + + switch (n_pci & 0xF0) { + case 0x00: + printf("[SF] ln: %-4d data:", n_pci & 0x0F); + datidx = ext+1; + break; + + case 0x10: + printf("[FF] ln: %-4d data:", + ((n_pci & 0x0F)<<8) + frame.data[ext+1] ); + datidx = ext+2; + break; + + case 0x20: + printf("[CF] sn: %X data:", n_pci & 0x0F); + datidx = ext+1; + break; + + case 0x30: + n_pci &= 0x0F; + printf("[FC] FC: %d ", n_pci); + + if (n_pci > 3) + n_pci = 3; + + printf("= %s # ", fc_info[n_pci]); + + printf("BS: %d %s# ", frame.data[ext+1], + (frame.data[ext+1])? "":"= off "); + + i = frame.data[ext+2]; + printf("STmin: 0x%02X = ", i); + + if (i < 0x80) + printf("%d ms", i); + else if (i > 0xF0 && i < 0xFA) + printf("%d us", (i & 0x0F) * 100); + else + printf("reserved"); + break; + + default: + printf("[??]"); + } + + if (datidx && frame.can_dlc > datidx) { + printf(" "); + for (i = datidx; i < frame.can_dlc; i++) { + printf("%02X ", frame.data[i]); + } + + if (asc) { + printf("%*s", ((7-ext) - (frame.can_dlc-datidx))*3 + 5 , + "- '"); + for (i = datidx; i < frame.can_dlc; i++) { + printf("%c",((frame.data[i] > 0x1F) && + (frame.data[i] < 0x7F))? + frame.data[i] : '.'); + } + printf("'"); + } + } + + if (color) + printf("%s", ATTRESET); + printf("\n"); + fflush(stdout); + } + } + + close(s); + + return 0; +} diff --git a/isotprecv.c b/isotprecv.c new file mode 100644 index 0000000..c2e1854 --- /dev/null +++ b/isotprecv.c @@ -0,0 +1,196 @@ +/* + * $Id: isotprecv.c 824 2008-09-02 07:01:51Z hartko $ + */ + +/* + * isotprecv.c + * + * Copyright (c) 2008 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define NO_CAN_ID 0xFFFFFFFFU + +void print_usage(char *prg) +{ + fprintf(stderr, "\nUsage: %s [options] \n", prg); + fprintf(stderr, "Options: -s (source can_id. Use 8 digits for extended IDs)\n"); + fprintf(stderr, " -d (destination can_id. Use 8 digits for extended IDs)\n"); + fprintf(stderr, " -x (extended addressing mode.)\n"); + fprintf(stderr, " -p (set and enable padding byte)\n"); + fprintf(stderr, " -P (check padding in SF/CF. (l)ength (c)ontent (a)ll)\n"); + fprintf(stderr, " -b (blocksize. 0 = off)\n"); + fprintf(stderr, " -m (STmin in ms/ns. See spec.)\n"); + fprintf(stderr, " -w (max. wait frame transmissions.)\n"); + fprintf(stderr, " -l (loop: do not exit after pdu receiption.)\n"); + fprintf(stderr, "\nCAN IDs and addresses are given and expected in hexadecimal values.\n"); + fprintf(stderr, "The pdu data is written on STDOUT in space separated ASCII hex values.\n"); + fprintf(stderr, "\n"); +} + +int main(int argc, char **argv) +{ + int s; + struct sockaddr_can addr; + struct ifreq ifr; + static struct can_isotp_options opts; + static struct can_isotp_fc_options fcopts; + int opt, i; + extern int optind, opterr, optopt; + int loop = 0; + + unsigned char msg[4096]; + int nbytes; + + addr.can_addr.tp.tx_id = addr.can_addr.tp.rx_id = NO_CAN_ID; + + while ((opt = getopt(argc, argv, "s:d:x:p:P:b:m:w:l")) != -1) { + switch (opt) { + case 's': + addr.can_addr.tp.tx_id = strtoul(optarg, (char **)NULL, 16); + if (strlen(optarg) > 7) + addr.can_addr.tp.tx_id |= CAN_EFF_FLAG; + break; + + case 'd': + addr.can_addr.tp.rx_id = strtoul(optarg, (char **)NULL, 16); + if (strlen(optarg) > 7) + addr.can_addr.tp.rx_id |= CAN_EFF_FLAG; + break; + + case 'x': + opts.flags |= CAN_ISOTP_EXTEND_ADDR; + opts.ext_address = strtoul(optarg, (char **)NULL, 16) & 0xFF; + break; + + case 'p': + opts.flags |= CAN_ISOTP_RX_PADDING; + opts.rxpad_content = strtoul(optarg, (char **)NULL, 16) & 0xFF; + break; + + case 'P': + if (optarg[0] == 'l') + opts.flags |= CAN_ISOTP_CHK_PAD_LEN; + else if (optarg[0] == 'c') + opts.flags |= CAN_ISOTP_CHK_PAD_DATA; + else if (optarg[0] == 'a') + opts.flags |= (CAN_ISOTP_CHK_PAD_DATA | CAN_ISOTP_CHK_PAD_DATA); + else { + printf("unknown padding check option '%c'.\n", optarg[0]); + print_usage(basename(argv[0])); + exit(0); + } + break; + + case 'b': + fcopts.bs = strtoul(optarg, (char **)NULL, 16) & 0xFF; + break; + + case 'm': + fcopts.stmin = strtoul(optarg, (char **)NULL, 16) & 0xFF; + break; + + case 'w': + fcopts.wftmax = strtoul(optarg, (char **)NULL, 16) & 0xFF; + break; + + case 'l': + loop = 1; + break; + + default: + fprintf(stderr, "Unknown option %c\n", opt); + print_usage(basename(argv[0])); + exit(0); + break; + } + } + + if ((argc - optind != 1) || + (addr.can_addr.tp.tx_id == NO_CAN_ID) || + (addr.can_addr.tp.rx_id == NO_CAN_ID)) { + print_usage(basename(argv[0])); + exit(0); + } + + if ((s = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP)) < 0) { + perror("socket"); + exit(1); + } + + setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts)); + setsockopt(s, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fcopts, sizeof(fcopts)); + + addr.can_family = AF_CAN; + strcpy(ifr.ifr_name, argv[optind]); + ioctl(s, SIOCGIFINDEX, &ifr); + addr.can_ifindex = ifr.ifr_ifindex; + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + close(s); + exit(1); + } + + do { + nbytes = read(s, msg, 4096); + if (nbytes > 0 && nbytes < 4096) + for (i=0; i < nbytes; i++) + printf("%02X ", msg[i]); + printf("\n"); + } while (loop); + + close(s); + + return 0; +} diff --git a/isotpsend.c b/isotpsend.c new file mode 100644 index 0000000..a666c99 --- /dev/null +++ b/isotpsend.c @@ -0,0 +1,178 @@ +/* + * $Id: isotpsend.c 824 2008-09-02 07:01:51Z hartko $ + */ + +/* + * isotpsend.c + * + * Copyright (c) 2008 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define NO_CAN_ID 0xFFFFFFFFU + +void print_usage(char *prg) +{ + fprintf(stderr, "\nUsage: %s [options] \n", prg); + fprintf(stderr, "Options: -s (source can_id. Use 8 digits for extended IDs)\n"); + fprintf(stderr, " -d (destination can_id. Use 8 digits for extended IDs)\n"); + fprintf(stderr, " -x (extended addressing mode. Use 'any' for all addresses)\n"); + fprintf(stderr, " -p (set and enable padding byte)\n"); + fprintf(stderr, " -P (check padding in FC. (l)ength (c)ontent (a)ll)\n"); + fprintf(stderr, " -t