Skip to content

Commit 5fd45e9

Browse files
committed
fix(agent): resolve default IPv4 adapter on Win7 without Get-NetRoute
1 parent 691875e commit 5fd45e9

2 files changed

Lines changed: 139 additions & 14 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ python -m agent.main --config configs\agent.json
159159
先定位默认出口网卡,依次尝试:
160160

161161
1. WMI `DefaultIPGateway`
162-
2. PowerShell `Get-NetRoute`
163-
3. PowerShell `Get-NetIPConfiguration`
162+
2. 若本机存在 `Get-NetRoute` cmdlet:PowerShell 默认路由,再 `Get-NetIPConfiguration`(避免仅按系统版本误判,Win7 兼容模式误报为 6.2+ 时不会误调)
163+
3. WMI `Win32_IP4RouteTable`(默认路由的 `InterfaceIndex` → 网卡名,Win7 不依赖 `IPAddress` 字符串反查)
164164
4. `route print -4` + WMI 反查
165165

166166
再用 `netsh interface ipv4` 应用配置:

agent/network_config.py

Lines changed: 137 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,16 @@ def _parse_wmic_list(text: str) -> List[Dict[str, str]]:
5858
if not line or "=" not in line:
5959
continue
6060
k, v = line.split("=", 1)
61-
item[k.strip().lower()] = v.strip()
61+
key = k.strip().lower()
62+
val = v.strip()
63+
# 同一记录里可能出现多行 IPAddress=(数组);后者会覆盖前者导致 Win7 上漏配
64+
if key in item:
65+
if val:
66+
prev_parts = [x.strip() for x in item[key].split(",") if x.strip()]
67+
if val not in prev_parts:
68+
item[key] = item[key] + "," + val
69+
else:
70+
item[key] = val
6271
if item:
6372
rows.append(item)
6473
return rows
@@ -78,6 +87,22 @@ def _clean_wmic_value(s: str) -> str:
7887
return s
7988

8089

