fix(network): auto-detect wired iface and ping via -I for hasInternet

- Scan sysfs for carrier when CLAWD_ETH_IFACE unset (end0/enp* etc.)
- Explicit CLAWD_ETH_IFACE still pins a single interface
- Ping fallback uses -I wired iface when default route fails (e.g. AP on wlan)
- Exclude can/docker/veth/wl* and similar from auto scan
- Clarify client log when waiting for WAN

Made-with: Cursor
This commit is contained in:
stswangzhiping
2026-03-27 14:30:23 +08:00
parent 88283ad6b6
commit 12366790d2
2 changed files with 69 additions and 20 deletions

View File

@@ -110,7 +110,7 @@ class ClawClient {
this._connectionStarted = true; this._connectionStarted = true;
await this._proceedWithConnection(); await this._proceedWithConnection();
} else if (!hasInternet()) { } else if (!hasInternet()) {
log.info('clawd', '等待网就绪(WiFi 配网或网线接入...'); log.info('clawd', '等待网就绪(可配网;有线已接时将自动识别网卡,含非 eth0 口...');
} }
} }

View File

@@ -9,43 +9,91 @@ const AP_SSID_PREFIX = 'ClawBox-';
const AP_IP = '10.42.0.1'; const AP_IP = '10.42.0.1';
const AP_PASSWORD = '12345678'; const AP_PASSWORD = '12345678';
const AP_IFACE = process.env.CLAWD_WIFI_IFACE || ''; const AP_IFACE = process.env.CLAWD_WIFI_IFACE || '';
const ETH_IFACE = process.env.CLAWD_ETH_IFACE || 'eth0';
const CON_NAME = 'clawd-hotspot'; const CON_NAME = 'clawd-hotspot';
/** /** 非 WiFi、非典型虚拟接口用于自动发现有线口如 end0、enp*,而非仅 eth0 */
* 检查有线网卡物理链路是否接通(读 sysfs carrier无延迟 function _isExcludedVirtualIface(name) {
*/ if (name === 'lo' || name === 'bonding_masters') return true;
function hasWiredCarrier() { if (name.startsWith('wl')) return true;
try { if (name.startsWith('docker')) return true;
const carrier = fs.readFileSync(`/sys/class/net/${ETH_IFACE}/carrier`, 'utf8').trim(); if (name.startsWith('veth')) return true;
return carrier === '1'; if (name.startsWith('virbr')) return true;
} catch (_) { if (name.startsWith('br-')) return true;
if (name.startsWith('tun') || name.startsWith('tap')) return true;
if (name.startsWith('wg') || name.startsWith('bond')) return true;
if (name.startsWith('can')) return true;
return false; return false;
} }
/**
* 返回当前链路 up 的有线网卡名。
* 若设置 CLAWD_ETH_IFACE只认该接口否则扫描 sysfs与仅默认 eth0 相比适配更多板型)。
*/
function getWiredIfaceWithCarrier() {
const explicit = process.env.CLAWD_ETH_IFACE;
if (explicit) {
try {
if (fs.readFileSync(`/sys/class/net/${explicit}/carrier`, 'utf8').trim() === '1') {
return explicit;
}
} catch (_) {}
return null;
}
try {
const names = fs.readdirSync('/sys/class/net');
for (const name of names.sort()) {
if (_isExcludedVirtualIface(name)) continue;
try {
if (fs.readFileSync(`/sys/class/net/${name}/carrier`, 'utf8').trim() === '1') {
return name;
}
} catch (_) {}
}
} catch (_) {}
return null;
} }
/** /**
* 检测是否有互联网连接(尝试 DNS 解析 + HTTP 连通性 * 是否存在任一带 carrier 的有线接口(无延迟
*/ */
function hasInternet() { function hasWiredCarrier() {
// 物理层快检:无 WiFi STA 且有线 carrier=0 → 立即返回 falsenmcli 有缓存,不可信) return getWiredIfaceWithCarrier() !== null;
if (!isWifiStaConnected() && !hasWiredCarrier()) return false; }
// 优先用 nmcli 的 connectivity check function _tryPingInternet() {
try {
const out = run('nmcli networking connectivity check').trim();
if (out === 'full' || out === 'limited') return true;
} catch (_) {}
// 兜底ping DNS
try { try {
run('ping -c 1 -W 3 8.8.8.8'); run('ping -c 1 -W 3 8.8.8.8');
return true; return true;
} catch (_) {} } catch (_) {}
// 开热点时默认路由可能走 wlan无 -I 的 ping 会误判;指定有线口再试
const wired = getWiredIfaceWithCarrier();
if (wired) {
try {
run(`ping -c 1 -W 3 -I ${wired} 8.8.8.8`);
return true;
} catch (_) {}
}
return false; return false;
} }
/**
* 检测是否有互联网连接nmcli 连通性 + ping 兜底)
*/
function hasInternet() {
// 物理层快检:无 WiFi STA 且无任何有线 carrier → 立即 falsenmcli 有缓存,不可信)
if (!isWifiStaConnected() && !hasWiredCarrier()) return false;
try {
const out = run('nmcli networking connectivity check').trim();
if (out === 'full' || out === 'limited') return true;
} catch (_) {}
return _tryPingInternet();
}
/** /**
* 获取默认 WiFi 接口名wlan0 等) * 获取默认 WiFi 接口名wlan0 等)
*/ */
@@ -250,6 +298,7 @@ function getLocalIps() {
module.exports = { module.exports = {
hasInternet, hasInternet,
hasWiredCarrier, hasWiredCarrier,
getWiredIfaceWithCarrier,
hasSavedWifiConnection, hasSavedWifiConnection,
isWifiStaConnected, isWifiStaConnected,
getWifiIface, getWifiIface,