Skip to content

Commit 5e74fa1

Browse files
author
docs-bot
committed
docs: update from source@901502d
1 parent e3c3b31 commit 5e74fa1

1 file changed

Lines changed: 24 additions & 8 deletions

File tree

docs/networking-from-zero.html

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ <h3 id="the-architecture">The architecture</h3>
671671
+-- ds-lan (host bridge - NO IP address, just a switch)
672672
| |
673673
| +-- ds-g[hash] (veth host-side, connected to OpenWRT's netns as eth1)
674-
| +-- ds-v[pid] (veth host-side, connected to Kali's netns as eth0)
674+
| +-- ds-c[pid] (veth host-side, connected to Kali's netns as eth0)
675675
|
676676
+-- [Kali container - net=gateway mode]
677677
netns: owns eth0 (LAN side - plugged into ds-lan bridge)
@@ -732,21 +732,37 @@ <h3 id="what-lazy-attachment-means">What "lazy attachment" means</h3>
732732
<p>This mimics how you might physically plug a cable into a router's LAN port after the router is already running.</p>
733733
<h3 id="why-resolvconf-is-left-alone-in-gateway-mode">Why resolv.conf is left alone in gateway mode</h3>
734734
<p>In NAT mode, Droidspaces writes <code>/etc/resolv.conf</code> inside the container, pointing to <code>1.1.1.1</code> or <code>8.8.8.8</code>.</p>
735-
<p>In gateway mode, Droidspaces does NOT write <code>resolv.conf</code> (unless you explicitly pass <code>--dns</code>). This is because OpenWRT's <code>dnsmasq</code> hands the DNS server address to the container via the DHCP lease. If Droidspaces also wrote a <code>resolv.conf</code>, it would conflict with what dnsmasq is providing - the container would use the wrong DNS and bypass OpenWRT's DNS filtering/caching entirely.</p>
735+
<p>In gateway mode, Droidspaces does NOT write a static <code>resolv.conf</code> (unless you explicitly pass <code>--dns</code>). This is because OpenWRT's <code>dnsmasq</code> hands the DNS server address to the container via the DHCP lease. If Droidspaces also wrote a <code>resolv.conf</code>, it would conflict with what dnsmasq is providing - the container would use the wrong DNS and bypass OpenWRT's DNS filtering/caching entirely.</p>
736+
<p>How this is wired depends on the client's init system:</p>
737+
<ul>
738+
<li><strong>systemd containers:</strong> <code>/etc/resolv.conf</code> is symlinked to <code>/run/systemd/resolve/resolv.conf</code>, which systemd-resolved populates from the DHCP lease.
739+
</li>
740+
<li><strong>non-systemd containers:</strong> Droidspaces leaves <code>/etc/resolv.conf</code> entirely alone, so the container's own DHCP client (udhcpc/dhclient) writes the gateway-supplied nameserver from the lease. (Earlier builds wrote a hardcoded <code>1.1.1.1</code>/<code>8.8.8.8</code> here, which silently bypassed the gateway's DNS - that is fixed.) If a minimal rootfs ships no DHCP resolv.conf hook, pass <code>--dns</code> to set one explicitly.
741+
</li>
742+
</ul>
736743
<h3 id="why-bridge-nf-call-iptables-is-set-to-0">Why bridge-nf-call-iptables is set to 0</h3>
737744
<p>The bridge <code>ds-lan</code> carries traffic between OpenWRT and Kali. By default, Linux can pass bridged traffic through the host's iptables. This would mean Android's iptables rules (which might drop or NAT things unexpectedly) would interfere with traffic that OpenWRT is supposed to be managing.</p>
738745
<p>Setting it to <code>0</code> tells Linux: "do not run iptables on bridged traffic." This keeps OpenWRT's firewall as the <em>only</em> firewall that sees this traffic, which is exactly what we want.</p>
739-
<h3 id="the-gateway-container-must-be-running-first">The gateway container must be running first</h3>
740-
<p>Start order matters. When a gateway-mode container starts, Droidspaces looks up the gateway container's live process ID to reach its network namespace. If the gateway container is not running at that moment, the network setup fails with a warning and the client container boots anyway - but with no network at all (only loopback). It does not retry on its own.</p>
741-
<p>The same logic applies after a gateway restart. A veth pair dies together: when the gateway container stops, the <code>eth1</code> end inside it is destroyed, and that destroys the host-side end too. Existing clients stay plugged into the bridge but have no router anymore. The LAN cable is re-plugged the next time <strong>any</strong> gateway-mode container starts on that segment - so after restarting the gateway, restart one client (or start a new one) to bring the segment back to life.</p>
746+
<h3 id="start-order-and-automatic-self-healing">Start order and automatic self-healing</h3>
747+
<p>All wiring for a gateway-mode client is done <strong>from the host side</strong> by a single function, <code>gateway_wire_client()</code>: it ensures the bridge and the gateway-side cable, creates the client's app veth, and moves+renames the peer into the client's namespace as <code>eth0</code> (pinned MAC, brought up) — the client's own boot code only brings up <code>lo</code>. Because the host owns every step, the same function wires a client whether it is just starting or already running.</p>
748+
<p>This gives a deliberately simple rule keyed on <strong>gateway liveness</strong>:</p>
749+
<ul>
750+
<li><strong>Gateway already running when a client starts</strong> → the client wires immediately (its own monitor calls <code>gateway_wire_client</code>).
751+
</li>
752+
<li><strong>Gateway not running when a client starts</strong> → the client wires <strong>nothing at all</strong> (no bridge, no veth, no <code>eth0</code>) and just boots. The work is deferred entirely to the gateway.
753+
</li>
754+
</ul>
755+
<p>Healing is therefore driven by <strong>the gateway itself</strong>, not by the clients. On every boot cycle the gateway container's monitor calls <code>ds_net_rewire_gateway_clients()</code>: it scans the running containers, finds the ones that delegate to this gateway, and runs <code>gateway_wire_client</code> for each — establishing the gateway-side <code>eth1</code> cable and every client's <code>eth0</code> into the gateway's <em>current</em> namespace. So when the gateway <strong>starts or reboots</strong>, every running client is (re)wired with <strong>no client restart required</strong>.</p>
756+
<p>Skipping all client wiring while the gateway is down (rather than half-wiring a bridge and a danging veth) also closes a race: a client started before its gateway can have its gateway/LAN settings (<code>--gateway-net</code>, <code>--host-bridge</code>, …) edited before the gateway comes up, and the gateway then wires every client from each client's <em>current</em> config — never a stale one.</p>
757+
<p>There is exactly <strong>one actor</strong> (the gateway) doing the wiring, so there is nothing to poll and no thundering herd. Wiring is serialised per segment (an advisory file lock) to keep concurrent client starts and the gateway's re-wire from racing. Both <code>eth1</code> (gateway side) and each <code>eth0</code> (client side) keep a <strong>stable MAC</strong> and are moved+renamed into their namespace in a single atomic step, so the container's own <code>netifd</code>/DHCP sees one persistent device rather than re-initialising a churning one.</p>
742758
<h3 id="what-happens-when-containers-stop">What happens when containers stop</h3>
743759
<p>Cleanup in gateway mode is deliberately minimal, matching the "plumbing only" philosophy:</p>
744760
<ul>
745-
<li><strong>A client stops:</strong> only that client's own veth (<code>ds-v<PID></code>) is removed. The bridge and the gateway's <code>eth1</code> stay up, so other clients on the segment are untouched.
761+
<li><strong>A client stops:</strong> only that client's own veth is removed (gateway clients use the <code>ds-c<PID></code> prefix, distinct from NAT's <code>ds-v<PID></code>). The bridge and the gateway's <code>eth1</code> stay up, so other clients on the segment are untouched.
746762
</li>
747-
<li><strong>The gateway stops:</strong> the gateway-side veth disappears with its namespace (see above), but the bridge itself stays.
763+
<li><strong>The last client stops while the gateway is still running:</strong> the bridge is <strong>kept</strong> (not reaped). Tearing it down would flap the gateway's live <code>eth1</code> carrier and occasionally make netifd report "device initialization failed"; an idle IP-less bridge is harmless and the next client reuses it.
748764
</li>
749-
<li>The delegated bridge (<code>ds-lan</code> etc.) is never torn down by Droidspaces. It is harmless when idle - it has no IP and carries no policy - and persists until you delete it manually or reboot the device.
765+
<li><strong>The gateway stops:</strong> the gateway-side veth disappears with its namespace. Once no clients remain <em>and</em> the gateway is gone, the now-idle bridge is reaped.
750766
</li>
751767
</ul>
752768
<hr>

0 commit comments

Comments
 (0)