Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions FORK_PRS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ become its own scoped PR. Issue numbers filled in once created.
| 10 | ✅ [#29](https://github.com/fugo101/microlink/issues/29) (done) | [`liestrela/microlink`](https://github.com/liestrela/microlink) | `1649d987` | Gate ESP32 temp-sensor feature behind `CONFIG_SOC_TEMP_SENSOR_SUPPORTED` — `esp_driver_tsens` was an unconditional `REQUIRES`, breaks build on SoC variants without a temp sensor. Cherry-picked verbatim, no adaptation needed. | 1 |
| 11 | [#30](https://github.com/fugo101/microlink/issues/30) | `cplewes` vs `Csontikka` (conflict) | `cplewes` `83a102be`/`01d51697` vs [`Csontikka/microlink`](https://github.com/Csontikka/microlink) `dd5714c4`→`2e68e546` | `ip_input()` vs `netif->input`/`tcpip_input` threading fix for decrypted WG RX pbufs — cplewes fixes a UAF this way, Csontikka's own branch later reverted the equivalent fix citing a throughput regression (~30pps cap). Needs research, not a blind cherry-pick | 2 |
| 12 | [#31](https://github.com/fugo101/microlink/issues/31) | `Csontikka/microlink` | `647c2ab0` | `LOCK_TCPIP_CORE` around `netif_set_link_up/down` — compare against our documented `netif->state` NULL-and-restore workaround in `ESP_IDF_6X_COMPAT.md` before adopting | 2 |
| 13 | [#32](https://github.com/fugo101/microlink/issues/32) | `Csontikka/microlink` | `5bda1783` | Peer direct-handshake init was one-shot; peers with dropped/unanswered handshakes stayed permanently blackholed — retry every 30s via timestamp instead of boolean latch | 2 |
| 13 | [#32](https://github.com/fugo101/microlink/issues/32) (done) | `Csontikka/microlink` | `5bda1783` | Peer direct-handshake init was one-shot; peers with dropped/unanswered handshakes stayed permanently blackholed — retry every 30s via timestamp instead of boolean latch. Adapted, not literal cherry-pick: the source commit's diff still cleared `peer->active` after `wireguardif_connect()`, which issue #26/#28's PR #53 (same session) proved permanently blocks a peer's handshake — kept that fix removed while porting the retry-timestamp logic. | 2 |
| 14 | [#33](https://github.com/fugo101/microlink/issues/33) | `Csontikka/microlink` | `2e68e546`, `f1de3143` | DISCO trust-expiry gate (15s→60s, checks `last_rx` first), NAT-rebind handshake-skip when data path still alive, SPIRAM pbuf headroom fix for `wg_udp_output_cb` | 2 |
| 15 | [#34](https://github.com/fugo101/microlink/issues/34) | `Csontikka/microlink` | `b7442d0f` | Handshake retries throttled to 1/tick round-robin instead of firing to every eligible peer in one tick (was blocking the caller ~280ms with 7 peers) | 2 |
| 16 | [#35](https://github.com/fugo101/microlink/issues/35) | `Csontikka/microlink` | `7a24a9b3`, `4d3b3add` | DERP client backpressure/reconnect-storm fixes: don't tear down on TLS write backpressure, TCP_NODELAY + coalesced writes, fix a hot-spinning I/O loop, split into concurrent reader/writer tasks — check overlap against cplewes's DERP session-resumption fix (#6) before scoping | 2 |
| 17 | [#36](https://github.com/fugo101/microlink/issues/36) | `Csontikka/microlink` | `27806be3` | `microlink_stop()` never closed `derp.sockfd`/`coord_sock`, letting `derp_tx` outlive the teardown wait → UAF — reconcile against cplewes's teardown UAF fix (#2), may be the same root cause | 2 |
| 17 | [#36](https://github.com/fugo101/microlink/issues/36) | `Csontikka/microlink` | `27806be3` | `microlink_stop()` never closed `derp.sockfd`/`coord_sock`, letting `derp_tx` outlive the teardown wait → UAF — reconcile against cplewes's teardown UAF fix (#2), may be the same root cause. **Investigated, not needed here**: this fork's `microlink_stop()` (from issue #21/#22's PR #47) never frees context until `ml_join_tasks()` proves every worker exited, and `ml_derp.c`'s blocking loops already call `ml_shutdown_pending()` on every SO_RCVTIMEO-bounded iteration (100-200ms) to bail out cooperatively — the source commit's "shutdown() the socket to force-wake a blocked task" fixes a UAF that doesn't exist here. Leaving open in case future review disagrees, but no port planned. | 2 |
| 18 | [#37](https://github.com/fugo101/microlink/issues/37) | `Csontikka/microlink` | `cbdf1603`, `aad403af`, `533f1f88`, `46e34917`, `017b3588`, `372ca277` | H2 frame reassembly across `noise_recv()` read boundaries — large MapResponses spanning reads deterministically corrupt the stream ("implausible message size"); genuine protocol-correctness bug independent of control-plane backend | 2 |
| 19 | [#38](https://github.com/fugo101/microlink/issues/38), [#39](https://github.com/fugo101/microlink/issues/39) | [`djorr5/microlink`](https://github.com/djorr5/microlink) | `67b230b2` | Two independent changes, split into two issues: (a) dynamic H2 RX window sizing based on free heap for RAM-constrained boards, roughly halving MapResponse-parsing PSRAM footprint; (b) `ip4_route_src_hook` to force tailnet-range traffic onto the WG netif directly | 2 |
| 20 | [#40](https://github.com/fugo101/microlink/issues/40) | [`AELovelace/LAIN-MicrolinkRouter`](https://github.com/AELovelace/LAIN-MicrolinkRouter) | `e1239460` | `microlink_set_exit_node()` — default-route exit-node support while preserving per-peer `/32` routes, with routing-loop and liveness fixes. New capability, not a bugfix — needs a product decision (does any current/future downstream want SoftAP/NAPT exit-node routing?) before it's worth the review cost | 2 |
Expand Down
9 changes: 7 additions & 2 deletions components/microlink/include/microlink_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,13 @@ typedef struct {
/* WireGuard peer index in wireguard-lwip */
int wg_peer_index;

/* On-demand handshake: tried once on first DISCO direct path discovery */
bool tried_initial_handshake;
/* On-demand handshake retry timestamp. When a direct DISCO PONG arrives
* for a peer without an active WG session, we fire a one-shot handshake
* init. A single boolean latch left a peer permanently un-sessioned if
* that one init was lost or unanswered. Instead, record
* ml_get_time_ms() of the last attempt and re-fire after
* INITIAL_HANDSHAKE_RETRY_MS so a dropped initiation gets another try. */
uint64_t last_init_handshake_ms;
} ml_peer_t;

/* ============================================================================
Expand Down
29 changes: 17 additions & 12 deletions components/microlink/src/ml_wg_mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1029,15 +1029,17 @@ static void process_disco_pong(microlink_t *ml, const ml_rx_packet_t *pkt,
(int)((pkt->src_ip >> 24) & 0xFF), (int)((pkt->src_ip >> 16) & 0xFF),
(int)((pkt->src_ip >> 8) & 0xFF), (int)(pkt->src_ip & 0xFF),
(int)pkt->src_port, p->hostname);
/* First direct path discovery — send a one-shot handshake
* via direct UDP. Do NOT use wireguardif_connect() which
* sets peer->active=true and causes infinite handshake
* retries (every 5s) when the peer has us trimmed.
* Instead, just fire a single handshake init. If the peer
* has us configured, it will respond and establish session.
* If not, we stop and wait for them to initiate. */
if (!p->tried_initial_handshake) {
p->tried_initial_handshake = true;
/* Direct path discovered but no WG session yet. Fire a
* handshake init via direct UDP, rate-limited so a
* dropped or unanswered init gets another try every
* INITIAL_HANDSHAKE_RETRY_MS instead of leaving the peer
* permanently un-sessioned after a single lost attempt. */
#define INITIAL_HANDSHAKE_RETRY_MS 30000ULL
bool first_try = (p->last_init_handshake_ms == 0);
bool retry_due = !first_try &&
(now - p->last_init_handshake_ms > INITIAL_HANDSHAKE_RETRY_MS);
if (first_try || retry_due) {
p->last_init_handshake_ms = now;
/* Store endpoint so wireguardif_connect sends to it */
wireguardif_update_endpoint(netif, (u8_t)p->wg_peer_index,
&ep_ip, pkt->src_port);
Expand All @@ -1047,11 +1049,14 @@ static void process_disco_pong(microlink_t *ml, const ml_rx_packet_t *pkt,
* checks before a peer may initiate a session at all.
* Clearing it here meant no session could ever be
* established with this peer — permanently blackholed
* on the direct path. tried_initial_handshake above is
* what actually bounds this to a one-shot attempt. */
* on the direct path. The retry timestamp above is
* what bounds re-attempts to once per
* INITIAL_HANDSHAKE_RETRY_MS. */
wireguardif_connect(netif, (u8_t)p->wg_peer_index);
ESP_LOGI(TAG, "WG one-shot handshake to %s (first direct path)", p->hostname);
ESP_LOGI(TAG, "WG direct handshake %s to %s",
first_try ? "init" : "retry", p->hostname);
}
#undef INITIAL_HANDSHAKE_RETRY_MS
}
}
}
Expand Down