[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250717130509.470850-1-idosch@nvidia.com>
Date: Thu, 17 Jul 2025 16:05:09 +0300
From: Ido Schimmel <idosch@...dia.com>
To: <netdev@...r.kernel.org>
CC: <dsahern@...il.com>, <stephen@...workplumber.org>, <razor@...ckwall.org>,
<petrm@...dia.com>, Ido Schimmel <idosch@...dia.com>
Subject: [PATCH iproute2-next] bridge: fdb: Add support for FDB activity notification control
Add support for FDB activity notification control [1].
Users can use this to enable activity notifications on a new FDB entry
that was learned on an ES (Ethernet Segment) peer and mark it as locally
inactive:
# bridge fdb add 00:11:22:33:44:55 dev bond1 master static activity_notify inactive
$ bridge -d fdb get 00:11:22:33:44:55 br br1
00:11:22:33:44:55 dev bond1 activity_notify inactive master br1 static
$ bridge -d -j -p fdb get 00:11:22:33:44:55 br br1
[ {
"mac": "00:11:22:33:44:55",
"ifname": "bond1",
"activity_notify": true,
"inactive": true,
"flags": [ ],
"master": "br1",
"state": "static"
} ]
User space will receive a notification when the entry becomes active and
the control plane will be able to mark the entry as locally active.
It is also possible to enable activity notifications on an existing
dynamic entry:
$ bridge -d -s -j -p fdb get 00:aa:bb:cc:dd:ee br br1
[ {
"mac": "00:aa:bb:cc:dd:ee",
"ifname": "bond1",
"used": 8,
"updated": 8,
"flags": [ ],
"master": "br1",
"state": ""
} ]
# bridge fdb replace 00:aa:bb:cc:dd:ee dev bond1 master static activity_notify norefresh
$ bridge -d -s -j -p fdb get 00:aa:bb:cc:dd:ee br br1
[ {
"mac": "00:aa:bb:cc:dd:ee",
"ifname": "bond1",
"activity_notify": true,
"used": 3,
"updated": 23,
"flags": [ ],
"master": "br1",
"state": "static"
} ]
The "norefresh" keyword is used to avoid resetting the entry's last
active time (i.e., "updated" time).
User space will receive a notification when the entry becomes inactive
and the control plane will be able to mark the entry as locally
inactive. Note that the entry was converted from a dynamic entry to a
static entry to prevent the kernel from automatically deleting it upon
inactivity.
An existing inactive entry can only be marked as active by the kernel or
by disabling and enabling activity notifications:
$ bridge -d fdb get 00:11:22:33:44:55 br br1
00:11:22:33:44:55 dev bond1 activity_notify inactive master br1 static
# bridge fdb replace 00:11:22:33:44:55 dev bond1 master static activity_notify
$ bridge -d fdb get 00:11:22:33:44:55 br br1
00:11:22:33:44:55 dev bond1 activity_notify inactive master br1 static
# bridge fdb replace 00:11:22:33:44:55 dev bond1 master static
# bridge fdb replace 00:11:22:33:44:55 dev bond1 master static activity_notify
$ bridge -d fdb get 00:11:22:33:44:55 br br1
00:11:22:33:44:55 dev bond1 activity_notify master br1 static
Marking an entry as inactive while activity notifications are disabled
does not make sense and will be rejected by the kernel:
# bridge fdb replace 00:11:22:33:44:55 dev bond1 master static inactive
RTNETLINK answers: Invalid argument
[1] https://lore.kernel.org/netdev/20200623204718.1057508-1-nikolay@cumulusnetworks.com/
Reviewed-by: Petr Machata <petrm@...dia.com>
Signed-off-by: Ido Schimmel <idosch@...dia.com>
---
I have a kernel selftest for this functionality. I will post it after
this patch is accepted.
---
bridge/fdb.c | 69 ++++++++++++++++++++++++++++++++++++++++++++---
man/man8/bridge.8 | 22 ++++++++++++++-
2 files changed, 87 insertions(+), 4 deletions(-)
diff --git a/bridge/fdb.c b/bridge/fdb.c
index 7b4443661e6b..d57b57503198 100644
--- a/bridge/fdb.c
+++ b/bridge/fdb.c
@@ -40,7 +40,8 @@ static void usage(void)
" [ self ] [ master ] [ use ] [ router ] [ extern_learn ]\n"
" [ sticky ] [ local | static | dynamic ] [ vlan VID ]\n"
" { [ dst IPADDR ] [ port PORT] [ vni VNI ] | [ nhid NHID ] }\n"
- " [ via DEV ] [ src_vni VNI ]\n"
+ " [ via DEV ] [ src_vni VNI ] [ activity_notify ]\n"
+ " [ inactive ] [ norefresh ]\n"
" bridge fdb [ show [ br BRDEV ] [ brport DEV ] [ vlan VID ]\n"
" [ state STATE ] [ dynamic ] ]\n"
" bridge fdb get [ to ] LLADDR [ br BRDEV ] { brport | dev } DEV\n"
@@ -142,6 +143,24 @@ static void fdb_print_stats(FILE *fp, const struct nda_cacheinfo *ci)
}
}
+static void fdb_print_ext_attrs(struct rtattr *nfea)
+{
+ struct rtattr *tb[NFEA_MAX + 1];
+
+ parse_rtattr_nested(tb, NFEA_MAX, nfea);
+
+ if (tb[NFEA_ACTIVITY_NOTIFY]) {
+ __u8 notify;
+
+ notify = rta_getattr_u8(tb[NFEA_ACTIVITY_NOTIFY]);
+ if (notify & FDB_NOTIFY_BIT)
+ print_bool(PRINT_ANY, "activity_notify",
+ "activity_notify ", true);
+ if (notify & FDB_NOTIFY_INACTIVE_BIT)
+ print_bool(PRINT_ANY, "inactive", "inactive ", true);
+ }
+}
+
int print_fdb(struct nlmsghdr *n, void *arg)
{
FILE *fp = arg;
@@ -172,8 +191,9 @@ int print_fdb(struct nlmsghdr *n, void *arg)
if (filter_state && !(r->ndm_state & filter_state))
return 0;
- parse_rtattr(tb, NDA_MAX, NDA_RTA(r),
- n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+ parse_rtattr_flags(tb, NDA_MAX, NDA_RTA(r),
+ n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)),
+ NLA_F_NESTED);
if (tb[NDA_FLAGS_EXT])
ext_flags = rta_getattr_u32(tb[NDA_FLAGS_EXT]);
@@ -273,6 +293,9 @@ int print_fdb(struct nlmsghdr *n, void *arg)
"linkNetNsId", "link-netnsid %d ",
rta_getattr_u32(tb[NDA_LINK_NETNSID]));
+ if (show_details && tb[NDA_FDB_EXT_ATTRS])
+ fdb_print_ext_attrs(tb[NDA_FDB_EXT_ATTRS]);
+
if (show_stats && tb[NDA_CACHEINFO])
fdb_print_stats(fp, RTA_DATA(tb[NDA_CACHEINFO]));
@@ -399,6 +422,34 @@ static int fdb_show(int argc, char **argv)
return 0;
}
+static void fdb_add_ext_attrs(struct nlmsghdr *n, int maxlen,
+ bool activity_notify, bool inactive,
+ bool norefresh)
+{
+ struct rtattr *nest;
+
+ if (!activity_notify && !inactive && !norefresh)
+ return;
+
+ nest = addattr_nest(n, maxlen, NDA_FDB_EXT_ATTRS | NLA_F_NESTED);
+
+ if (activity_notify || inactive) {
+ __u8 notify = 0;
+
+ if (activity_notify)
+ notify |= FDB_NOTIFY_BIT;
+ if (inactive)
+ notify |= FDB_NOTIFY_INACTIVE_BIT;
+
+ addattr8(n, maxlen, NFEA_ACTIVITY_NOTIFY, notify);
+ }
+
+ if (norefresh)
+ addattr_l(n, maxlen, NFEA_DONT_REFRESH, NULL, 0);
+
+ addattr_nest_end(n, nest);
+}
+
static int fdb_modify(int cmd, int flags, int argc, char **argv)
{
struct {
@@ -412,6 +463,9 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
.ndm.ndm_family = PF_BRIDGE,
.ndm.ndm_state = NUD_NOARP,
};
+ bool activity_notify = false;
+ bool norefresh = false;
+ bool inactive = false;
char *addr = NULL;
char *d = NULL;
char abuf[ETH_ALEN];
@@ -495,6 +549,12 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
req.ndm.ndm_flags |= NTF_EXT_LEARNED;
} else if (matches(*argv, "sticky") == 0) {
req.ndm.ndm_flags |= NTF_STICKY;
+ } else if (strcmp(*argv, "activity_notify") == 0) {
+ activity_notify = true;
+ } else if (strcmp(*argv, "inactive") == 0) {
+ inactive = true;
+ } else if (strcmp(*argv, "norefresh") == 0) {
+ norefresh = true;
} else {
if (strcmp(*argv, "to") == 0)
NEXT_ARG();
@@ -559,6 +619,9 @@ static int fdb_modify(int cmd, int flags, int argc, char **argv)
if (!req.ndm.ndm_ifindex)
return nodev(d);
+ fdb_add_ext_attrs(&req.n, sizeof(req), activity_notify, inactive,
+ norefresh);
+
if (rtnl_talk(&rth, &req.n, NULL) < 0)
return -1;
diff --git a/man/man8/bridge.8 b/man/man8/bridge.8
index 08f329c6bca6..fe800d3fe290 100644
--- a/man/man8/bridge.8
+++ b/man/man8/bridge.8
@@ -91,7 +91,8 @@ bridge \- show / manipulate bridge addresses and devices
.B via
.IR DEVICE " ] | "
.B nhid
-.IR NHID " } "
+.IR NHID " } [ "
+.BR activity_notify " ] [ " inactive " ] [ " norefresh " ]
.ti -8
.BR "bridge fdb" " [ [ " show " ] [ "
@@ -860,6 +861,25 @@ remote VXLAN tunnel endpoint.
ecmp nexthop group for the VXLAN device driver
to reach remote VXLAN tunnel endpoints.
+.TP
+.B activity_notify
+enable activity notifications on an existing or a new FDB entry. This keyword
+only makes sense for non-dynamic entries as dynamic entries are deleted upon
+inactivity. An entry is assumed to be active unless the \fBinactive\fR keyword
+is specified.
+
+.TP
+.B inactive
+mark an FDB entry as inactive. This keyword only makes sense in conjunction
+with the \fBactivity_notify\fR keyword and usually only when adding a new FDB
+entry as opposed to replacing an existing one.
+
+.TP
+.B norefresh
+avoid resetting an FDB entry's activity (i.e., its last updated time). This can
+be useful, for example, when one wants to enable activity notifications on an
+existing entry without modifying its last updated time.
+
.SS bridge fdb append - append a forwarding database entry
This command adds a new fdb entry with an already known
.IR LLADDR .
--
2.50.0
Powered by blists - more mailing lists