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:
@@ -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 口)...');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测是否有互联网连接(尝试 DNS 解析 + HTTP 连通性)
|
* 返回当前链路 up 的有线网卡名。
|
||||||
|
* 若设置 CLAWD_ETH_IFACE,只认该接口;否则扫描 sysfs(与仅默认 eth0 相比适配更多板型)。
|
||||||
*/
|
*/
|
||||||
function hasInternet() {
|
function getWiredIfaceWithCarrier() {
|
||||||
// 物理层快检:无 WiFi STA 且有线 carrier=0 → 立即返回 false(nmcli 有缓存,不可信)
|
const explicit = process.env.CLAWD_ETH_IFACE;
|
||||||
if (!isWifiStaConnected() && !hasWiredCarrier()) return false;
|
if (explicit) {
|
||||||
|
try {
|
||||||
|
if (fs.readFileSync(`/sys/class/net/${explicit}/carrier`, 'utf8').trim() === '1') {
|
||||||
|
return explicit;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// 优先用 nmcli 的 connectivity check
|
|
||||||
try {
|
try {
|
||||||
const out = run('nmcli networking connectivity check').trim();
|
const names = fs.readdirSync('/sys/class/net');
|
||||||
if (out === 'full' || out === 'limited') return true;
|
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 (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
// 兜底:ping DNS
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否存在任一带 carrier 的有线接口(无延迟)
|
||||||
|
*/
|
||||||
|
function hasWiredCarrier() {
|
||||||
|
return getWiredIfaceWithCarrier() !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _tryPingInternet() {
|
||||||
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 → 立即 false(nmcli 有缓存,不可信)
|
||||||
|
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,
|
||||||
|
|||||||
Reference in New Issue
Block a user