Skip to content

Commit 146de4b

Browse files
authored
Merge pull request #120 from danielinux/802.1q
Add support for VLAN 802.1q tagging/filtering
2 parents 8d2d948 + 8825f53 commit 146de4b

12 files changed

Lines changed: 1958 additions & 24 deletions

File tree

.github/workflows/stm32h563-m33mu.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,49 @@ jobs:
449449
if [ -f /tmp/tcpdump.pid ]; then
450450
sudo kill "$(cat /tmp/tcpdump.pid)" 2>/dev/null || true
451451
fi
452+
453+
stm32h563_m33mu_vlan:
454+
runs-on: ubuntu-latest
455+
timeout-minutes: 20
456+
container:
457+
image: ghcr.io/wolfssl/wolfboot-ci-m33mu:v1.2
458+
options: --privileged
459+
460+
steps:
461+
- uses: actions/checkout@v4
462+
463+
- name: Install host tools
464+
run: |
465+
set -euo pipefail
466+
apt-get update
467+
# iproute2: 'ip' command (tap, vlan link-add)
468+
# tcpdump: packet capture on tap0
469+
# tshark: filter/parse the pcap for VID + direction assertions
470+
# sudo: the integration script wraps privileged ops with sudo
471+
apt-get install -y sudo iproute2 tcpdump tshark
472+
473+
- name: Run VLAN integration test (TCP echo over 802.1Q)
474+
timeout-minutes: 15
475+
env:
476+
VLAN_VID: "100"
477+
VLAN_PCP: "0"
478+
M33MU_TIMEOUT: "60"
479+
run: |
480+
set -euo pipefail
481+
# The script builds the firmware with ENABLE_VLAN=1, sets up tap0
482+
# + tap0.${VLAN_VID}, boots m33mu, probes the TCP echo service on
483+
# port 7 over the VLAN, and asserts via tshark that 802.1Q frames
484+
# flowed in both directions on VID=${VLAN_VID}.
485+
bash tools/scripts/debug-m33mu-vlan-local.sh
486+
487+
- name: Upload VLAN artifacts on failure
488+
if: failure()
489+
uses: actions/upload-artifact@v4
490+
with:
491+
name: vlan-debug-artifacts
492+
path: |
493+
/tmp/m33mu-vlan.log
494+
/tmp/m33mu-vlan.pcap
495+
/tmp/m33mu-vlan-tshark.txt
496+
/tmp/m33mu-vlan-echo.txt
497+
if-no-files-found: ignore

Makefile

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,8 @@ UNIT_TEST_SRCS:=src/test/unit/unit.c \
447447
src/test/unit/unit_tests_dhcp_edges.c \
448448
src/test/unit/unit_tests_ip_arp_recv.c \
449449
src/test/unit/unit_tests_dns_edges.c \
450-
src/test/unit/unit_tests_misc_edges.c
450+
src/test/unit/unit_tests_misc_edges.c \
451+
src/test/unit/unit_tests_vlan.c
451452

452453
unit: build/test/unit
453454

@@ -461,6 +462,9 @@ build/test/unit: $(UNIT_TEST_SRCS)
461462
unit-multicast: CFLAGS+=-DIP_MULTICAST
462463
unit-multicast: clean-unit unit
463464

465+
unit-vlan: CFLAGS+=-DWOLFIP_VLAN=1 -DWOLFIP_MAX_INTERFACES=6
466+
unit-vlan: clean-unit unit
467+
464468
ESP_UNIT_CHECK_CFLAGS := $(CHECK_PKG_CFLAGS)
465469
ifeq ($(UNAME_S),Darwin)
466470
ifneq ($(CHECK_PREFIX),)
@@ -523,6 +527,8 @@ COV_UNIT:=$(COV_DIR)/unit
523527
COV_UNIT_O:=$(COV_DIR)/unit.o
524528
COV_MCAST_UNIT:=$(COV_DIR)/unit-multicast
525529
COV_MCAST_UNIT_O:=$(COV_DIR)/unit-multicast.o
530+
COV_VLAN_UNIT:=$(COV_DIR)/unit-vlan
531+
COV_VLAN_UNIT_O:=$(COV_DIR)/unit-vlan.o
526532

