diff options
author | Dario Binacchi <dario.binacchi@amarulasolutions.com> | 2024-02-25 10:13:33 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2024-02-26 14:10:10 +0100 |
commit | f873c63085e3f3745040286999779077ccc03987 (patch) | |
tree | deaf346e3dc5cb50f8c526460d225a715bea4125 | |
parent | 758b21402abc7015cfc54eb21a2e7eead1ecf6ba (diff) | |
download | busybox-w32-f873c63085e3f3745040286999779077ccc03987.tar.gz busybox-w32-f873c63085e3f3745040286999779077ccc03987.tar.bz2 busybox-w32-f873c63085e3f3745040286999779077ccc03987.zip |
ip link: support for the CAN netlink
I developed this application to test the Linux kernel series [1]. As
described in it I could not use the iproute2 package since the
microcontroller is without MMU.
function old new delta
do_set_can - 920 +920
packed_usage 34645 34908 +263
get_float_1000 - 164 +164
.rodata 105427 105539 +112
do_iplink 1313 1381 +68
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 3/0 up/down: 1527/0) Total: 1527 bytes
cc: Marc Kleine-Budde <mkl@pengutronix.de>
[1] https://marc.info/?l=linux-netdev&m=167999323611710&w=2
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | configs/TEST_nommu_defconfig | 1 | ||||
-rw-r--r-- | networking/ip.c | 55 | ||||
-rw-r--r-- | networking/libiproute/iplink.c | 288 |
3 files changed, 333 insertions, 11 deletions
diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig index 415f5a802..fa3e96326 100644 --- a/configs/TEST_nommu_defconfig +++ b/configs/TEST_nommu_defconfig | |||
@@ -703,6 +703,7 @@ CONFIG_FEATURE_INETD_RPC=y | |||
703 | CONFIG_IP=y | 703 | CONFIG_IP=y |
704 | CONFIG_FEATURE_IP_ADDRESS=y | 704 | CONFIG_FEATURE_IP_ADDRESS=y |
705 | CONFIG_FEATURE_IP_LINK=y | 705 | CONFIG_FEATURE_IP_LINK=y |
706 | CONFIG_FEATURE_IP_LINK_CAN=y | ||
706 | CONFIG_FEATURE_IP_ROUTE=y | 707 | CONFIG_FEATURE_IP_ROUTE=y |
707 | CONFIG_FEATURE_IP_TUNNEL=y | 708 | CONFIG_FEATURE_IP_TUNNEL=y |
708 | CONFIG_FEATURE_IP_RULE=y | 709 | CONFIG_FEATURE_IP_RULE=y |
diff --git a/networking/ip.c b/networking/ip.c index 36126b747..02f93adbf 100644 --- a/networking/ip.c +++ b/networking/ip.c | |||
@@ -74,6 +74,12 @@ | |||
74 | //config: help | 74 | //config: help |
75 | //config: Configure network devices with "ip". | 75 | //config: Configure network devices with "ip". |
76 | //config: | 76 | //config: |
77 | //config:config FEATURE_IP_LINK_CAN | ||
78 | //config: bool "ip link set type can" | ||
79 | //config: default y | ||
80 | //config: help | ||
81 | //config: Configure CAN devices with "ip". | ||
82 | //config: | ||
77 | //config:config FEATURE_IP_ROUTE | 83 | //config:config FEATURE_IP_ROUTE |
78 | //config: bool "ip route" | 84 | //config: bool "ip route" |
79 | //config: default y | 85 | //config: default y |
@@ -152,16 +158,61 @@ | |||
152 | //usage:#define iplink_trivial_usage | 158 | //usage:#define iplink_trivial_usage |
153 | //usage: /*Usage:iplink*/"set IFACE [up|down] [arp on|off] [multicast on|off]\n" | 159 | //usage: /*Usage:iplink*/"set IFACE [up|down] [arp on|off] [multicast on|off]\n" |
154 | //usage: " [promisc on|off] [mtu NUM] [name NAME] [qlen NUM] [address MAC]\n" | 160 | //usage: " [promisc on|off] [mtu NUM] [name NAME] [qlen NUM] [address MAC]\n" |
155 | //usage: " [master IFACE | nomaster] [netns PID]" | 161 | //usage: " [master IFACE | nomaster] [netns PID]"IF_FEATURE_IP_LINK_CAN(" [type TYPE ARGS]") |
156 | // * short help shows only "set" command, long help continues (with just one "\n") | 162 | // * short help shows only "set" command, long help continues (with just one "\n") |
157 | // * and shows all other commands: | 163 | // * and shows all other commands: |
158 | //usage:#define iplink_full_usage "\n" | 164 | //usage:#define iplink_full_usage "\n" |
159 | //usage: "iplink add [link IFACE] IFACE [address MAC] type TYPE [ARGS]\n" | 165 | //usage: "iplink add [link IFACE] IFACE [address MAC] type TYPE [ARGS]\n" |
160 | //usage: "iplink delete IFACE type TYPE [ARGS]\n" | 166 | //usage: "iplink delete IFACE type TYPE [ARGS]\n" |
161 | //usage: " TYPE ARGS := vlan VLANARGS | vrf table NUM\n" | 167 | //usage: " TYPE ARGS := vlan VLANARGS | vrf table NUM"IF_FEATURE_IP_LINK_CAN(" | can CANARGS")"\n" |
162 | //usage: " VLANARGS := id VLANID [protocol 802.1q|802.1ad] [reorder_hdr on|off]\n" | 168 | //usage: " VLANARGS := id VLANID [protocol 802.1q|802.1ad] [reorder_hdr on|off]\n" |
163 | //usage: " [gvrp on|off] [mvrp on|off] [loose_binding on|off]\n" | 169 | //usage: " [gvrp on|off] [mvrp on|off] [loose_binding on|off]\n" |
170 | //usage: IF_FEATURE_IP_LINK_CAN( | ||
171 | //usage: " CANARGS := [bitrate BITRATE [sample-point SAMPLE-POINT]] |\n" | ||
172 | //usage: " [tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n" | ||
173 | //usage: " phase-seg2 PHASE-SEG2 [sjw SJW]]\n" | ||
174 | //usage: " [dbitrate BITRATE [dsample-point SAMPLE-POINT]] |\n" | ||
175 | //usage: " [dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n" | ||
176 | //usage: " dphase-seg2 PHASE-SEG2 [dsjw SJW]]\n" | ||
177 | //usage: " [loopback on|off] [listen-only on|off] [triple-sampling on|off]\n" | ||
178 | //usage: " [one-shot on|off] [berr-reporting on|off]\n" | ||
179 | //usage: " [fd on|off] [fd-non-iso on|off] [presume-ack on|off]\n" | ||
180 | //usage: " [restart-ms TIME-MS] [restart]\n" | ||
181 | //usage: " [termination 0..65535]\n" | ||
182 | //usage: ) | ||
164 | //usage: "iplink show [IFACE]" | 183 | //usage: "iplink show [IFACE]" |
184 | //upstream man ip-link-can: | ||
185 | //Usage: ip link set DEVICE type can | ||
186 | // [ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | | ||
187 | // [ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1 | ||
188 | // phase-seg2 PHASE-SEG2 [ sjw SJW ] ] | ||
189 | // | ||
190 | // [ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] | | ||
191 | // [ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1 | ||
192 | // dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ] | ||
193 | // | ||
194 | // [ loopback { on | off } ] | ||
195 | // [ listen-only { on | off } ] | ||
196 | // [ triple-sampling { on | off } ] | ||
197 | // [ one-shot { on | off } ] | ||
198 | // [ berr-reporting { on | off } ] | ||
199 | // [ fd { on | off } ] | ||
200 | // [ fd-non-iso { on | off } ] | ||
201 | // [ presume-ack { on | off } ] | ||
202 | // | ||
203 | // [ restart-ms TIME-MS ] | ||
204 | // [ restart ] | ||
205 | // | ||
206 | // [ termination { 0..65535 } ] | ||
207 | // | ||
208 | // Where: BITRATE := { 1..1000000 } | ||
209 | // SAMPLE-POINT := { 0.000..0.999 } | ||
210 | // TQ := { NUMBER } | ||
211 | // PROP-SEG := { 1..8 } | ||
212 | // PHASE-SEG1 := { 1..8 } | ||
213 | // PHASE-SEG2 := { 1..8 } | ||
214 | // SJW := { 1..4 } | ||
215 | // RESTART-MS := { 0 | NUMBER } | ||
165 | //upstream man ip-link: | 216 | //upstream man ip-link: |
166 | //===================== | 217 | //===================== |
167 | //ip link add [link DEV] [ name ] NAME | 218 | //ip link add [link DEV] [ name ] NAME |
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c index 9eb0b4f5f..37ed114bc 100644 --- a/networking/libiproute/iplink.c +++ b/networking/libiproute/iplink.c | |||
@@ -11,6 +11,9 @@ | |||
11 | #include <netinet/if_ether.h> | 11 | #include <netinet/if_ether.h> |
12 | 12 | ||
13 | #include <linux/if_vlan.h> | 13 | #include <linux/if_vlan.h> |
14 | #if ENABLE_FEATURE_IP_LINK_CAN | ||
15 | # include <linux/can/netlink.h> | ||
16 | #endif | ||
14 | #include "ip_common.h" /* #include "libbb.h" is inside */ | 17 | #include "ip_common.h" /* #include "libbb.h" is inside */ |
15 | #include "rt_names.h" | 18 | #include "rt_names.h" |
16 | #include "utils.h" | 19 | #include "utils.h" |
@@ -28,6 +31,11 @@ | |||
28 | #undef IFLA_VLAN_PROTOCOL | 31 | #undef IFLA_VLAN_PROTOCOL |
29 | #define IFLA_VLAN_PROTOCOL 5 | 32 | #define IFLA_VLAN_PROTOCOL 5 |
30 | 33 | ||
34 | #ifndef NLMSG_TAIL | ||
35 | #define NLMSG_TAIL(nmsg) \ | ||
36 | ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) | ||
37 | #endif | ||
38 | |||
31 | #ifndef IFLA_LINKINFO | 39 | #ifndef IFLA_LINKINFO |
32 | # define IFLA_LINKINFO 18 | 40 | # define IFLA_LINKINFO 18 |
33 | # define IFLA_INFO_KIND 1 | 41 | # define IFLA_INFO_KIND 1 |
@@ -55,6 +63,11 @@ struct ifla_vlan_flags { | |||
55 | 63 | ||
56 | #define str_on_off "on\0""off\0" | 64 | #define str_on_off "on\0""off\0" |
57 | 65 | ||
66 | enum { | ||
67 | PARM_on = 0, | ||
68 | PARM_off | ||
69 | }; | ||
70 | |||
58 | /* Exits on error */ | 71 | /* Exits on error */ |
59 | static int get_ctl_fd(void) | 72 | static int get_ctl_fd(void) |
60 | { | 73 | { |
@@ -241,10 +254,257 @@ static void die_must_be_on_off(const char *msg) | |||
241 | bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg); | 254 | bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg); |
242 | } | 255 | } |
243 | 256 | ||
257 | #if ENABLE_FEATURE_IP_LINK_CAN | ||
258 | static uint32_t get_float_1000(char *arg, const char *errmsg) | ||
259 | { | ||
260 | uint32_t ret; | ||
261 | double d; | ||
262 | char *ptr; | ||
263 | |||
264 | errno = 0; | ||
265 | //TODO: needs setlocale(LC_NUMERIC, "C")? | ||
266 | d = strtod(arg, &ptr); | ||
267 | if (errno || ptr == arg || *ptr | ||
268 | || d > (0xFFFFFFFFU / 1000) || d < 0 | ||
269 | ) { | ||
270 | invarg_1_to_2(arg, errmsg); /* does not return */ | ||
271 | } | ||
272 | ret = d * 1000; | ||
273 | |||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | static void do_set_can(char *dev, char **argv) | ||
278 | { | ||
279 | struct can_bittiming bt = {}, dbt = {}; | ||
280 | struct can_ctrlmode cm = {}; | ||
281 | char *keyword; | ||
282 | static const char keywords[] ALIGN1 = | ||
283 | "bitrate\0""sample-point\0""tq\0" | ||
284 | "prop-seg\0""phase-seg1\0""phase-seg2\0""sjw\0" | ||
285 | "dbitrate\0""dsample-point\0""dtq\0" | ||
286 | "dprop-seg\0""dphase-seg1\0""dphase-seg2\0""dsjw\0" | ||
287 | "loopback\0""listen-only\0""triple-sampling\0" | ||
288 | "one-shot\0""berr-reporting\0" | ||
289 | "fd\0""fd-non-iso\0""presume-ack\0" | ||
290 | "cc-len8-dlc\0""restart\0""restart-ms\0" | ||
291 | "termination\0"; | ||
292 | enum { ARG_bitrate = 0, ARG_sample_point, ARG_tq, | ||
293 | ARG_prop_seg, ARG_phase_seg1, ARG_phase_seg2, ARG_sjw, | ||
294 | ARG_dbitrate, ARG_dsample_point, ARG_dtq, | ||
295 | ARG_dprop_seg, ARG_dphase_seg1, ARG_dphase_seg2, ARG_dsjw, | ||
296 | ARG_loopback, ARG_listen_only, ARG_triple_sampling, | ||
297 | ARG_one_shot, ARG_berr_reporting, | ||
298 | ARG_fd, ARG_fd_non_iso, ARG_presume_ack, | ||
299 | ARG_cc_len8_dlc, ARG_restart, ARG_restart_ms, | ||
300 | ARG_termination }; | ||
301 | struct rtnl_handle rth; | ||
302 | struct { | ||
303 | struct nlmsghdr n; | ||
304 | struct ifinfomsg i; | ||
305 | char buf[1024]; | ||
306 | } req; | ||
307 | size_t dev_len; | ||
308 | struct rtattr *linkinfo, *data; | ||
309 | smalluint key, param; | ||
310 | |||
311 | memset(&req, 0, sizeof(req)); | ||
312 | |||
313 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | ||
314 | req.n.nlmsg_flags = NLM_F_REQUEST; | ||
315 | req.n.nlmsg_type = RTM_NEWLINK; | ||
316 | req.i.ifi_family = preferred_family; | ||
317 | xrtnl_open(&rth); | ||
318 | req.i.ifi_index = xll_name_to_index(dev); | ||
319 | dev_len = strlen(dev); | ||
320 | if (dev_len < 2 || dev_len > IFNAMSIZ) | ||
321 | invarg_1_to_2(dev, "dev"); | ||
322 | |||
323 | addattr_l(&req.n, sizeof(req), IFLA_IFNAME, dev, dev_len); | ||
324 | linkinfo = NLMSG_TAIL(&req.n); | ||
325 | addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); | ||
326 | addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, (void *)"can", | ||
327 | strlen("can")); | ||
328 | data = NLMSG_TAIL(&req.n); | ||
329 | addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); | ||
330 | while (*argv) { | ||
331 | key = index_in_substrings(keywords, *argv); | ||
332 | keyword = *argv; | ||
333 | //printf("%s: key: %d, *argv: %s\n", __func__, key, *argv); | ||
334 | switch (key) { | ||
335 | case ARG_bitrate: | ||
336 | case ARG_tq: | ||
337 | case ARG_prop_seg: | ||
338 | case ARG_phase_seg1: | ||
339 | case ARG_phase_seg2: | ||
340 | case ARG_sjw: { | ||
341 | uint32_t *val; | ||
342 | NEXT_ARG(); | ||
343 | if (key == ARG_bitrate) | ||
344 | val = &bt.bitrate; | ||
345 | else if (key == ARG_tq) | ||
346 | val = &bt.tq; | ||
347 | else if (key == ARG_prop_seg) | ||
348 | val = &bt.prop_seg; | ||
349 | else if (key == ARG_phase_seg1) | ||
350 | val = &bt.phase_seg1; | ||
351 | else if (key == ARG_phase_seg2) | ||
352 | val = &bt.phase_seg2; | ||
353 | else | ||
354 | val = &bt.sjw; | ||
355 | |||
356 | *val = get_u32(*argv, keyword); | ||
357 | break; | ||
358 | } | ||
359 | case ARG_sample_point: { | ||
360 | NEXT_ARG(); | ||
361 | bt.sample_point = get_float_1000(*argv, keyword); | ||
362 | break; | ||
363 | } | ||
364 | case ARG_dbitrate: | ||
365 | case ARG_dtq: | ||
366 | case ARG_dprop_seg: | ||
367 | case ARG_dphase_seg1: | ||
368 | case ARG_dphase_seg2: | ||
369 | case ARG_dsjw: { | ||
370 | uint32_t *val; | ||
371 | NEXT_ARG(); | ||
372 | if (key == ARG_dbitrate) | ||
373 | val = &dbt.bitrate; | ||
374 | else if (key == ARG_dtq) | ||
375 | val = &dbt.tq; | ||
376 | else if (key == ARG_dprop_seg) | ||
377 | val = &dbt.prop_seg; | ||
378 | else if (key == ARG_dphase_seg1) | ||
379 | val = &dbt.phase_seg1; | ||
380 | else if (key == ARG_dphase_seg2) | ||
381 | val = &dbt.phase_seg2; | ||
382 | else | ||
383 | val = &dbt.sjw; | ||
384 | |||
385 | *val = get_u32(*argv, keyword); | ||
386 | break; | ||
387 | } | ||
388 | case ARG_dsample_point: { | ||
389 | NEXT_ARG(); | ||
390 | dbt.sample_point = get_float_1000(*argv, keyword); | ||
391 | break; | ||
392 | } | ||
393 | case ARG_loopback: | ||
394 | case ARG_listen_only: | ||
395 | case ARG_triple_sampling: | ||
396 | case ARG_one_shot: | ||
397 | case ARG_berr_reporting: | ||
398 | case ARG_fd: | ||
399 | case ARG_fd_non_iso: | ||
400 | case ARG_presume_ack: | ||
401 | case ARG_cc_len8_dlc: { | ||
402 | uint32_t flag = 0; | ||
403 | |||
404 | NEXT_ARG(); | ||
405 | param = index_in_strings(str_on_off, *argv); | ||
406 | if (param < 0) | ||
407 | die_must_be_on_off(keyword); | ||
408 | |||
409 | if (key == ARG_loopback) | ||
410 | flag = CAN_CTRLMODE_LOOPBACK; | ||
411 | else if (key == ARG_listen_only) | ||
412 | flag = CAN_CTRLMODE_LISTENONLY; | ||
413 | else if (key == ARG_triple_sampling) | ||
414 | flag = CAN_CTRLMODE_3_SAMPLES; | ||
415 | else if (key == ARG_one_shot) | ||
416 | flag = CAN_CTRLMODE_ONE_SHOT; | ||
417 | else if (key == ARG_berr_reporting) | ||
418 | flag = CAN_CTRLMODE_BERR_REPORTING; | ||
419 | else if (key == ARG_fd) | ||
420 | flag = CAN_CTRLMODE_FD; | ||
421 | else if (key == ARG_fd_non_iso) | ||
422 | flag = CAN_CTRLMODE_FD_NON_ISO; | ||
423 | else if (key == ARG_presume_ack) | ||
424 | flag = CAN_CTRLMODE_PRESUME_ACK; | ||
425 | else | ||
426 | #if defined(CAN_CTRLMODE_CC_LEN8_DLC) | ||
427 | flag = CAN_CTRLMODE_CC_LEN8_DLC; | ||
428 | #else | ||
429 | die_must_be_on_off(keyword); | ||
430 | #endif | ||
431 | cm.mask |= flag; | ||
432 | if (param == PARM_on) | ||
433 | cm.flags |= flag; | ||
434 | |||
435 | break; | ||
436 | } | ||
437 | case ARG_restart: { | ||
438 | uint32_t val = 1; | ||
439 | /*NEXT_ARG(); - WRONG? */ | ||
440 | addattr_l(&req.n, sizeof(req), IFLA_CAN_RESTART, &val, sizeof(val)); | ||
441 | break; | ||
442 | } | ||
443 | case ARG_restart_ms: { | ||
444 | uint32_t val; | ||
445 | NEXT_ARG(); | ||
446 | val = get_u32(*argv, keyword); | ||
447 | addattr_l(&req.n, sizeof(req), IFLA_CAN_RESTART_MS, &val, sizeof(val)); | ||
448 | break; | ||
449 | } | ||
450 | case ARG_termination: { | ||
451 | uint16_t val; | ||
452 | NEXT_ARG(); | ||
453 | val = get_u16(*argv, keyword); | ||
454 | addattr_l(&req.n, sizeof(req), IFLA_CAN_TERMINATION, &val, sizeof(val)); | ||
455 | break; | ||
456 | } | ||
457 | default: | ||
458 | break; | ||
459 | } | ||
460 | argv++; | ||
461 | } | ||
462 | |||
463 | if (bt.bitrate || bt.tq) | ||
464 | addattr_l(&req.n, sizeof(req), IFLA_CAN_BITTIMING, &bt, sizeof(bt)); | ||
465 | |||
466 | if (cm.mask) | ||
467 | addattr_l(&req.n, sizeof(req), IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); | ||
468 | |||
469 | data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; | ||
470 | linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo; | ||
471 | |||
472 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | ||
473 | xfunc_die(); | ||
474 | } | ||
475 | |||
476 | static void set_type(char *type, char *dev, char **argv) | ||
477 | { | ||
478 | /* When we have more than just "type can ARGS" supported, maybe: | ||
479 | static const char keywords[] ALIGN1 = "" | ||
480 | IF_FEATURE_IP_LINK_CAN("can\0") | ||
481 | ; | ||
482 | typedef void FAST_FUNC(*ip_type_set_func_ptr_t)(char*, char**); | ||
483 | static const ip_type_set_func_ptr_t funcs[] ALIGN_PTR = { | ||
484 | IF_FEATURE_IP_LINK_CAN(do_set_can,) | ||
485 | }; | ||
486 | ip_type_set_func_ptr_t func; | ||
487 | int key; | ||
488 | |||
489 | key = index_in_substrings(keywords, type); | ||
490 | if (key < 0) | ||
491 | invarg_1_to_2(type, "type"); | ||
492 | func = funcs[key]; | ||
493 | func(dev, argv); | ||
494 | */ | ||
495 | if (strcmp(type, "can") != 0) | ||
496 | invarg_1_to_2(type, "type"); | ||
497 | do_set_can(dev, argv); | ||
498 | } | ||
499 | #endif | ||
500 | |||
244 | /* Return value becomes exitcode. It's okay to not return at all */ | 501 | /* Return value becomes exitcode. It's okay to not return at all */ |
245 | static int do_set(char **argv) | 502 | static int do_set(char **argv) |
246 | { | 503 | { |
247 | char *dev = NULL; | 504 | char *dev = NULL; |
505 | #if ENABLE_FEATURE_IP_LINK_CAN | ||
506 | char *type = NULL; | ||
507 | #endif | ||
248 | uint32_t mask = 0; | 508 | uint32_t mask = 0; |
249 | uint32_t flags = 0; | 509 | uint32_t flags = 0; |
250 | int qlen = -1; | 510 | int qlen = -1; |
@@ -261,18 +521,24 @@ static int do_set(char **argv) | |||
261 | "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" | 521 | "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" |
262 | "arp\0""promisc\0""address\0""netns\0" | 522 | "arp\0""promisc\0""address\0""netns\0" |
263 | "master\0""nomaster\0" | 523 | "master\0""nomaster\0" |
524 | #if ENABLE_FEATURE_IP_LINK_CAN | ||
525 | "type\0" | ||
526 | #endif | ||
264 | "dev\0" /* must be last */; | 527 | "dev\0" /* must be last */; |
265 | enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast, | 528 | enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast, |
266 | ARG_arp, ARG_promisc, ARG_addr, ARG_netns, | 529 | ARG_arp, ARG_promisc, ARG_addr, ARG_netns, |
267 | ARG_master, ARG_nomaster, | 530 | ARG_master, ARG_nomaster, |
531 | #if ENABLE_FEATURE_IP_LINK_CAN | ||
532 | ARG_type, | ||
533 | #endif | ||
268 | ARG_dev }; | 534 | ARG_dev }; |
269 | enum { PARM_on = 0, PARM_off }; | ||
270 | smalluint key; | 535 | smalluint key; |
271 | 536 | ||
272 | while (*argv) { | 537 | while (*argv) { |
273 | /* substring search ensures that e.g. "addr" and "address" | 538 | /* substring search ensures that e.g. "addr" and "address" |
274 | * are both accepted */ | 539 | * are both accepted */ |
275 | key = index_in_substrings(keywords, *argv); | 540 | key = index_in_substrings(keywords, *argv); |
541 | //printf("%s: key: %d, *argv: %s\n", __func__, key, *argv); | ||
276 | if (key == ARG_up) { | 542 | if (key == ARG_up) { |
277 | mask |= IFF_UP; | 543 | mask |= IFF_UP; |
278 | flags |= IFF_UP; | 544 | flags |= IFF_UP; |
@@ -304,6 +570,13 @@ static int do_set(char **argv) | |||
304 | } else if (key == ARG_netns) { | 570 | } else if (key == ARG_netns) { |
305 | NEXT_ARG(); | 571 | NEXT_ARG(); |
306 | netns = get_unsigned(*argv, "netns"); | 572 | netns = get_unsigned(*argv, "netns"); |
573 | #if ENABLE_FEATURE_IP_LINK_CAN | ||
574 | } else if (key == ARG_type) { | ||
575 | NEXT_ARG(); | ||
576 | type = *argv; | ||
577 | argv++; | ||
578 | break; | ||
579 | #endif | ||
307 | } else if (key >= ARG_dev) { | 580 | } else if (key >= ARG_dev) { |
308 | /* ^^^^^^ ">=" here results in "dev IFACE" treated as default */ | 581 | /* ^^^^^^ ">=" here results in "dev IFACE" treated as default */ |
309 | if (key == ARG_dev) { | 582 | if (key == ARG_dev) { |
@@ -311,6 +584,7 @@ static int do_set(char **argv) | |||
311 | } | 584 | } |
312 | if (dev) | 585 | if (dev) |
313 | duparg2("dev", *argv); | 586 | duparg2("dev", *argv); |
587 | |||
314 | dev = *argv; | 588 | dev = *argv; |
315 | } else { | 589 | } else { |
316 | /* "on|off" options */ | 590 | /* "on|off" options */ |
@@ -496,6 +770,10 @@ static int do_set(char **argv) | |||
496 | } | 770 | } |
497 | if (mask) | 771 | if (mask) |
498 | do_chflags(dev, flags, mask); | 772 | do_chflags(dev, flags, mask); |
773 | #if ENABLE_FEATURE_IP_LINK_CAN | ||
774 | if (type) | ||
775 | set_type(type, dev, argv); | ||
776 | #endif | ||
499 | return 0; | 777 | return 0; |
500 | } | 778 | } |
501 | 779 | ||
@@ -531,10 +809,6 @@ static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size) | |||
531 | PROTO_8021Q = 0, | 809 | PROTO_8021Q = 0, |
532 | PROTO_8021AD, | 810 | PROTO_8021AD, |
533 | }; | 811 | }; |
534 | enum { | ||
535 | PARM_on = 0, | ||
536 | PARM_off | ||
537 | }; | ||
538 | int arg; | 812 | int arg; |
539 | uint16_t id, proto; | 813 | uint16_t id, proto; |
540 | struct ifla_vlan_flags flags = {}; | 814 | struct ifla_vlan_flags flags = {}; |
@@ -610,10 +884,6 @@ static void vrf_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size) | |||
610 | addattr_l(n, size, IFLA_VRF_TABLE, &table, sizeof(table)); | 884 | addattr_l(n, size, IFLA_VRF_TABLE, &table, sizeof(table)); |
611 | } | 885 | } |
612 | 886 | ||
613 | #ifndef NLMSG_TAIL | ||
614 | #define NLMSG_TAIL(nmsg) \ | ||
615 | ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) | ||
616 | #endif | ||
617 | /* Return value becomes exitcode. It's okay to not return at all */ | 887 | /* Return value becomes exitcode. It's okay to not return at all */ |
618 | static int do_add_or_delete(char **argv, const unsigned rtm) | 888 | static int do_add_or_delete(char **argv, const unsigned rtm) |
619 | { | 889 | { |