From b592ec5b1f6bae9d32b1d49f7f33b3cbb2b99854 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 7 Aug 2024 13:02:05 +0200 Subject: [PATCH 1/5] canbusload: fix string length check for CAN FD The CAN interface description can now have two bitrates. Extend the length check to handle the CAN FD data bitrate correctly. Fixes: 6382765bf6ae ("canbusload: count databitrate seperately") Signed-off-by: Oliver Hartkopp --- canbusload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/canbusload.c b/canbusload.c index d2906ae..d7165da 100644 --- a/canbusload.c +++ b/canbusload.c @@ -309,7 +309,7 @@ int main(int argc, char **argv) ptr = argv[optind + i]; nbytes = strlen(ptr); - if (nbytes >= (int)(IFNAMSIZ + sizeof("@1000000") + 1)) { + if (nbytes >= (int)(IFNAMSIZ + sizeof("@1000000,2000000") + 1)) { printf("name of CAN device '%s' is too long!\n", ptr); return 1; } From 7665e1e23675a3523b37f5b1aaf3e4d488540d6a Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 7 Aug 2024 19:57:08 +0200 Subject: [PATCH 2/5] canbusload: add auto detection of CAN interfaces Rework to use a single CAN socket for the received CAN traffic. This allows to shutdown and restart various CAN interfaces without terminating the application. Additionally an auto detection has been implemented with the 'any' CAN interface. E.g. with any@500000,2000000 CAN interfaces that have not been defined before are assigned with the given 'any' bitrates when a CAN frame from this CAN interface has been received. Signed-off-by: Oliver Hartkopp --- canbusload.c | 170 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 107 insertions(+), 63 deletions(-) diff --git a/canbusload.c b/canbusload.c index d7165da..385c3f8 100644 --- a/canbusload.c +++ b/canbusload.c @@ -65,7 +65,8 @@ #include "terminal.h" #include "canframelen.h" -#define MAXSOCK 16 /* max. number of CAN interfaces given on the cmdline */ +#define ANYDEV "any" /* name of interface to receive from any CAN interface */ +#define MAXDEVS 20 /* max. number of CAN interfaces given on the cmdline */ #define PERCENTRES 5 /* resolution in percent for bargraph */ #define NUMBAR (100 / PERCENTRES) /* number of bargraph elements */ @@ -74,13 +75,14 @@ extern int optind, opterr, optopt; static struct { char devname[IFNAMSIZ + 1]; + int ifindex; unsigned int bitrate; unsigned int dbitrate; unsigned int recv_frames; unsigned int recv_bits_total; unsigned int recv_bits_payload; unsigned int recv_bits_dbitrate; -} stat[MAXSOCK + 1]; +} stat[MAXDEVS + 1]; static volatile int running = 1; static volatile sig_atomic_t signal_num; @@ -107,8 +109,9 @@ static void print_usage(char *prg) fprintf(stderr, " -i (ignore bitstuffing in bandwidth calculation)\n"); fprintf(stderr, " -e (exact calculation of stuffed bits)\n"); fprintf(stderr, "\n"); - fprintf(stderr, "Up to %d CAN interfaces with mandatory bitrate can be specified on the \n", MAXSOCK); - fprintf(stderr, "commandline in the form: @[,]\n\n"); + fprintf(stderr, "Up to %d CAN interfaces with mandatory bitrate can be specified on the \n", MAXDEVS); + fprintf(stderr, "commandline in the form: @[,]\n"); + fprintf(stderr, "The interface name 'any' enables an auto detection with the given bitrate[s]\n\n"); fprintf(stderr, "The bitrate is mandatory as it is needed to know the CAN bus bitrate to\n"); fprintf(stderr, "calculate the bus load percentage based on the received CAN frames.\n"); fprintf(stderr, "Due to the bitstuffing estimation the calculated busload may exceed 100%%.\n"); @@ -243,14 +246,16 @@ static void printstats(int signo) int main(int argc, char **argv) { fd_set rdfs; - int s[MAXSOCK]; - + int s; int opt; char *ptr, *nptr; struct sockaddr_can addr; struct canfd_frame frame; int nbytes, i; - struct ifreq ifr; + + int have_anydev = 0; + unsigned int anydev_bitrate = 0; + unsigned int anydev_dbitrate = 0; signal(SIGTERM, sigterm); signal(SIGHUP, sigterm); @@ -300,13 +305,14 @@ int main(int argc, char **argv) currmax = argc - optind; /* find real number of CAN devices */ - if (currmax > MAXSOCK) { - printf("More than %d CAN devices given on commandline!\n", MAXSOCK); + if (currmax > MAXDEVS) { + printf("More than %d CAN devices given on commandline!\n", MAXDEVS); return 1; } + /* prefill stat[] array with given interface assignments */ for (i = 0; i < currmax; i++) { - ptr = argv[optind + i]; + ptr = argv[optind + i + have_anydev]; nbytes = strlen(ptr); if (nbytes >= (int)(IFNAMSIZ + sizeof("@1000000,2000000") + 1)) { @@ -314,13 +320,7 @@ int main(int argc, char **argv) return 1; } - pr_debug("open %d '%s'.\n", i, ptr); - - s[i] = socket(PF_CAN, SOCK_RAW, CAN_RAW); - if (s[i] < 0) { - perror("socket"); - return 1; - } + pr_debug("handle %d '%s'.\n", i, ptr); nptr = strchr(ptr, '@'); @@ -330,22 +330,24 @@ int main(int argc, char **argv) return 1; } + /* interface name length */ nbytes = nptr - ptr; /* interface name is up the first '@' */ - if (nbytes >= (int)IFNAMSIZ) { printf("name of CAN device '%s' is too long!\n", ptr); return 1; } + /* copy interface name to stat[] entry */ strncpy(stat[i].devname, ptr, nbytes); - memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name)); - strncpy(ifr.ifr_name, ptr, nbytes); if (nbytes > max_devname_len) max_devname_len = nbytes; /* for nice printing */ char *endp; - stat[i].bitrate = strtol(nptr + 1, &endp, 0); /* bitrate is placed behind the '@' */ + /* bitrate is placed behind the '@' */ + stat[i].bitrate = strtol(nptr + 1, &endp, 0); + + /* check for CAN FD additional data bitrate */ if (*endp == ',') /* data bitrate is placed behind the ',' */ stat[i].dbitrate = strtol(endp + 1, &endp, 0); @@ -357,28 +359,45 @@ int main(int argc, char **argv) return 1; } + /* length of entire (data)bitrate description */ nbytes = strlen(nptr + 1); if (nbytes > max_bitrate_len) max_bitrate_len = nbytes; /* for nice printing */ - pr_debug("using interface name '%s'.\n", ifr.ifr_name); - - /* try to switch the socket into CAN FD mode */ - const int canfd_on = 1; - setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)); - - if (ioctl(s[i], SIOCGIFINDEX, &ifr) < 0) { - perror("SIOCGIFINDEX"); - exit(1); + /* handling for 'any' device */ + if (have_anydev == 0 && strcmp(ANYDEV, stat[i].devname) == 0) { + anydev_bitrate = stat[i].bitrate; + anydev_dbitrate = stat[i].dbitrate; + /* no real interface: remove this command line entry */ + have_anydev = 1; + currmax--; + i--; + } else { + stat[i].ifindex = if_nametoindex(stat[i].devname); + if (!stat[i].ifindex) { + printf("invalid CAN device '%s'!\n", stat[i].devname); + return 1; + } + pr_debug("using interface name '%s'.\n", stat[i].devname); } + } - addr.can_family = AF_CAN; - addr.can_ifindex = ifr.ifr_ifindex; + s = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (s < 0) { + perror("socket"); + return 1; + } - if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0) { - perror("bind"); - return 1; - } + /* try to switch the socket into CAN FD mode */ + const int canfd_on = 1; + setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)); + + addr.can_family = AF_CAN; + addr.can_ifindex = 0; /* any CAN device */ + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + return 1; } alarm(1); @@ -387,42 +406,67 @@ int main(int argc, char **argv) printf("%s", CLR_SCREEN); while (running) { - FD_ZERO(&rdfs); - for (i = 0; i < currmax; i++) - FD_SET(s[i], &rdfs); + socklen_t len = sizeof(addr); + int flags = 0; - if (select(s[currmax - 1] + 1, &rdfs, NULL, NULL, NULL) < 0) { + FD_ZERO(&rdfs); + FD_SET(s, &rdfs); + + if (select(s + 1, &rdfs, NULL, NULL, NULL) < 0) { //perror("pselect"); continue; } - for (i = 0; i < currmax; i++) { /* check all CAN RAW sockets */ + nbytes = recvfrom(s, &frame, sizeof(struct canfd_frame), + flags, (struct sockaddr*)&addr, &len); - if (FD_ISSET(s[i], &rdfs)) { - nbytes = read(s[i], &frame, sizeof(frame)); - - if (nbytes < 0) { - perror("read"); - return 1; - } - - if (nbytes < (int)sizeof(struct can_frame)) { - fprintf(stderr, "read: incomplete CAN frame\n"); - return 1; - } - - stat[i].recv_frames++; - stat[i].recv_bits_payload += frame.len * 8; - stat[i].recv_bits_dbitrate += can_frame_dbitrate_length( - &frame, mode, sizeof(frame)); - stat[i].recv_bits_total += can_frame_length(&frame, - mode, nbytes); - } + if (nbytes < 0) { + perror("read"); + return 1; } + + if (nbytes != (int)sizeof(struct can_frame) && + nbytes != (int)sizeof(struct canfd_frame)) { + fprintf(stderr, "read: incomplete CAN frame\n"); + return 1; + } + + /* find received ifindex in stat[] array */ + for (i = 0; i < currmax; i++) { + if (stat[i].ifindex == addr.can_ifindex) + break; + } + + /* not found? check for unknown interface */ + if (i >= currmax) { + /* drop unwanted traffic */ + if (have_anydev == 0) + continue; + + /* can we add another interface? */ + if (currmax >= MAXDEVS) + continue; + + /* add an new entry */ + stat[i].ifindex = addr.can_ifindex; + stat[i].bitrate = anydev_bitrate; + stat[i].dbitrate = anydev_dbitrate; + if_indextoname(addr.can_ifindex, stat[i].devname); + nbytes = strlen(stat[i].devname); + if (nbytes > max_devname_len) + max_devname_len = nbytes; /* for nice printing */ + currmax++; + } + + stat[i].recv_frames++; + stat[i].recv_bits_payload += frame.len * 8; + stat[i].recv_bits_dbitrate += can_frame_dbitrate_length( + &frame, mode, sizeof(frame)); + stat[i].recv_bits_total += can_frame_length(&frame, + mode, nbytes); } - for (i = 0; i < currmax; i++) - close(s[i]); + close(s); if (signal_num) return 128 + signal_num; From 724e6f7c113328374e63c528622c23abdfdc25d0 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 7 Aug 2024 21:29:49 +0200 Subject: [PATCH 3/5] canbusload: use recvmsg() instead of recvfrom() To access msg_flags for RX/TX direction detection. Signed-off-by: Oliver Hartkopp --- canbusload.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/canbusload.c b/canbusload.c index 385c3f8..d7469c4 100644 --- a/canbusload.c +++ b/canbusload.c @@ -251,6 +251,8 @@ int main(int argc, char **argv) char *ptr, *nptr; struct sockaddr_can addr; struct canfd_frame frame; + struct iovec iov; + struct msghdr msg; int nbytes, i; int have_anydev = 0; @@ -405,10 +407,14 @@ int main(int argc, char **argv) if (redraw) printf("%s", CLR_SCREEN); - while (running) { - socklen_t len = sizeof(addr); - int flags = 0; + /* these settings are static and can be held out of the hot path */ + iov.iov_base = &frame; + msg.msg_name = &addr; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + while (running) { FD_ZERO(&rdfs); FD_SET(s, &rdfs); @@ -417,8 +423,13 @@ int main(int argc, char **argv) continue; } - nbytes = recvfrom(s, &frame, sizeof(struct canfd_frame), - flags, (struct sockaddr*)&addr, &len); + /* these settings may be modified by recvmsg() */ + iov.iov_len = sizeof(frame); + msg.msg_namelen = sizeof(addr); + msg.msg_controllen = 0; + msg.msg_flags = 0; + + nbytes = recvmsg(s, &msg, 0); if (nbytes < 0) { perror("read"); From fc2473424ea82b71ec11dcb3c4b090fe85cf7161 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 8 Aug 2024 10:04:55 +0200 Subject: [PATCH 4/5] canbusload: show RX/TX direction in bargraph Signed-off-by: Oliver Hartkopp --- canbusload.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/canbusload.c b/canbusload.c index d7469c4..3420bab 100644 --- a/canbusload.c +++ b/canbusload.c @@ -75,6 +75,7 @@ extern int optind, opterr, optopt; static struct { char devname[IFNAMSIZ + 1]; + char recv_direction; int ifindex; unsigned int bitrate; unsigned int dbitrate; @@ -215,7 +216,7 @@ static void printstats(int signo) for (j = 0; j < NUMBAR; j++) { if (j < percent / PERCENTRES) - printf("X"); + printf("%c", stat[i].recv_direction); else printf("."); } @@ -233,6 +234,7 @@ static void printstats(int signo) stat[i].recv_bits_total = 0; stat[i].recv_bits_dbitrate = 0; stat[i].recv_bits_payload = 0; + stat[i].recv_direction = '.'; } if (!redraw) @@ -366,6 +368,8 @@ int main(int argc, char **argv) if (nbytes > max_bitrate_len) max_bitrate_len = nbytes; /* for nice printing */ + stat[i].recv_direction = '.'; + /* handling for 'any' device */ if (have_anydev == 0 && strcmp(ANYDEV, stat[i].devname) == 0) { anydev_bitrate = stat[i].bitrate; @@ -462,6 +466,7 @@ int main(int argc, char **argv) stat[i].ifindex = addr.can_ifindex; stat[i].bitrate = anydev_bitrate; stat[i].dbitrate = anydev_dbitrate; + stat[i].recv_direction = '.'; if_indextoname(addr.can_ifindex, stat[i].devname); nbytes = strlen(stat[i].devname); if (nbytes > max_devname_len) @@ -469,6 +474,20 @@ int main(int argc, char **argv) currmax++; } + if (msg.msg_flags & MSG_DONTROUTE) { + /* TX direction */ + if (stat[i].recv_direction == '.') + stat[i].recv_direction = 'T'; + else if (stat[i].recv_direction == 'R') + stat[i].recv_direction = 'X'; + } else { + /* RX direction */ + if (stat[i].recv_direction == '.') + stat[i].recv_direction = 'R'; + else if (stat[i].recv_direction == 'T') + stat[i].recv_direction = 'X'; + } + stat[i].recv_frames++; stat[i].recv_bits_payload += frame.len * 8; stat[i].recv_bits_dbitrate += can_frame_dbitrate_length( From dced0a6ec7ff66021cae47eaf8d2c817f12640dd Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 8 Aug 2024 16:12:47 +0200 Subject: [PATCH 5/5] canbusload: fix and improve the bitrate output With the introduction of CAN FD the bitrate has not been printed correcty. Fix the CAN FD bitrate output and try to shrink the bitrates by using kilo or Mega suffixes: 500000 -> 500k Fixes: 6382765bf6ae ("canbusload: count databitrate seperately") Signed-off-by: Oliver Hartkopp --- canbusload.c | 59 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/canbusload.c b/canbusload.c index 3420bab..753d658 100644 --- a/canbusload.c +++ b/canbusload.c @@ -70,11 +70,13 @@ #define PERCENTRES 5 /* resolution in percent for bargraph */ #define NUMBAR (100 / PERCENTRES) /* number of bargraph elements */ +#define BRSTRLEN 20 extern int optind, opterr, optopt; static struct { char devname[IFNAMSIZ + 1]; + char bitratestr[BRSTRLEN]; /* 100000/2000000 => 100k/2M */ char recv_direction; int ifindex; unsigned int bitrate; @@ -88,7 +90,7 @@ static struct { static volatile int running = 1; static volatile sig_atomic_t signal_num; static int max_devname_len; /* to prevent frazzled device name output */ -static int max_bitrate_len; +static int max_bitratestr_len; static int currmax; static unsigned char redraw; static unsigned char timestamp; @@ -117,14 +119,13 @@ static void print_usage(char *prg) fprintf(stderr, "calculate the bus load percentage based on the received CAN frames.\n"); fprintf(stderr, "Due to the bitstuffing estimation the calculated busload may exceed 100%%.\n"); fprintf(stderr, "For each given interface the data is presented in one line which contains:\n\n"); - fprintf(stderr, "(interface) (received CAN frames) (used bits total) (used bits for payload)\n"); + fprintf(stderr, "(interface) (received CAN frames) (bits total) (bits payload) (bits payload brs)\n"); fprintf(stderr, "\nExamples:\n"); - fprintf(stderr, "\nuser$> canbusload can0@100000 can1@500000 can2@500000 can3@500000 -r -t -b -c\n\n"); - fprintf(stderr, "%s 2014-02-01 21:13:16 (worst case bitstuffing)\n", prg); - fprintf(stderr, " can0@100000 805 74491 36656 74%% |XXXXXXXXXXXXXX......|\n"); - fprintf(stderr, " can1@500000 796 75140 37728 15%% |XXX.................|\n"); - fprintf(stderr, " can2@500000 0 0 0 0%% |....................|\n"); - fprintf(stderr, " can3@500000 47 4633 2424 0%% |....................|\n"); + fprintf(stderr, "\nuser$> canbusload can0@100000 can1@500000,2000000 can2@500000 -r -t -b -c\n\n"); + fprintf(stderr, "%s 2024-08-08 16:30:05 (worst case bitstuffing)\n", prg); + fprintf(stderr, " can0@100k 192 21980 9136 0 21%% |TTTT................|\n"); + fprintf(stderr, " can1@500k/2M 2651 475500 234448 131825 74%% |XXXXXXXXXXXXXX......|\n"); + fprintf(stderr, " can2@500k 855 136777 62968 35219 27%% |RRRRR...............|\n"); fprintf(stderr, "\n"); } @@ -134,6 +135,32 @@ static void sigterm(int signo) signal_num = signo; } +static int add_bitrate(char *brstr, unsigned int bitrate) +{ + if (bitrate % 1000000 == 0) + return sprintf(brstr, "%dM", bitrate / 1000000); + + if (bitrate % 1000 == 0) + return sprintf(brstr, "%dk", bitrate / 1000); + + return sprintf(brstr, "%d", bitrate); +} + +static void create_bitrate_string(int stat_idx, int *max_bitratestr_len) +{ + int ptr; + + ptr = add_bitrate(&stat[stat_idx].bitratestr[0], stat[stat_idx].bitrate); + + if (stat[stat_idx].bitrate != stat[stat_idx].dbitrate) { + ptr += sprintf(&stat[stat_idx].bitratestr[ptr], "/"); + ptr += add_bitrate(&stat[stat_idx].bitratestr[ptr], stat[stat_idx].dbitrate); + } + + if (ptr > *max_bitratestr_len) + *max_bitratestr_len = ptr; +} + static void printstats(int signo) { int i, j, percent; @@ -198,9 +225,9 @@ static void printstats(int signo) else percent = 0; - printf(" %*s@%-*d %5d %7d %6d %6d %3d%%", + printf(" %*s@%-*s %5d %7d %7d %7d %3d%%", max_devname_len, stat[i].devname, - max_bitrate_len, stat[i].bitrate, + max_bitratestr_len, stat[i].bitratestr, stat[i].recv_frames, stat[i].recv_bits_total, stat[i].recv_bits_payload, @@ -260,6 +287,7 @@ int main(int argc, char **argv) int have_anydev = 0; unsigned int anydev_bitrate = 0; unsigned int anydev_dbitrate = 0; + char anydev_bitratestr[BRSTRLEN]; /* 100000/2000000 => 100k/2M */ signal(SIGTERM, sigterm); signal(SIGHUP, sigterm); @@ -358,15 +386,14 @@ int main(int argc, char **argv) else stat[i].dbitrate = stat[i].bitrate; - if (!stat[i].bitrate || stat[i].bitrate > 1000000) { + if (!stat[i].bitrate || stat[i].bitrate > 1000000 || + !stat[i].dbitrate || stat[i].dbitrate > 8000000) { printf("invalid bitrate for CAN device '%s'!\n", ptr); return 1; } - /* length of entire (data)bitrate description */ - nbytes = strlen(nptr + 1); - if (nbytes > max_bitrate_len) - max_bitrate_len = nbytes; /* for nice printing */ + /* prepare bitrate string for hot path */ + create_bitrate_string(i, &max_bitratestr_len); stat[i].recv_direction = '.'; @@ -374,6 +401,7 @@ int main(int argc, char **argv) if (have_anydev == 0 && strcmp(ANYDEV, stat[i].devname) == 0) { anydev_bitrate = stat[i].bitrate; anydev_dbitrate = stat[i].dbitrate; + memcpy(anydev_bitratestr, stat[i].bitratestr, BRSTRLEN); /* no real interface: remove this command line entry */ have_anydev = 1; currmax--; @@ -466,6 +494,7 @@ int main(int argc, char **argv) stat[i].ifindex = addr.can_ifindex; stat[i].bitrate = anydev_bitrate; stat[i].dbitrate = anydev_dbitrate; + memcpy(stat[i].bitratestr, anydev_bitratestr, BRSTRLEN); stat[i].recv_direction = '.'; if_indextoname(addr.can_ifindex, stat[i].devname); nbytes = strlen(stat[i].devname);