527533
$(COV_UNIT_O): $(UNIT_TEST_SRCS)
528534
@mkdir -p $(COV_DIR)
@@ -594,6 +600,39 @@ cov-multicast: unit-multicast $(COV_MCAST_UNIT)
594600
--html-details -o build/coverage/multicast.html
595601
@$(OPEN_CMD) build/coverage/multicast.html
596602

603+
$(COV_VLAN_UNIT_O): $(UNIT_TEST_SRCS)
604+
@mkdir -p $(COV_DIR)
605+
@echo "[CC] unit.c (vlan coverage)"
606+
@$(CC) $(UNIT_CFLAGS) $(CFLAGS) -DWOLFIP_VLAN=1 -DWOLFIP_MAX_INTERFACES=6 --coverage -c src/test/unit/unit.c -o $(COV_VLAN_UNIT_O)
607+
608+
$(COV_VLAN_UNIT): LDFLAGS+=--coverage $(UNIT_LIBS)
609+
$(COV_VLAN_UNIT): $(COV_VLAN_UNIT_O)
610+
@echo "[LD] $@"
611+
@$(CC) $(COV_VLAN_UNIT_O) -o $(COV_VLAN_UNIT) $(UNIT_LDFLAGS) $(LDFLAGS)
612+
613+
cov-vlan: unit-vlan $(COV_VLAN_UNIT)
614+
@echo "[RUN] unit vlan (coverage)"
615+
@rm -f $(COV_DIR)/*.gcda
616+
@$(COV_VLAN_UNIT)
617+
@echo "[COV] gcovr vlan html"
618+
@mkdir -p build/coverage
619+
@gcovr -r . --exclude "src/test/unit/.*" \
620+
--gcov-ignore-errors=no_working_dir_found \
621+
--merge-mode-functions=merge-use-line-min \
622+
--html-details -o build/coverage/vlan.html
623+
@$(OPEN_CMD) build/coverage/vlan.html
624+
625+
autocov-vlan: unit-vlan $(COV_VLAN_UNIT)
626+
@echo "[RUN] unit vlan (coverage)"
627+
@rm -f $(COV_DIR)/*.gcda
628+
@$(COV_VLAN_UNIT)
629+
@echo "[COV] gcovr vlan html"
630+
@mkdir -p build/coverage
631+
@gcovr -r . --exclude "src/test/unit/.*" \
632+
--gcov-ignore-errors=no_working_dir_found \
633+
--merge-mode-functions=merge-use-line-min \
634+
--html-details -o build/coverage/vlan.html
635+
597636
# Install dynamic library to re-link linux applications
598637
#
599638
install:
@@ -692,7 +731,7 @@ build/test/test-wolfguard-interop: src/test/test_wolfguard_interop.c src/port/po
692731
clean-test-wolfguard-interop:
693732
@rm -f build/test/test-wolfguard-interop build/test/test_wolfguard_interop.o build/test/linux_tun.o
694733

695-
.PHONY: clean all static cppcheck cov autocov autocov-multicast cov-multicast unit-multicast unit-asan unit-ubsan unit-leaksan clean-unit \
734+
.PHONY: clean all static cppcheck cov autocov autocov-multicast cov-multicast unit-multicast unit-vlan cov-vlan autocov-vlan unit-asan unit-ubsan unit-leaksan clean-unit \
696735
unit-esp-asan unit-esp-ubsan unit-esp-leaksan clean-unit-esp \
697736
unit-wolfguard unit-wolfguard-asan unit-wolfguard-ubsan clean-unit-wolfguard \
698737
test-wolfguard-loopback test-wolfguard-loopback-asan test-wolfguard-loopback-ubsan \

config.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,29 @@
7878
#error "WOLFIP_ENABLE_LOOPBACK requires WOLFIP_MAX_INTERFACES > 1"
7979
#endif
8080

81+
/* 802.1Q VLAN support. Off by default; when off, all VLAN code is removed
82+
* by the preprocessor and behavior/ABI of the stack is unchanged.
83+
*
84+
* WOLFIP_VLAN_MAX is a hard cap on the number of *simultaneously live*
85+
* VLAN sub-interfaces. The capacity must fit alongside the physical
86+
* interface and, when loopback is enabled, also the loopback slot. */
87+
#ifndef WOLFIP_VLAN
88+
#define WOLFIP_VLAN 0
89+
#endif
90+
#ifndef WOLFIP_VLAN_MAX
91+
#define WOLFIP_VLAN_MAX 4
92+
#endif
93+
#if WOLFIP_VLAN
94+
#if WOLFIP_ENABLE_LOOPBACK
95+
#define WOLFIP_VLAN_RESERVED_SLOTS 2 /* loopback + 1 physical */
96+
#else
97+
#define WOLFIP_VLAN_RESERVED_SLOTS 1 /* 1 physical */
98+
#endif
99+
#if (WOLFIP_MAX_INTERFACES < (WOLFIP_VLAN_RESERVED_SLOTS + WOLFIP_VLAN_MAX))
100+
#error "WOLFIP_VLAN requires WOLFIP_MAX_INTERFACES >= 1 (physical) + (WOLFIP_ENABLE_LOOPBACK ? 1 : 0) + WOLFIP_VLAN_MAX"
101+
#endif
102+
#endif
103+
81104
/* Linux test configuration */
82105
#define WOLFIP_IP "10.10.10.2"
83106
#define HOST_STACK_IP "10.10.10.1"

