ifconfig: Support VLAN ID in static/deladdr

Add an optional "vlan <n>" argument to the bridge static and deladdr
commands to allow addresses to be added to / removed from a particular
vlan.  No changes to if_bridge are required as the kernel API already
supports this, it just wasn't exposed in ifconfig.

Add tests for the new functionality, and improve the test for the
existing "static" command.

Reviewed by:		kevans
Differential Revision:	https://reviews.freebsd.org/D51243

(cherry picked from commit 3650722abf2922893540361a1369b54abc5ff8d2)
This commit is contained in:
Lexi Winter 2025-07-31 16:56:51 +01:00 committed by Franco Fichtner
parent f74bfeb140
commit 2e20b10703
3 changed files with 138 additions and 25 deletions

View file

@ -401,43 +401,80 @@ setbridge_flushall(if_ctx *ctx, const char *val __unused, int dummy __unused)
err(1, "BRDGFLUSH");
}
static void
setbridge_static(if_ctx *ctx, const char *val, const char *mac)
static int
setbridge_static(if_ctx *ctx, int argc, const char *const *argv)
{
struct ifbareq req;
struct ether_addr *ea;
int arg;
if (argc < 2)
errx(1, "usage: static <interface> <address> [vlan <id>]");
arg = 0;
memset(&req, 0, sizeof(req));
strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname));
ea = ether_aton(mac);
if (ea == NULL)
errx(1, "%s: invalid address: %s", val, mac);
memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
req.ifba_flags = IFBAF_STATIC;
req.ifba_vlan = 0; /* XXX allow user to specify */
strlcpy(req.ifba_ifsname, argv[arg], sizeof(req.ifba_ifsname));
++arg;
ea = ether_aton(argv[arg]);
if (ea == NULL)
errx(1, "invalid address: %s", argv[arg]);
memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
++arg;
req.ifba_vlan = 0;
if (argc > 2 && strcmp(argv[arg], "vlan") == 0) {
if (argc < 3)
errx(1, "usage: static <interface> <address> "
"[vlan <id>]");
++arg;
if (get_vlan_id(argv[arg], &req.ifba_vlan) < 0)
errx(1, "invalid vlan id: %s", argv[arg]);
++arg;
}
if (do_cmd(ctx, BRDGSADDR, &req, sizeof(req), 1) < 0)
err(1, "BRDGSADDR %s", val);
err(1, "BRDGSADDR");
return arg;
}
static void
setbridge_deladdr(if_ctx *ctx, const char *val, int dummy __unused)
static int
setbridge_deladdr(if_ctx *ctx, int argc, const char *const *argv)
{
struct ifbareq req;
struct ether_addr *ea;
int arg;
if (argc < 1)
errx(1, "usage: deladdr <address> [vlan <id>]");
arg = 0;
memset(&req, 0, sizeof(req));
ea = ether_aton(val);
ea = ether_aton(argv[arg]);
if (ea == NULL)
errx(1, "invalid address: %s", val);
errx(1, "invalid address: %s", argv[arg]);
memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
++arg;
req.ifba_vlan = 0;
if (argc >= 2 && strcmp(argv[arg], "vlan") == 0) {
if (argc < 3)
errx(1, "usage: deladdr <address> [vlan <id>]");
++arg;
if (get_vlan_id(argv[arg], &req.ifba_vlan) < 0)
errx(1, "invalid vlan id: %s", argv[arg]);
++arg;
}
if (do_cmd(ctx, BRDGDADDR, &req, sizeof(req), 1) < 0)
err(1, "BRDGDADDR %s", val);
err(1, "BRDGDADDR");
return arg;
}
static void
@ -660,8 +697,8 @@ static struct cmd bridge_cmds[] = {
DEF_CMD_ARG("-autoptp", unsetbridge_autoptp),
DEF_CMD("flush", 0, setbridge_flush),
DEF_CMD("flushall", 0, setbridge_flushall),
DEF_CMD_ARG2("static", setbridge_static),
DEF_CMD_ARG("deladdr", setbridge_deladdr),
DEF_CMD_VARG("static", setbridge_static),
DEF_CMD_VARG("deladdr", setbridge_deladdr),
DEF_CMD("addr", 1, setbridge_addr),
DEF_CMD_ARG("maxaddr", setbridge_maxaddr),
DEF_CMD_ARG("hellotime", setbridge_hellotime),

View file

@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd July 11, 2025
.Dd July 30, 2025
.Dt IFCONFIG 8
.Os
.Sh NAME
@ -2521,15 +2521,23 @@ is zero, then address cache entries will not be expired.
The default is 1200 seconds.
.It Cm addr
Display the addresses that have been learned by the bridge.
.It Cm static Ar interface-name Ar address
Add a static entry into the address cache pointing to
.It Cm static Ar interface-name Ar address Op Cm vlan Ar vlan-id
Add a static entry into the address cache for pointing to
.Ar interface-name .
If
.Ar vlan-id
is specified, the entry is added for that VLAN, otherwise it is added
for VLAN 0.
.Pp
Static entries are never aged out of the cache or re-placed, even if the
address is seen on a different interface.
.It Cm deladdr Ar address
.It Cm deladdr Ar address Op Cm vlan Ar vlan-id
Delete
.Ar address
from the address cache.
from the address cache. If
.Ar vlan-id
is specified, the entry is deleted from that VLAN's address table,
otherwise it is deleted from the VLAN 0 address table.
.It Cm flush
Delete all dynamically-learned addresses from the address cache.
.It Cm flushall

View file

@ -245,7 +245,8 @@ static_body()
jexec one ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05
# List addresses
atf_check -s exit:0 -o ignore \
atf_check -s exit:0 \
-o match:"00:01:02:03:04:05 Vlan0 ${epair}a 0 flags=1<STATIC>" \
jexec one ifconfig ${bridge} addr
# Delete with bad address format
@ -266,6 +267,72 @@ static_cleanup()
vnet_cleanup
}
atf_test_case "vstatic" "cleanup"
vstatic_head()
{
atf_set descr 'Bridge VLAN static address test'
atf_set require.user root
}
vstatic_body()
{
vnet_init
vnet_init_bridge
epair=$(vnet_mkepair)
bridge=$(vnet_mkbridge)
vnet_mkjail one ${bridge} ${epair}a
ifconfig ${epair}b up
jexec one ifconfig ${bridge} up
jexec one ifconfig ${epair}a up
jexec one ifconfig ${bridge} addm ${epair}a
# Wrong interface
atf_check -s exit:1 -o ignore -e ignore jexec one \
ifconfig ${bridge} static ${epair}b 00:01:02:03:04:05 vlan 10
# Bad address format
atf_check -s exit:1 -o ignore -e ignore jexec one \
ifconfig ${bridge} static ${epair}a 00:01:02:03:04 vlan 10
# Invalid VLAN ID
atf_check -s exit:1 -o ignore -e ignore jexec one \
ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 vlan 5000
# Correct add
atf_check -s exit:0 -o ignore jexec one \
ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 vlan 10
# List addresses
atf_check -s exit:0 \
-o match:"00:01:02:03:04:05 Vlan10 ${epair}a 0 flags=1<STATIC>" \
jexec one ifconfig ${bridge} addr
# Delete with bad address format
atf_check -s exit:1 -o ignore -e ignore jexec one \
ifconfig ${bridge} deladdr 00:01:02:03:04 vlan 10
# Delete with unlisted address
atf_check -s exit:1 -o ignore -e ignore jexec one \
ifconfig ${bridge} deladdr 00:01:02:03:04:06 vlan 10
# Delete with wrong vlan id
atf_check -s exit:1 -o ignore -e ignore jexec one \
ifconfig ${bridge} deladdr 00:01:02:03:04:05 vlan 20
# Correct delete
atf_check -s exit:0 -o ignore jexec one \
ifconfig ${bridge} deladdr 00:01:02:03:04:05 vlan 10
}
vstatic_cleanup()
{
vnet_cleanup
}
atf_test_case "span" "cleanup"
span_head()
{
@ -835,6 +902,7 @@ atf_init_test_cases()
atf_add_test_case "stp"
atf_add_test_case "stp_vlan"
atf_add_test_case "static"
atf_add_test_case "vstatic"
atf_add_test_case "span"
atf_add_test_case "inherit_mac"
atf_add_test_case "delete_with_members"