Skip to content

client: policy routing on Linux - #492

Draft
kp-antonio-yang wants to merge 7 commits into
mainfrom
policy-routing
Draft

client: policy routing on Linux#492
kp-antonio-yang wants to merge 7 commits into
mainfrom
policy-routing

Conversation

@kp-antonio-yang

@kp-antonio-yang kp-antonio-yang commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Description

Introduces RouteMode::Fwmark (Linux only) and makes it the default route mode on Linux. Instead of the reactive /32 host-route approach, four ip rule entries are installed before any tunnel route is added:

Rule MARKED:          fwmark <MARK>      → main         # tunnel socket → WAN
Rule SERVER:          to <SERVER_IP>/32  → main         # rp_filter fix
Rule MARKED_FALLBACK: fwmark <MARK>      → unreachable  # loop-breaker (roam guard)
Rule TUNNEL:          (no condition)     → <TABLE>      # all other traffic → tunnel

On Linux (non-mobile), the outside UDP socket is always stamped with SO_MARK when fwmark != 0, regardless of route mode. Under RouteMode::Fwmark this causes Rule MARKED to steer the socket's packets via main; because the kernel owns the main-table default route, no host route is installed and there is nothing to race against during a roam. In RouteMode::NoExec the four ip rules are never installed, so the mark is present on the socket but has no routing effect, and in this mode SO_MARK can be overwriten and turn it off by --fwmark=0 in command line.

Additional changes in this PR:

  • FWMarkConfig parameters are user-configurablefwmark, fwmark_route_table, rule_priority_server, rule_priority_fwmark_fallback, and rule_priority_tunnel are exposed as config file and CLI options. Config::validate() enforces that priorities are in the required ordering.
  • Tunnel table registered in /etc/iproute2/rt_tablesregister_rt_table/unregister_rt_table helpers register the tunnel routing table under the human-readable name lightway-tunnel so that ip rule show displays from all lookup lightway-tunnel instead of a bare numeric id. Registration failures are logged but do not abort rule installation.
  • DTLS debug log added — a debug log in lightway-core/src/connection.rs surfaces silent DTLS record drops (0 frames decoded, treated as a replay attack) to aid diagnosis of UDP session recovery failures after NAT rebinds.
  • Documentationdocs/policy_routing.md covers the race condition, the four ip rules and their roles, a full packet lifetime trace, RouteMode comparison, FWMarkConfig parameters, and initialization/shutdown ordering.

Motivation and Context

Without fwmark mode, the tunnel avoids routing its own encrypted packets back into itself via a Lightway-managed /32 host route for the VPN server. After a network change (Wi-Fi roam, NAT rebind) there is a window where that host route is gone but not yet replaced: the server IP matches the tunnel catch-all, packets are re-encapsulated each lap, and the link saturates.

RouteMode::Fwmark eliminates this race entirely — the ip rules are independent of route table state and require no teardown/reinstall during roams. Rule MARKED_FALLBACK additionally breaks any residual encapsulation loop during a roam when the main table has no route at all, returning ENETUNREACH and treating it as a transient send failure.

How Has This Been Tested?

  • Verified ip rule show displays named table entries (lightway-tunnel) after connection, and that they are cleaned up on disconnect.
  • Verified no encapsulation loop occurs during a simulated Wi-Fi roam (disconnect/reconnect) with RouteMode::Fwmark active.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • The correct base branch is being used, if not main
  • Any update to xenon/libxenon-src is accompanied by a reference to the specific commit in the git changelog

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code coverage summary for dabc993:

