0001-examples-add-rtnl-link-can.patch 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. From 80442c4e32cde0b27b471c5c9688ee8488f14bec Mon Sep 17 00:00:00 2001
  2. From: Dario Binacchi <dario.binacchi@amarulasolutions.com>
  3. Date: Thu, 20 Apr 2023 21:21:15 +0200
  4. Subject: [PATCH] examples: add rtnl-link-can
  5. I developed this application to test the Linux kernel series referenced below.
  6. I could not use the iproute2 package since the microcontroller is without MMU.
  7. On suggestion of the Linux CAN subsystem maintainer I decided to upstream it.
  8. Cc: Marc Kleine-Budde <mkl@pengutronix.de>
  9. Link: https://marc.info/?l=linux-netdev&m=167999323611710&w=2
  10. Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
  11. Signed-off-by: Florian Westphal <fw@strlen.de>
  12. Upstream: https://git.netfilter.org/libmnl/commit/?id=80442c4e32cde0b27b471c5c9688ee8488f14bec
  13. ---
  14. examples/rtnl/Makefile.am | 4 +
  15. examples/rtnl/rtnl-link-can.c | 452 ++++++++++++++++++++++++++++++++++
  16. 2 files changed, 456 insertions(+)
  17. create mode 100644 examples/rtnl/rtnl-link-can.c
  18. diff --git a/examples/rtnl/Makefile.am b/examples/rtnl/Makefile.am
  19. index dd8a77d914b6..5a28e0965926 100644
  20. --- a/examples/rtnl/Makefile.am
  21. +++ b/examples/rtnl/Makefile.am
  22. @@ -2,6 +2,7 @@ include $(top_srcdir)/Make_global.am
  23. check_PROGRAMS = rtnl-addr-add \
  24. rtnl-addr-dump \
  25. + rtnl-link-can \
  26. rtnl-link-dump rtnl-link-dump2 rtnl-link-dump3 \
  27. rtnl-link-event \
  28. rtnl-link-set \
  29. @@ -13,6 +14,9 @@ check_PROGRAMS = rtnl-addr-add \
  30. rtnl_addr_add_SOURCES = rtnl-addr-add.c
  31. rtnl_addr_add_LDADD = ../../src/libmnl.la
  32. +rtnl_link_can_SOURCES = rtnl-link-can.c
  33. +rtnl_link_can_LDADD = ../../src/libmnl.la
  34. +
  35. rtnl_addr_dump_SOURCES = rtnl-addr-dump.c
  36. rtnl_addr_dump_LDADD = ../../src/libmnl.la
  37. diff --git a/examples/rtnl/rtnl-link-can.c b/examples/rtnl/rtnl-link-can.c
  38. new file mode 100644
  39. index 000000000000..8ed70d141475
  40. --- /dev/null
  41. +++ b/examples/rtnl/rtnl-link-can.c
  42. @@ -0,0 +1,452 @@
  43. +/* This example is placed in the public domain. */
  44. +#include <errno.h>
  45. +#include <limits.h>
  46. +#include <stdio.h>
  47. +#include <stdlib.h>
  48. +#include <string.h>
  49. +#include <unistd.h>
  50. +#include <time.h>
  51. +
  52. +#include <libmnl/libmnl.h>
  53. +#include <linux/can/netlink.h>
  54. +#include <linux/if.h>
  55. +#include <linux/if_link.h>
  56. +#include <linux/rtnetlink.h>
  57. +
  58. +static void incomplete_command(void) __attribute__((noreturn));
  59. +
  60. +#define NEXT_ARG() \
  61. + do { \
  62. + if (argc <= 0) incomplete_command(); \
  63. + argv++; \
  64. + argc--; \
  65. + } while (0)
  66. +
  67. +static void duparg2(const char *key, const char *arg)
  68. +{
  69. + fprintf(stderr,
  70. + "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n",
  71. + key, arg);
  72. + exit(-1);
  73. +}
  74. +
  75. +static void incomplete_command(void)
  76. +{
  77. + fprintf(stderr, "Command line is not complete. Try option \"help\"\n");
  78. + exit(EXIT_FAILURE);
  79. +}
  80. +
  81. +/* Returns false if 'prefix' is a not empty prefix of 'string'.
  82. + */
  83. +static bool matches(const char *prefix, const char *string)
  84. +{
  85. + if (!*prefix)
  86. + return true;
  87. +
  88. + while (*string && *prefix == *string) {
  89. + prefix++;
  90. + string++;
  91. + }
  92. +
  93. + return !!*prefix;
  94. +}
  95. +
  96. +static int get_u16(__u16 *val, const char *arg, int base)
  97. +{
  98. + unsigned long res;
  99. + char *ptr;
  100. +
  101. + if (!arg || !*arg)
  102. + return -1;
  103. +
  104. + res = strtoul(arg, &ptr, base);
  105. +
  106. + /* empty string or trailing non-digits */
  107. + if (!ptr || ptr == arg || *ptr)
  108. + return -1;
  109. +
  110. + /* overflow */
  111. + if (res == ULONG_MAX && errno == ERANGE)
  112. + return -1;
  113. +
  114. + if (res > 0xFFFFUL)
  115. + return -1;
  116. +
  117. + *val = res;
  118. + return 0;
  119. +}
  120. +
  121. +static int get_u32(__u32 *val, const char *arg, int base)
  122. +{
  123. + unsigned long res;
  124. + char *ptr;
  125. +
  126. + if (!arg || !*arg)
  127. + return -1;
  128. +
  129. + res = strtoul(arg, &ptr, base);
  130. +
  131. + /* empty string or trailing non-digits */
  132. + if (!ptr || ptr == arg || *ptr)
  133. + return -1;
  134. +
  135. + /* overflow */
  136. + if (res == ULONG_MAX && errno == ERANGE)
  137. + return -1;
  138. +
  139. + /* in case UL > 32 bits */
  140. + if (res > 0xFFFFFFFFUL)
  141. + return -1;
  142. +
  143. + *val = res;
  144. + return 0;
  145. +}
  146. +
  147. +static int get_float(float *val, const char *arg)
  148. +{
  149. + float res;
  150. + char *ptr;
  151. +
  152. + if (!arg || !*arg)
  153. + return -1;
  154. +
  155. + res = strtof(arg, &ptr);
  156. + if (!ptr || ptr == arg || *ptr)
  157. + return -1;
  158. +
  159. + *val = res;
  160. + return 0;
  161. +}
  162. +
  163. +static void set_ctrlmode(char *name, char *arg,
  164. + struct can_ctrlmode *cm, __u32 flags)
  165. +{
  166. + if (strcmp(arg, "on") == 0) {
  167. + cm->flags |= flags;
  168. + } else if (strcmp(arg, "off") != 0) {
  169. + fprintf(stderr,
  170. + "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n",
  171. + name, arg);
  172. + exit(EXIT_FAILURE);
  173. + }
  174. +
  175. + cm->mask |= flags;
  176. +}
  177. +
  178. +static void invarg(const char *msg, const char *arg)
  179. +{
  180. + fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg);
  181. + exit(-1);
  182. +}
  183. +
  184. +static void print_usage(FILE *f)
  185. +{
  186. + fprintf(f,
  187. + "Usage: ip link set DEVICE type can\n"
  188. + "\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] |\n"
  189. + "\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n \t phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n"
  190. + "\n"
  191. + "\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n"
  192. + "\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n"
  193. + "\n"
  194. + "\t[ loopback { on | off } ]\n"
  195. + "\t[ listen-only { on | off } ]\n"
  196. + "\t[ triple-sampling { on | off } ]\n"
  197. + "\t[ one-shot { on | off } ]\n"
  198. + "\t[ berr-reporting { on | off } ]\n"
  199. + "\t[ fd { on | off } ]\n"
  200. + "\t[ fd-non-iso { on | off } ]\n"
  201. + "\t[ presume-ack { on | off } ]\n"
  202. + "\t[ cc-len8-dlc { on | off } ]\n"
  203. + "\n"
  204. + "\t[ restart-ms TIME-MS ]\n"
  205. + "\t[ restart ]\n"
  206. + "\n"
  207. + "\t[ termination { 0..65535 } ]\n"
  208. + "\n"
  209. + "\tWhere: BITRATE := { 1..1000000 }\n"
  210. + "\t SAMPLE-POINT := { 0.000..0.999 }\n"
  211. + "\t TQ := { NUMBER }\n"
  212. + "\t PROP-SEG := { 1..8 }\n"
  213. + "\t PHASE-SEG1 := { 1..8 }\n"
  214. + "\t PHASE-SEG2 := { 1..8 }\n"
  215. + "\t SJW := { 1..4 }\n"
  216. + "\t RESTART-MS := { 0 | NUMBER }\n"
  217. + );
  218. +}
  219. +
  220. +static void usage(void)
  221. +{
  222. + print_usage(stderr);
  223. +}
  224. +
  225. +static int iplink_set_can_parse(int argc, char **argv, struct nlmsghdr *nlh)
  226. +{
  227. + struct can_bittiming bt = {}, dbt = {};
  228. + struct can_ctrlmode cm = {};
  229. +
  230. + while (argc > 0) {
  231. + if (matches(*argv, "bitrate") == 0) {
  232. + NEXT_ARG();
  233. + if (get_u32(&bt.bitrate, *argv, 0))
  234. + invarg("invalid \"bitrate\" value\n", *argv);
  235. + } else if (matches(*argv, "sample-point") == 0) {
  236. + float sp;
  237. +
  238. + NEXT_ARG();
  239. + if (get_float(&sp, *argv))
  240. + invarg("invalid \"sample-point\" value\n",
  241. + *argv);
  242. +
  243. + bt.sample_point = (__u32)(sp * 1000);
  244. + } else if (matches(*argv, "tq") == 0) {
  245. + NEXT_ARG();
  246. + if (get_u32(&bt.tq, *argv, 0))
  247. + invarg("invalid \"tq\" value\n", *argv);
  248. + } else if (matches(*argv, "prop-seg") == 0) {
  249. + NEXT_ARG();
  250. + if (get_u32(&bt.prop_seg, *argv, 0))
  251. + invarg("invalid \"prop-seg\" value\n", *argv);
  252. + } else if (matches(*argv, "phase-seg1") == 0) {
  253. + NEXT_ARG();
  254. + if (get_u32(&bt.phase_seg1, *argv, 0))
  255. + invarg("invalid \"phase-seg1\" value\n", *argv);
  256. + } else if (matches(*argv, "phase-seg2") == 0) {
  257. + NEXT_ARG();
  258. + if (get_u32(&bt.phase_seg2, *argv, 0))
  259. + invarg("invalid \"phase-seg2\" value\n", *argv);
  260. + } else if (matches(*argv, "sjw") == 0) {
  261. + NEXT_ARG();
  262. + if (get_u32(&bt.sjw, *argv, 0))
  263. + invarg("invalid \"sjw\" value\n", *argv);
  264. + } else if (matches(*argv, "dbitrate") == 0) {
  265. + NEXT_ARG();
  266. + if (get_u32(&dbt.bitrate, *argv, 0))
  267. + invarg("invalid \"dbitrate\" value\n", *argv);
  268. + } else if (matches(*argv, "dsample-point") == 0) {
  269. + float sp;
  270. +
  271. + NEXT_ARG();
  272. + if (get_float(&sp, *argv))
  273. + invarg("invalid \"dsample-point\" value\n", *argv);
  274. + dbt.sample_point = (__u32)(sp * 1000);
  275. + } else if (matches(*argv, "dtq") == 0) {
  276. + NEXT_ARG();
  277. + if (get_u32(&dbt.tq, *argv, 0))
  278. + invarg("invalid \"dtq\" value\n", *argv);
  279. + } else if (matches(*argv, "dprop-seg") == 0) {
  280. + NEXT_ARG();
  281. + if (get_u32(&dbt.prop_seg, *argv, 0))
  282. + invarg("invalid \"dprop-seg\" value\n", *argv);
  283. + } else if (matches(*argv, "dphase-seg1") == 0) {
  284. + NEXT_ARG();
  285. + if (get_u32(&dbt.phase_seg1, *argv, 0))
  286. + invarg("invalid \"dphase-seg1\" value\n", *argv);
  287. + } else if (matches(*argv, "dphase-seg2") == 0) {
  288. + NEXT_ARG();
  289. + if (get_u32(&dbt.phase_seg2, *argv, 0))
  290. + invarg("invalid \"dphase-seg2\" value\n", *argv);
  291. + } else if (matches(*argv, "dsjw") == 0) {
  292. + NEXT_ARG();
  293. + if (get_u32(&dbt.sjw, *argv, 0))
  294. + invarg("invalid \"dsjw\" value\n", *argv);
  295. + } else if (matches(*argv, "loopback") == 0) {
  296. + NEXT_ARG();
  297. + set_ctrlmode("loopback", *argv, &cm,
  298. + CAN_CTRLMODE_LOOPBACK);
  299. + } else if (matches(*argv, "listen-only") == 0) {
  300. + NEXT_ARG();
  301. + set_ctrlmode("listen-only", *argv, &cm,
  302. + CAN_CTRLMODE_LISTENONLY);
  303. + } else if (matches(*argv, "triple-sampling") == 0) {
  304. + NEXT_ARG();
  305. + set_ctrlmode("triple-sampling", *argv, &cm,
  306. + CAN_CTRLMODE_3_SAMPLES);
  307. + } else if (matches(*argv, "one-shot") == 0) {
  308. + NEXT_ARG();
  309. + set_ctrlmode("one-shot", *argv, &cm,
  310. + CAN_CTRLMODE_ONE_SHOT);
  311. + } else if (matches(*argv, "berr-reporting") == 0) {
  312. + NEXT_ARG();
  313. + set_ctrlmode("berr-reporting", *argv, &cm,
  314. + CAN_CTRLMODE_BERR_REPORTING);
  315. + } else if (matches(*argv, "fd") == 0) {
  316. + NEXT_ARG();
  317. + set_ctrlmode("fd", *argv, &cm,
  318. + CAN_CTRLMODE_FD);
  319. + } else if (matches(*argv, "fd-non-iso") == 0) {
  320. + NEXT_ARG();
  321. + set_ctrlmode("fd-non-iso", *argv, &cm,
  322. + CAN_CTRLMODE_FD_NON_ISO);
  323. + } else if (matches(*argv, "presume-ack") == 0) {
  324. + NEXT_ARG();
  325. + set_ctrlmode("presume-ack", *argv, &cm,
  326. + CAN_CTRLMODE_PRESUME_ACK);
  327. +#if defined(CAN_CTRLMODE_CC_LEN8_DLC)
  328. + } else if (matches(*argv, "cc-len8-dlc") == 0) {
  329. + NEXT_ARG();
  330. + set_ctrlmode("cc-len8-dlc", *argv, &cm,
  331. + CAN_CTRLMODE_CC_LEN8_DLC);
  332. +#endif
  333. + } else if (matches(*argv, "restart") == 0) {
  334. + __u32 val = 1;
  335. +
  336. + mnl_attr_put(nlh, IFLA_CAN_RESTART, sizeof(val), &val);
  337. + } else if (matches(*argv, "restart-ms") == 0) {
  338. + __u32 val;
  339. +
  340. + NEXT_ARG();
  341. + if (get_u32(&val, *argv, 0))
  342. + invarg("invalid \"restart-ms\" value\n", *argv);
  343. +
  344. + mnl_attr_put(nlh, IFLA_CAN_RESTART_MS, sizeof(val), &val);
  345. + } else if (matches(*argv, "termination") == 0) {
  346. + __u16 val;
  347. +
  348. + NEXT_ARG();
  349. + if (get_u16(&val, *argv, 0))
  350. + invarg("invalid \"termination\" value\n",
  351. + *argv);
  352. +
  353. + mnl_attr_put(nlh, IFLA_CAN_TERMINATION, sizeof(val), &val);
  354. + } else {
  355. + fprintf(stderr, "unknown option \"%s\"\n", *argv);
  356. + usage();
  357. + return -1;
  358. + }
  359. +
  360. + NEXT_ARG();
  361. + }
  362. +
  363. + if (bt.bitrate || bt.tq)
  364. + mnl_attr_put(nlh, IFLA_CAN_BITTIMING, sizeof(bt), &bt);
  365. +
  366. + if (cm.mask)
  367. + mnl_attr_put(nlh, IFLA_CAN_CTRLMODE, sizeof(cm), &cm);
  368. +
  369. + return 0;
  370. +}
  371. +
  372. +int main(int argc, char *argv[])
  373. +{
  374. + char buf[MNL_SOCKET_BUFFER_SIZE];
  375. + struct mnl_socket *nl;
  376. + struct nlmsghdr *nlh;
  377. + struct ifinfomsg *ifm;
  378. + int ret;
  379. + unsigned int seq, portid;
  380. + struct nlattr *linkinfo, *data;
  381. + const char *signatures[] = {
  382. + "ip", "link", "set", ""
  383. + };
  384. + char *type = NULL;
  385. + char *dev = NULL;
  386. + int i;
  387. +
  388. + NEXT_ARG();
  389. + for (i = 0; argc > 0 && signatures[i][0];) {
  390. + if (matches(*argv, signatures[i]))
  391. + incomplete_command();
  392. +
  393. + NEXT_ARG();
  394. + i++;
  395. + }
  396. +
  397. + if (argc == 0)
  398. + incomplete_command();
  399. +
  400. + nlh = mnl_nlmsg_put_header(buf);
  401. + nlh->nlmsg_type = RTM_NEWLINK;
  402. + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
  403. + nlh->nlmsg_seq = seq = time(NULL);
  404. + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
  405. + ifm->ifi_family = AF_UNSPEC;
  406. + ifm->ifi_change = 0;
  407. + ifm->ifi_flags = 0;
  408. +
  409. + while (argc > 0) {
  410. + if (matches(*argv, "up") == 0) {
  411. + ifm->ifi_change |= IFF_UP;
  412. + ifm->ifi_flags |= IFF_UP;
  413. + } else if (matches(*argv, "down") == 0) {
  414. + ifm->ifi_change |= IFF_UP;
  415. + ifm->ifi_flags &= ~IFF_UP;
  416. + } else if (matches(*argv, "type") == 0) {
  417. + NEXT_ARG();
  418. + type = *argv;
  419. + NEXT_ARG();
  420. + break;
  421. + } else if (matches(*argv, "help") == 0) {
  422. + usage();
  423. + exit(EXIT_FAILURE);
  424. + } else {
  425. + if (matches(*argv, "dev") == 0)
  426. + NEXT_ARG();
  427. +
  428. + if (dev)
  429. + duparg2("dev", *argv);
  430. +
  431. + dev = *argv;
  432. + }
  433. +
  434. + NEXT_ARG();
  435. + }
  436. +
  437. + if (dev)
  438. + mnl_attr_put_str(nlh, IFLA_IFNAME, dev);
  439. +
  440. + if (type) {
  441. + if (matches(type, "can")) {
  442. + fprintf(stderr, "unknown type \"%s\"\n", type);
  443. + usage();
  444. + exit(EXIT_FAILURE);
  445. + }
  446. +
  447. + linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
  448. + mnl_attr_put_str(nlh, IFLA_INFO_KIND, "can");
  449. + data = mnl_attr_nest_start(nlh, IFLA_INFO_DATA);
  450. +
  451. + if (iplink_set_can_parse(argc, argv, nlh))
  452. + return -1;
  453. +
  454. + mnl_attr_nest_end(nlh, data);
  455. + mnl_attr_nest_end(nlh, linkinfo);
  456. + }
  457. +
  458. + nl = mnl_socket_open(NETLINK_ROUTE);
  459. + if (nl == NULL) {
  460. + perror("mnl_socket_open");
  461. + exit(EXIT_FAILURE);
  462. + }
  463. +
  464. + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
  465. + perror("mnl_socket_bind");
  466. + exit(EXIT_FAILURE);
  467. + }
  468. +
  469. + portid = mnl_socket_get_portid(nl);
  470. +
  471. + mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
  472. + sizeof(struct ifinfomsg));
  473. +
  474. + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
  475. + perror("mnl_socket_sendto");
  476. + exit(EXIT_FAILURE);
  477. + }
  478. +
  479. + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
  480. + if (ret == -1) {
  481. + perror("mnl_socket_recvfrom");
  482. + exit(EXIT_FAILURE);
  483. + }
  484. +
  485. + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
  486. + if (ret == -1) {
  487. + perror("mnl_cb_run");
  488. + exit(EXIT_FAILURE);
  489. + }
  490. +
  491. + mnl_socket_close(nl);
  492. +
  493. + return 0;
  494. +}
  495. --
  496. 2.32.0