| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Debugging embedded network systems after hardware integration is too late.
EdgeNetSwitch is a deterministic C++20 runtime for validating and reasoning about networked systems before hardware exists.
Embedded networking systems are often debugged too late: after hardware is available, after kernel integration has started, and after concurrency bugs are already mixed with driver, BSP, and timing behavior.
That makes packet loss, shutdown races, observability gaps, and lifecycle accounting errors difficult to reproduce. The core runtime needs to be designed and validated before it is buried under platform-specific complexity.
EdgeNetSwitch models a small network runtime in C++20. UDP traffic enters through configured ingress endpoints, packets move through a synchronous event bus and bounded worker handoffs, switching decisions are computed in-process, and runtime state is inspected through a UNIX-socket control plane.
The runtime keeps concurrency, resource ownership, overload behavior, telemetry, replay, failure injection, and shutdown sequencing explicit so they can be tested before hardware or kernel integration hides the failure modes.
The system enables early validation of:
v1.9.6 established multi-endpoint UDP ingress through IngressManager, configured listen and peer endpoints, and one runtime-owned lifecycle identity source. v1.9.7 keeps that architecture and makes the Linux UDP receive boundary visible inside the runtime.
UdpReceiver now uses recvmsg() so receive flags and ancillary/control metadata can be inspected. Kernel receive timestamps, receive-queue drop metadata, per-socket receive-buffer state, and kernel-to-userspace receive timing are exposed through packet and ingress observability.
Truncated UDP receives terminate as DatagramTruncated before application parsing and validation. This keeps receive-buffer truncation distinct from the payload_too_large application policy, while the MTU-1500 investigation separately observed IPv4 fragmentation and complete UDP delivery after reassembly.
Burst-pressure validation exercised the existing 256-datagram per-dispatch receive budget and the level-triggered readiness path. A datagram on a second ingress endpoint was received and processed while the first endpoint remained under load; the experiment did not establish immediate ordering after a particular budget-exhaustion event.
flowchart LR
subgraph Inputs["External Inputs"]
UdpEndpoints["UDP Endpoints"]
end
subgraph Coordination["Runtime Coordination"]
IngressManager["IngressManager"]
IngressEndpoints["UdpReceiver / UdpReadyHandler endpoints"]
Bus["MessagingBus"]
end
subgraph Processing["Processing"]
PacketProcessor["PacketProcessor"]
SwitchForwardingEngine["SwitchForwardingEngine"]
TransportManager["TransportManager"]
end
subgraph Network["Network I/O"]
PortBackend["PortBackend"]
UdpPeers["UDP Peers"]
end
subgraph Observability["Observability"]
RuntimeObservability["Runtime Observability"]
end
UdpEndpoints --> IngressEndpoints
IngressManager -. owns .-> IngressEndpoints
IngressEndpoints --> Bus
Bus --> PacketProcessor
PacketProcessor --> SwitchForwardingEngine
SwitchForwardingEngine --> TransportManager
TransportManager --> PortBackend
PortBackend --> UdpPeers
Bus -. runtime events .-> RuntimeObservability
IngressManager owns the lifetime of all configured ingress endpoints, with one UdpReceiver and one UdpReadyHandler per endpoint. Runtime configuration maps logical switch ports to independent ingress and egress UDP endpoints, while one runtime-owned LifecycleIdGenerator keeps lifecycle IDs globally unique across all endpoints. UdpReceiver also forms the Linux receive-observability boundary through recvmsg() flags and ancillary metadata. Detailed runtime architecture is intentionally kept under the docs/ directory rather than in this README.
The transport layer is the boundary between switching decisions and outbound packet I/O.
PortBackend is the per-port transmit interface. Backends accept a processed packet and return a TransmitResult that identifies success, unavailable backend, down port, invalid packet, or native send failure.
VirtualPortBackend implements the same interface for simulated transmit paths. It preserves the transport contract without creating a socket.
UdpPortBackend implements the socket-backed transport path. It creates a UDP socket, owns it through the existing RAII FileDescriptor wrapper, records it in FdRegistry when a registry is provided, and sends packet payload bytes to the configured IPv4 endpoint.
TransportManager owns registered backends by port ID, dispatches forwarding egress ports to the matching backend, and keeps transport policy out of the switching engine. It converts backend outcomes into runtime counters, allowing successful transmissions, failures, unavailable backends, and other transport events to be observed through the control plane.
TransportCounters expose transmit visibility for successful packets, transmitted bytes, failed sends, unavailable backends, down ports, and invalid packets.
The control plane exposes these counters through:
echo "1.2|transport-stats" | nc -U /tmp/edgenetswitch.sock
echo "1.2|transport-stats:json" | nc -U /tmp/edgenetswitch.sockSee CHANGELOG.md for the architectural milestone history and release-level engineering notes.
v1.9.7 is complete.
The runtime now combines multi-endpoint UDP ingress, explicit receive-path visibility and kernel receive metadata, receive-layer truncation semantics, bounded ingress draining, deterministic switching, and transport forwarding.
This project is designed for engineers working on:
git clone https://github.com/togunchan/EdgeNetSwitch.git
cd EdgeNetSwitch
git submodule update --init --recursivecmake -S . -B build -DBUILD_TESTING=ON
cmake --build build./build/EdgeNetSwitchDaemonctest --test-dir build --output-on-failureThe test suite covers lifecycle accounting, bounded async packet processing, deterministic failure injection, replay equivalence, switching decisions, forwarding-event ordering, terminal observable ordering, descriptor ownership, and Linux epoll / eventfd behavior. Runtime tests validate unknown unicast flooding, learning-switch behavior, MAC table aging, multi-endpoint UDP ingress, global lifecycle ID generation, and the end-to-end forwarding pipeline.
Send a valid packet to any configured ingress endpoint:
printf 'id=1;\nsrc=00:11:22:33:44:55;\ndst=ff:ff:ff:ff:ff:ff;\npayload=Hello EdgeNetSwitch\n' \
| nc -u 127.0.0.1 9001
printf 'id=2;\nsrc=00:11:22:33:44:66;\ndst=ff:ff:ff:ff:ff:ff;\npayload=Hello EdgeNetSwitch\n' \
| nc -u 127.0.0.1 9002Inspect system state:
echo "1.2|packet-stats:json" | nc -U /tmp/edgenetswitch.sock
echo "1.2|ingress-stats:json" | nc -U /tmp/edgenetswitch.sock
echo "1.2|fd-status" | nc -U /tmp/edgenetswitch.sock
echo "1.2|fd-status:json" | nc -U /tmp/edgenetswitch.sock
echo "1.2|show-config:json" | nc -U /tmp/edgenetswitch.sockInject deterministic switching traffic through the control plane:
echo "1.2|send-packet:broadcast" | nc -U /tmp/edgenetswitch.sock
echo "1.2|send-packet:learn" | nc -U /tmp/edgenetswitch.sock
echo "1.2|send-packet:topology-demo" | nc -U /tmp/edgenetswitch.sock
echo "1.2|show:mac-table" | nc -U /tmp/edgenetswitch.sockThis demonstrates readiness-driven multi-endpoint UDP ingress, receive-path and per-ingress socket visibility, lifecycle tracking, MAC learning, forwarding decision observability, descriptor lifecycle visibility, configuration inspection, and packet-path telemetry without hardware dependencies.
This project is primarily a systems architecture exploration.
Contributions, experiments, and technical discussions are welcome.
Open an issue for:
| Back | FazBrowse Home | New Git URL |