mirror_iproute2/ip/iplink_macvlan.c
Thomas Karlsson 42f5642a40 iplink:macvlan: Added bcqueuelen parameter
This patch allows the user to set and retrieve the
IFLA_MACVLAN_BC_QUEUE_LEN parameter via the bcqueuelen
command line argument

This parameter controls the requested size of the queue for
broadcast and multicast packages in the macvlan driver.

If not specified, the driver default (1000) will be used.

Note: The request is per macvlan but the actually used queue
length per port is the maximum of any request to any macvlan
connected to the same port.

For this reason, the used queue length IFLA_MACVLAN_BC_QUEUE_LEN_USED
is also retrieved and displayed in order to aid in the understanding
of the setting. However, it can of course not be directly set.

Signed-off-by: Thomas Karlsson <thomas.karlsson@paneda.se>
Signed-off-by: David Ahern <dsahern@gmail.com>
2020-12-16 04:02:07 +00:00

304 lines
7.7 KiB
C

/*
* iplink_macvlan.c macvlan/macvtap device support
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* 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]\n"
"\n"
"MODE: private | vepa | bridge | passthru | source\n"
"MODE_FLAG: null | nopromisc\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",
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\" 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 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, "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, "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 (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 (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);
}
/* 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,
};