daniel-p-carvalho opened a new pull request, #20519:
URL: https://github.com/apache/nuttx/pull/20519
## Summary
NuttX has no layer 2 bridge: `SIOCBRADDBR`/`SIOCBRADDIF` and
`include/nuttx/net/if_bridge.h` only define numbers. This PR adds a
transparent learning Ethernet bridge (IEEE 802.1D model, no spanning tree)
in `net/bridge`.
**Draft to discuss the design** (also sent to dev@): it adds a field to
`struct net_driver_s`, hooks in the RX entry points and a poll type in
`devif_poll()`, so feedback on these choices is welcome before polishing.
Motivation: an Ethernet to Wi-Fi bridge (PIC32MZ-W1, #20448), where Wi-Fi
clients (access point mode) and the wired device must share one LAN, with
DHCP served either by the wired device through the bridge or by NuttX on
the bridge interface. Routing/NAT cannot do this.
### How it works
* A bridge is a virtual network device (`br0`) created with `SIOCBRADDBR`.
Ethernet or IEEE 802.11 devices are added as ports with `SIOCBRADDIF`
(`struct ifreq`: bridge in `ifr_name`, port in `ifr_ifindex`, as in
Linux). The bridge holds the IP configuration; it takes the MAC address
of its first port and the smallest MTU of its ports.
* **RX:** a port is marked by the new `d_bridge` field of
`struct net_driver_s`. Frames received on a port go to `bridge_input()`
instead of the stack. Hooks:
* `netdev_upper_rxpoll_work()` (after `pkt_input()`): upper-half drivers
pass every frame, any ethertype.
* Top of `ipv4_input()`, `ipv6_input()` and `arp_input()`: covers drivers
that do not use the upper-half interface (only IP/ARP reach the bridge
there, since those drivers drop other ethertypes themselves).
* **Forwarding:** source MACs are learned in a filtering database (fixed
size, lazy ageing, oldest entry replaced when full). Known unicast goes
to its port, unknown unicast/broadcast/multicast is flooded, frames for
the bridge or port MACs and all group frames go to the local stack
through `br0`. `01:80:C2:00:00:0x` are never forwarded.
* **TX:** forwarded frames are queued per egress port (IOB queue, bounded by
`CONFIG_NET_BRIDGE_TXQ_LEN`). The port is notified with the new
`BRIDGE_POLL` poll type and `devif_poll()` hands the queued frames to the
driver as they are (no `devif_out()`), so it works with upper-half and
other drivers alike.
* **Locking:** the upper-half `txavail` locks the device synchronously, so
notifying port B from the RX path of port A while B forwards to A would
deadlock. Port notification and the TX poll of `br0` therefore run on
LPWORK, with no device locked. Lock order:
`br_cfglock -> port d_lock -> bridge d_lock -> br_lock`.
### Commits
1. `drivers/net/netdev_upperhalf.c`: nxstyle fixes only (checkpatch checks
the whole touched file).
2. `net/bridge`: the bridge, hooks, `BRIDGE_POLL`, docs
(`Documentation/os/networking/bridge.rst`).
3. Documentation of the `brctl` command (apache/nuttx-apps#APPSPR).
4. `sim:bridge` configuration.
### Open questions
* Is the `d_bridge` + hooks approach acceptable, or should bridging be
limited to upper-half drivers (like VLAN), with a single hook?
* Is LPWORK the right context, or should the bridge follow the upper-half
work queue configuration?
* There is no API yet to list the filtering database (Linux uses
`SIOCDEVPRIVATE`/sysfs/netlink): procfs or netlink?
* No STP and no VLAN filtering (documented limitations).
## Impact
* New option `CONFIG_NET_BRIDGE` (default n). With it disabled, there is no
code change other than the nxstyle commit.
* With it enabled: one pointer added to `struct net_driver_s`, a
`d_bridge != NULL` test per received frame, and `NETDEV_IFINDEX` is
selected.
* Needs `CONFIG_SCHED_LPWORK` and `CONFIG_IOB_NCHAINS > 0`.
* Unicast forwarding needs ports that accept foreign destination MACs
(`CONFIG_NET_PROMISCUOUS` where the driver supports it, Wi-Fi in AP mode).
* New `sim:bridge` configuration; `brctl` is in apache/nuttx-apps#APPSPR.
## Testing
Host: Ubuntu Linux x86_64. `sim:bridge` (two TAP devices); on the host each
TAP device is moved to its own network namespace:
```
netns bra: tap0 10.0.0.1 --- eth0 [NuttX br0 10.0.0.2] eth1 --- tap1
10.0.0.3 :netns brb
```
NuttX side: `ifup eth0; ifup eth1; brctl addbr br0; brctl addif br0 eth0;
brctl addif br0 eth1; ifconfig br0 10.0.0.2 netmask 255.255.255.0; ifup br0`.
A script ran the following checks, all passing:
| Test | Result |
|---|---|
| ping bra -> br0, brb -> br0 (local delivery on each port) | 0% loss |
| ping bra -> brb (forwarded), also 1472-byte payload with DF (1514-byte
frames) | 0% loss |
| broadcast ping from bra answered by brb (flooding) | OK |
| ping from NuttX to bra and brb (TX through br0) | 0% loss |
| iperf TCP bra -> brb (forwarded), 10 s | 308 Mbits/sec |
| iperf UDP 20 Mbit/s bra -> brb (forwarded) | 1833/17835 lost (*) |
| iperf TCP bra -> NuttX iperf server on br0 | 107 Mbits/sec |
| ping brb -> bra after the load | 0% loss |
| `brctl delbr` while br0 up | EBUSY |
| `brctl delif br0 eth1`, then ping bra -> brb | no forwarding |
| `ifdown br0; brctl delbr br0` | br0 removed |
(*) Drops on the egress port queue (16 frames); the sim reads the TAP
devices in bursts. TCP is not affected.
Build checks: `sim:bridge` with make and CMake, `sim:tcpblaster` (network,
no bridge), `./tools/checkpatch.sh -g` and `refresh.sh sim:bridge` clean.
Not tested on hardware yet (planned on PIC32MZ-W1 Ethernet + Wi-Fi AP).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]