Merge remote-tracking branch 'main/main' into next
Signed-off-by: David Ahern <dsahern@kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index c9ea3ea..82043c1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -31,6 +31,8 @@
L: bridge@lists.linux-foundation.org (moderated for non-subscribers)
F: bridge/*
F: ip/iplink_bridge*
+F: lib/bridge*
+F: include/bridge*
Data Center Bridging - dcb
M: Petr Machata <me@pmachata.org>
diff --git a/bridge/mdb.c b/bridge/mdb.c
index 196363a..7249097 100644
--- a/bridge/mdb.c
+++ b/bridge/mdb.c
@@ -256,6 +256,8 @@
print_string(PRINT_ANY, NULL, " %s", "added_by_star_ex");
if (e->flags & MDB_FLAGS_BLOCKED)
print_string(PRINT_ANY, NULL, " %s", "blocked");
+ if (e->flags & MDB_FLAGS_OFFLOAD_FAILED)
+ print_string(PRINT_ANY, NULL, " %s", "offload_failed");
close_json_array(PRINT_JSON, NULL);
if (e->vid)
diff --git a/bridge/vlan.c b/bridge/vlan.c
index ea4aff9..3c24020 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -15,6 +15,7 @@
#include "json_print.h"
#include "libnetlink.h"
#include "br_common.h"
+#include "bridge.h"
#include "utils.h"
static unsigned int filter_index, filter_vlan;
@@ -705,47 +706,6 @@
return 0;
}
-static void print_vlan_flags(__u16 flags)
-{
- if (flags == 0)
- return;
-
- open_json_array(PRINT_JSON, "flags");
- if (flags & BRIDGE_VLAN_INFO_PVID)
- print_string(PRINT_ANY, NULL, " %s", "PVID");
-
- if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
- print_string(PRINT_ANY, NULL, " %s", "Egress Untagged");
- close_json_array(PRINT_JSON, NULL);
-}
-
-static void __print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
-{
- print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
- print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
- vstats->rx_bytes);
- print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n",
- vstats->rx_packets);
-
- print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
- print_lluint(PRINT_ANY, "tx_bytes", "TX: %llu bytes",
- vstats->tx_bytes);
- print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
- vstats->tx_packets);
-}
-
-static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
-{
- open_json_object(NULL);
-
- print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
- print_vlan_flags(vstats->flags);
- print_nl();
- __print_one_vlan_stats(vstats);
-
- close_json_object();
-}
-
static void print_vlan_stats_attr(struct rtattr *attr, int ifindex)
{
struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
@@ -783,7 +743,7 @@
print_string(PRINT_FP, NULL,
"%-" textify(IFNAMSIZ) "s ", "");
}
- print_one_vlan_stats(vstats);
+ bridge_print_vlan_stats(vstats);
}
/* vlan_port is opened only if there are any vlan stats */
@@ -892,6 +852,11 @@
print_uint(PRINT_ANY, "mcast_querier", "mcast_querier %u ",
rta_getattr_u8(vattr));
}
+ if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE]) {
+ struct rtattr *attr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE];
+
+ bridge_print_mcast_querier_state(attr);
+ }
if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]) {
vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION];
print_uint(PRINT_ANY, "mcast_igmp_version",
@@ -1025,7 +990,7 @@
print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
}
print_range("vlan", vinfo->vid, vrange);
- print_vlan_flags(vinfo->flags);
+ bridge_print_vlan_flags(vinfo->flags);
print_nl();
print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
print_stp_state(state);
@@ -1051,7 +1016,7 @@
}
print_nl();
if (show_stats)
- __print_one_vlan_stats(&vstats);
+ bridge_print_vlan_stats_only(&vstats);
close_json_object();
}
@@ -1334,7 +1299,7 @@
open_json_object(NULL);
print_range("vlan", last_vid_start, vinfo->vid);
- print_vlan_flags(vinfo->flags);
+ bridge_print_vlan_flags(vinfo->flags);
close_json_object();
print_nl();
}
diff --git a/devlink/devlink.c b/devlink/devlink.c
index d14f3f4..171b853 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -310,6 +310,7 @@
#define DL_OPT_PORT_FN_RATE_TX_WEIGHT BIT(56)
#define DL_OPT_PORT_FN_CAPS BIT(57)
#define DL_OPT_PORT_FN_MAX_IO_EQS BIT(58)
+#define DL_OPT_PORT_FN_RATE_TC_BWS BIT(59)
struct dl_opts {
uint64_t present; /* flags of present items */
@@ -372,6 +373,7 @@
uint32_t rate_tx_weight;
char *rate_node_name;
const char *rate_parent_node;
+ uint32_t rate_tc_bw[DEVLINK_RATE_TCS_MAX];
uint32_t linecard_index;
const char *linecard_type;
bool selftests_opt[DEVLINK_ATTR_SELFTEST_ID_MAX + 1];
@@ -1699,6 +1701,84 @@
return err;
}
+static int
+parse_tc_bw_arg(const char *tc_bw_str, int *tc_index, uint32_t *tc_bw)
+{
+ char *index, *value, *endptr;
+ char *input = NULL;
+ int err;
+
+ input = strdup(tc_bw_str);
+ if (!input)
+ return -ENOMEM;
+
+ err = str_split_by_char(input, &index, &value, ':');
+ if (err) {
+ pr_err("Invalid format in token: %s\n", input);
+ goto out;
+ }
+
+ *tc_index = strtoul(index, &endptr, 10);
+ if (endptr && *endptr) {
+ pr_err("Invalid traffic class index: %s\n", index);
+ err = -EINVAL;
+ goto out;
+ }
+
+ *tc_bw = strtoul(value, &endptr, 10);
+ if (endptr && *endptr) {
+ pr_err("Invalid bandwidth value: %s\n", value);
+ err = -EINVAL;
+ goto out;
+ }
+
+out:
+ free(input);
+ return err;
+}
+
+static int parse_tc_bw_args(struct dl *dl, uint32_t *tc_bw)
+{
+ bool parsed_indices[DEVLINK_RATE_TCS_MAX] = {};
+ const char *tc_bw_str;
+ int index, err, i;
+ uint32_t bw;
+
+ memset(tc_bw, 0, sizeof(uint32_t) * DEVLINK_RATE_TCS_MAX);
+
+ for (i = 0; i < DEVLINK_RATE_TCS_MAX; i++) {
+ err = dl_argv_str(dl, &tc_bw_str);
+ if (err) {
+ fprintf(stderr,
+ "Error parsing tc-bw: example usage: tc-bw 0:60 1:10 2:0 3:0 4:30 5:0 6:0 7:0\n");
+ return err;
+ }
+
+ err = parse_tc_bw_arg(tc_bw_str, &index, &bw);
+ if (err)
+ return err;
+
+ if (index < 0 || index >= DEVLINK_RATE_TCS_MAX) {
+ fprintf(stderr,
+ "Error parsing tc-bw: invalid index: %d, use values between 0 and %d\n",
+ index, DEVLINK_RATE_TC_INDEX_MAX);
+ return -EINVAL;
+ }
+
+ if (parsed_indices[index]) {
+ fprintf(stderr,
+ "Error parsing tc-bw: duplicate index : %d\n",
+ index);
+ return -EINVAL;
+ }
+
+ tc_bw[index] = bw;
+ parsed_indices[index] = true;
+ }
+
+ return 0;
+}
+
static int dl_argv_parse(struct dl *dl, uint64_t o_required,
uint64_t o_optional)
{
@@ -2237,6 +2317,13 @@
dl_arg_inc(dl);
opts->rate_parent_node = "";
o_found |= DL_OPT_PORT_FN_RATE_PARENT;
+ } else if (dl_argv_match(dl, "tc-bw") &&
+ (o_all & DL_OPT_PORT_FN_RATE_TC_BWS)) {
+ dl_arg_inc(dl);
+ err = parse_tc_bw_args(dl, opts->rate_tc_bw);
+ if (err)
+ return err;
+ o_found |= DL_OPT_PORT_FN_RATE_TC_BWS;
} else if (dl_argv_match(dl, "lc") &&
(o_all & DL_OPT_LINECARD)) {
dl_arg_inc(dl);
@@ -2678,6 +2765,20 @@
if (opts->present & DL_OPT_PORT_FN_RATE_PARENT)
mnl_attr_put_strz(nlh, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,
opts->rate_parent_node);
+ if (opts->present & DL_OPT_PORT_FN_RATE_TC_BWS) {
+ struct nlattr *nla_tc_bw_entry;
+ int i;
+
+ for (i = 0; i < DEVLINK_RATE_TCS_MAX; i++) {
+ nla_tc_bw_entry =
+ mnl_attr_nest_start(nlh,
+ DEVLINK_ATTR_RATE_TC_BWS);
+ mnl_attr_put_u8(nlh, DEVLINK_RATE_TC_ATTR_INDEX, i);
+ mnl_attr_put_u32(nlh, DEVLINK_RATE_TC_ATTR_BW,
+ opts->rate_tc_bw[i]);
+ mnl_attr_nest_end(nlh, nla_tc_bw_entry);
+ }
+ }
if (opts->present & DL_OPT_LINECARD)
mnl_attr_put_u32(nlh, DEVLINK_ATTR_LINECARD_INDEX,
opts->linecard_index);
@@ -5366,7 +5467,78 @@
}
}
-static void pr_out_port_fn_rate(struct dl *dl, struct nlattr **tb)
+static const enum mnl_attr_data_type
+rate_tc_bws_policy[DEVLINK_RATE_TC_ATTR_BW + 1] = {
+ [DEVLINK_RATE_TC_ATTR_INDEX] = MNL_TYPE_U8,
+ [DEVLINK_RATE_TC_ATTR_BW] = MNL_TYPE_U32,
+};
+
+static int rate_tc_bw_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type;
+
+ if (mnl_attr_type_valid(attr, DEVLINK_RATE_TC_ATTR_MAX) < 0)
+ return MNL_CB_OK;
+
+ type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_validate(attr, rate_tc_bws_policy[type]) < 0)
+ return MNL_CB_ERROR;
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+parse_rate_tc_bw(struct nlattr *nla_tc_bw, uint8_t *tc_index, uint32_t *tc_bw)
+{
+ struct nlattr *tb_tc_bw[DEVLINK_RATE_TC_ATTR_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(nla_tc_bw, rate_tc_bw_attr_cb, tb_tc_bw) != MNL_CB_OK)
+ return MNL_CB_ERROR;
+
+ if (!tb_tc_bw[DEVLINK_RATE_TC_ATTR_INDEX] ||
+ !tb_tc_bw[DEVLINK_RATE_TC_ATTR_BW])
+ return MNL_CB_ERROR;
+
+ *tc_index = mnl_attr_get_u8(tb_tc_bw[DEVLINK_RATE_TC_ATTR_INDEX]);
+ *tc_bw = mnl_attr_get_u32(tb_tc_bw[DEVLINK_RATE_TC_ATTR_BW]);
+
+ return MNL_CB_OK;
+}
+
+static void pr_out_port_fn_rate_tc_bw(struct dl *dl, const struct nlmsghdr *nlh)
+{
+ struct nlattr *nla_tc_bw;
+
+ mnl_attr_for_each(nla_tc_bw, nlh, sizeof(struct genlmsghdr)) {
+ uint8_t tc_index;
+ uint32_t tc_bw;
+
+ if (mnl_attr_get_type(nla_tc_bw) != DEVLINK_ATTR_RATE_TC_BWS)
+ continue;
+
+ if (parse_rate_tc_bw(nla_tc_bw, &tc_index, &tc_bw) != MNL_CB_OK)
+ continue;
+
+ if (tc_bw) {
+ char buf[32];
+
+ if (dl->json_output) {
+ snprintf(buf, sizeof(buf), "tc_%u", tc_index);
+ print_uint(PRINT_JSON, buf, "%u", tc_bw);
+ } else {
+ snprintf(buf, sizeof(buf), " tc_%u bw %u",
+ tc_index, tc_bw);
+ print_string(PRINT_ANY, NULL, "%s", buf);
+ }
+ }
+ }
+}
+
+static void pr_out_port_fn_rate(struct dl *dl, const struct nlmsghdr *nlh,
+ struct nlattr **tb)
{
if (!tb[DEVLINK_ATTR_RATE_NODE_NAME])
@@ -5412,6 +5584,7 @@
print_uint(PRINT_ANY, "tx_weight",
" tx_weight %u", weight);
}
+
if (tb[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]) {
const char *parent =
mnl_attr_get_str(tb[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]);
@@ -5419,6 +5592,9 @@
print_string(PRINT_ANY, "parent", " parent %s", parent);
}
+ if (tb[DEVLINK_ATTR_RATE_TC_BWS])
+ pr_out_port_fn_rate_tc_bw(dl, nlh);
+
pr_out_port_handle_end(dl);
}
@@ -5434,7 +5610,7 @@
!tb[DEVLINK_ATTR_RATE_NODE_NAME]) {
return MNL_CB_ERROR;
}
- pr_out_port_fn_rate(dl, tb);
+ pr_out_port_fn_rate(dl, nlh, tb);
return MNL_CB_OK;
}
@@ -5443,12 +5619,13 @@
pr_err("Usage: devlink port function rate help\n");
pr_err(" devlink port function rate show [ DEV/{ PORT_INDEX | NODE_NAME } ]\n");
pr_err(" devlink port function rate add DEV/NODE_NAME\n");
- pr_err(" [ tx_share VAL ][ tx_max VAL ][ tx_priority N ][ tx_weight N ][ { parent NODE_NAME | noparent } ]\n");
+ pr_err(" [ tx_share VAL ][ tx_max VAL ][ tx_priority N ][ tx_weight N ][ tc-bw INDEX:N ... INDEX:N ][ { parent NODE_NAME | noparent } ]\n");
pr_err(" devlink port function rate del DEV/NODE_NAME\n");
pr_err(" devlink port function rate set DEV/{ PORT_INDEX | NODE_NAME }\n");
- pr_err(" [ tx_share VAL ][ tx_max VAL ][ tx_priority N ][ tx_weight N ][ { parent NODE_NAME | noparent } ]\n\n");
+ pr_err(" [ tx_share VAL ][ tx_max VAL ][ tx_priority N ][ tx_weight N ][ tc-bw INDEX:N ... INDEX:N ][ { parent NODE_NAME | noparent } ]\n\n");
pr_err(" VAL - float or integer value in units of bits or bytes per second (bit|bps)\n");
pr_err(" N - integer representing priority/weight of the node among siblings\n");
+ pr_err(" INDEX - integer representing traffic class index in the tc-bw option, ranging from 0 to 7\n");
pr_err(" and SI (k-, m-, g-, t-) or IEC (ki-, mi-, gi-, ti-) case-insensitive prefix.\n");
pr_err(" Bare number, means bits per second, is possible.\n\n");
pr_err(" For details refer to devlink-rate(8) man page.\n");
@@ -5503,7 +5680,8 @@
DL_OPT_PORT_FN_RATE_TX_SHARE | DL_OPT_PORT_FN_RATE_TX_MAX |
DL_OPT_PORT_FN_RATE_TX_PRIORITY |
DL_OPT_PORT_FN_RATE_TX_WEIGHT |
- DL_OPT_PORT_FN_RATE_PARENT);
+ DL_OPT_PORT_FN_RATE_PARENT |
+ DL_OPT_PORT_FN_RATE_TC_BWS);
if (err)
return err;
@@ -5538,6 +5716,25 @@
return mnlu_gen_socket_sndrcv(&dl->nlg, nlh, NULL, NULL);
}
+static void parse_tc_bw_entries(const struct nlmsghdr *nlh,
+ struct dl_opts *opts)
+{
+ struct nlattr *nla_tc_bw;
+
+ mnl_attr_for_each(nla_tc_bw, nlh, sizeof(struct genlmsghdr)) {
+ uint8_t tc_index;
+ uint32_t tc_bw;
+
+ if (mnl_attr_get_type(nla_tc_bw) != DEVLINK_ATTR_RATE_TC_BWS)
+ continue;
+
+ if (parse_rate_tc_bw(nla_tc_bw, &tc_index, &tc_bw) != MNL_CB_OK)
+ continue;
+
+ opts->rate_tc_bw[tc_index] = tc_bw;
+ }
+}
+
static int port_fn_get_rates_cb(const struct nlmsghdr *nlh, void *data)
{
struct dl_opts *opts = data;
@@ -5563,6 +5760,10 @@
if (tb[DEVLINK_ATTR_RATE_TX_WEIGHT])
opts->rate_tx_weight =
mnl_attr_get_u32(tb[DEVLINK_ATTR_RATE_TX_WEIGHT]);
+
+ if (tb[DEVLINK_ATTR_RATE_TC_BWS])
+ parse_tc_bw_entries(nlh, opts);
+
return MNL_CB_OK;
}
@@ -5578,7 +5779,8 @@
DL_OPT_PORT_FN_RATE_TX_MAX |
DL_OPT_PORT_FN_RATE_TX_PRIORITY |
DL_OPT_PORT_FN_RATE_TX_WEIGHT |
- DL_OPT_PORT_FN_RATE_PARENT);
+ DL_OPT_PORT_FN_RATE_PARENT |
+ DL_OPT_PORT_FN_RATE_TC_BWS);
if (err)
return err;
diff --git a/include/bridge.h b/include/bridge.h
new file mode 100644
index 0000000..8b0942b
--- /dev/null
+++ b/include/bridge.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BRIDGE_H__
+#define __BRIDGE_H__ 1
+
+#include <linux/if_bridge.h>
+#include <linux/rtnetlink.h>
+
+void bridge_print_vlan_flags(__u16 flags);
+void bridge_print_vlan_stats_only(const struct bridge_vlan_xstats *vstats);
+void bridge_print_vlan_stats(const struct bridge_vlan_xstats *vstats);
+
+void bridge_print_mcast_querier_state(const struct rtattr *vtb);
+
+#endif /* __BRIDGE_H__ */
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 9a1bdc9..a89df2a 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -221,6 +221,11 @@
*/
};
+/* IEEE 802.1Qaz standard supported values. */
+
+#define DEVLINK_RATE_TCS_MAX 8
+#define DEVLINK_RATE_TC_INDEX_MAX (DEVLINK_RATE_TCS_MAX - 1)
+
enum devlink_rate_type {
DEVLINK_RATE_TYPE_LEAF,
DEVLINK_RATE_TYPE_NODE,
@@ -629,6 +634,8 @@
DEVLINK_ATTR_REGION_DIRECT, /* flag */
+ DEVLINK_ATTR_RATE_TC_BWS, /* nested */
+
/* Add new attributes above here, update the spec in
* Documentation/netlink/specs/devlink.yaml and re-generate
* net/devlink/netlink_gen.c.
@@ -638,6 +645,15 @@
DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1
};
+enum devlink_rate_tc_attr {
+ DEVLINK_RATE_TC_ATTR_UNSPEC,
+ DEVLINK_RATE_TC_ATTR_INDEX, /* u8 */
+ DEVLINK_RATE_TC_ATTR_BW, /* u32 */
+
+ __DEVLINK_RATE_TC_ATTR_MAX,
+ DEVLINK_RATE_TC_ATTR_MAX = __DEVLINK_RATE_TC_ATTR_MAX - 1
+};
+
/* Mapping between internal resource described by the field and system
* structure
*/
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index bb94d88..b450757 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -1396,6 +1396,7 @@
IFLA_VXLAN_LOCALBYPASS,
IFLA_VXLAN_LABEL_POLICY, /* IPv6 flow label policy; ifla_vxlan_label_policy */
IFLA_VXLAN_RESERVED_BITS,
+ IFLA_VXLAN_MC_ROUTE,
__IFLA_VXLAN_MAX
};
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
@@ -1532,6 +1533,7 @@
IFLA_BOND_MISSED_MAX,
IFLA_BOND_NS_IP6_TARGET,
IFLA_BOND_COUPLED_CONTROL,
+ IFLA_BOND_BROADCAST_NEIGH,
__IFLA_BOND_MAX,
};
diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h
index 8a6d5d4..fa0d4dc 100644
--- a/include/uapi/linux/if_tun.h
+++ b/include/uapi/linux/if_tun.h
@@ -93,6 +93,15 @@
#define TUN_F_USO4 0x20 /* I can handle USO for IPv4 packets */
#define TUN_F_USO6 0x40 /* I can handle USO for IPv6 packets */
+/* I can handle TSO/USO for UDP tunneled packets */
+#define TUN_F_UDP_TUNNEL_GSO 0x080
+
+/*
+ * I can handle TSO/USO for UDP tunneled packets requiring csum offload for
+ * the outer header
+ */
+#define TUN_F_UDP_TUNNEL_GSO_CSUM 0x100
+
/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */
#define TUN_PKT_STRIP 0x0001
struct tun_pi {
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 776fb41..6713015 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -152,7 +152,6 @@
/*
* IPV6 socket options
*/
-#if __UAPI_DEF_IPV6_OPTIONS
#define IPV6_ADDRFORM 1
#define IPV6_2292PKTINFO 2
#define IPV6_2292HOPOPTS 3
@@ -169,8 +168,10 @@
#define IPV6_MULTICAST_IF 17
#define IPV6_MULTICAST_HOPS 18
#define IPV6_MULTICAST_LOOP 19
+#if __UAPI_DEF_IPV6_OPTIONS
#define IPV6_ADD_MEMBERSHIP 20
#define IPV6_DROP_MEMBERSHIP 21
+#endif
#define IPV6_ROUTER_ALERT 22
#define IPV6_MTU_DISCOVER 23
#define IPV6_MTU 24
@@ -203,7 +204,6 @@
#define IPV6_IPSEC_POLICY 34
#define IPV6_XFRM_POLICY 35
#define IPV6_HDRINCL 36
-#endif
/*
* Multicast:
diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h
index 5e67a7e..1401f57 100644
--- a/include/uapi/linux/neighbour.h
+++ b/include/uapi/linux/neighbour.h
@@ -54,6 +54,7 @@
/* Extended flags under NDA_FLAGS_EXT: */
#define NTF_EXT_MANAGED (1 << 0)
#define NTF_EXT_LOCKED (1 << 1)
+#define NTF_EXT_EXT_VALIDATED (1 << 2)
/*
* Neighbor Cache Entry States.
@@ -92,6 +93,10 @@
* bridge in response to a host trying to communicate via a locked bridge port
* with MAB enabled. Their purpose is to notify user space that a host requires
* authentication.
+ *
+ * NTF_EXT_EXT_VALIDATED flagged neighbor entries were externally validated by
+ * a user space control plane. The kernel will not remove or invalidate them,
+ * but it can probe them and notify user space when they become reachable.
*/
struct nda_cacheinfo {
diff --git a/include/uapi/linux/netconf.h b/include/uapi/linux/netconf.h
index 229e885..546014b 100644
--- a/include/uapi/linux/netconf.h
+++ b/include/uapi/linux/netconf.h
@@ -19,6 +19,7 @@
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
NETCONFA_INPUT,
NETCONFA_BC_FORWARDING,
+ NETCONFA_FORCE_FORWARDING,
__NETCONFA_MAX
};
#define NETCONFA_MAX (__NETCONFA_MAX - 1)
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 958d940..15d1a37 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -1211,4 +1211,72 @@
#define TCA_ETS_MAX (__TCA_ETS_MAX - 1)
+/* DUALPI2 */
+enum tc_dualpi2_drop_overload {
+ TC_DUALPI2_DROP_OVERLOAD_OVERFLOW = 0,
+ TC_DUALPI2_DROP_OVERLOAD_DROP = 1,
+ __TCA_DUALPI2_DROP_OVERLOAD_MAX,
+};
+#define TCA_DUALPI2_DROP_OVERLOAD_MAX (__TCA_DUALPI2_DROP_OVERLOAD_MAX - 1)
+
+enum tc_dualpi2_drop_early {
+ TC_DUALPI2_DROP_EARLY_DROP_DEQUEUE = 0,
+ TC_DUALPI2_DROP_EARLY_DROP_ENQUEUE = 1,
+ __TCA_DUALPI2_DROP_EARLY_MAX,
+};
+#define TCA_DUALPI2_DROP_EARLY_MAX (__TCA_DUALPI2_DROP_EARLY_MAX - 1)
+
+enum tc_dualpi2_ecn_mask {
+ TC_DUALPI2_ECN_MASK_L4S_ECT = 1,
+ TC_DUALPI2_ECN_MASK_CLA_ECT = 2,
+ TC_DUALPI2_ECN_MASK_ANY_ECT = 3,
+ __TCA_DUALPI2_ECN_MASK_MAX,
+};
+#define TCA_DUALPI2_ECN_MASK_MAX (__TCA_DUALPI2_ECN_MASK_MAX - 1)
+
+enum tc_dualpi2_split_gso {
+ TC_DUALPI2_SPLIT_GSO_NO_SPLIT_GSO = 0,
+ TC_DUALPI2_SPLIT_GSO_SPLIT_GSO = 1,
+ __TCA_DUALPI2_SPLIT_GSO_MAX,
+};
+#define TCA_DUALPI2_SPLIT_GSO_MAX (__TCA_DUALPI2_SPLIT_GSO_MAX - 1)
+
+enum {
+ TCA_DUALPI2_UNSPEC,
+ TCA_DUALPI2_LIMIT, /* Packets */
+ TCA_DUALPI2_MEMORY_LIMIT, /* Bytes */
+ TCA_DUALPI2_TARGET, /* us */
+ TCA_DUALPI2_TUPDATE, /* us */
+ TCA_DUALPI2_ALPHA, /* Hz scaled up by 256 */
+ TCA_DUALPI2_BETA, /* Hz scaled up by 256 */
+ TCA_DUALPI2_STEP_THRESH_PKTS, /* Step threshold in packets */
+ TCA_DUALPI2_STEP_THRESH_US, /* Step threshold in microseconds */
+ TCA_DUALPI2_MIN_QLEN_STEP, /* Minimum qlen to apply STEP_THRESH */
+ TCA_DUALPI2_COUPLING, /* Coupling factor between queues */
+ TCA_DUALPI2_DROP_OVERLOAD, /* Whether to drop on overload */
+ TCA_DUALPI2_DROP_EARLY, /* Whether to drop on enqueue */
+ TCA_DUALPI2_C_PROTECTION, /* Percentage */
+ TCA_DUALPI2_ECN_MASK, /* L4S queue classification mask */
+ TCA_DUALPI2_SPLIT_GSO, /* Split GSO packets at enqueue */
+ TCA_DUALPI2_PAD,
+ __TCA_DUALPI2_MAX
+};
+
+#define TCA_DUALPI2_MAX (__TCA_DUALPI2_MAX - 1)
+
+struct tc_dualpi2_xstats {
+ __u32 prob; /* current probability */
+ __u32 delay_c; /* current delay in C queue */
+ __u32 delay_l; /* current delay in L queue */
+ __u32 packets_in_c; /* number of packets enqueued in C queue */
+ __u32 packets_in_l; /* number of packets enqueued in L queue */
+ __u32 maxq; /* maximum queue size */
+ __u32 ecn_mark; /* packets marked with ecn*/
+ __u32 step_marks; /* ECN marks due to the step AQM */
+ __s32 credit; /* current c_protection credit */
+ __u32 memory_used; /* Memory used by both queues */
+ __u32 max_memory_used; /* Maximum used memory */
+ __u32 memory_limit; /* Memory limit of both queues */
+};
+
#endif
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index 1d234d7..49f5640 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -186,6 +186,7 @@
LINUX_MIB_TIMEWAITKILLED, /* TimeWaitKilled */
LINUX_MIB_PAWSACTIVEREJECTED, /* PAWSActiveRejected */
LINUX_MIB_PAWSESTABREJECTED, /* PAWSEstabRejected */
+ LINUX_MIB_BEYOND_WINDOW, /* BeyondWindow */
LINUX_MIB_TSECRREJECTED, /* TSEcrRejected */
LINUX_MIB_PAWS_OLD_ACK, /* PAWSOldAck */
LINUX_MIB_PAWS_TW_REJECTED, /* PAWSTimewait */
diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
index c0a434a..05748dd 100644
--- a/include/uapi/linux/virtio_net.h
+++ b/include/uapi/linux/virtio_net.h
@@ -70,6 +70,28 @@
* with the same MAC.
*/
#define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO 65 /* Driver can receive
+ * GSO-over-UDP-tunnel packets
+ */
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM 66 /* Driver handles
+ * GSO-over-UDP-tunnel
+ * packets with partial csum
+ * for the outer header
+ */
+#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO 67 /* Device can receive
+ * GSO-over-UDP-tunnel packets
+ */
+#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM 68 /* Device handles
+ * GSO-over-UDP-tunnel
+ * packets with partial csum
+ * for the outer header
+ */
+
+/* Offloads bits corresponding to VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO{,_CSUM}
+ * features
+ */
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED 46
+#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED 47
#ifndef VIRTIO_NET_NO_LEGACY
#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */
@@ -131,12 +153,17 @@
#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */
#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */
#define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc info in csum_ fields */
+#define VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM 8 /* UDP tunnel csum offload */
__u8 flags;
#define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */
#define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */
#define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */
#define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */
#define VIRTIO_NET_HDR_GSO_UDP_L4 5 /* GSO frame, IPv4& IPv6 UDP (USO) */
+#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 0x20 /* UDPv4 tunnel present */
+#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 0x40 /* UDPv6 tunnel present */
+#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL (VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 | \
+ VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6)
#define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */
__u8 gso_type;
__virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */
@@ -181,6 +208,12 @@
__le16 padding;
};
+struct virtio_net_hdr_v1_hash_tunnel {
+ struct virtio_net_hdr_v1_hash hash_hdr;
+ __le16 outer_th_offset;
+ __le16 inner_nh_offset;
+};
+
#ifndef VIRTIO_NET_NO_LEGACY
/* This header comes first in the scatter-gather list.
* For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 37de09d..3f55ea3 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -195,7 +195,6 @@
const struct ipstats_stat_desc desc;
int xstats_at;
int link_type_at;
- int inner_max;
int inner_at;
void (*show_cb)(const struct rtattr *at);
};
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 70b3d51..bbe48a4 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -959,12 +959,25 @@
}
}
+static int get_rtattr(struct nlmsghdr *n, struct rtattr **tb)
+{
+ int len = n->nlmsg_len;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0)
+ return -1;
+
+ parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, NLA_F_NESTED);
+
+ return 0;
+}
+
int print_linkinfo(struct nlmsghdr *n, void *arg)
{
FILE *fp = (FILE *)arg;
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct rtattr *tb[IFLA_MAX+1];
- int len = n->nlmsg_len;
const char *name;
unsigned int m_flag = 0;
SPRINT_BUF(b1);
@@ -973,8 +986,7 @@
if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
return 0;
- len -= NLMSG_LENGTH(sizeof(*ifi));
- if (len < 0)
+ if (get_rtattr(n, tb) < 0)
return -1;
if (filter.ifindex && ifi->ifi_index != filter.ifindex)
@@ -984,8 +996,6 @@
if (filter.down && ifi->ifi_flags&IFF_UP)
return -1;
- parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, NLA_F_NESTED);
-
name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]);
if (!name)
return -1;
@@ -1182,6 +1192,11 @@
"max_mtu", "maxmtu %u ",
rta_getattr_u32(tb[IFLA_MAX_MTU]));
+ if (tb[IFLA_NETNS_IMMUTABLE] &&
+ rta_getattr_u8(tb[IFLA_NETNS_IMMUTABLE]))
+ print_bool(PRINT_ANY, "netns-immutable", "netns-immutable ",
+ true);
+
if (tb[IFLA_LINKINFO])
print_linktype(fp, tb[IFLA_LINKINFO]);
@@ -2116,6 +2131,28 @@
return 0;
}
+static void group_filter(struct nlmsg_chain *linfo)
+{
+ struct nlmsg_list *l, **lp;
+
+ lp = &linfo->head;
+ while ((l = *lp) != NULL) {
+ struct nlmsghdr *n = &l->h;
+ struct rtattr *tb[IFLA_MAX+1];
+
+ if (get_rtattr(n, tb) < 0)
+ return;
+
+ if (tb[IFLA_GROUP]) {
+ if (rta_getattr_u32(tb[IFLA_GROUP]) != filter.group) {
+ *lp = l->next;
+ free(l);
+ } else
+ lp = &l->next;
+ }
+ }
+}
+
static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
{
struct nlmsg_chain linfo = { NULL, NULL};
@@ -2294,6 +2331,9 @@
ipaddr_filter(&linfo, ainfo);
}
+ if (filter.group != -1)
+ group_filter(&linfo);
+
for (l = linfo.head; l; l = l->next) {
struct nlmsghdr *n = &l->h;
struct ifinfomsg *ifi = NLMSG_DATA(n);
diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c
index 62dd907..d6960f6 100644
--- a/ip/iplink_bond.c
+++ b/ip/iplink_bond.c
@@ -940,7 +940,6 @@
.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("802.3ad"),
.xstats_at = IFLA_STATS_LINK_XSTATS,
.link_type_at = LINK_XSTATS_TYPE_BOND,
- .inner_max = BOND_XSTATS_MAX,
.inner_at = BOND_XSTATS_3AD,
.show_cb = &bond_print_3ad_stats,
};
@@ -962,7 +961,6 @@
.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("802.3ad"),
.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
.link_type_at = LINK_XSTATS_TYPE_BOND,
- .inner_max = BOND_XSTATS_MAX,
.inner_at = BOND_XSTATS_3AD,
.show_cb = &bond_print_3ad_stats,
};
diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c
index 1fe8955..76e6908 100644
--- a/ip/iplink_bridge.c
+++ b/ip/iplink_bridge.c
@@ -14,6 +14,7 @@
#include <linux/if_bridge.h>
#include <net/if.h>
+#include "bridge.h"
#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
@@ -62,6 +63,7 @@
" [ nf_call_iptables NF_CALL_IPTABLES ]\n"
" [ nf_call_ip6tables NF_CALL_IP6TABLES ]\n"
" [ nf_call_arptables NF_CALL_ARPTABLES ]\n"
+ " [ mdb_offload_fail_notification MDB_OFFLOAD_FAIL_NOTIFICATION ]\n"
"\n"
"Where: VLAN_PROTOCOL := { 802.1Q | 802.1ad }\n"
);
@@ -413,6 +415,18 @@
addattr8(n, 1024, IFLA_BR_NF_CALL_ARPTABLES,
nf_call_arpt);
+ } else if (strcmp(*argv, "mdb_offload_fail_notification") == 0) {
+ __u32 mofn_bit = 1 << BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION;
+ __u8 mofn;
+
+ NEXT_ARG();
+ if (get_u8(&mofn, *argv, 0))
+ invarg("invalid mdb_offload_fail_notification", *argv);
+ bm.optmask |= 1 << BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION;
+ if (mofn)
+ bm.optval |= mofn_bit;
+ else
+ bm.optval &= ~mofn_bit;
} else if (matches(*argv, "help") == 0) {
explain();
return -1;
@@ -620,6 +634,7 @@
rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING]));
if (tb[IFLA_BR_MULTI_BOOLOPT]) {
+ __u32 mofn_bit = 1 << BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION;
__u32 mcvl_bit = 1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING;
__u32 no_ll_learn_bit = 1 << BR_BOOLOPT_NO_LL_LEARN;
__u32 mst_bit = 1 << BR_BOOLOPT_MST_ENABLE;
@@ -641,6 +656,11 @@
"mst_enabled",
"mst_enabled %u ",
!!(bm->optval & mst_bit));
+ if (bm->optmask & mofn_bit)
+ print_uint(PRINT_ANY,
+ "mdb_offload_fail_notification",
+ "mdb_offload_fail_notification %u ",
+ !!(bm->optval & mofn_bit));
}
if (tb[IFLA_BR_MCAST_ROUTER])
@@ -662,62 +682,9 @@
rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER]));
if (tb[IFLA_BR_MCAST_QUERIER_STATE]) {
- struct rtattr *bqtb[BRIDGE_QUERIER_MAX + 1];
- SPRINT_BUF(other_time);
+ struct rtattr *vtb = tb[IFLA_BR_MCAST_QUERIER_STATE];
- parse_rtattr_nested(bqtb, BRIDGE_QUERIER_MAX, tb[IFLA_BR_MCAST_QUERIER_STATE]);
- memset(other_time, 0, sizeof(other_time));
-
- open_json_object("mcast_querier_state_ipv4");
- if (bqtb[BRIDGE_QUERIER_IP_ADDRESS]) {
- print_string(PRINT_FP,
- NULL,
- "%s ",
- "mcast_querier_ipv4_addr");
- print_color_string(PRINT_ANY,
- COLOR_INET,
- "mcast_querier_ipv4_addr",
- "%s ",
- format_host_rta(AF_INET, bqtb[BRIDGE_QUERIER_IP_ADDRESS]));
- }
- if (bqtb[BRIDGE_QUERIER_IP_PORT])
- print_uint(PRINT_ANY,
- "mcast_querier_ipv4_port",
- "mcast_querier_ipv4_port %u ",
- rta_getattr_u32(bqtb[BRIDGE_QUERIER_IP_PORT]));
- if (bqtb[BRIDGE_QUERIER_IP_OTHER_TIMER])
- print_string(PRINT_ANY,
- "mcast_querier_ipv4_other_timer",
- "mcast_querier_ipv4_other_timer %s ",
- sprint_time64(
- rta_getattr_u64(bqtb[BRIDGE_QUERIER_IP_OTHER_TIMER]),
- other_time));
- close_json_object();
- open_json_object("mcast_querier_state_ipv6");
- if (bqtb[BRIDGE_QUERIER_IPV6_ADDRESS]) {
- print_string(PRINT_FP,
- NULL,
- "%s ",
- "mcast_querier_ipv6_addr");
- print_color_string(PRINT_ANY,
- COLOR_INET6,
- "mcast_querier_ipv6_addr",
- "%s ",
- format_host_rta(AF_INET6, bqtb[BRIDGE_QUERIER_IPV6_ADDRESS]));
- }
- if (bqtb[BRIDGE_QUERIER_IPV6_PORT])
- print_uint(PRINT_ANY,
- "mcast_querier_ipv6_port",
- "mcast_querier_ipv6_port %u ",
- rta_getattr_u32(bqtb[BRIDGE_QUERIER_IPV6_PORT]));
- if (bqtb[BRIDGE_QUERIER_IPV6_OTHER_TIMER])
- print_string(PRINT_ANY,
- "mcast_querier_ipv6_other_timer",
- "mcast_querier_ipv6_other_timer %s ",
- sprint_time64(
- rta_getattr_u64(bqtb[BRIDGE_QUERIER_IPV6_OTHER_TIMER]),
- other_time));
- close_json_object();
+ bridge_print_mcast_querier_state(vtb);
}
if (tb[IFLA_BR_MCAST_HASH_ELASTICITY])
@@ -959,6 +926,26 @@
close_json_object();
}
+static void bridge_print_stats_vlan(const struct rtattr *attr)
+{
+ const struct bridge_vlan_xstats *vstats = RTA_DATA(attr);
+
+ print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
+ bridge_print_vlan_stats(vstats);
+}
+
+static int bridge_stat_desc_show_xstats(struct ipstats_stat_show_attrs *attrs,
+ const struct ipstats_stat_desc *desc)
+{
+ int ret;
+
+ open_json_array(PRINT_JSON, "vlans");
+ ret = ipstats_stat_desc_show_xstats(attrs, desc);
+ close_json_array(PRINT_JSON, "vlans");
+
+ return ret;
+}
+
static void bridge_print_stats_attr(struct rtattr *attr, int ifindex)
{
struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
@@ -1056,7 +1043,6 @@
.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("stp"),
.xstats_at = IFLA_STATS_LINK_XSTATS,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
- .inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_STP,
.show_cb = &bridge_print_stats_stp,
};
@@ -1066,15 +1052,31 @@
.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("mcast"),
.xstats_at = IFLA_STATS_LINK_XSTATS,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
- .inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_MCAST,
.show_cb = &bridge_print_stats_mcast,
};
+#define IPSTATS_STAT_DESC_BRIDGE_VLAN { \
+ .name = "vlan", \
+ .kind = IPSTATS_STAT_DESC_KIND_LEAF, \
+ .show = &bridge_stat_desc_show_xstats, \
+ .pack = &ipstats_stat_desc_pack_xstats, \
+ }
+
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_bridge_vlan = {
+ .desc = IPSTATS_STAT_DESC_BRIDGE_VLAN,
+ .xstats_at = IFLA_STATS_LINK_XSTATS,
+ .link_type_at = LINK_XSTATS_TYPE_BRIDGE,
+ .inner_at = BRIDGE_XSTATS_VLAN,
+ .show_cb = &bridge_print_stats_vlan,
+};
+
static const struct ipstats_stat_desc *
ipstats_stat_desc_xstats_bridge_subs[] = {
&ipstats_stat_desc_xstats_bridge_stp.desc,
&ipstats_stat_desc_xstats_bridge_mcast.desc,
+ &ipstats_stat_desc_xstats_bridge_vlan.desc,
};
const struct ipstats_stat_desc ipstats_stat_desc_xstats_bridge_group = {
@@ -1089,7 +1091,6 @@
.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("stp"),
.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
- .inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_STP,
.show_cb = &bridge_print_stats_stp,
};
@@ -1099,15 +1100,24 @@
.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("mcast"),
.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
- .inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_MCAST,
.show_cb = &bridge_print_stats_mcast,
};
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_slave_bridge_vlan = {
+ .desc = IPSTATS_STAT_DESC_BRIDGE_VLAN,
+ .xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
+ .link_type_at = LINK_XSTATS_TYPE_BRIDGE,
+ .inner_at = BRIDGE_XSTATS_VLAN,
+ .show_cb = &bridge_print_stats_vlan,
+};
+
static const struct ipstats_stat_desc *
ipstats_stat_desc_xstats_slave_bridge_subs[] = {
&ipstats_stat_desc_xstats_slave_bridge_stp.desc,
&ipstats_stat_desc_xstats_slave_bridge_mcast.desc,
+ &ipstats_stat_desc_xstats_slave_bridge_vlan.desc,
};
const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bridge_group = {
diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c
index 9649a8e..a6e9539 100644
--- a/ip/iplink_vxlan.c
+++ b/ip/iplink_vxlan.c
@@ -37,6 +37,7 @@
{ "remcsum_tx", IFLA_VXLAN_REMCSUM_TX, false },
{ "remcsum_rx", IFLA_VXLAN_REMCSUM_RX, false },
{ "localbypass", IFLA_VXLAN_LOCALBYPASS, true },
+ { "mcroute", IFLA_VXLAN_MC_ROUTE, false },
};
static void print_explain(FILE *f)
@@ -67,6 +68,7 @@
" [ [no]localbypass ]\n"
" [ [no]external ] [ gbp ] [ gpe ]\n"
" [ [no]vnifilter ]\n"
+ " [ [no]mcroute ]\n"
"\n"
"Where: VNI := 0-16777215\n"
" ADDR := { IP_ADDRESS | any }\n"
@@ -378,6 +380,14 @@
check_duparg(&attrs, IFLA_VXLAN_VNIFILTER,
*argv, *argv);
addattr8(n, 1024, IFLA_VXLAN_VNIFILTER, 0);
+ } else if (!strcmp(*argv, "mcroute")) {
+ check_duparg(&attrs, IFLA_VXLAN_MC_ROUTE,
+ *argv, *argv);
+ addattr8(n, 1024, IFLA_VXLAN_MC_ROUTE, 1);
+ } else if (!strcmp(*argv, "nomcroute")) {
+ check_duparg(&attrs, IFLA_VXLAN_MC_ROUTE,
+ *argv, *argv);
+ addattr8(n, 1024, IFLA_VXLAN_MC_ROUTE, 0);
} else if (matches(*argv, "help") == 0) {
explain();
return -1;
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index bd7f44e..e678545 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -47,7 +47,7 @@
"Usage: ip neigh { add | del | change | replace }\n"
" { ADDR [ lladdr LLADDR ] [ nud STATE ] proxy ADDR }\n"
" [ dev DEV ] [ router ] [ use ] [ managed ] [ extern_learn ]\n"
- " [ protocol PROTO ]\n"
+ " [ extern_valid ] [ protocol PROTO ]\n"
"\n"
" ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"
" [ vrf NAME ] [ nomaster ]\n"
@@ -152,6 +152,8 @@
req.ndm.ndm_state = NUD_NONE;
} else if (matches(*argv, "extern_learn") == 0) {
req.ndm.ndm_flags |= NTF_EXT_LEARNED;
+ } else if (strcmp(*argv, "extern_valid") == 0) {
+ ext_flags |= NTF_EXT_EXT_VALIDATED;
} else if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
dev = *argv;
@@ -446,6 +448,8 @@
print_null(PRINT_ANY, "extern_learn", "%s ", "extern_learn");
if (r->ndm_flags & NTF_OFFLOADED)
print_null(PRINT_ANY, "offload", "%s ", "offload");
+ if (ext_flags & NTF_EXT_EXT_VALIDATED)
+ print_null(PRINT_ANY, "extern_valid", "%s ", "extern_valid");
if (show_stats) {
if (tb[NDA_CACHEINFO])
diff --git a/ip/ipntable.c b/ip/ipntable.c
index 4ce02a3..54db9b6 100644
--- a/ip/ipntable.c
+++ b/ip/ipntable.c
@@ -40,7 +40,8 @@
"PARMS := [ base_reachable MSEC ] [ retrans MSEC ] [ gc_stale MSEC ]\n"
" [ delay_probe MSEC ] [ queue LEN ]\n"
" [ app_probes VAL ] [ ucast_probes VAL ] [ mcast_probes VAL ]\n"
- " [ anycast_delay MSEC ] [ proxy_delay MSEC ] [ proxy_queue LEN ]\n"
+ " [ mcast_reprobes VAL ] [ anycast_delay MSEC ]\n"
+ " [ proxy_delay MSEC ] [ proxy_queue LEN ]\n"
" [ locktime MSEC ]\n"
);
@@ -223,6 +224,17 @@
rta_addattr32(parms_rta, sizeof(parms_buf),
NDTPA_MCAST_PROBES, mprobe);
parms_change = 1;
+ } else if (strcmp(*argv, "mcast_reprobes") == 0) {
+ __u32 mreprobe;
+
+ NEXT_ARG();
+
+ if (get_u32(&mreprobe, *argv, 0))
+ invarg("\"mcast_reprobes\" value is invalid", *argv);
+
+ rta_addattr32(parms_rta, sizeof(parms_buf),
+ NDTPA_MCAST_REPROBES, mreprobe);
+ parms_change = 1;
} else if (strcmp(*argv, "anycast_delay") == 0) {
__u64 anycast_delay;
@@ -440,6 +452,13 @@
"mcast_probes %u ", mprobe);
}
+ if (tpb[NDTPA_MCAST_REPROBES]) {
+ __u32 mreprobe = rta_getattr_u32(tpb[NDTPA_MCAST_REPROBES]);
+
+ print_uint(PRINT_ANY, "mcast_reprobes",
+ "mcast_reprobes %u ", mreprobe);
+ }
+
print_string(PRINT_FP, NULL, "%s ", _SL_);
if (tpb[NDTPA_ANYCAST_DELAY]) {
diff --git a/ip/iproute.c b/ip/iproute.c
index 0e2c171..c253889 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -1134,6 +1134,27 @@
return 0;
}
+static unsigned int parse_features(int *argcp, char ***argvp)
+{
+ unsigned int features = 0;
+ char **argv = *argvp;
+ int argc = *argcp;
+
+ while (++argv, --argc > 0) {
+ if (strcmp(*argv, "ecn") == 0) {
+ features |= RTAX_FEATURE_ECN;
+ } else if (strcmp(*argv, "tcp_usec_ts") == 0) {
+ features |= RTAX_FEATURE_TCP_USEC_TS;
+ } else {
+ break;
+ }
+ }
+
+ *argcp = argc;
+ *argvp = argv;
+ return features;
+}
+
static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
{
struct {
@@ -1374,17 +1395,14 @@
} else if (matches(*argv, "features") == 0) {
unsigned int features = 0;
- while (argc > 0) {
- NEXT_ARG();
+ features = parse_features(&argc, &argv);
+ if (!features)
+ invarg("\"features\" value not valid\n", *argv);
- if (strcmp(*argv, "ecn") == 0)
- features |= RTAX_FEATURE_ECN;
- else if (strcmp(*argv, "tcp_usec_ts") == 0)
- features |= RTAX_FEATURE_TCP_USEC_TS;
- else
- invarg("\"features\" value not valid\n", *argv);
- break;
- }
+ /* parse_features stops at the first feature it can't
+ * parse, rewind one argument back.
+ */
+ PREV_ARG();
rta_addattr32(mxrta, sizeof(mxbuf),
RTAX_FEATURES, features);
diff --git a/ip/ipstats.c b/ip/ipstats.c
index cb9d9cb..f0f8dcd 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
-#include <alloca.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
@@ -590,7 +589,7 @@
{
struct ipstats_stat_desc_xstats *xdesc;
const struct rtattr *at;
- struct rtattr **tb;
+ const struct rtattr *i;
int err;
xdesc = container_of(desc, struct ipstats_stat_desc_xstats, desc);
@@ -600,15 +599,13 @@
if (at == NULL)
return err;
- tb = alloca(sizeof(*tb) * (xdesc->inner_max + 1));
- err = parse_rtattr_nested(tb, xdesc->inner_max, at);
- if (err != 0)
- return err;
-
- if (tb[xdesc->inner_at] != NULL) {
- print_nl();
- xdesc->show_cb(tb[xdesc->inner_at]);
+ rtattr_for_each_nested(i, at) {
+ if (i->rta_type == xdesc->inner_at) {
+ print_nl();
+ xdesc->show_cb(i);
+ }
}
+
return 0;
}
diff --git a/lib/Makefile b/lib/Makefile
index aa7bbd2..0ba6294 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -5,7 +5,8 @@
UTILOBJ = utils.o utils_math.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \
inet_proto.o namespace.o json_writer.o json_print.o json_print_math.o \
- names.o color.o bpf_legacy.o bpf_glue.o exec.o fs.o cg_map.o ppp_proto.o
+ names.o color.o bpf_legacy.o bpf_glue.o exec.o fs.o cg_map.o \
+ ppp_proto.o bridge.o
ifeq ($(HAVE_ELF),y)
ifeq ($(HAVE_LIBBPF),y)
diff --git a/lib/bridge.c b/lib/bridge.c
new file mode 100644
index 0000000..5386aa0
--- /dev/null
+++ b/lib/bridge.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <net/if.h>
+
+#include "bridge.h"
+#include "utils.h"
+
+void bridge_print_vlan_flags(__u16 flags)
+{
+ if (flags == 0)
+ return;
+
+ open_json_array(PRINT_JSON, "flags");
+ if (flags & BRIDGE_VLAN_INFO_PVID)
+ print_string(PRINT_ANY, NULL, " %s", "PVID");
+
+ if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
+ print_string(PRINT_ANY, NULL, " %s", "Egress Untagged");
+ close_json_array(PRINT_JSON, NULL);
+}
+
+void bridge_print_vlan_stats_only(const struct bridge_vlan_xstats *vstats)
+{
+ print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
+ print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
+ vstats->rx_bytes);
+ print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n",
+ vstats->rx_packets);
+
+ print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
+ print_lluint(PRINT_ANY, "tx_bytes", "TX: %llu bytes",
+ vstats->tx_bytes);
+ print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
+ vstats->tx_packets);
+}
+
+void bridge_print_vlan_stats(const struct bridge_vlan_xstats *vstats)
+{
+ open_json_object(NULL);
+
+ print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
+ bridge_print_vlan_flags(vstats->flags);
+ print_nl();
+ bridge_print_vlan_stats_only(vstats);
+
+ close_json_object();
+}
+
+void bridge_print_mcast_querier_state(const struct rtattr *vtb)
+{
+ struct rtattr *bqtb[BRIDGE_QUERIER_MAX + 1];
+ const char *querier_ip;
+ SPRINT_BUF(other_time);
+ __u64 tval;
+
+ parse_rtattr_nested(bqtb, BRIDGE_QUERIER_MAX, vtb);
+ memset(other_time, 0, sizeof(other_time));
+
+ open_json_object("mcast_querier_state_ipv4");
+ if (bqtb[BRIDGE_QUERIER_IP_ADDRESS]) {
+ querier_ip = format_host_rta(AF_INET,
+ bqtb[BRIDGE_QUERIER_IP_ADDRESS]);
+ print_string(PRINT_FP, NULL, "%s ",
+ "mcast_querier_ipv4_addr");
+ print_color_string(PRINT_ANY, COLOR_INET,
+ "mcast_querier_ipv4_addr", "%s ",
+ querier_ip);
+ }
+ if (bqtb[BRIDGE_QUERIER_IP_PORT])
+ print_uint(PRINT_ANY, "mcast_querier_ipv4_port",
+ "mcast_querier_ipv4_port %u ",
+ rta_getattr_u32(bqtb[BRIDGE_QUERIER_IP_PORT]));
+ if (bqtb[BRIDGE_QUERIER_IP_OTHER_TIMER]) {
+ tval = rta_getattr_u64(bqtb[BRIDGE_QUERIER_IP_OTHER_TIMER]);
+ print_string(PRINT_ANY,
+ "mcast_querier_ipv4_other_timer",
+ "mcast_querier_ipv4_other_timer %s ",
+ sprint_time64(tval, other_time));
+ }
+ close_json_object();
+ open_json_object("mcast_querier_state_ipv6");
+ if (bqtb[BRIDGE_QUERIER_IPV6_ADDRESS]) {
+ querier_ip = format_host_rta(AF_INET6,
+ bqtb[BRIDGE_QUERIER_IPV6_ADDRESS]);
+ print_string(PRINT_FP, NULL, "%s ",
+ "mcast_querier_ipv6_addr");
+ print_color_string(PRINT_ANY, COLOR_INET6,
+ "mcast_querier_ipv6_addr", "%s ",
+ querier_ip);
+ }
+ if (bqtb[BRIDGE_QUERIER_IPV6_PORT])
+ print_uint(PRINT_ANY, "mcast_querier_ipv6_port",
+ "mcast_querier_ipv6_port %u ",
+ rta_getattr_u32(bqtb[BRIDGE_QUERIER_IPV6_PORT]));
+ if (bqtb[BRIDGE_QUERIER_IPV6_OTHER_TIMER]) {
+ tval = rta_getattr_u64(bqtb[BRIDGE_QUERIER_IPV6_OTHER_TIMER]);
+ print_string(PRINT_ANY,
+ "mcast_querier_ipv6_other_timer",
+ "mcast_querier_ipv6_other_timer %s ",
+ sprint_time64(tval, other_time));
+ }
+ close_json_object();
+}
diff --git a/lib/color.c b/lib/color.c
index 3c6db08..aa11235 100644
--- a/lib/color.c
+++ b/lib/color.c
@@ -24,7 +24,7 @@
C_BOLD_RED,
C_BOLD_GREEN,
C_BOLD_YELLOW,
- C_BOLD_BLUE,
+ C_BOLD_LIGHT_BLUE,
C_BOLD_MAGENTA,
C_BOLD_CYAN,
C_BOLD_WHITE,
@@ -42,7 +42,7 @@
"\e[1;31m",
"\e[1;32m",
"\e[1;33m",
- "\e[1;34m",
+ "\e[1;94m",
"\e[1;35m",
"\e[1;36m",
"\e[1;37m",
@@ -66,13 +66,17 @@
C_BOLD_CYAN,
C_BOLD_YELLOW,
C_BOLD_MAGENTA,
- C_BOLD_BLUE,
+ C_BOLD_LIGHT_BLUE,
C_BOLD_GREEN,
C_BOLD_RED,
C_CLEAR
};
-static int is_dark_bg;
+/*
+ * Assume dark background until we know otherwise. The dark-background
+ * colours work better on a light background than vice versa.
+ */
+static int is_dark_bg = 1;
static int color_is_enabled;
static void enable_color(void)
@@ -138,12 +142,12 @@
/*
* COLORFGBG environment variable usually contains either two or three
* values separated by semicolons; we want the last value in either case.
- * If this value is 0-6 or 8, background is dark.
+ * If this value is 0-6 or 8, background is dark; otherwise it's light.
*/
if (p && (p = strrchr(p, ';')) != NULL
- && ((p[1] >= '0' && p[1] <= '6') || p[1] == '8')
- && p[2] == '\0')
- is_dark_bg = 1;
+ && !(((p[1] >= '0' && p[1] <= '6') || p[1] == '8')
+ && p[2] == '\0'))
+ is_dark_bg = 0;
}
__attribute__((format(printf, 3, 4)))
diff --git a/man/man8/devlink-rate.8 b/man/man8/devlink-rate.8
index f09ac4a..47e2ebc 100644
--- a/man/man8/devlink-rate.8
+++ b/man/man8/devlink-rate.8
@@ -28,6 +28,7 @@
.RB [ " tx_max \fIVALUE " ]
.RB [ " tx_priority \fIN " ]
.RB [ " tx_weight \fIN " ]
+.RB [ " tc-bw \fIINDEX:N " ]
.RB "[ {" " parent \fINODE_NAME " | " noparent " "} ]"
.ti -8
@@ -36,6 +37,7 @@
.RB [ " tx_max \fIVALUE " ]
.RB [ " tx_priority \fIN " ]
.RB [ " tx_weight \fIN " ]
+.RB [ " tc-bw \fIINDEX:N " ]
.RB "[ {" " parent \fINODE_NAME " | " noparent " "} ]"
.ti -8
@@ -101,6 +103,12 @@
siblings. Values are relative like a percentage points, they basically tell
how much BW should node take relative to it's siblings.
.PP
+.BI tc-bw " INDEX:N"
+- allows the user to assign relative bandwidth shares to specific traffic
+classes using the IEEE 802.1Qaz standard. The values determine how bandwidth
+is distributed between traffic classes in proportion to one another.
+If not specified, the default bandwidth allocation is applied.
+.PP
.TP 8
.I VALUE
These parameter accept a floating point number, possibly followed by either a
@@ -142,6 +150,12 @@
.RE
.PP
.TP 8
+.I INDEX
+These parameters represent the traffic class index in the \fItc-bw\fR option.
+The traffic class is specified as an integer value, ranging from 0 to 7, which
+maps to the defined traffic classes under the IEEE 802.1Qaz standard.
+.PP
+.TP 8
.I N
These parameter accept integer meaning weight or priority of a node.
.PP
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
index d6e05d9..e3297c5 100644
--- a/man/man8/ip-link.8.in
+++ b/man/man8/ip-link.8.in
@@ -663,6 +663,8 @@
.B gpe
] [
.RB [ no ] vnifilter
+] [
+.RB [ no ] mcroute
]
.in +8
@@ -797,6 +799,14 @@
in the vni filtering table.
.sp
+.RB [ no ] mcroute
+- when the VXLAN tunnel has a multicast remote, whether the underlay packets
+should be sent directly to the physical device (the default), or whether they
+should be multicast-routed. In the latter case, for purposes of matching a
+multicast route, (S,G) are, respectively, local and remote address of the
+tunnel, and iif is the tunnel physical device.
+
+.sp
.B gbp
- enables the Group Policy extension (VXLAN-GBP).
@@ -1768,6 +1778,8 @@
.BI nf_call_ip6tables " NF_CALL_IP6TABLES "
] [
.BI nf_call_arptables " NF_CALL_ARPTABLES "
+] [
+.BI mdb_offload_fail_notification " MDB_OFFLOAD_FAIL_NOTIFICATION "
]
.in +8
@@ -1992,6 +2004,13 @@
.RI ( NF_CALL_ARPTABLES " == 0) "
arptables hooks on the bridge.
+.BI mdb_offload_fail_notification " MDB_OFFLOAD_FAIL_NOTIFICATION "
+- turn mdb offload fail notification on
+.RI ( MDB_OFFLOAD_FAIL_NOTIFICATION " > 0) "
+or off
+.RI ( MDB_OFFLOAD_FAIL_NOTIFICATION " == 0). "
+Default is
+.BR 0 .
.in -8
diff --git a/man/man8/ip-neighbour.8 b/man/man8/ip-neighbour.8
index 6fed47c..1f890c0 100644
--- a/man/man8/ip-neighbour.8
+++ b/man/man8/ip-neighbour.8
@@ -27,7 +27,8 @@
.BR router " ] [ "
.BR use " ] [ "
.BR managed " ] [ "
-.BR extern_learn " ]"
+.BR extern_learn " ] [ "
+.BR extern_valid " ]"
.ti -8
.BR "ip neigh" " { " show " | " flush " } [ " proxy " ] [ " to
@@ -116,6 +117,13 @@
Kernel will not gc such an entry.
.TP
+.BI extern_valid
+this neigh entry was learned and determined to be valid externally. The kernel
+will not remove or invalidate the entry, but it can probe the entry and notify
+user space when the entry becomes reachable. The kernel will return the entry
+to stale state if it did not receive a confirmation after probing the entry.
+
+.TP
.BI lladdr " LLADDRESS"
the link layer address of the neighbour.
.I LLADDRESS
diff --git a/man/man8/ip-ntable.8 b/man/man8/ip-ntable.8
index 4f0f2e5..56108af 100644
--- a/man/man8/ip-ntable.8
+++ b/man/man8/ip-ntable.8
@@ -42,6 +42,8 @@
.IR VAL " ] ["
.B mcast_probes
.IR VAL " ] ["
+.B mcast_reprobes
+.IR VAL " ] ["
.B anycast_delay
.IR MSEC " ] ["
.B proxy_delay
diff --git a/man/man8/ip-stats.8 b/man/man8/ip-stats.8
index 2633645..e9ff49d 100644
--- a/man/man8/ip-stats.8
+++ b/man/man8/ip-stats.8
@@ -152,9 +152,15 @@
.in 21
.ti 14
-.B subgroup bridge \fR[\fB suite stp \fR] [\fB suite mcast \fR]
-- Statistics for STP and, respectively, IGMP / MLD (under the keyword
-\fBmcast\fR) traffic on bridges and their slaves.
+.B subgroup bridge\fR - Various statistics on bridges and their slaves.
+
+.ti 21
+.BR "suite stp " "- STP statistics"
+.br
+.BR "suite mcast " "- IGMP / MLD statistics"
+.br
+.BR "suite vlan " "- per-VLAN traffic statistics"
+.br
.ti 14
.B subgroup bond \fR[\fB suite 802.3ad \fR]