123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- /*
- * Copyright (c) 2011 EIA Electronics
- *
- * Authors:
- * Kurt Van Dijck <kurt.van.dijck@eia.be>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the version 2 of the GNU General Public License
- * as published by the Free Software Foundation
- */
- #include <signal.h>
- #include <time.h>
- #include <inttypes.h>
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <getopt.h>
- #include <error.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <net/if.h>
- #include <linux/can.h>
- #include <linux/can/j1939.h>
- #include "libj1939.h"
- static const char help_msg[] =
- "jacd: An SAE J1939 address claiming daemon" "\n"
- "Usage: jacd [options] NAME [INTF]" "\n"
- "\n"
- " -v, --verbose Increase verbosity" "\n"
- " -r, --range=RANGE Ranges of source addresses" "\n"
- " e.g. 80,50-100,200-210 (defaults to 0-253)" "\n"
- " -c, --cache=FILE Cache file to save/restore the source address" "\n"
- " -a, --address=ADDRESS Start with Source Address ADDRESS" "\n"
- " -p, --prefix=STR Prefix to use when logging" "\n"
- "\n"
- "NAME is the 64bit nodename" "\n"
- "\n"
- "Example:" "\n"
- "jacd -r 100,80-120 -c /tmp/1122334455667788.jacd 1122334455667788" "\n"
- ;
- #ifdef _GNU_SOURCE
- static struct option long_opts[] = {
- { "help", no_argument, NULL, '?', },
- { "verbose", no_argument, NULL, 'v', },
- { "range", required_argument, NULL, 'r', },
- { "cache", required_argument, NULL, 'c', },
- { "address", required_argument, NULL, 'a', },
- { "prefix", required_argument, NULL, 'p', },
- { },
- };
- #else
- #define getopt_long(argc, argv, optstring, longopts, longindex) \
- getopt((argc), (argv), (optstring))
- #endif
- static const char optstring[] = "vr:c:a:p:?";
- /* byte swap functions */
- static inline int host_is_little_endian(void)
- {
- static const uint16_t endian_test = 1;
- return *(const uint8_t *)&endian_test;
- }
- static __attribute__((unused)) void bswap(void *vptr, int size)
- {
- uint8_t *p0, *pe;
- uint8_t tmp;
- p0 = vptr;
- pe = &p0[size-1];
- for (; p0 < pe; ++p0, --pe) {
- tmp = *p0;
- *p0 = *pe;
- *pe = tmp;
- }
- }
- /* rate-limiting for errors */
- static inline int must_warn(int ret)
- {
- if (ret >= 0)
- return 0;
- switch (errno) {
- case EINTR:
- case ENOBUFS:
- return 0;
- }
- return 1;
- }
- /* global variables */
- static char default_range[] = "0x80-0xfd";
- static const char default_intf[] = "can0";
- static struct {
- int verbose;
- const char *cachefile;
- const char *intf;
- char *ranges;
- uint64_t name;
- uint8_t current_sa;
- uint8_t last_sa;
- int sig_term;
- int sig_alrm;
- int sig_usr1;
- int state;
- #define STATE_INITIAL 0
- #define STATE_REQ_SENT 1
- #define STATE_REQ_PENDING 2 /* wait 1250 msec for first claim */
- #define STATE_OPERATIONAL 3
- } s = {
- .intf = default_intf,
- .ranges = default_range,
- .current_sa = J1939_IDLE_ADDR,
- .last_sa = J1939_NO_ADDR,
- };
- struct {
- uint64_t name;
- int flags;
- #define F_USE 0x01
- #define F_SEEN 0x02
- } addr[J1939_IDLE_ADDR /* =254 */];
- /* lookup by name */
- static int lookup_name(uint64_t name)
- {
- int j;
- for (j = 0; j < J1939_IDLE_ADDR; ++j) {
- if (addr[j].name == name)
- return j;
- }
- return J1939_IDLE_ADDR;
- }
- /* parse address range */
- static int parse_range(char *str)
- {
- char *tok, *endp;
- int a0, ae;
- int j, cnt;
- cnt = 0;
- for (tok = strtok(str, ",;"); tok; tok = strtok(NULL, ",;")) {
- a0 = ae = strtoul(tok, &endp, 0);
- if (endp <= tok)
- error(1, 0, "parsing range '%s'", tok);
- if (*endp == '-') {
- tok = endp+1;
- ae = strtoul(tok, &endp, 0);
- if (endp <= tok)
- error(1, 0, "parsing addr '%s'", tok);
- if (ae < a0)
- ae = a0;
- }
- for (j = a0; j <= ae; ++j, ++cnt) {
- if (j == J1939_IDLE_ADDR)
- break;
- addr[j].flags |= F_USE;
- }
- }
- return cnt;
- }
- /* j1939 socket */
- static const struct j1939_filter filt[] = {
- {
- .pgn = 0x0ee00,
- .pgn_mask = 0x3ff00,
- }, {
- .pgn = 0x0ea00,
- .pgn_mask = 0x3ff00,
- }, {
- .pgn = 0x0fed8,
- .pgn_mask = 0x3ffff,
- },
- };
- static int open_socket(const char *device, uint64_t name)
- {
- int ret, sock;
- int value;
- struct sockaddr_can saddr = {
- .can_family = AF_CAN,
- .can_addr.j1939 = {
- .name = name,
- .addr = J1939_IDLE_ADDR,
- .pgn = 0x0ee00,
- },
- .can_ifindex = if_nametoindex(s.intf),
- };
- if (s.verbose)
- fprintf(stderr, "- socket(PF_CAN, SOCK_DGRAM, CAN_J1939);\n");
- sock = ret = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
- if (ret < 0)
- error(1, errno, "socket(j1939)");
- if (s.verbose)
- fprintf(stderr, "- setsockopt(, SOL_SOCKET, SO_BINDTODEVICE, %s, %zd);\n", device, strlen(device));
- ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
- device, strlen(device));
- if (ret < 0)
- error(1, errno, "bindtodevice %s", device);
- if (s.verbose)
- fprintf(stderr, "- setsockopt(, SOL_CAN_J1939, SO_J1939_FILTER, <filter>, %zd);\n", sizeof(filt));
- ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_FILTER,
- &filt, sizeof(filt));
- if (ret < 0)
- error(1, errno, "setsockopt filter");
- value = 1;
- if (s.verbose)
- fprintf(stderr, "- setsockopt(, SOL_CAN_J1939, SO_J1939_RECV_OWN, %d, %zd);\n", value, sizeof(value));
- ret = setsockopt(sock, SOL_CAN_J1939, SO_J1939_RECV_OWN,
- &value, sizeof(value));
- if (ret < 0)
- error(1, errno, "setsockopt receive own msgs");
- if (s.verbose)
- fprintf(stderr, "- bind(, %s, %zi);\n", libj1939_addr2str(&saddr), sizeof(saddr));
- ret = bind(sock, (void *)&saddr, sizeof(saddr));
- if (ret < 0)
- error(1, errno, "bind()");
- return sock;
- }
- /* real IO function */
- static int repeat_address(int sock, uint64_t name)
- {
- int ret;
- uint8_t dat[8];
- memcpy(dat, &name, 8);
- if (!host_is_little_endian())
- bswap(dat, 8);
- if (s.verbose)
- fprintf(stderr, "- send(, %" PRId64 ", 8, 0);\n", name);
- ret = send(sock, dat, 8, 0);
- if (must_warn(ret))
- error(1, errno, "send address claim for 0x%02x", s.last_sa);
- return ret;
- }
- static int claim_address(int sock, uint64_t name, int sa)
- {
- int ret;
- struct sockaddr_can saddr = {
- .can_family = AF_CAN,
- .can_addr.j1939 = {
- .name = name,
- .addr = sa,
- .pgn = 0x0ee00,
- },
- .can_ifindex = if_nametoindex(s.intf),
- };
- if (s.verbose)
- fprintf(stderr, "- bind(, %s, %zi);\n", libj1939_addr2str(&saddr), sizeof(saddr));
- ret = bind(sock, (void *)&saddr, sizeof(saddr));
- if (ret < 0)
- error(1, errno, "rebind with sa 0x%02x", sa);
- s.last_sa = sa;
- return repeat_address(sock, name);
- }
- static int request_addresses(int sock)
- {
- static const uint8_t dat[3] = { 0, 0xee, 0, };
- int ret;
- static const struct sockaddr_can saddr = {
- .can_family = AF_CAN,
- .can_addr.j1939.pgn = 0x0ea00,
- .can_addr.j1939.addr = J1939_NO_ADDR,
- };
- if (s.verbose)
- fprintf(stderr, "- sendto(, { 0, 0xee, 0, }, %zi, 0, %s, %zi);\n", sizeof(dat), libj1939_addr2str(&saddr), sizeof(saddr));
- ret = sendto(sock, dat, sizeof(dat), 0, (void *)&saddr, sizeof(saddr));
- if (must_warn(ret))
- error(1, errno, "send request for address claims");
- return ret;
- }
- /* real policy */
- static int choose_new_sa(uint64_t name, int sa)
- {
- int j, cnt;
- /* test current entry */
- if ((sa < J1939_IDLE_ADDR) && (addr[sa].flags & F_USE)) {
- j = sa;
- if (!addr[j].name || (addr[j].name == name) || (addr[j].name > name))
- return j;
- }
- /* take first empty spot */
- for (j = 0; j < J1939_IDLE_ADDR; ++j) {
- if (!(addr[j].flags & F_USE))
- continue;
- if (!addr[j].name || (addr[j].name == name))
- return j;
- }
- /*
- * no empty spot found
- * take next (relative to @sa) spot that we can
- * successfully contest
- */
- j = sa + 1;
- for (cnt = 0; cnt < J1939_IDLE_ADDR; ++j, ++cnt) {
- if (j >= J1939_IDLE_ADDR)
- j = 0;
- if (!(addr[j].flags & F_USE))
- continue;
- if (name < addr[j].name)
- return j;
- }
- return J1939_IDLE_ADDR;
- }
- /* signa handling */
- static void sighandler(int sig, siginfo_t *info, void *vp)
- {
- switch (sig) {
- case SIGINT:
- case SIGTERM:
- s.sig_term = 1;
- break;
- case SIGALRM:
- s.sig_alrm = 1;
- break;
- case SIGUSR1:
- s.sig_usr1 = 1;
- break;
- }
- }
- static void install_signal(int sig)
- {
- int ret;
- struct sigaction sigact = {
- .sa_sigaction = sighandler,
- .sa_flags = SA_SIGINFO,
- };
- sigfillset(&sigact.sa_mask);
- ret = sigaction(sig, &sigact, NULL);
- if (ret < 0)
- error(1, errno, "sigaction for signal %i", sig);
- }
- static void schedule_itimer(int msec)
- {
- int ret;
- struct itimerval val = {};
- val.it_value.tv_sec = msec / 1000;
- val.it_value.tv_usec = (msec % 1000) * 1000;
- s.sig_alrm = 0;
- do {
- ret = setitimer(ITIMER_REAL, &val, NULL);
- } while ((ret < 0) && (errno == EINTR));
- if (ret < 0)
- error(1, errno, "setitimer %i msec", msec);
- }
- /* dump status */
- static inline int addr_status_mine(int sa)
- {
- if (sa == s.current_sa)
- return '*';
- else if (addr[sa].flags & F_USE)
- return '+';
- else
- return '-';
- }
- static void dump_status(void)
- {
- int j;
- for (j = 0; j < J1939_IDLE_ADDR; ++j) {
- if (!addr[j].flags && !addr[j].name)
- continue;
- fprintf(stdout, "%02x: %c", j, addr_status_mine(j));
- if (addr[j].name)
- fprintf(stdout, " %016llx", (long long)addr[j].name);
- else
- fprintf(stdout, " -");
- fprintf(stdout, "\n");
- }
- fflush(stdout);
- }
- /* cache file */
- static void save_cache(void)
- {
- FILE *fp;
- time_t t;
- if (!s.cachefile)
- return;
- fp = fopen(s.cachefile, "w");
- if (!fp)
- error(1, errno, "fopen %s, w", s.cachefile);
- time(&t);
- fprintf(fp, "# saved on %s\n", ctime(&t));
- fprintf(fp, "\n");
- fprintf(fp, "0x%02x\n", s.current_sa);
- fclose(fp);
- }
- static void restore_cache(void)
- {
- FILE *fp;
- int ret;
- char *endp;
- char *line = 0;
- size_t sz = 0;
- if (!s.cachefile)
- return;
- fp = fopen(s.cachefile, "r");
- if (!fp) {
- if (ENOENT == errno)
- return;
- error(1, errno, "fopen %s, r", s.cachefile);
- }
- while (!feof(fp)) {
- ret = getline(&line, &sz, fp);
- if (ret <= 0)
- continue;
- if (line[0] == '#')
- continue;
- ret = strtoul(line, &endp, 0);
- if ((endp > line) && (ret >= 0) && (ret <= J1939_IDLE_ADDR)) {
- s.current_sa = ret;
- break;
- }
- }
- fclose(fp);
- if (line)
- free(line);
- }
- /* main */
- int main(int argc, char *argv[])
- {
- int ret, sock, pgn, sa, opt;
- socklen_t slen;
- uint8_t dat[9];
- struct sockaddr_can saddr;
- uint64_t cmd_name;
- #ifdef _GNU_SOURCE
- program_invocation_name = program_invocation_short_name;
- #endif
- /* argument parsing */
- while ((opt = getopt_long(argc, argv, optstring, long_opts, NULL)) != -1)
- switch (opt) {
- case 'v':
- ++s.verbose;
- break;
- case 'c':
- s.cachefile = optarg;
- break;
- case 'r':
- s.ranges = optarg;
- break;
- case 'a':
- s.current_sa = strtoul(optarg, 0, 0);
- break;
- case 'p':
- #ifdef _GNU_SOURCE
- asprintf(&program_invocation_name, "%s.%s", program_invocation_short_name, optarg);
- #else
- error(0, 0, "compile with -D_GNU_SOURCE to use -p");
- #endif
- break;
- default:
- fputs(help_msg, stderr);
- exit(1);
- break;
- }
- if (argv[optind])
- s.name = strtoull(argv[optind++], 0, 16);
- if (argv[optind])
- s.intf = argv[optind++];
- /* args done */
- restore_cache();
- ret = parse_range(s.ranges);
- if (!ret)
- error(1, 0, "no addresses in range");
- if ((s.current_sa < J1939_IDLE_ADDR) && !(addr[s.current_sa].flags & F_USE)) {
- if (s.verbose)
- error(0, 0, "forget saved address 0x%02x", s.current_sa);
- s.current_sa = J1939_IDLE_ADDR;
- }
- if (s.verbose)
- error(0, 0, "ready for %s:%016llx", s.intf, (long long)s.name);
- if (!s.intf || !s.name)
- error(1, 0, "bad arguments");
- ret = sock = open_socket(s.intf, s.name);
- install_signal(SIGTERM);
- install_signal(SIGINT);
- install_signal(SIGALRM);
- install_signal(SIGUSR1);
- install_signal(SIGUSR2);
- while (!s.sig_term) {
- if (s.sig_usr1) {
- s.sig_usr1 = 0;
- dump_status();
- }
- switch (s.state) {
- case STATE_INITIAL:
- ret = request_addresses(sock);
- if (ret < 0)
- error(1, errno, "could not sent initial request");
- s.state = STATE_REQ_SENT;
- break;
- case STATE_REQ_PENDING:
- if (!s.sig_alrm)
- break;
- s.sig_alrm = 0;
- /* claim addr */
- sa = choose_new_sa(s.name, s.current_sa);
- if (sa == J1939_IDLE_ADDR)
- error(1, 0, "no free address to use");
- ret = claim_address(sock, s.name, sa);
- if (ret < 0)
- schedule_itimer(50);
- s.state = STATE_OPERATIONAL;
- break;
- case STATE_OPERATIONAL:
- if (s.sig_alrm) {
- s.sig_alrm = 0;
- ret = repeat_address(sock, s.name);
- if (ret < 0)
- schedule_itimer(50);
- }
- break;
- }
- slen = sizeof(saddr);
- ret = recvfrom(sock, dat, sizeof(dat), 0, (void *)&saddr, &slen);
- if (ret < 0) {
- if (EINTR == errno)
- continue;
- error(1, errno, "recvfrom()");
- }
- switch (saddr.can_addr.j1939.pgn) {
- case 0x0ea00:
- if (ret < 3)
- break;
- pgn = dat[0] + (dat[1] << 8) + ((dat[2] & 0x03) << 16);
- if (pgn != 0x0ee00)
- /* not interested */
- break;
- if (s.state == STATE_REQ_SENT) {
- if (s.verbose)
- error(0, 0, "request sent, pending for 1250 ms");
- schedule_itimer(1250);
- s.state = STATE_REQ_PENDING;
- } else if (s.state == STATE_OPERATIONAL) {
- ret = claim_address(sock, s.name, s.current_sa);
- if (ret < 0)
- schedule_itimer(50);
- }
- break;
- case 0x0ee00:
- if (saddr.can_addr.j1939.addr >= J1939_IDLE_ADDR) {
- sa = lookup_name(saddr.can_addr.j1939.name);
- if (sa < J1939_IDLE_ADDR)
- addr[sa].name = 0;
- break;
- }
- sa = lookup_name(saddr.can_addr.j1939.name);
- if ((sa != saddr.can_addr.j1939.addr) && (sa < J1939_IDLE_ADDR))
- /* update cache */
- addr[sa].name = 0;
- /* shortcut */
- sa = saddr.can_addr.j1939.addr;
- addr[sa].name = saddr.can_addr.j1939.name;
- addr[sa].flags |= F_SEEN;
- if (s.name == saddr.can_addr.j1939.name) {
- /* ourselve, disable itimer */
- s.current_sa = sa;
- if (s.verbose)
- error(0, 0, "claimed 0x%02x", sa);
- } else if (sa == s.current_sa) {
- if (s.verbose)
- error(0, 0, "address collision for 0x%02x", sa);
- if (s.name > saddr.can_addr.j1939.name) {
- sa = choose_new_sa(s.name, sa);
- if (sa == J1939_IDLE_ADDR) {
- error(0, 0, "no address left");
- /* put J1939_IDLE_ADDR in cache file */
- s.current_sa = sa;
- goto done;
- }
- }
- ret = claim_address(sock, s.name, sa);
- if (ret < 0)
- schedule_itimer(50);
- }
- break;
- case 0x0fed8:
- if (!host_is_little_endian())
- bswap(dat, 8);
- memcpy(&cmd_name, dat, 8);
- if (cmd_name == s.name) {
- ret = claim_address(sock, s.name, dat[8]);
- if (ret < 0)
- schedule_itimer(50);
- }
- break;
- }
- }
- done:
- if (s.verbose)
- error(0, 0, "shutdown");
- claim_address(sock, s.name, J1939_IDLE_ADDR);
- save_cache();
- return 0;
- }
|