90+
def _wmic_multivalue_tokens(s: str) -> List[str]:
91+
"""展开 WMIC 里逗号/花括号数组字段(如 IPAddress),保留全部原子值供匹配。"""
92+
s = (s or "").strip()
93+
if not s:
94+
return []
95+
inner = s[1:-1].strip() if s.startswith("{") and s.endswith("}") else s
96+
out: List[str] = []
97+
for part in re.split(r"\s*,\s*", inner):
98+
p = part.strip()
99+
if p.startswith('"') and p.endswith('"'):
100+
p = p[1:-1]
101+
if p:
102+
out.append(p)
103+
return out
104+
105+
81106
def _wmic_nic_name_for_index(idx: int) -> Tuple[Optional[str], str]:
82107
try:
83108
p2 = subprocess.run(
@@ -159,6 +184,93 @@ def _interface_from_wmi_default_gateway() -> Tuple[Optional[str], str]:
159184
return None, msg
160185

161186

187+
def _windows_has_net_tcpip_ps_cmdlets() -> bool:
188+
"""Get-NetRoute / Get-NetIPConfiguration 需 Win8+(6.2)或 Win10+ 且带 NetTCPIP;Win7 无此 cmdlet。"""
189+
if sys.platform != "win32":
190+
return False
191+
v = sys.getwindowsversion()
192+
if v.major > 6:
193+
return True
194+
if v.major == 6 and v.minor >= 2:
195+
return True
196+
return False
197+
198+
199+
def _powershell_has_get_net_route_cmdlet() -> bool:
200+
"""
201+
是否实际存在 Get-NetRoute(NetTCPIP 模块)。
202+
仅用 getwindowsversion 不可靠:兼容模式/部分环境会误报为 6.2+,Win7 仍会执行失败。
203+
"""
204+
if sys.platform != "win32":
205+
return False
206+
ps = "if (Get-Command Get-NetRoute -ErrorAction SilentlyContinue) { exit 0 } else { exit 1 }"
207+
try:
208+
p = subprocess.run(
209+
[
210+
"powershell",
211+
"-NoProfile",
212+
"-NonInteractive",
213+
"-Command",
214+
ps,
215+
],
216+
capture_output=True,
217+
text=True,
218+
creationflags=_subprocess_flags(),
219+
timeout=20,
220+
)
221+
return p.returncode == 0
222+
except (OSError, subprocess.TimeoutExpired, ValueError):
223+
return False
224+
225+
226+
def _interface_from_wmi_ip4_route_table() -> Tuple[Optional[str], str]:
227+
"""
228+
Win7+:WMI Win32_IP4RouteTable 取默认路由 (0.0.0.0/0.0.0.0) 的 InterfaceIndex,
229+
不依赖 Win32_NetworkAdapterConfiguration.DefaultIPGateway 或 IPAddress 字符串反查。
230+
"""
231+
try:
232+
p = subprocess.run(
233+
[
234+
"wmic",
235+
"path",
236+
"Win32_IP4RouteTable",
237+
"where",
238+
"Destination='0.0.0.0' and Mask='0.0.0.0'",
239+
"get",
240+
"InterfaceIndex,Metric1",
241+
"/format:list",
242+
],
243+
capture_output=True,
244+
text=True,
245+
creationflags=_subprocess_flags(),
246+
timeout=30,
247+
)
248+
except OSError as e:
249+
return None, "wmic Win32_IP4RouteTable unavailable: %s" % e
250+
if p.returncode != 0:
251+
return None, (p.stderr or p.stdout or "wmic Win32_IP4RouteTable failed").strip()[:500]
252+
candidates: List[Tuple[int, int]] = [] # (metric, interface_index)
253+
for row in _parse_wmic_list(p.stdout or ""):
254+
m = re.search(r"\d+", row.get("interfaceindex", ""))
255+
if not m:
256+
continue
257+
idx = int(m.group())
258+
met_s = row.get("metric1", "").strip()
259+
try:
260+
metric = int(met_s) if met_s.isdigit() else 9999
261+
except ValueError:
262+
metric = 9999
263+
candidates.append((metric, idx))
264+
if not candidates:
265+
return None, "wmi Win32_IP4RouteTable no default row"
266+
candidates.sort(key=lambda x: x[0])
267+
idx = candidates[0][1]
268+
name, msg = _wmic_nic_name_for_index(idx)
269+
if name:
270+
return name, "wmi Win32_IP4RouteTable (%s)" % msg
271+
return None, msg
272+
273+
162274
def _interface_from_powershell_net_route() -> Tuple[Optional[str], str]:
163275
"""
164276
Win8+:用默认路由所在接口(不依赖 WMI 的 DefaultIPGateway 字段,避免静态/特殊驱动下为空)。
@@ -248,10 +360,10 @@ def _interface_from_route_print(interface_ip: str) -> Tuple[Optional[str], str]:
248360
target = interface_ip.strip().lower()
249361
for row in _parse_wmic_list(p.stdout or ""):
250362
raw_ip = row.get("ipaddress", "")
251-
cleaned = _clean_wmic_value(raw_ip).lower()
252-
# 可能为 "a.b.c.d" 或逗号分隔多个
253-
parts = re.split(r"[\s,;]+", cleaned)
254-
if target not in parts and target not in cleaned.replace("{", "").replace("}", ""):
363+
tokens = [t.lower() for t in _wmic_multivalue_tokens(raw_ip)]
364+
if not tokens:
365+
tokens = [_clean_wmic_value(raw_ip).lower()]
366+
if target not in tokens:
255367
continue
256368
m = re.search(r"\d+", row.get("interfaceindex", ""))
257369
if not m:
@@ -308,7 +420,8 @@ def _interface_from_route_print_only() -> Tuple[Optional[str], str]:
308420
def get_default_ipv4_interface_name() -> Tuple[Optional[str], str]:
309421
"""
310422
返回 (供 netsh 使用的接口名, 诊断说明)。
311-
依次尝试:WMI DefaultIPGateway → PowerShell 默认路由 → PowerShell NetIPConfiguration → route print。
423+
依次尝试:WMI DefaultIPGateway →(若存在 cmdlet)PowerShell 默认路由 / NetIPConfiguration
424+
→ WMI Win32_IP4RouteTable → route print + WMI 反查。
312425
"""
313426
if sys.platform != "win32":
314427
return None, "not windows"
@@ -320,13 +433,25 @@ def get_default_ipv4_interface_name() -> Tuple[Optional[str], str]:
320433
if name:
321434
return name, why
322435

323-
name, why = _interface_from_powershell_net_route()
324-
tried.append("ps-route:%s" % why)
325-
if name:
326-
return name, why
436+
if _powershell_has_get_net_route_cmdlet():
437+
name, why = _interface_from_powershell_net_route()
438+
tried.append("ps-route:%s" % why)
439+
if name:
440+
return name, why
441+
442+
name, why = _interface_from_powershell_net_ip_configuration()
443+
tried.append("ps-ipcfg:%s" % why)
444+
if name:
445+
return name, why
446+
else:
447+
tried.append(
448+
"ps-route:skipped (no Get-NetRoute cmdlet; os claims net_tcpip=%s)"
449+
% _windows_has_net_tcpip_ps_cmdlets()
450+
)
451+
tried.append("ps-ipcfg:skipped (same)")
327452

328-
name, why = _interface_from_powershell_net_ip_configuration()
329-
tried.append("ps-ipcfg:%s" % why)
453+
name, why = _interface_from_wmi_ip4_route_table()
454+
tried.append("ip4route:%s" % why)
330455
if name:
331456
return name, why
332457

0 commit comments

Comments
 (0)