Filename                                                          Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
lightway-app-utils/src/args/cipher.rs                                   5                 5     0.00%           1                 1     0.00%           5                 5     0.00%           0                 0         -
lightway-app-utils/src/args/connection_type.rs                         11                 5    54.55%           3                 1    66.67%          11                 5    54.55%           0                 0         -
lightway-app-utils/src/args/duration.rs                               121                22    81.82%          11                 5    54.55%          64                15    76.56%           0                 0         -
lightway-app-utils/src/args/ip_map.rs                                  27                27     0.00%           5                 5     0.00%          19                19     0.00%           0                 0         -
lightway-app-utils/src/args/keyshare.rs                                 5                 5     0.00%           1                 1     0.00%           5                 5     0.00%           0                 0         -
lightway-app-utils/src/args/logging.rs                                 37                37     0.00%           3                 3     0.00%          31                31     0.00%           0                 0         -
lightway-app-utils/src/args/nonzero_duration.rs                       128                24    81.25%          10                 4    60.00%          68                16    76.47%           0                 0         -
lightway-app-utils/src/cmsg.rs                                        238                63    73.53%          20                 5    75.00%         188                49    73.94%           0                 0         -
lightway-app-utils/src/connection_ticker.rs                           229                17    92.58%          28                 4    85.71%         126                15    88.10%           0                 0         -
lightway-app-utils/src/dplpmtud_timer.rs                              209                13    93.78%          22                 4    81.82%         117                11    90.60%           0                 0         -
lightway-app-utils/src/event_stream.rs                                 19                 0   100.00%           3                 0   100.00%          11                 0   100.00%           0                 0         -
lightway-app-utils/src/metrics.rs                                       4                 4     0.00%           2                 2     0.00%           4                 4     0.00%           0                 0         -
lightway-app-utils/src/network_change_monitor.rs                      348                87    75.00%          29                 8    72.41%         200                75    62.50%           0                 0         -
lightway-app-utils/src/packet_codec.rs                                  2                 2     0.00%           1                 1     0.00%           1                 1     0.00%           0                 0         -
lightway-app-utils/src/sockopt/ip_mtu_discover.rs                      81                81     0.00%           6                 6     0.00%          72                72     0.00%           0                 0         -
lightway-app-utils/src/sockopt/ip_pktinfo.rs                           14                 1    92.86%           1                 0   100.00%          16                 1    93.75%           0                 0         -
lightway-app-utils/src/tun.rs                                         478               478     0.00%          48                48     0.00%         306               306     0.00%           0                 0         -
lightway-app-utils/src/utils.rs                                        21                21     0.00%           1                 1     0.00%          11                11     0.00%           0                 0         -
lightway-boring/src/aes256.rs                                         224                 5    97.77%          13                 0   100.00%          97                 0   100.00%           0                 0         -
lightway-boring/src/cert.rs                                            15                 1    93.33%           1                 0   100.00%           7                 0   100.00%           0                 0         -
lightway-boring/src/chacha20_poly1305.rs                              232                 6    97.41%          16                 3    81.25%         107                 3    97.20%           0                 0         -
lightway-boring/src/config.rs                                         111                17    84.68%          16                 4    75.00%          82                14    82.93%           0                 0         -
lightway-boring/src/context.rs                                        391                89    77.24%          31                 6    80.65%         247                35    85.83%           0                 0         -
lightway-boring/src/error.rs                                           37                 8    78.38%           4                 2    50.00%          28                 6    78.57%           0                 0         -
lightway-boring/src/session.rs                                        821               167    79.66%          53                 8    84.91%         448               101    77.46%           0                 0         -
lightway-boring/src/test_utils.rs                                     329                35    89.36%          25                 5    80.00%         210                24    88.57%           0                 0         -
lightway-boring/src/types.rs                                          137                10    92.70%          16                 1    93.75%          92                 9    90.22%           0                 0         -
lightway-client/src/config.rs                                         495               117    76.36%          25                 9    64.00%         344                79    77.03%           0                 0         -
lightway-client/src/dns_manager.rs                                     18                18     0.00%           4                 4     0.00%          16                16     0.00%           0                 0         -
lightway-client/src/io/inside.rs                                        3                 3     0.00%           1                 1     0.00%           3                 3     0.00%           0                 0         -
lightway-client/src/io/inside/tun.rs                                   86                86     0.00%          13                13     0.00%          63                63     0.00%           0                 0         -
lightway-client/src/io/outside.rs                                      14                14     0.00%           2                 2     0.00%          12                12     0.00%           0                 0         -
lightway-client/src/io/outside/tcp.rs                                 131               131     0.00%          15                15     0.00%          86                86     0.00%           0                 0         -
lightway-client/src/io/outside/udp.rs                                 225               225     0.00%          22                22     0.00%         154               154     0.00%           0                 0         -
lightway-client/src/io/outside/udp/batch_receive.rs                    99                13    86.87%          11                 1    90.91%          84                13    84.52%           0                 0         -
lightway-client/src/keepalive.rs                                      687                45    93.45%          59                 6    89.83%         357                24    93.28%           0                 0         -
lightway-client/src/lib.rs                                           1486               910    38.76%         114                65    42.98%        1062               716    32.58%           0                 0         -
lightway-client/src/main.rs                                           277               277     0.00%          17                17     0.00%         157               157     0.00%           0                 0         -
lightway-client/src/platform/linux/dns_manager.rs                     118                91    22.88%          14                 9    35.71%          87                62    28.74%           0                 0         -
lightway-client/src/platform/linux/dns_manager/direct_file.rs         106                42    60.38%          12                 6    50.00%          65                29    55.38%           0                 0         -
lightway-client/src/platform/linux/dns_manager/resolvconf.rs           61                61     0.00%           8                 8     0.00%          52                52     0.00%           0                 0         -
lightway-client/src/platform/linux/dns_manager/resolvectl.rs           41                41     0.00%           5                 5     0.00%          34                34     0.00%           0                 0         -
lightway-client/src/policy_routing.rs                                 223               223     0.00%          19                19     0.00%         183               183     0.00%           0                 0         -
lightway-client/src/route_manager.rs                                 1188               149    87.46%          80                 4    95.00%         664                75    88.70%           0                 0         -
lightway-core/src/borrowed_bytesmut.rs                                373                 0   100.00%          24                 0   100.00%         185                 0   100.00%           0                 0         -
lightway-core/src/builder_predicates.rs                                24                12    50.00%           4                 2    50.00%          24                12    50.00%           0                 0         -
lightway-core/src/cipher.rs                                           203                21    89.66%          10                 0   100.00%         101                 4    96.04%           0                 0         -
lightway-core/src/connection.rs                                      2120               829    60.90%          95                22    76.84%        1519               566    62.74%           0                 0         -
lightway-core/src/connection/builders.rs                              252                46    81.75%          22                 8    63.64%         260                53    79.62%           0                 0         -
lightway-core/src/connection/dplpmtud.rs                             1741                81    95.35%          63                 0   100.00%         830                 7    99.16%           0                 0         -
lightway-core/src/connection/expresslane.rs                            88                 0   100.00%          10                 0   100.00%          74                 0   100.00%           0                 0         -
lightway-core/src/connection/fragment_map.rs                          366                 6    98.36%          25                 0   100.00%         254                 3    98.82%           0                 0         -
lightway-core/src/connection/io_adapter.rs                            882               154    82.54%          56                 9    83.93%         470                98    79.15%           0                 0         -
lightway-core/src/connection/key_update.rs                             34                13    61.76%           5                 0   100.00%          38                19    50.00%           0                 0         -
lightway-core/src/context.rs                                          219                42    80.82%          29                 8    72.41%         243                46    81.07%           0                 0         -
lightway-core/src/context/ip_pool.rs                                    8                 3    62.50%           1                 0   100.00%           5                 0   100.00%           0                 0         -
lightway-core/src/context/server_auth.rs                               37                37     0.00%           4                 4     0.00%          26                26     0.00%           0                 0         -
lightway-core/src/encoding_request_states.rs                            3                 0   100.00%           1                 0   100.00%           3                 0   100.00%           0                 0         -
lightway-core/src/gso.rs                                             1104                88    92.03%          41                 3    92.68%         519                55    89.40%           0                 0         -
lightway-core/src/io.rs                                                10                10     0.00%           3                 3     0.00%           9                 9     0.00%           0                 0         -
lightway-core/src/keyshare.rs                                           5                 0   100.00%           1                 0   100.00%           5                 0   100.00%           0                 0         -
lightway-core/src/lib.rs                                                9                 0   100.00%           3                 0   100.00%           9                 0   100.00%           0                 0         -
lightway-core/src/metrics.rs                                           81                66    18.52%          33                27    18.18%          73                58    20.55%           0                 0         -
lightway-core/src/packet.rs                                            38                10    73.68%           4                 1    75.00%          30                 6    80.00%           0                 0         -
lightway-core/src/plugin.rs                                           307                17    94.46%          22                 4    81.82%         148                10    93.24%           0                 0         -
lightway-core/src/tls/mod.rs                                            4                 4     0.00%           1                 1     0.00%           3                 3     0.00%           0                 0         -
lightway-core/src/utils.rs                                            341                28    91.79%          24                 3    87.50%         170                18    89.41%           0                 0         -
lightway-core/src/version.rs                                           93                 0   100.00%          17                 0   100.00%          82                 0   100.00%           0                 0         -
lightway-core/src/wire.rs                                             438                39    91.10%          29                 0   100.00%         235                 9    96.17%           0                 0         -
lightway-core/src/wire/auth_failure.rs                                 27                 1    96.30%           3                 0   100.00%          17                 0   100.00%           0                 0         -
lightway-core/src/wire/auth_request.rs                                684                57    91.67%          34                 1    97.06%         351                24    93.16%           0                 0         -
lightway-core/src/wire/auth_success_with_config_ipv4.rs               222                 3    98.65%          11                 0   100.00%         124                 0   100.00%           0                 0         -
lightway-core/src/wire/data.rs                                         51                 1    98.04%           5                 0   100.00%          33                 0   100.00%           0                 0         -
lightway-core/src/wire/data_frag.rs                                   127                 1    99.21%          14                 0   100.00%          80                 0   100.00%           0                 0         -
lightway-core/src/wire/encoding_request.rs                             82                 2    97.56%           6                 0   100.00%          42                 1    97.62%           0                 0         -
lightway-core/src/wire/encoding_response.rs                            82                 2    97.56%           6                 0   100.00%          42                 1    97.62%           0                 0         -
lightway-core/src/wire/expresslane_config.rs                          188                 1    99.47%          10                 0   100.00%         113                 0   100.00%           0                 0         -
lightway-core/src/wire/expresslane_data.rs                             48                21    56.25%           5                 2    60.00%          52                23    55.77%           0                 0         -
lightway-core/src/wire/ping.rs                                         95                 2    97.89%           7                 0   100.00%          59                 0   100.00%           0                 0         -
lightway-core/src/wire/pong.rs                                        109                 2    98.17%           8                 0   100.00%          72                 0   100.00%           0                 0         -
lightway-core/src/wire/server_config.rs                                69                 2    97.10%           4                 0   100.00%          37                 0   100.00%           0                 0         -
lightway-expresslane/src/aead.rs                                       29                29     0.00%           2                 2     0.00%          23                23     0.00%           0                 0         -
lightway-expresslane/src/aead/pool.rs                                 127                71    44.09%           9                 4    55.56%          90                50    44.44%           0                 0         -
lightway-expresslane/src/key.rs                                        24                 0   100.00%           5                 0   100.00%          17                 0   100.00%           0                 0         -
lightway-expresslane/src/replay_window.rs                             269                 4    98.51%          20                 0   100.00%         174                 4    97.70%           0                 0         -
lightway-expresslane/src/session.rs                                   469               195    58.42%          19                 2    89.47%         283               114    59.72%           0                 0         -
lightway-expresslane/src/version.rs                                    32                 0   100.00%           4                 0   100.00%          25                 0   100.00%           0                 0         -
lightway-server/src/auth.rs                                           273                49    82.05%          22                 6    72.73%         171                28    83.63%           0                 0         -
lightway-server/src/config.rs                                         159                 0   100.00%          11                 0   100.00%         130                 0   100.00%           0                 0         -
lightway-server/src/connection.rs                                     137               137     0.00%          11                11     0.00%         110               110     0.00%           0                 0         -
lightway-server/src/connection_manager.rs                             346               346     0.00%          42                42     0.00%         303               303     0.00%           0                 0         -
lightway-server/src/connection_manager/connection_map.rs              407                20    95.09%          21                 1    95.24%         228                 8    96.49%           0                 0         -
lightway-server/src/io/inside.rs                                      100                21    79.00%          23                 9    60.87%          69                19    72.46%           0                 0         -
lightway-server/src/io/inside/tun.rs                                   61                61     0.00%          14                14     0.00%          45                45     0.00%           0                 0         -
lightway-server/src/io/outside/tcp.rs                                  93                93     0.00%          10                10     0.00%          68                68     0.00%           0                 0         -
lightway-server/src/io/outside/udp.rs                                 483               428    11.39%          27                24    11.11%         331               297    10.27%           0                 0         -
lightway-server/src/io/outside/udp/batch_receive.rs                   467                19    95.93%          20                 1    95.00%         266                15    94.36%           0                 0         -
lightway-server/src/io/outside/udp/send_queue.rs                      381                26    93.18%          24                 1    95.83%         209                16    92.34%           0                 0         -
lightway-server/src/ip_manager.rs                                     542                50    90.77%          22                 4    81.82%         242                23    90.50%           0                 0         -
lightway-server/src/ip_manager/ip_pool.rs                             545                 0   100.00%          28                 0   100.00%         257                 0   100.00%           0                 0         -
lightway-server/src/lib.rs                                            606               502    17.16%          34                26    23.53%         414               341    17.63%           0                 0         -
lightway-server/src/main.rs                                           201               201     0.00%          10                10     0.00%          96                96     0.00%           0                 0         -
lightway-server/src/metrics.rs                                        287               265     7.67%          99                89    10.10%         243               223     8.23%           0                 0         -
lightway-server/src/offload_stats.rs                                  125                74    40.80%           8                 3    62.50%          82                42    48.78%           0                 0         -
lightway-server/src/statistics.rs                                     133                59    55.64%           8                 4    50.00%          91                39    57.14%           0                 0         -
uniffi-bindgen/src/uniffi-bindgen.rs                                    3                 3     0.00%           1                 1     0.00%           3                 3     0.00%           0                 0         -
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL                                                               26395              7930    69.96%        1925               696    63.84%       16303              5509    66.21%           0                 0         -

