|
ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
|
This sketch turns the board into a reverse port forwarder: anything that connects to the ESP32 on a chosen front port is relayed to an internal host:port the board can reach, and replies come back automatically. It is the DNAT / "publish a service" pattern - expose a device that sits on a segment you can only reach through the board.
You can try it with no special hardware: Part 1 stands up a throwaway origin service and a client with one command each.
The board listens on a front port (say 8080). When a client connects there, the relay opens a second connection out to the origin (say an internal web server on 192.168.1.60:80) and shovels bytes between the two in both directions until either side closes. The client thinks it is talking to the board; it is really talking to the origin behind it.
Wiring is two calls:
The server's normal handle() loop pumps the relay - no extra task.
Security. The relay forwards to whatever origin you publish and does not authenticate the inbound side, so it is an open door to that origin for anyone who can reach the front port. Only publish trusted internal targets, and keep the front port off untrusted networks (put an ACL or VPN in front if needed).
On a machine on your network (note its IP with hostname -I / ipconfig), run a tiny origin web server:
Point the sketch's origin at it: set ORIGIN_HOST to that machine's IP and ORIGIN_PORT to 8000 (see Part 2). Flash the board, then from any machine connect to the board on the front port and you reach that web server:
You should get the directory listing the Python server is serving - fetched through the ESP32.
Open PortForward.ino and edit the lines marked CHANGE ME:
| Line | Set it to |
|---|---|
SSID | your WiFi network name |
PASSWORD | your WiFi password |
FRONT_PORT | the port to open on the board (e.g. 8080) |
ORIGIN_HOST | the internal host to forward to (the Part 1 machine's IP) |
ORIGIN_PORT | that service's port (e.g. 8000) |
Flash and open Serial Monitor @ 115200; you should see:
ORIGIN_HOST/ORIGIN_PORT, or a firewall). The board dials the origin when the inbound connection arrives; if that fails it drops the inbound.PC_RELAY_MAX_PUBLISH ports were published.PC_RELAY_MAX_CONNS (raise it in protocore_config.h).server.listen + pc_relay_publish once per port (up to PC_RELAY_MAX_PUBLISH), each to a different origin.PC_RELAY_BUF bytes per pump step; raise it for bulk transfers, at the cost of RAM per connection. HW-verified on an ESP32-S3 over a W5500 wired link: a 1 MB file pulled through the front port came back byte-exact (SHA256 matched the origin). Expect ~44 KB/s on a single-NIC relay - every byte crosses the one interface twice (origin->board, board->client) and the rate is round-trip-latency bound, so the relay suits publishing a service / small control files rather than bulk transfer.The relay lives inside the library, so the flag must reach the whole build:
(The Arduino IDE reads the flag from build_opt.h beside the sketch automatically.)
server.listen(port, PROTO_RELAY) registers a listener whose accepted connections are handled by the relay's ProtoHandler. On accept it dials the origin through pc_client (the shared outbound TCP transport) and pairs the two sockets in a pc_relay. Each server.handle() tick calls pc_relay_step, which moves whatever bytes are ready in each direction, carrying anything the far side is too busy to accept yet (backpressure). When either side closes, the relay tears the other down. The byte-pump engine is transport-agnostic and unit-tested on its own; this listener is the thin glue that binds its seams to the server's inbound connection and an outbound pc_client.