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/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/if_link.h b/include/uapi/linux/if_link.h
index bb94d88..cdb5acc 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)
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/mptcp_pm.h b/include/uapi/linux/mptcp_pm.h
index 2f59b1a..6b5ef79 100644
--- a/include/uapi/linux/mptcp_pm.h
+++ b/include/uapi/linux/mptcp_pm.h
@@ -27,14 +27,14 @@
* token, rem_id.
* @MPTCP_EVENT_SUB_ESTABLISHED: A new subflow has been established. 'error'
* should not be set. Attributes: token, family, loc_id, rem_id, saddr4 |
- * saddr6, daddr4 | daddr6, sport, dport, backup, if_idx [, error].
+ * saddr6, daddr4 | daddr6, sport, dport, backup, if-idx [, error].
* @MPTCP_EVENT_SUB_CLOSED: A subflow has been closed. An error (copy of
* sk_err) could be set if an error has been detected for this subflow.
* Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 |
- * daddr6, sport, dport, backup, if_idx [, error].
+ * daddr6, sport, dport, backup, if-idx [, error].
* @MPTCP_EVENT_SUB_PRIORITY: The priority of a subflow has changed. 'error'
* should not be set. Attributes: token, family, loc_id, rem_id, saddr4 |
- * saddr6, daddr4 | daddr6, sport, dport, backup, if_idx [, error].
+ * saddr6, daddr4 | daddr6, sport, dport, backup, if-idx [, error].
* @MPTCP_EVENT_LISTENER_CREATED: A new PM listener is created. Attributes:
* family, sport, saddr4 | saddr6.
* @MPTCP_EVENT_LISTENER_CLOSED: A PM listener is closed. Attributes: family,
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/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/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]