agent网卡探测方法修改。

网卡流量探测兼容没配IP的
This commit is contained in:
gaoyutao
2025-12-17 18:20:41 +08:00
parent a9ca7f7ca6
commit 1c1cdad5e0
2 changed files with 457 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
dist/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml
@@ -0,0 +1,411 @@
package com.tongran.agent.client.utils;
import com.tongran.agent.client.core.vo.NetworkInterfaceInfo;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 网络接口工具类(通过 ip 命令获取)
*/
public class NetworkInterfaceUtil {
/**
* 收集所有 Ethernet 类型网卡信息
*/
public static List<NetworkInterfaceInfo> collectNetworkInfo() throws Exception {
List<NetworkInterfaceInfo> result = new ArrayList<>();
Map<String, NetworkInterfaceInfo> parentMap = new HashMap<>();
List<NetworkInterfaceInfo> childTempList = new ArrayList<>();
// 获取所有网卡详细信息
List<NetworkInterfaceDetails> allInterfaces = getNetworkInterfacesFromIpLink();
AssertLog.info("从 ip link 获取到 {} 个网络接口", allInterfaces.size());
for (NetworkInterfaceDetails interfaceDetail : allInterfaces) {
String name = interfaceDetail.name;
String realName = interfaceDetail.realName;
boolean isUp = "UP".equals(interfaceDetail.state);
// 调试信息
AssertLog.info("开始处理网卡: {}", name);
AssertLog.info(" 状态: {}, 是否虚拟: {}",
isUp ? "UP" : "DOWN", interfaceDetail.isVirtual);
// 跳过回环、关闭的接口
if (name.equals("lo") || !isUp) {
AssertLog.info(" 跳过网卡 {}: 回环或未启用", name);
continue;
}
// 判断是否为 Ethernet(通过名称约定)
if (!isEthernetInterface(interfaceDetail)) {
AssertLog.info(" 跳过网卡 {}: 非以太网接口", name);
continue;
}
// 获取IP地址
String ipv4 = getIPv4AddressFromIpAddr(name);
String ipv6 = getIPv6AddressFromIpAddr(name);
AssertLog.info(" IPv4: {}, IPv6: {}", ipv4, ipv6);
// 检查IP4信息
// if (ipv4.equals("N/A")) {
// AssertLog.info(" 跳过网卡 {}: 无IPv4地址", name);
// continue;
// }
// 检查物理链路状态
boolean isConnected = isInterfaceConnected(name);
if (!isConnected) {
AssertLog.info(" 跳过网卡 {}: 物理链路未连接", name);
continue;
}
// 获取网关
String gateway = AgentUtil.getGateway(name, "www.baidu.com");
AssertLog.info(" 网关: {}", gateway);
if ("N/A".equals(gateway)) {
AssertLog.info(" 跳过网卡 {}: 无法获取网关", name);
continue;
}
// 获取公网IP
String publicIp = null;
String ipInfo = null;
if (gateway != null) {
ipInfo = PublicIpFetcher.getPublicIp();
publicIp = PublicIpFetcher.extractIp(ipInfo);
if (publicIp != null) {
AssertLog.info(" 公网 IP: {}", publicIp);
} else {
AssertLog.info(" 未能提取公网 IP 地址");
}
if (StringUtils.isBlank(publicIp)) {
AssertLog.info(" 跳过网卡 {}: 无公网IP", name);
continue;
}
}
// 创建网卡信息对象
NetworkInterfaceInfo info = NetworkInterfaceInfo.builder()
.name(name)
.type("Ethernet")
.mac(interfaceDetail.macAddress)
.ipv4(ipv4)
.ipv6(ipv6)
.gateway(gateway)
.publicIp(publicIp)
.build();
// 如果有公网 IP,查询归属地
if (publicIp != null && !publicIp.isEmpty() && ipInfo != null) {
// 提取归属地信息
PublicIpFetcher.IpLocationInfo location = PublicIpFetcher.extractLocation(ipInfo);
info.setProvince(location.getProvince());
info.setCity(location.getCity());
info.setCarrier(location.getCarrier());
}
// 判断父子关系
if (interfaceDetail.parent != null && !interfaceDetail.parent.isEmpty()) {
// 子接口
AssertLog.info(" 识别为子接口: {}", name);
String parentName = interfaceDetail.parent;
// 递归查找最终的父网卡
parentName = findRootParentInterface(parentName, allInterfaces);
if (!interfaceDetail.parent.equals(parentName)) {
AssertLog.info(" {} 的最终父接口是: {} (原始父接口: {})",
name, parentName, interfaceDetail.parent);
}
info.setParentInterface(parentName);
// 查找父接口
NetworkInterfaceInfo parent = parentMap.get(parentName);
if (parent != null) {
// 设置父子关系
if (parent.getSubInterfaces() == null) {
parent.setSubInterfaces(new ArrayList<>());
}
parent.getSubInterfaces().add(info);
AssertLog.info(" 添加到父接口 {} 的子接口列表", parentName);
} else {
// 父接口还未处理,先放到临时列表
childTempList.add(info);
AssertLog.info(" 父接口 {} 还未处理,放入临时列表", parentName);
}
} else {
// 父接口
AssertLog.info(" 识别为父接口: {}", name);
parentMap.put(name, info);
}
}
// 处理父接口加入结果
for (NetworkInterfaceInfo parent : parentMap.values()) {
result.add(parent);
AssertLog.info("父接口 {} 加入结果列表", parent.getName());
}
// 处理子接口(父接口未出现的情况)
for (NetworkInterfaceInfo child : childTempList) {
String parentName = child.getParentInterface();
NetworkInterfaceInfo parent = parentMap.get(parentName);
if (parent != null) {
// 建立父子关系
if (parent.getSubInterfaces() == null) {
parent.setSubInterfaces(new ArrayList<>());
}
parent.getSubInterfaces().add(child);
AssertLog.info("子接口 {} 关联到父接口 {}", child.getName(), parentName);
} else {
// 确实没有父接口,作为独立接口
result.add(child);
AssertLog.info("子接口 {} 无父接口,作为独立接口", child.getName());
}
}
AssertLog.info("共收集到 {} 个有效网卡接口", result.size());
return result;
}
/**
* 递归查找最终的父网卡
*/
private static String findRootParentInterface(String currentParent, List<NetworkInterfaceDetails> allInterfaces) {
// 查找当前父网卡的详细信息
for (NetworkInterfaceDetails iface : allInterfaces) {
if (iface.name.equals(currentParent) && iface.parent != null && !iface.parent.isEmpty()) {
// 如果当前父网卡还有父网卡,继续递归查找
return findRootParentInterface(iface.parent, allInterfaces);
}
}
// 返回最终的父网卡名称
return currentParent;
}
/**
* 通过 ip link 命令获取网卡详细信息
*/
private static List<NetworkInterfaceDetails> getNetworkInterfacesFromIpLink() throws Exception {
List<NetworkInterfaceDetails> interfaces = new ArrayList<>();
Process process = Runtime.getRuntime().exec("ip -d link show");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
NetworkInterfaceDetails currentInterface = null;
Pattern interfacePattern = Pattern.compile("^(\\d+):\\s+(\\S+?)(?:@(\\S+))?\\s*:");
Pattern linkPattern = Pattern.compile("\\s+link/(\\S+)\\s+([0-9a-f:]+)");
Pattern statePattern = Pattern.compile("state\\s+(UP|DOWN|UNKNOWN)");
while ((line = reader.readLine()) != null) {
Matcher interfaceMatcher = interfacePattern.matcher(line);
if (interfaceMatcher.find()) {
// 保存上一个接口
if (currentInterface != null) {
interfaces.add(currentInterface);
}
// 创建新的接口
currentInterface = new NetworkInterfaceDetails();
currentInterface.index = Integer.parseInt(interfaceMatcher.group(1));
currentInterface.name = interfaceMatcher.group(2).trim();
// 提取父接口(如果有@符号)
if (interfaceMatcher.group(3) != null) {
currentInterface.parent = interfaceMatcher.group(3).trim();
currentInterface.realName = currentInterface.name + "@" + currentInterface.parent;
}
// 检查是否是虚拟接口
String ifaceName = currentInterface.name;
if (ifaceName.contains(".") || ifaceName.startsWith("xdbvl") ||
ifaceName.startsWith("docker") || ifaceName.startsWith("veth") ||
ifaceName.startsWith("br-") || ifaceName.startsWith("virbr")) {
currentInterface.isVirtual = true;
}
}
if (currentInterface != null) {
// 解析状态
Matcher stateMatcher = statePattern.matcher(line);
if (stateMatcher.find()) {
currentInterface.state = stateMatcher.group(1);
}
// 解析MAC地址
Matcher linkMatcher = linkPattern.matcher(line);
if (linkMatcher.find()) {
currentInterface.macAddress = linkMatcher.group(2);
}
}
}
// 添加最后一个接口
if (currentInterface != null) {
interfaces.add(currentInterface);
}
reader.close();
process.waitFor();
// 补充父接口信息
Map<String, String> interfaceNames = new HashMap<>();
for (NetworkInterfaceDetails iface : interfaces) {
interfaceNames.put(iface.name, iface.name);
}
// 确保所有父接口都存在
for (NetworkInterfaceDetails iface : interfaces) {
if (iface.parent != null && !interfaceNames.containsKey(iface.parent)) {
// 如果父接口不存在,可能是类似 eth0.203 的情况
if (iface.parent.contains(".")) {
String[] parts = iface.parent.split("\\.");
if (parts.length > 0) {
iface.parent = parts[0];
}
}
}
}
return interfaces;
}
/**
* 从 ip addr 命令获取IPv4地址
*/
private static String getIPv4AddressFromIpAddr(String interfaceName) throws Exception {
String command = String.format("ip -4 addr show %s 2>/dev/null || echo ''", interfaceName);
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("inet ")) {
String[] parts = line.trim().split("\\s+");
if (parts.length >= 2) {
String ipWithMask = parts[1];
reader.close();
process.waitFor();
return ipWithMask.split("/")[0];
}
}
}
reader.close();
process.waitFor();
return "N/A";
}
/**
* 从 ip addr 命令获取IPv6地址
*/
private static String getIPv6AddressFromIpAddr(String interfaceName) throws Exception {
String command = String.format("ip -6 addr show %s 2>/dev/null | grep -v scope.link || echo ''", interfaceName);
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("inet6 ")) {
String[] parts = line.trim().split("\\s+");
if (parts.length >= 2) {
String ipWithMask = parts[1];
reader.close();
process.waitFor();
return ipWithMask.split("/")[0];
}
}
}
reader.close();
process.waitFor();
return "N/A";
}
/**
* 判断是否为以太网接口
*/
private static boolean isEthernetInterface(NetworkInterfaceDetails interfaceDetail) {
String name = interfaceDetail.name;
// 判断是否为以太网接口
if (name.startsWith("eth") || // Linux 传统
name.startsWith("en") || // systemd 命名 (enp3s0)
name.startsWith("em") // 有些主板网卡
) {
return true;
}
// 如果有父接口,也可能是有效的子接口
if (interfaceDetail.parent != null && !interfaceDetail.parent.isEmpty()) {
return true;
}
return false;
}
/**
* 检查物理链路状态
*/
private static boolean isInterfaceConnected(String interfaceName) {
try {
String os = System.getProperty("os.name").toLowerCase();
Process process;
if (os.contains("win")) {
// Windows: 使用 wmic 检查网卡是否启用
process = Runtime.getRuntime().exec(
"wmic nic where \"NetEnabled=true\" get Name");
} else {
// Linux: 检查 operstate
process = Runtime.getRuntime().exec(
"cat /sys/class/net/" + interfaceName + "/operstate");
}
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (os.contains("win")) {
if (line.trim().equalsIgnoreCase(interfaceName)) {
return true;
}
} else {
return "up".equals(line.trim());
}
}
reader.close();
return false;
} catch (Exception e) {
System.err.println("无法检查接口状态: " + interfaceName);
return false; // 保守处理
}
}
/**
* 内部类:网卡详情
*/
static class NetworkInterfaceDetails {
int index;
String name;
String realName;
String state = "UNKNOWN";
String macAddress = "";
String parent = null;
boolean isVirtual = false;
}
}