src/port/stm32h563/Makefile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ ENABLE_MQTT_BROKER ?= 0
3535
# wolfBoot update partition. TZEN=0 only.
3636
ENABLE_TFTP ?= 0
3737

38+
# 802.1Q VLAN sub-interface support. Set ENABLE_VLAN=1 to enable
39+
# WOLFIP_VLAN at compile time and run all DHCP/TFTP/etc. traffic over
40+
# a VLAN sub-interface with VID = VLAN_VID. Static IP configuration on
41+
# the sub-interface is selected via VLAN_IP / VLAN_MASK / VLAN_GW; the
42+
# physical interface stays untagged with no IP.
43+
ENABLE_VLAN ?= 0
44+
VLAN_VID ?= 100
45+
VLAN_PCP ?= 0
46+
VLAN_IP ?= 10.10.100.2
47+
VLAN_MASK ?= 255.255.255.0
48+
VLAN_GW ?= 10.10.100.1
49+
3850
# FreeRTOS integration: set FREERTOS=1 to run the HTTPS server from a
3951
# FreeRTOS task using the blocking BSD socket wrapper layer.
4052
FREERTOS ?= 0
@@ -360,6 +372,18 @@ SRCS += $(ROOT)/src/tftp/wolftftp.c
360372

361373
endif # ENABLE_TFTP
362374

375+
# -----------------------------------------------------------------------------
376+
# 802.1Q VLAN
377+
# -----------------------------------------------------------------------------
378+
ifeq ($(ENABLE_VLAN),1)
379+
CFLAGS += -DENABLE_VLAN -DWOLFIP_VLAN=1
380+
# Need room for 1 loopback + 1 physical + at least 1 VLAN sub-iface.
381+
# wolfIP default WOLFIP_MAX_INTERFACES is 2; bump to 6 to leave headroom.
382+
CFLAGS += -DWOLFIP_MAX_INTERFACES=6
383+
CFLAGS += -DVLAN_VID=$(VLAN_VID) -DVLAN_PCP=$(VLAN_PCP)
384+
CFLAGS += -DVLAN_IP=\"$(VLAN_IP)\" -DVLAN_MASK=\"$(VLAN_MASK)\" -DVLAN_GW=\"$(VLAN_GW)\"
385+
endif
386+
363387
# -----------------------------------------------------------------------------
364388
# Build rules
365389
# -----------------------------------------------------------------------------

src/port/stm32h563/config.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,29 @@
5252
#define WOLFIP_ENABLE_DHCP 1
5353
#endif
5454

