void prframe(FILE *file, struct timeval *tv, int dev, struct can_frame *cf) { fprintf(file, "(%ld.%06ld) ", tv->tv_sec, tv->tv_usec); if (dev > 0) fprintf(file, "can%d ", dev-1); else fprintf(file, "canX "); fprint_canframe(file, cf, "\n", 0); }
int main(int argc, char **argv) { fd_set rdfs; int s[MAXSOCK]; int bridge = 0; unsigned char timestamp = 0; unsigned char dropmonitor = 0; unsigned char silent = SILENT_INI; unsigned char silentani = 0; unsigned char color = 0; unsigned char view = 0; unsigned char log = 0; unsigned char logfrmt = 0; int count = 0; int rcvbuf_size = 0; int opt, ret; int currmax, numfilter; char *ptr, *nptr; struct sockaddr_can addr; char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; struct can_filter *rfilter; can_err_mask_t err_mask; struct can_frame frame; int nbytes, i; struct ifreq ifr; struct timeval tv, last_tv; FILE *logfile = NULL; signal(SIGTERM, sigterm); signal(SIGHUP, sigterm); signal(SIGINT, sigterm); last_tv.tv_sec = 0; last_tv.tv_usec = 0; while ((opt = getopt(argc, argv, "t:ciaSs:b:B:ldLn:r:h?")) != -1) { switch (opt) { case 't': timestamp = optarg[0]; if ((timestamp != 'a') && (timestamp != 'A') && (timestamp != 'd') && (timestamp != 'z')) { fprintf(stderr, "%s: unknown timestamp mode '%c' - ignored\n", basename(argv[0]), optarg[0]); timestamp = 0; } break; case 'c': color++; break; case 'i': view |= CANLIB_VIEW_BINARY; break; case 'a': view |= CANLIB_VIEW_ASCII; break; case 'S': view |= CANLIB_VIEW_SWAP; break; case 's': silent = atoi(optarg); if (silent > SILENT_ON) { print_usage(basename(argv[0])); exit(1); } break; case 'b': case 'B': if (strlen(optarg) >= IFNAMSIZ) { fprintf(stderr, "Name of CAN device '%s' is too long!\n\n", optarg); return 1; } else { bridge = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (bridge < 0) { perror("bridge socket"); return 1; } addr.can_family = AF_CAN; strcpy(ifr.ifr_name, optarg); if (ioctl(bridge, SIOCGIFINDEX, &ifr) < 0) perror("SIOCGIFINDEX"); addr.can_ifindex = ifr.ifr_ifindex; if (!addr.can_ifindex) { perror("invalid bridge interface"); return 1; } /* disable default receive filter on this write-only RAW socket */ setsockopt(bridge, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); if (opt == 'B') { const int loopback = 0; setsockopt(bridge, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)); } if (bind(bridge, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bridge bind"); return 1; } } break; case 'l': log = 1; break; case 'd': dropmonitor = 1; break; case 'L': logfrmt = 1; break; case 'n': count = atoi(optarg); if (count < 1) { print_usage(basename(argv[0])); exit(1); } break; case 'r': rcvbuf_size = atoi(optarg); if (rcvbuf_size < 1) { print_usage(basename(argv[0])); exit(1); } break; default: print_usage(basename(argv[0])); exit(1); break; } } if (optind == argc) { print_usage(basename(argv[0])); exit(0); } if (logfrmt && view) { fprintf(stderr, "Log file format selected: Please disable ASCII/BINARY/SWAP options!\n"); exit(0); } if (silent == SILENT_INI) { if (log) { fprintf(stderr, "Disabled standard output while logging.\n"); silent = SILENT_ON; /* disable output on stdout */ } else silent = SILENT_OFF; /* default output */ } currmax = argc - optind; /* find real number of CAN devices */ if (currmax > MAXSOCK) { fprintf(stderr, "More than %d CAN devices given on commandline!\n", MAXSOCK); return 1; } for (i=0; i < currmax; i++) { ptr = argv[optind+i]; nptr = strchr(ptr, ','); #ifdef DEBUG printf("open %d '%s'.\n", i, ptr); #endif s[i] = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (s[i] < 0) { perror("socket"); return 1; } cmdlinename[i] = ptr; /* save pointer to cmdline name of this socket */ if (nptr) nbytes = nptr - ptr; /* interface name is up the first ',' */ else nbytes = strlen(ptr); /* no ',' found => no filter definitions */ if (nbytes >= IFNAMSIZ) { fprintf(stderr, "name of CAN device '%s' is too long!\n", ptr); return 1; } if (nbytes > max_devname_len) max_devname_len = nbytes; /* for nice printing */ addr.can_family = AF_CAN; memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name)); strncpy(ifr.ifr_name, ptr, nbytes); #ifdef DEBUG printf("using interface name '%s'.\n", ifr.ifr_name); #endif if (strcmp(ANYDEV, ifr.ifr_name)) { if (ioctl(s[i], SIOCGIFINDEX, &ifr) < 0) { perror("SIOCGIFINDEX"); exit(1); } addr.can_ifindex = ifr.ifr_ifindex; } else addr.can_ifindex = 0; /* any can interface */ if (nptr) { /* found a ',' after the interface name => check for filters */ /* determine number of filters to alloc the filter space */ numfilter = 0; ptr = nptr; while (ptr) { numfilter++; ptr++; /* hop behind the ',' */ ptr = strchr(ptr, ','); /* exit condition */ } rfilter = malloc(sizeof(struct can_filter) * numfilter); if (!rfilter) { fprintf(stderr, "Failed to create filter space!\n"); return 1; } numfilter = 0; err_mask = 0; while (nptr) { ptr = nptr+1; /* hop behind the ',' */ nptr = strchr(ptr, ','); /* update exit condition */ if (sscanf(ptr, "%x:%x", &rfilter[numfilter].can_id, &rfilter[numfilter].can_mask) == 2) { rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG; numfilter++; } else if (sscanf(ptr, "%x~%x", &rfilter[numfilter].can_id, &rfilter[numfilter].can_mask) == 2) { rfilter[numfilter].can_id |= CAN_INV_FILTER; rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG; numfilter++; } else if (sscanf(ptr, "#%x", &err_mask) != 1) { fprintf(stderr, "Error in filter option parsing: '%s'\n", ptr); return 1; } } if (err_mask) setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask)); if (numfilter) setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FILTER, rfilter, numfilter * sizeof(struct can_filter)); free(rfilter); } /* if (nptr) */ if (rcvbuf_size) { int curr_rcvbuf_size; socklen_t curr_rcvbuf_size_len = sizeof(curr_rcvbuf_size); /* try SO_RCVBUFFORCE first, if we run with CAP_NET_ADMIN */ if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf_size, sizeof(rcvbuf_size)) < 0) { #ifdef DEBUG printf("SO_RCVBUFFORCE failed so try SO_RCVBUF ...\n"); #endif if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size)) < 0) { perror("setsockopt SO_RCVBUF"); return 1; } if (getsockopt(s[i], SOL_SOCKET, SO_RCVBUF, &curr_rcvbuf_size, &curr_rcvbuf_size_len) < 0) { perror("getsockopt SO_RCVBUF"); return 1; } /* Only print a warning the first time we detect the adjustment */ /* n.b.: The wanted size is doubled in Linux in net/sore/sock.c */ if (!i && curr_rcvbuf_size < rcvbuf_size*2) fprintf(stderr, "The socket receive buffer size was " "adjusted due to /proc/sys/net/core/rmem_max.\n"); } } if (timestamp || log || logfrmt) { const int timestamp_on = 1; if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMP, ×tamp_on, sizeof(timestamp_on)) < 0) { perror("setsockopt SO_TIMESTAMP"); return 1; } } if (dropmonitor) { const int dropmonitor_on = 1; if (setsockopt(s[i], SOL_SOCKET, SO_RXQ_OVFL, &dropmonitor_on, sizeof(dropmonitor_on)) < 0) { perror("setsockopt SO_RXQ_OVFL not supported by your Linux Kernel"); return 1; } } if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; } } if (log) { time_t currtime; struct tm now; char fname[sizeof("candump-2006-11-20_202026.log")+1]; if (time(&currtime) == (time_t)-1) { perror("time"); return 1; } localtime_r(&currtime, &now); sprintf(fname, "candump-%04d-%02d-%02d_%02d%02d%02d.log", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec); if (silent != SILENT_ON) printf("\nWarning: console output active while logging!"); fprintf(stderr, "\nEnabling Logfile '%s'\n\n", fname); logfile = fopen(fname, "w"); if (!logfile) { perror("logfile"); return 1; } } /* 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 = &ctrlmsg; while (running) { FD_ZERO(&rdfs); for (i=0; i<currmax; i++) FD_SET(s[i], &rdfs); if ((ret = select(s[currmax-1]+1, &rdfs, NULL, NULL, NULL)) < 0) { //perror("select"); running = 0; continue; } for (i=0; i<currmax; i++) { /* check all CAN RAW sockets */ if (FD_ISSET(s[i], &rdfs)) { int idx; /* these settings may be modified by recvmsg() */ iov.iov_len = sizeof(frame); msg.msg_namelen = sizeof(addr); msg.msg_controllen = sizeof(ctrlmsg); msg.msg_flags = 0; nbytes = recvmsg(s[i], &msg, 0); if (nbytes < 0) { perror("read"); return 1; } if (nbytes < sizeof(struct can_frame)) { fprintf(stderr, "read: incomplete CAN frame\n"); return 1; } if (count && (--count == 0)) running = 0; if (bridge) { nbytes = write(bridge, &frame, sizeof(struct can_frame)); if (nbytes < 0) { perror("bridge write"); return 1; } else if (nbytes < sizeof(struct can_frame)) { fprintf(stderr,"bridge write: incomplete CAN frame\n"); return 1; } } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg && (cmsg->cmsg_level == SOL_SOCKET); cmsg = CMSG_NXTHDR(&msg,cmsg)) { if (cmsg->cmsg_type == SO_TIMESTAMP) tv = *(struct timeval *)CMSG_DATA(cmsg); else if (cmsg->cmsg_type == SO_RXQ_OVFL) dropcnt[i] = *(__u32 *)CMSG_DATA(cmsg); } /* check for (unlikely) dropped frames on this specific socket */ if (dropcnt[i] != last_dropcnt[i]) { __u32 frames; if (dropcnt[i] > last_dropcnt[i]) frames = dropcnt[i] - last_dropcnt[i]; else frames = 4294967295U - last_dropcnt[i] + dropcnt[i]; /* 4294967295U == UINT32_MAX */ if (silent != SILENT_ON) printf("DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n", frames, (frames > 1)?"s":"", cmdlinename[i], dropcnt[i]); if (log) fprintf(logfile, "DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n", frames, (frames > 1)?"s":"", cmdlinename[i], dropcnt[i]); last_dropcnt[i] = dropcnt[i]; } idx = idx2dindex(addr.can_ifindex, s[i]); if (log) { /* log CAN frame with absolute timestamp & device */ fprintf(logfile, "(%ld.%06ld) ", tv.tv_sec, tv.tv_usec); fprintf(logfile, "%*s ", max_devname_len, devname[idx]); /* without seperator as logfile use-case is parsing */ fprint_canframe(logfile, &frame, "\n", 0); } if (logfrmt) { /* print CAN frame in log file style to stdout */ printf("(%ld.%06ld) ", tv.tv_sec, tv.tv_usec); printf("%*s ", max_devname_len, devname[idx]); fprint_canframe(stdout, &frame, "\n", 0); goto out_fflush; /* no other output to stdout */ } if (silent != SILENT_OFF){ if (silent == SILENT_ANI) { printf("%c\b", anichar[silentani%=MAXANI]); silentani++; } goto out_fflush; /* no other output to stdout */ } printf(" %s", (color>2)?col_on[idx%MAXCOL]:""); 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; } printf(" %s", (color && (color<3))?col_on[idx%MAXCOL]:""); printf("%*s", max_devname_len, devname[idx]); printf("%s ", (color==1)?col_off:""); fprint_long_canframe(stdout, &frame, NULL, view); printf("%s", (color>1)?col_off:""); printf("\n"); } out_fflush: fflush(stdout); } } for (i=0; i<currmax; i++) close(s[i]); if (bridge) close(bridge); if (log) fclose(logfile); return 0; }
int main(int argc, char **argv) { unsigned long gap = DEFAULT_GAP; unsigned char extended = 0; unsigned char fix_id = 0; unsigned char fix_data = 0; unsigned char fix_dlc = 0; unsigned char default_frame = 1; unsigned char verbose = 0; int opt; int s; /* socket */ struct sockaddr_can addr; static struct can_frame frame; int nbytes; struct ifreq ifr; struct timespec ts; signal(SIGTERM, sigterm); signal(SIGHUP, sigterm); signal(SIGINT, sigterm); while ((opt = getopt(argc, argv, "g:eIDLf:v")) != -1) { switch (opt) { case 'g': gap = strtoul(optarg, NULL, 10); break; case 'e': extended = 1; break; case 'I': fix_id = 1; break; case 'D': fix_data = 1; break; case 'L': fix_dlc = 1; break; case 'f': default_frame = 0; if (parse_canframe(optarg, &frame)) { fprintf(stderr, "'%s' is a wrong CAN frame format.\n", optarg); exit(1); } break; case 'v': verbose = 1; break; default: print_usage(basename(argv[0])); exit(1); break; } } if (optind == argc) { print_usage(basename(argv[0])); exit(0); } ts.tv_sec = gap / 1000; ts.tv_nsec = (gap % 1000) * 1000000; if (default_frame) { if (extended) frame.can_id = 0x12345678 | CAN_EFF_FLAG; else frame.can_id = 0x123; frame.can_dlc = 8; frame.data[0] = 0x01; frame.data[1] = 0x23; frame.data[2] = 0x45; frame.data[3] = 0x67; frame.data[4] = 0x89; frame.data[5] = 0xAB; frame.data[6] = 0xCD; frame.data[7] = 0xEF; } if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { perror("socket"); return 1; } addr.can_family = AF_CAN; strcpy(ifr.ifr_name, argv[optind]); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) perror("SIOCGIFINDEX"); addr.can_ifindex = ifr.ifr_ifindex; /* disable default receive filter on this RAW socket */ /* This is obsolete as we do not read from the socket at all, but for */ /* this reason we can remove the receive list in the Kernel to save a */ /* little (really a very little!) CPU usage. */ setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; } while (running) { if (!fix_id) { frame.can_id = random(); if (extended) { frame.can_id &= CAN_EFF_MASK; frame.can_id |= CAN_EFF_FLAG; } else frame.can_id &= CAN_SFF_MASK; } if (!fix_dlc) { frame.can_dlc = random() & 0xF; if (frame.can_dlc & 8) frame.can_dlc = 8; /* for about 50% of the frames */ } if (!fix_data) { /* that's what the 64 bit alignment of data[] is for ... :) */ *(unsigned long*)(&frame.data[0]) = random(); *(unsigned long*)(&frame.data[4]) = random(); } if ((nbytes = write(s, &frame, sizeof(struct can_frame))) < 0) { perror("write"); return 1; } else if (nbytes < sizeof(struct can_frame)) { fprintf(stderr, "write: incomplete CAN frame\n"); return 1; } if (gap) /* gap == 0 => performance test :-] */ if (nanosleep(&ts, NULL)) return 1; if (verbose) #if 0 fprint_long_canframe(stdout, &frame, "\n", 1); #else fprint_canframe(stdout, &frame, "\n", 1); #endif } close(s); return 0; }