✅ Region coverage 69% passes
✅ Line coverage 66% passes

@kp-antonio-yang
kp-antonio-yang force-pushed the policy-routing branch 20 times, most recently from 2d54621 to 2a98efd Compare July 30, 2026 08:54
@kp-antonio-yang
kp-antonio-yang marked this pull request as ready for review July 30, 2026 10:17
@kp-antonio-yang
kp-antonio-yang requested a review from a team as a code owner July 30, 2026 10:17
@kp-antonio-yang
kp-antonio-yang marked this pull request as draft August 4, 2026 06:41
@kp-antonio-yang
kp-antonio-yang force-pushed the policy-routing branch 2 times, most recently from afa6a96 to c454693 Compare August 4, 2026 09:19
@kp-antonio-yang
kp-antonio-yang force-pushed the policy-routing branch 3 times, most recently from f63efa6 to 51c68eb Compare August 4, 2026 09:49
@kp-antonio-yang
kp-antonio-yang marked this pull request as ready for review August 4, 2026 09:55
@kp-antonio-yang
kp-antonio-yang marked this pull request as draft August 6, 2026 06:12
@kp-antonio-yang
kp-antonio-yang force-pushed the policy-routing branch 5 times, most recently from 4410022 to e264dfe Compare August 12, 2026 02:55
@kp-antonio-yang kp-antonio-yang changed the title Policy routing client: policy routing on Linux Aug 28, 2026
Diagnostic `debug!` log in `lightway-core/src/connection.rs` to surface DTLS
silent drops. 0 frames decoded and treated as a replay attack in server
side. It is useful for diagnosing UDP session recovery failures after NAT rebinds.
Without fwmark mode, the tunnel keeps its own encrypted packets out of
the tunnel via a Lightway-managed /32 host route for the VPN server.
After a network change there is a window where that route is gone but
not yet replaced: the server IP matches the tunnel catch-all, packets
are re-encapsulated each lap, and the link saturates.

