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]

Reply via email to