agent网卡探测方法修改。
网卡流量探测兼容没配IP的
This commit is contained in:
+46
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user