This commit is contained in:
eric
2026-02-04 06:20:15 +00:00
parent bd862cb238
commit 7de3b87680
38 changed files with 173 additions and 104 deletions

View File

@@ -16,7 +16,7 @@ When using WireGuard together with MWAN3 on OpenWrt, the tunnel can fail to esta
<a class=heading-link href=#environment><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i>
<span class=sr-only>Link to heading</span></a></h3><ul><li>OpenWrt 24.10.x</li><li>MWAN3 for multi-WAN policy routing</li><li>WireGuard interface configured with broad <code>allowed_ips</code> covering default route (<code>0.0.0.0/1</code> and <code>128.0.0.0/1</code> or <code>0.0.0.0/0</code>)</li></ul><h3 id=symptoms>Symptoms
<a class=heading-link href=#symptoms><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i>
<span class=sr-only>Link to heading</span></a></h3><ul><li><code>wg show</code> indicates the interface is listening, but <code>transfer: 0 B received</code> persists after bringing the tunnel up.</li><li>Intermittent reachability to public IPs until routing settles.</li><li><code>ip route</code> shows multiple defaults via WANs; a host route to the peer IP may exist but is still overridden by policy routing once MWAN3 applies rules.</li></ul><p>Example observations (sanitized):</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>wg show
<span class=sr-only>Link to heading</span></a></h3><ul><li><code>wg show</code> indicates the interface is listening, but <code>transfer: 0 B received</code> persists after bringing the tunnel up.</li><li>Intermittent reachability to public IPs until routing settles.</li><li><code>ip route</code> shows multiple defaults via WANs; a host route to the peer IP may exist but is still overridden by policy routing once MWAN3 applies rules.</li></ul><p>Example observations (sanitized):</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-bash data-lang=bash><span style=display:flex><span>wg show
</span></span><span style=display:flex><span>interface: wireguard
</span></span><span style=display:flex><span> public key: &lt;redacted&gt;
</span></span><span style=display:flex><span> listening port: <span style=color:#a5d6ff>39345</span>
@@ -33,22 +33,22 @@ When using WireGuard together with MWAN3 on OpenWrt, the tunnel can fail to esta
<a class=heading-link href=#root-cause><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i>
<span class=sr-only>Link to heading</span></a></h3><p>With default-route <code>allowed_ips</code>, WireGuard installs routes so that all outbound traffic prefers the tunnel. MWAN3 then applies policy rules that also match “all traffic,” including the UDP packets to the WireGuard peers public IP. If those packets are selected to go via the <code>wireguard</code> interface (or a table whose default is the tunnel), the handshake cannot succeed. This creates a chicken-and-egg dependency.</p><h3 id=fix-exclude-the-wireguard-endpoint-from-mwan3-default-policy>Fix: Exclude the WireGuard Endpoint from MWAN3 Default Policy
<a class=heading-link href=#fix-exclude-the-wireguard-endpoint-from-mwan3-default-policy><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i>
<span class=sr-only>Link to heading</span></a></h3><p>Force traffic to the WireGuard peer public endpoint to use a physical WAN policy. This guarantees the handshake packets always reach the Internet outside of the tunnel.</p><p>Steps:</p><ol><li>Resolve the peer endpoint IP (if you only have a hostname)</li></ol><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>nslookup vpn.example.com
<span class=sr-only>Link to heading</span></a></h3><p>Force traffic to the WireGuard peer public endpoint to use a physical WAN policy. This guarantees the handshake packets always reach the Internet outside of the tunnel.</p><p>Steps:</p><ol><li>Resolve the peer endpoint IP (if you only have a hostname)</li></ol><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-bash data-lang=bash><span style=display:flex><span>nslookup vpn.example.com
</span></span><span style=display:flex><span><span style=color:#8b949e;font-style:italic># =&gt; use the returned A/AAAA address(es) in the rule below</span>
</span></span></code></pre></div><ol start=2><li>Add an MWAN3 rule targeting the endpoint IP</li></ol><p>Edit <code>/etc/config/mwan3</code> and place this rule before the default v4 rule so it takes precedence:</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-fallback data-lang=fallback><span style=display:flex><span>config rule &#39;wireguard_endpoint&#39;
</span></span></code></pre></div><ol start=2><li>Add an MWAN3 rule targeting the endpoint IP</li></ol><p>Edit <code>/etc/config/mwan3</code> and place this rule before the default v4 rule so it takes precedence:</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-fallback data-lang=fallback><span style=display:flex><span>config rule &#39;wireguard_endpoint&#39;
</span></span><span style=display:flex><span> option dest_ip &#39;203.0.113.55&#39; # peer public IP
</span></span><span style=display:flex><span> option proto &#39;udp&#39;
</span></span><span style=display:flex><span> option use_policy &#39;wan_only&#39; # a policy that prefers a physical WAN
</span></span><span style=display:flex><span> option family &#39;ipv4&#39;
</span></span></code></pre></div><p>Notes:</p><ul><li>Use the actual public IP of your WireGuard server. MWAN3 rules match IPs, not hostnames.</li><li>If you have multiple WAN policies (e.g., <code>wan_only</code>, <code>wphone_only</code>), choose the one that must carry the VPN handshake.</li></ul><ol start=3><li>(Optional) Assign a metric on the WireGuard interface</li></ol><p>This is not strictly required for the fix but keeps routing behavior deterministic when multiple defaults exist.</p><p>Edit <code>/etc/config/network</code>:</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-fallback data-lang=fallback><span style=display:flex><span>config interface &#39;wireguard&#39;
</span></span></code></pre></div><p>Notes:</p><ul><li>Use the actual public IP of your WireGuard server. MWAN3 rules match IPs, not hostnames.</li><li>If you have multiple WAN policies (e.g., <code>wan_only</code>, <code>wphone_only</code>), choose the one that must carry the VPN handshake.</li></ul><ol start=3><li>(Optional) Assign a metric on the WireGuard interface</li></ol><p>This is not strictly required for the fix but keeps routing behavior deterministic when multiple defaults exist.</p><p>Edit <code>/etc/config/network</code>:</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-fallback data-lang=fallback><span style=display:flex><span>config interface &#39;wireguard&#39;
</span></span><span style=display:flex><span> option proto &#39;wireguard&#39;
</span></span><span style=display:flex><span> option private_key &#39;&lt;redacted&gt;&#39;
</span></span><span style=display:flex><span> list addresses &#39;192.168.3.2/32&#39;
</span></span><span style=display:flex><span> option metric &#39;5&#39;
</span></span></code></pre></div><ol start=4><li>Apply changes</li></ol><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>/etc/init.d/network restart <span style=color:#ff7b72;font-weight:700>&amp;&amp;</span> /etc/init.d/mwan3 restart
</span></span></code></pre></div><ol start=4><li>Apply changes</li></ol><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-bash data-lang=bash><span style=display:flex><span>/etc/init.d/network restart <span style=color:#ff7b72;font-weight:700>&amp;&amp;</span> /etc/init.d/mwan3 restart
</span></span></code></pre></div><h3 id=validation>Validation
<a class=heading-link href=#validation><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i>
<span class=sr-only>Link to heading</span></a></h3><p>Confirm that the endpoint is routed via a physical WAN and that the tunnel is passing traffic.</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span><span style=color:#8b949e;font-style:italic># Verify policy routing for the endpoint</span>
<span class=sr-only>Link to heading</span></a></h3><p>Confirm that the endpoint is routed via a physical WAN and that the tunnel is passing traffic.</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-bash data-lang=bash><span style=display:flex><span><span style=color:#8b949e;font-style:italic># Verify policy routing for the endpoint</span>
</span></span><span style=display:flex><span>ip route get 203.0.113.55
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#8b949e;font-style:italic># MWAN3 status should show your WANs online</span>
@@ -60,7 +60,7 @@ When using WireGuard together with MWAN3 on OpenWrt, the tunnel can fail to esta
<a class=heading-link href=#operational-considerations><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i>
<span class=sr-only>Link to heading</span></a></h3><ul><li>Endpoint IP changes: If the server endpoint is behind DDNS, you must update the rule when its IP changes. Options include:<ul><li>Use a small script triggered by DDNS updates to modify the MWAN3 rule and reload.</li><li>Maintain an IP set and populate it from DDNS; match the set in firewall/PBR and keep MWAN3 in sync.</li></ul></li><li>IPv6: Repeat the approach with an IPv6 rule if your peer uses IPv6. Ensure <code>family 'ipv6'</code> and the correct policy are set.</li><li>Multiple peers: Create one rule per peer endpoint IP.</li><li>Ordering: Keep the endpoint rule above broad default rules so it always wins.</li></ul><h3 id=minimal-example-config-snippets-sanitized>Minimal Example Config Snippets (Sanitized)
<a class=heading-link href=#minimal-example-config-snippets-sanitized><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i>
<span class=sr-only>Link to heading</span></a></h3><p><code>/etc/config/network</code> (relevant parts):</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-fallback data-lang=fallback><span style=display:flex><span>config interface &#39;wireguard&#39;
<span class=sr-only>Link to heading</span></a></h3><p><code>/etc/config/network</code> (relevant parts):</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-fallback data-lang=fallback><span style=display:flex><span>config interface &#39;wireguard&#39;
</span></span><span style=display:flex><span> option proto &#39;wireguard&#39;
</span></span><span style=display:flex><span> option private_key &#39;&lt;redacted&gt;&#39;
</span></span><span style=display:flex><span> list addresses &#39;192.168.3.2/32&#39;
@@ -74,7 +74,7 @@ When using WireGuard together with MWAN3 on OpenWrt, the tunnel can fail to esta
</span></span><span style=display:flex><span> list allowed_ips &#39;0.0.0.0/1&#39;
</span></span><span style=display:flex><span> list allowed_ips &#39;128.0.0.0/1&#39;
</span></span><span style=display:flex><span> option route_allowed_ips &#39;1&#39;
</span></span></code></pre></div><p><code>/etc/config/mwan3</code> (relevant parts):</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-fallback data-lang=fallback><span style=display:flex><span>config policy &#39;wan_only&#39;
</span></span></code></pre></div><p><code>/etc/config/mwan3</code> (relevant parts):</p><div class=highlight><pre tabindex=0 style=color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none><code class=language-fallback data-lang=fallback><span style=display:flex><span>config policy &#39;wan_only&#39;
</span></span><span style=display:flex><span> list use_member &#39;wan_m1_w3&#39;
</span></span><span style=display:flex><span> option last_resort &#39;unreachable&#39;
</span></span><span style=display:flex><span>
@@ -98,4 +98,4 @@ When using WireGuard together with MWAN3 on OpenWrt, the tunnel can fail to esta
2016 -
2026
Eric X. Liu
<a href="https://git.ericxliu.me/eric/ericxliu-me/commit/6100dca">[6100dca]</a></section></footer></main><script src=/js/coder.min.6ae284be93d2d19dad1f02b0039508d9aab3180a12a06dcc71b0b0ef7825a317.js integrity="sha256-auKEvpPS0Z2tHwKwA5UI2aqzGAoSoG3McbCw73gloxc="></script><script defer src=https://static.cloudflareinsights.com/beacon.min.js data-cf-beacon='{"token": "987638e636ce4dbb932d038af74c17d1"}'></script></body></html>
<a href="https://git.ericxliu.me/eric/ericxliu-me/commit/45629c5">[45629c5]</a></section></footer></main><script src=/js/coder.min.6ae284be93d2d19dad1f02b0039508d9aab3180a12a06dcc71b0b0ef7825a317.js integrity="sha256-auKEvpPS0Z2tHwKwA5UI2aqzGAoSoG3McbCw73gloxc="></script><script defer src=https://static.cloudflareinsights.com/beacon.min.js data-cf-beacon='{"token": "987638e636ce4dbb932d038af74c17d1"}'></script></body></html>