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