55+
/* 802.1Q VLAN sub-interface support. Off by default; enable on the make
56+
* command line with ENABLE_VLAN=1 (the Makefile then passes -DWOLFIP_VLAN=1
57+
* and bumps WOLFIP_MAX_INTERFACES so the sub-interface fits).
58+
*
59+
* The capacity check accounts for 1 physical + (loopback ? 1 : 0) +
60+
* WOLFIP_VLAN_MAX sub-interface slots. */
61+
#ifndef WOLFIP_VLAN
62+
#define WOLFIP_VLAN 0
63+
#endif
64+
#ifndef WOLFIP_VLAN_MAX
65+
#define WOLFIP_VLAN_MAX 4
66+
#endif
67+
#if WOLFIP_VLAN
68+
#if WOLFIP_ENABLE_LOOPBACK
69+
#define WOLFIP_VLAN_RESERVED_SLOTS 2
70+
#else
71+
#define WOLFIP_VLAN_RESERVED_SLOTS 1
72+
#endif
73+
#if (WOLFIP_MAX_INTERFACES < (WOLFIP_VLAN_RESERVED_SLOTS + WOLFIP_VLAN_MAX))
74+
#error "WOLFIP_VLAN requires WOLFIP_MAX_INTERFACES >= 1 (physical) + (WOLFIP_ENABLE_LOOPBACK ? 1 : 0) + WOLFIP_VLAN_MAX"
75+
#endif
76+
#endif
77+
5578
/* Static IP fallback (used when DHCP is disabled or times out) */
5679
#define WOLFIP_IP "192.168.12.11"
5780
#define WOLFIP_NETMASK "255.255.255.0"

src/port/stm32h563/main.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,44 @@ int main(void)
886886
uart_puts("\n");
887887
}
888888

889+
#ifdef ENABLE_VLAN
890+
/* 802.1Q VLAN sub-interface: create a logical interface on top of the
891+
* physical (untagged) interface and run all traffic through it. The
892+
* physical interface stays without an IP; sockets bound on the VLAN
893+
* IP will automatically tag outgoing frames and accept incoming frames
894+
* matching the configured VID. */
895+
{
896+
unsigned int vlan_idx = 0;
897+
ip4 vip = atoip4(VLAN_IP);
898+
ip4 vnm = atoip4(VLAN_MASK);
899+
ip4 vgw = atoip4(VLAN_GW);
900+
int v_ret;
901+
902+
uart_puts("Creating VLAN sub-interface VID=");
903+
uart_putdec((uint32_t)(VLAN_VID));
904+
uart_puts(" PCP=");
905+
uart_putdec((uint32_t)(VLAN_PCP));
906+
uart_puts(" on physical if 0\n");
907+
v_ret = wolfIP_vlan_create(IPStack, 0, (uint16_t)(VLAN_VID),
908+
(uint8_t)(VLAN_PCP), 0, &vlan_idx);
909+
if (v_ret < 0) {
910+
uart_puts(" ERROR: wolfIP_vlan_create failed (-");
911+
uart_putdec((uint32_t)(-v_ret));
912+
uart_puts(")\n");
913+
} else {
914+
uart_puts(" VLAN sub-iface at idx ");
915+
uart_putdec((uint32_t)vlan_idx);
916+
uart_puts("\n IP: ");
917+
uart_putip4(vip);
918+
uart_puts("\n Mask: ");
919+
uart_putip4(vnm);
920+
uart_puts("\n GW: ");
921+
uart_putip4(vgw);
922+
uart_puts("\n");
923+
wolfIP_ipconfig_set_ex(IPStack, vlan_idx, vip, vnm, vgw);
924+
}
925+
}
926+
#else /* ENABLE_VLAN */
889927
#ifdef DHCP
890928
{
891929
uint32_t dhcp_start_tick;
@@ -958,6 +996,7 @@ int main(void)
958996
wolfIP_ipconfig_set(IPStack, ip, nm, gw);
959997
}
960998
#endif
999+
#endif /* ENABLE_VLAN */
9611000

9621001
#ifdef WOLFIP_USE_FREERTOS
9631002
uart_puts("Starting FreeRTOS BSD socket layer...\n");

src/test/unit/unit.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "unit_tests_ip_arp_recv.c"
3737
#include "unit_tests_dns_edges.c"
3838
#include "unit_tests_misc_edges.c"
39+
#include "unit_tests_vlan.c"
3940