RouteMode::Fwmark (Linux only) fixes this by installing four ip rules
before any tunnel route is added:

  MARKED:          fwmark <MARK>       → main         # tunnel socket uses WAN route
  SERVER:          to <SERVER_IP>/32   → main         # rp_filter: accept server replies
  MARKED_FALLBACK: fwmark <MARK>       → unreachable  # drop marked traffic if main is empty
  TUNNEL:          (any)               → <TABLE>      # everything else goes through tunnel

The outside socket is stamped with SO_MARK so rule MARKED always applies.
The kernel owns the main table default route, so no host route is
installed and there is nothing to race against.

Rule SERVER prevents strict rp_filter from dropping inbound server packets
when main is the only table that routes the server IP correctly.
Rule MARKED_FALLBACK breaks the encapsulation loop during a roam when main has no
route at all: ENETUNREACH is treated as a transient send failure.
- Expose fwmark, fwmark_route_table, rule_priority_server,
rule_priority_fwmark_fallback, and rule_priority_tunnel as config and
CLI options. Defaults
- Config::validate() enforces the priorities are in required ordering
Add register_rt_table/unregister_rt_table helpers so `ip rule show`
displays `from all lookup lightway-tunnel` instead of a bare numeric
table id. Only the tunnel routing table is registered, since ip rules
themselves have no name field and are identified by priority.

