diff options
Diffstat (limited to '')
-rw-r--r-- | ip/iplink_macvlan.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c new file mode 100644 index 0000000..6bdc76d --- /dev/null +++ b/ip/iplink_macvlan.c @@ -0,0 +1,329 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * iplink_macvlan.c macvlan/macvtap device support + * + * Authors: Patrick McHardy <kaber@trash.net> + * Arnd Bergmann <arnd@arndb.de> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <linux/if_link.h> +#include <linux/if_ether.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#define pfx_err(lu, ...) { \ + fprintf(stderr, "%s: ", lu->id); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +} + +static void print_explain(struct link_util *lu, FILE *f) +{ + fprintf(f, + "Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS [bcqueuelen BC_QUEUE_LEN] [bclim BCLIM]\n" + "\n" + "MODE: private | vepa | bridge | passthru | source\n" + "MODE_FLAG: null | nopromisc | nodst\n" + "MODE_OPTS: for mode \"source\":\n" + "\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr> ... ] ] | flush }\n" + "BC_QUEUE_LEN: Length of the rx queue for broadcast/multicast: [0-4294967295]\n" + "BCLIM: Threshold for broadcast queueing: 32-bit integer\n", + lu->id + ); +} + +static void explain(struct link_util *lu) +{ + print_explain(lu, stderr); +} + + +static int mode_arg(const char *arg) +{ + fprintf(stderr, + "Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n", + arg); + return -1; +} + +static int flag_arg(const char *arg) +{ + fprintf(stderr, + "Error: argument of \"flag\" must be \"nopromisc\", \"nodst\" or \"null\", not \"%s\"\n", + arg); + return -1; +} + +static int bc_queue_len_arg(const char *arg) +{ + fprintf(stderr, + "Error: argument of \"bcqueuelen\" must be a positive integer [0-4294967295], not \"%s\"\n", + arg); + return -1; +} + +static int bclim_arg(const char *arg) +{ + fprintf(stderr, "Error: illegal value for \"bclim\": \"%s\"\n", arg); + return -1; +} + +static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u32 mode = 0; + __u16 flags = 0; + __u32 mac_mode = 0; + int has_flags = 0; + char mac[ETH_ALEN]; + struct rtattr *nmac; + + while (argc > 0) { + if (matches(*argv, "mode") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "private") == 0) + mode = MACVLAN_MODE_PRIVATE; + else if (strcmp(*argv, "vepa") == 0) + mode = MACVLAN_MODE_VEPA; + else if (strcmp(*argv, "bridge") == 0) + mode = MACVLAN_MODE_BRIDGE; + else if (strcmp(*argv, "passthru") == 0) + mode = MACVLAN_MODE_PASSTHRU; + else if (strcmp(*argv, "source") == 0) + mode = MACVLAN_MODE_SOURCE; + else + return mode_arg(*argv); + } else if (matches(*argv, "flag") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "nopromisc") == 0) + flags |= MACVLAN_FLAG_NOPROMISC; + else if (strcmp(*argv, "nodst") == 0) + flags |= MACVLAN_FLAG_NODST; + else if (strcmp(*argv, "null") == 0) + flags |= 0; + else + return flag_arg(*argv); + + has_flags = 1; + + } else if (matches(*argv, "macaddr") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "add") == 0) { + mac_mode = MACVLAN_MACADDR_ADD; + } else if (strcmp(*argv, "del") == 0) { + mac_mode = MACVLAN_MACADDR_DEL; + } else if (strcmp(*argv, "set") == 0) { + mac_mode = MACVLAN_MACADDR_SET; + } else if (strcmp(*argv, "flush") == 0) { + mac_mode = MACVLAN_MACADDR_FLUSH; + } else { + explain(lu); + return -1; + } + + addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode); + + if (mac_mode == MACVLAN_MACADDR_ADD || + mac_mode == MACVLAN_MACADDR_DEL) { + NEXT_ARG(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) + return -1; + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac, + ETH_ALEN); + } + + if (mac_mode == MACVLAN_MACADDR_SET) { + nmac = addattr_nest(n, 1024, + IFLA_MACVLAN_MACADDR_DATA); + while (NEXT_ARG_OK()) { + NEXT_ARG_FWD(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) { + PREV_ARG(); + break; + } + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, + &mac, ETH_ALEN); + } + addattr_nest_end(n, nmac); + } + } else if (matches(*argv, "nopromisc") == 0) { + flags |= MACVLAN_FLAG_NOPROMISC; + has_flags = 1; + } else if (matches(*argv, "nodst") == 0) { + flags |= MACVLAN_FLAG_NODST; + has_flags = 1; + } else if (matches(*argv, "bcqueuelen") == 0) { + __u32 bc_queue_len; + NEXT_ARG(); + + if (get_u32(&bc_queue_len, *argv, 0)) { + return bc_queue_len_arg(*argv); + } + addattr32(n, 1024, IFLA_MACVLAN_BC_QUEUE_LEN, bc_queue_len); + } else if (!strcmp(*argv, "bclim")) { + __s32 bclim; + NEXT_ARG(); + + if (get_s32(&bclim, *argv, 0)) { + return bclim_arg(*argv); + } + addattr_l(n, 1024, IFLA_MACVLAN_BC_CUTOFF, + &bclim, sizeof(bclim)); + } else if (matches(*argv, "help") == 0) { + explain(lu); + return -1; + } else { + pfx_err(lu, "unknown option \"%s\"?", *argv); + explain(lu); + return -1; + } + argc--, argv++; + } + + if (mode) + addattr32(n, 1024, IFLA_MACVLAN_MODE, mode); + + if (has_flags) { + if (flags & MACVLAN_FLAG_NOPROMISC && + mode != MACVLAN_MODE_PASSTHRU) { + pfx_err(lu, "nopromisc flag only valid in passthru mode"); + explain(lu); + return -1; + } + addattr16(n, 1024, IFLA_MACVLAN_FLAGS, flags); + } + return 0; +} + +static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + __u32 mode; + __u16 flags; + __u32 count; + unsigned char *addr; + int len; + struct rtattr *rta; + + if (!tb) + return; + + if (!tb[IFLA_MACVLAN_MODE] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32)) + return; + + mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]); + print_string(PRINT_ANY, + "mode", + "mode %s ", + mode == MACVLAN_MODE_PRIVATE ? "private" + : mode == MACVLAN_MODE_VEPA ? "vepa" + : mode == MACVLAN_MODE_BRIDGE ? "bridge" + : mode == MACVLAN_MODE_PASSTHRU ? "passthru" + : mode == MACVLAN_MODE_SOURCE ? "source" + : "unknown"); + + if (!tb[IFLA_MACVLAN_FLAGS] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16)) + flags = 0; + else + flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]); + + if (flags & MACVLAN_FLAG_NOPROMISC) + print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true); + + if (flags & MACVLAN_FLAG_NODST) + print_bool(PRINT_ANY, "nodst", "nodst ", true); + + if (tb[IFLA_MACVLAN_BC_QUEUE_LEN] && + RTA_PAYLOAD(tb[IFLA_MACVLAN_BC_QUEUE_LEN]) >= sizeof(__u32)) { + __u32 bc_queue_len = rta_getattr_u32(tb[IFLA_MACVLAN_BC_QUEUE_LEN]); + print_luint(PRINT_ANY, "bcqueuelen", "bcqueuelen %lu ", bc_queue_len); + } + + if (tb[IFLA_MACVLAN_BC_QUEUE_LEN_USED] && + RTA_PAYLOAD(tb[IFLA_MACVLAN_BC_QUEUE_LEN_USED]) >= sizeof(__u32)) { + __u32 bc_queue_len = rta_getattr_u32(tb[IFLA_MACVLAN_BC_QUEUE_LEN_USED]); + print_luint(PRINT_ANY, "usedbcqueuelen", "usedbcqueuelen %lu ", bc_queue_len); + } + + if (tb[IFLA_MACVLAN_BC_CUTOFF] && + RTA_PAYLOAD(tb[IFLA_MACVLAN_BC_CUTOFF]) >= sizeof(__s32)) { + __s32 bclim = rta_getattr_s32(tb[IFLA_MACVLAN_BC_CUTOFF]); + print_int(PRINT_ANY, "bclim", "bclim %d ", bclim); + } + + /* in source mode, there are more options to print */ + + if (mode != MACVLAN_MODE_SOURCE) + return; + + if (!tb[IFLA_MACVLAN_MACADDR_COUNT] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32)) + return; + + count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]); + print_int(PRINT_ANY, "macaddr_count", "remotes (%d) ", count); + + if (!tb[IFLA_MACVLAN_MACADDR_DATA]) + return; + + rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]); + len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]); + + open_json_array(PRINT_JSON, "macaddr_data"); + for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + if (rta->rta_type != IFLA_MACVLAN_MACADDR || + RTA_PAYLOAD(rta) < 6) + continue; + addr = RTA_DATA(rta); + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), + "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + print_string(PRINT_JSON, NULL, NULL, b1); + } else { + fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + } + } + close_json_array(PRINT_JSON, NULL); +} + +static void macvlan_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(lu, f); +} + +struct link_util macvlan_link_util = { + .id = "macvlan", + .maxattr = IFLA_MACVLAN_MAX, + .parse_opt = macvlan_parse_opt, + .print_opt = macvlan_print_opt, + .print_help = macvlan_print_help, +}; + +struct link_util macvtap_link_util = { + .id = "macvtap", + .maxattr = IFLA_MACVLAN_MAX, + .parse_opt = macvlan_parse_opt, + .print_opt = macvlan_print_opt, + .print_help = macvlan_print_help, +}; |