4041
Suite *wolf_suite(void)
4142
{
@@ -1481,6 +1482,44 @@ Suite *wolf_suite(void)
14811482
#endif /* WOLFIP_PACKET_SOCKETS */
14821483
tcase_add_test(tc_core, test_bind_port_in_use_different_ips_no_collision);
14831484

1485+
#if WOLFIP_VLAN
1486+
/* --- unit_tests_vlan.c (30 tests for 802.1Q support) --- */
1487+
tcase_add_test(tc_proto, test_vlan_api_create_basic);
1488+
tcase_add_test(tc_proto, test_vlan_api_create_vid_max_ok);
1489+
tcase_add_test(tc_proto, test_vlan_api_create_vid_4095_rejected);
1490+
tcase_add_test(tc_proto, test_vlan_api_create_vid_above_max_rejected);
1491+
tcase_add_test(tc_proto, test_vlan_api_create_pcp_above_7_rejected);
1492+
tcase_add_test(tc_proto, test_vlan_api_create_dei_above_1_rejected);
1493+
tcase_add_test(tc_proto, test_vlan_api_create_duplicate_vid_rejected);
1494+
tcase_add_test(tc_proto, test_vlan_api_create_same_vid_two_parents_ok);
1495+
tcase_add_test(tc_proto, test_vlan_api_create_parent_not_physical_rejected);
1496+
tcase_add_test(tc_proto, test_vlan_api_create_exhausts_max);
1497+
tcase_add_test(tc_proto, test_vlan_api_create_uninitialized_parent_rejected);
1498+
tcase_add_test(tc_proto, test_vlan_api_create_loopback_parent_rejected);
1499+
tcase_add_test(tc_proto, test_vlan_api_create_null_args_rejected);
1500+
tcase_add_test(tc_proto, test_vlan_api_delete_basic);
1501+
tcase_add_test(tc_proto, test_vlan_api_delete_physical_rejected);
1502+
tcase_add_test(tc_proto, test_vlan_api_delete_bad_ifidx_rejected);
1503+
tcase_add_test(tc_proto, test_vlan_api_get_null_args_rejected);
1504+
tcase_add_test(tc_proto, test_vlan_api_get_dangling_parent_pointer_rejected);
1505+
tcase_add_test(tc_proto, test_vlan_tx_active_without_parent_rejected);
1506+
tcase_add_test(tc_proto, test_vlan_tx_tag_inserted);
1507+
tcase_add_test(tc_proto, test_vlan_tx_pcp_and_dei_encoded);
1508+
tcase_add_test(tc_proto, test_vlan_tx_vid_zero_priority_tag);
1509+
tcase_add_test(tc_proto, test_vlan_tx_vid_4094_encoded);
1510+
tcase_add_test(tc_proto, test_vlan_tx_oversize_rejected);
1511+
tcase_add_test(tc_proto, test_vlan_tx_runt_rejected);
1512+
tcase_add_test(tc_proto, test_vlan_rx_tagged_match_delivered);
1513+
tcase_add_test(tc_proto, test_vlan_rx_tagged_mismatch_dropped);
1514+
tcase_add_test(tc_proto, test_vlan_rx_untagged_on_physical_ok);
1515+
tcase_add_test(tc_proto, test_vlan_rx_runt_tagged_dropped);
1516+
tcase_add_test(tc_proto, test_vlan_rx_multiple_subs_correct_dispatch);
1517+
tcase_add_test(tc_proto, test_vlan_rx_delete_then_dropped);
1518+
tcase_add_test(tc_proto, test_vlan_rx_dei_bit_accepted);
1519+
tcase_add_test(tc_proto, test_vlan_rx_tagged_arp_processed);
1520+
tcase_add_test(tc_proto, test_vlan_mtu_inherited);
1521+
#endif /* WOLFIP_VLAN */
1522+
14841523
suite_add_tcase(s, tc_core);
14851524
suite_add_tcase(s, tc_utils);
14861525
suite_add_tcase(s, tc_proto);

src/test/unit/unit_shared.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@
2929
#undef WOLFIP_PACKET_SOCKETS
3030
#define WOLFIP_PACKET_SOCKETS 1
3131
#undef WOLFIP_MAX_INTERFACES
32+
#if WOLFIP_VLAN
33+
/* Need room for 1 loopback + 1 physical + multiple VLAN sub-ifaces. */
34+
#define WOLFIP_MAX_INTERFACES 6
35+
#else
3236
#define WOLFIP_MAX_INTERFACES 3
37+
#endif
3338
#undef WOLFIP_ENABLE_LOOPBACK
3439
#define WOLFIP_ENABLE_LOOPBACK 1
3540
#undef WOLFIP_ENABLE_FORWARDING

0 commit comments

Comments
 (0)