register_rt_table:
  - In debug builds, reads rt_tables first: no-ops if the entry is
    already present with the correct id, errors if it exists with a
    different id.
  - In release builds, appends the line unconditionally — duplicates
    are harmless because the kernel never reads this file.
  - Called during install(); failures are logged as error but do not
    abort rule installation, since the numeric table id works fine
    without a registered name.

unregister_rt_table:
  - Strips every line matching the table name on cleanup.
  - Read/write errors are warned and swallowed so cleanup never aborts.
set up Fwmark as RouteMode::Default on Linux to avoid roaming issue
Add a new docs/policy_routing.md covering the firewall-mark-based policy
routing that Lightway uses on Linux to eliminate the encapsulation loop
race condition during Wi-Fi roams.

The document explains:
  - The race condition inherent to the reactive /32 host-route approach
  - The four ip rules (MARKED, SERVER, MARKED_FALLBACK, TUNNEL) and how
    each steers packets via fwmark and the private tunnel routing table
  - A full packet lifetime trace (outbound and inbound) for a ping
  - RouteMode comparison (Fwmark vs Default vs Lan vs NoExec)
  - FWMarkConfig parameters, defaults, and validation constraints
  - Initialization/shutdown ordering and key source files

Also link the new page in the docs README table of contents.
…e loss

Adds a Docker Compose stack and test script that simulate a client
switching WiFi networks by deleting the active physical interface while
the VPN is running, then verifying the client reconnects over the
surviving interface.

The compose file (docker-compose.wifi-switch.yml) attaches both the
server and client to two bridge networks (frontend 192.168.200.0/24 and
frontend2 192.168.201.0/24), giving the client container two physical
interfaces that can each independently reach the server.

The test script (run-wifi-switch-test.sh):
- Verifies VPN masquerade is working before the switch
- Dynamically detects the active interface via `ip route get` rather
  than hardcoding eth0, since Docker does not guarantee network ordering
- Deletes that interface to trigger a network change event in the client
- Polls for reconnection (up to 60s) and fails fast once it succeeds
- Re-verifies VPN masquerade is working after reconnection
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants