sessiond: never reapply clat — it destroys the carrier's own routes
Measured on the phone. After one PreferLink the clat device was up with its 192.0.0.1/32 address and *zero* routes: no default, and no 205.151.11.13/32, which is the only path to the MMS proxy. ip route get 205.151.11.13 went out wlan0. Restored by restarting blueline-clat.service. nmcli device reapply resets a device to its connection's config, so it is destructive to precisely the routes a sidecar daemon owns - and the CLAT daemon owns all of clat's. TASK-49 acceptance #2 says carrier services ride the carrier; this broke it on the first action it ever took. The gsm connection was the wrong target too. The modem is v6-only and the v4 default for cellular is installed by the CLAT daemon as 'default dev clat scope link metric 2048', not by the gsm connection, so a route-metric set there is a number nothing reads. Wifi's metric is the only lever: under 2048 wifi wins, over it the carrier does. One connection modified, one device reapplied, and the carrier's routes left alone.
This commit is contained in:
parent
ac084b461a
commit
7e91de1956
1 changed files with 42 additions and 24 deletions
|
|
@ -378,47 +378,65 @@ fn execute(shared: &Arc<Shared>, action: Action) {
|
||||||
run_executor(shared, program, &args, label, action);
|
run_executor(shared, program, &args, label, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Route metrics for the two bearers.
|
/// The cellular v4 default's metric, which is a fixed reference point rather
|
||||||
|
/// than something to set.
|
||||||
///
|
///
|
||||||
/// The numbers are NetworkManager's own defaults for the winning case (wifi
|
/// The modem is v6-only; the v4 default for the carrier is installed by the
|
||||||
/// 600), with the loser pushed clear of NM's +20000 connectivity penalty so a
|
/// CLAT daemon on the `clat` tun as `default dev clat scope link metric 2048`,
|
||||||
/// penalised-but-preferred link still beats the demoted one. That penalty is
|
/// not by the `gsm` connection. So the *only* lever needed is wifi's metric:
|
||||||
/// what inverted the metrics on 2026-07-31: wifi went to 20600 and the clat
|
/// below 2048 it wins, above 2048 the carrier does. Setting anything on the
|
||||||
/// default at 2048 won, on a link that could not resolve.
|
/// gsm connection would be adjusting a number nothing reads.
|
||||||
|
const CLAT_DEFAULT_METRIC: u32 = 2048;
|
||||||
|
|
||||||
|
/// Wifi's metric when it should win: NetworkManager's own default, and
|
||||||
|
/// comfortably under the clat default.
|
||||||
const METRIC_PREFERRED: &str = "600";
|
const METRIC_PREFERRED: &str = "600";
|
||||||
|
|
||||||
|
/// Wifi's metric when the carrier should win. Above 2048, and clear of NM's
|
||||||
|
/// +20000 connectivity penalty so a penalised link cannot accidentally land
|
||||||
|
/// back under the CLAT default. That penalty is what inverted things on
|
||||||
|
/// 2026-07-31: wifi went to 20600 and the clat default won on a link that
|
||||||
|
/// could not resolve.
|
||||||
const METRIC_DEMOTED: &str = "30000";
|
const METRIC_DEMOTED: &str = "30000";
|
||||||
|
|
||||||
/// Tell NetworkManager which link ordinary traffic should take.
|
/// Tell NetworkManager which link ordinary traffic should take.
|
||||||
///
|
///
|
||||||
/// Both connections are modified every time rather than only the winner: the
|
/// **Only the wifi connection is touched, and only `wlan0` is reapplied.** An
|
||||||
/// loser's metric is half the decision, and leaving it at whatever it happened
|
/// earlier version also modified the `gsm` connection and reapplied `clat`, and
|
||||||
/// to be is how the original inversion survived.
|
/// that reapply wiped every route the CLAT daemon had installed out-of-band —
|
||||||
|
/// including the `205.151.11.13/32` pin that is the only path to the MMS proxy.
|
||||||
|
/// Measured on the phone 2026-08-01: after one reapply the clat device was up
|
||||||
|
/// with an address and *zero* routes, and the carrier path was gone until
|
||||||
|
/// `blueline-clat.service` was restarted. `nmcli device reapply` resets a
|
||||||
|
/// device to its connection's config, so it is destructive to exactly the
|
||||||
|
/// routes a sidecar daemon owns. Do not reapply a device whose routes NM did
|
||||||
|
/// not write.
|
||||||
fn apply_link_preference(shared: &Arc<Shared>, bearer: crate::sessiond::bearer::Bearer) {
|
fn apply_link_preference(shared: &Arc<Shared>, bearer: crate::sessiond::bearer::Bearer) {
|
||||||
use crate::sessiond::bearer::Bearer;
|
use crate::sessiond::bearer::Bearer;
|
||||||
let (wifi_metric, wwan_metric) = match bearer {
|
debug_assert!(METRIC_PREFERRED.parse::<u32>().unwrap() < CLAT_DEFAULT_METRIC);
|
||||||
Bearer::Wifi => (METRIC_PREFERRED, METRIC_DEMOTED),
|
debug_assert!(METRIC_DEMOTED.parse::<u32>().unwrap() > CLAT_DEFAULT_METRIC);
|
||||||
Bearer::Cellular => (METRIC_DEMOTED, METRIC_PREFERRED),
|
|
||||||
|
let metric = match bearer {
|
||||||
|
Bearer::Wifi => METRIC_PREFERRED,
|
||||||
|
Bearer::Cellular => METRIC_DEMOTED,
|
||||||
};
|
};
|
||||||
for (kind, metric) in [("802-11-wireless", wifi_metric), ("gsm", wwan_metric)] {
|
for conn in active_connections_of_type("802-11-wireless") {
|
||||||
for conn in active_connections_of_type(kind) {
|
let _ = std::process::Command::new("nmcli")
|
||||||
let _ = std::process::Command::new("nmcli")
|
.args(["connection", "modify", &conn, "ipv4.route-metric", metric])
|
||||||
.args(["connection", "modify", &conn, "ipv4.route-metric", metric])
|
.status();
|
||||||
.status();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Reapply rather than up/down. Cycling the connection is what made the old
|
// Reapply rather than up/down. Cycling the connection is what made the old
|
||||||
// gate's corrections into NM events that re-entered it; `reapply` changes
|
// gate's corrections into NM events that re-entered it; `reapply` changes
|
||||||
// the live config without a state transition, so nothing observing NM sees
|
// the live config without a state transition, so nothing observing NM sees
|
||||||
// a link flap.
|
// a link flap.
|
||||||
for dev in ["wlan0", "clat"] {
|
let _ = std::process::Command::new("nmcli")
|
||||||
let _ = std::process::Command::new("nmcli")
|
.args(["device", "reapply", "wlan0"])
|
||||||
.args(["device", "reapply", dev])
|
.status();
|
||||||
.status();
|
|
||||||
}
|
|
||||||
shared.lock().device_state.record_error(
|
shared.lock().device_state.record_error(
|
||||||
"bearer",
|
"bearer",
|
||||||
"prefer-link",
|
"prefer-link",
|
||||||
&format!("preferred {}", bearer.as_str()),
|
&format!("preferred {} (wifi route-metric {metric})", bearer.as_str()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue