From 5f67018d3555c43577235e586dc194f209289be4 Mon Sep 17 00:00:00 2001 From: gaoyutao Date: Thu, 4 Dec 2025 18:28:59 +0800 Subject: [PATCH] =?UTF-8?q?agent=E5=AE=9A=E6=97=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E3=80=81=E8=84=9A=E6=9C=AC=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E6=89=A7=E8=A1=8C=E4=BC=98=E5=8C=96=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96ipv4=E3=80=81ipv6=E9=87=87=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/netty/enpoint/AgentEndpoint.java | 4 +- .../scheduler/service/AppInitializer.java | 2 + .../scheduler/service/BusinessTasks.java | 2 + .../task/SpecificTimeTaskService.java | 14 +- .../agent/client/service/AgentService.java | 2 + .../client/service/impl/AgentServiceImpl.java | 165 ++++++++++------ .../client/service/impl/NetServiceImpl.java | 186 ++++++++++-------- .../tongran/agent/client/utils/AgentUtil.java | 133 +++++++++++++ 8 files changed, 355 insertions(+), 153 deletions(-) diff --git a/src/main/java/com/tongran/agent/client/netty/enpoint/AgentEndpoint.java b/src/main/java/com/tongran/agent/client/netty/enpoint/AgentEndpoint.java index b9e72e2..ad29ca6 100644 --- a/src/main/java/com/tongran/agent/client/netty/enpoint/AgentEndpoint.java +++ b/src/main/java/com/tongran/agent/client/netty/enpoint/AgentEndpoint.java @@ -225,9 +225,9 @@ public class AgentEndpoint { if(StringUtils.isNotBlank(md5) && StringUtils.isNotBlank(versionUpdateEO.getFileMd5()) && StringUtils.equals(md5,versionUpdateEO.getFileMd5())){ //更改全局变量 - GlobalConfig.isCollect = false; +// GlobalConfig.isCollect = false; //调用采集任务 - agentService.cancelCollect(); +// agentService.cancelCollect(); //所有文件下载完成,执行脚本命令 agentService.command(policy, clientId,MsgEnum.Agent版本更新应答.getValue()); }else{ diff --git a/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java b/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java index 4ab873a..67e54f7 100644 --- a/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java +++ b/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java @@ -97,6 +97,8 @@ public class AppInitializer implements CommandLineRunner { agentService.checkFirewall(); AssertLog.info("启动检查防火墙策略定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 600000); dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000); + AssertLog.info("检测agent更新配置"); + agentService.checkAgentUpdate(); }else{ //未注册,发送注册 diff --git a/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java b/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java index 362032e..1c97508 100644 --- a/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java +++ b/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java @@ -852,6 +852,8 @@ public class BusinessTasks { agentService.checkFirewall(); AssertLog.info("启动检查防火墙策略定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 600000); dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000); + AssertLog.info("检测agent更新配置"); + agentService.checkAgentUpdate(); }else{ //未注册,发送注册 try { diff --git a/src/main/java/com/tongran/agent/client/scheduler/task/SpecificTimeTaskService.java b/src/main/java/com/tongran/agent/client/scheduler/task/SpecificTimeTaskService.java index cdb6b35..9410fba 100644 --- a/src/main/java/com/tongran/agent/client/scheduler/task/SpecificTimeTaskService.java +++ b/src/main/java/com/tongran/agent/client/scheduler/task/SpecificTimeTaskService.java @@ -2,12 +2,15 @@ package com.tongran.agent.client.scheduler.task; import com.alibaba.fastjson2.JSONObject; import com.tongran.agent.client.core.config.ApplicationProperties; +import com.tongran.agent.client.core.config.GlobalConfig; import com.tongran.agent.client.core.enums.MsgEnum; import com.tongran.agent.client.core.session.SessionManager; import com.tongran.agent.client.netty.model.Message; import com.tongran.agent.client.scheduler.service.AsyncCommandExecutor; +import com.tongran.agent.client.service.AgentService; import com.tongran.agent.client.utils.AgentDataUtil; import com.tongran.agent.client.utils.AgentUtil; +import com.tongran.agent.client.utils.AssertLog; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -27,7 +30,9 @@ public class SpecificTimeTaskService { @Resource private ApplicationProperties properties; - + @Resource + private AgentService agentService; + @Resource private SpecificTimeTaskConfig taskConfig; @@ -81,6 +86,11 @@ public class SpecificTimeTaskService { String key = request.getTaskName()+"-"+System.currentTimeMillis(); if(StringUtils.equals(request.getDataType(), MsgEnum.Agent版本更新应答.getValue())){ try { + AssertLog.info("定时更新启动...暂停心跳和采集任务"); + //更改全局变量 + GlobalConfig.isCollect = false; + //调用采集任务 + agentService.cancelCollect(); //设置检查回滚任务 String SCRIPT_PATH = properties.getScriptPath()+"/rollback-tragent.sh"; AgentDataUtil.chmod(SCRIPT_PATH,"775"); @@ -113,7 +123,7 @@ public class SpecificTimeTaskService { json.put("resMsg", ""); json.put("result", jsonObject.toJSONString()); json.put("timestamp",timestamps); - Message message = Message.builder().clientId(request.getClientId()).dataType(request.getDataType()).data(jsonObject.toJSONString()).build(); + Message message = Message.builder().clientId(request.getClientId()).dataType(request.getDataType()).data(json.toJSONString()).build(); if (Objects.nonNull(sessionManager.getSessionById(request.getClientId()))) { System.out.println("发送执行结果: " + json.toJSONString()); // 注意:toJSONString() sessionManager.writeAndFlush(sessionManager.getSessionById(request.getClientId()).getChannel(), message); diff --git a/src/main/java/com/tongran/agent/client/service/AgentService.java b/src/main/java/com/tongran/agent/client/service/AgentService.java index 964bb66..0c40dfe 100644 --- a/src/main/java/com/tongran/agent/client/service/AgentService.java +++ b/src/main/java/com/tongran/agent/client/service/AgentService.java @@ -36,4 +36,6 @@ public interface AgentService { void checkFirewall(); void checkAndAddFirewallPeriodically(); + + void checkAgentUpdate(); } diff --git a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java index 6f1822c..1a05b10 100644 --- a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java +++ b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java @@ -166,35 +166,24 @@ public class AgentServiceImpl implements AgentService { .clientId(clientId) .dataType(dataType) .build(); - try { - taskService.createSpecificTimeTask(request); - JSONObject json = new JSONObject(); - json.put("resCode",1); - json.put("resMsg", "执行脚本策略定时任务保存成功"); - json.put("timestamp",timestamp); - // 判定客户端与服务端是否连接 - if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { - Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.执行脚本策略应答.getValue()) - .data(json.toString()).build(); - sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message); - AssertLog.info("发送执行脚本策略应答={}",JSON.toJSONString(message)); - } - } catch (Exception e) { - JSONObject json = new JSONObject(); - json.put("resCode",0); - json.put("resMsg", "执行脚本策略定时任务保存失败"); - json.put("timestamp",timestamp); - // 判定客户端与服务端是否连接 - if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { - Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.执行脚本策略应答.getValue()) - .data(json.toString()).build(); - sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message); - AssertLog.info("发送执行脚本策略应答={}",JSON.toJSONString(message)); + if(StringUtils.equals(dataType, MsgEnum.Agent版本更新应答.getValue())){ + // 将request对象转换为JSON字符串 + String jsonContent = "updateMsg=" + JSONObject.toJSONString(request); + // 检查并创建外置目录 + if (AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(properties.getConfPath())) { + // 将JSON字符串写入文件 + AgentUtil.bufferedWriter(properties.getConfPath() + "/agentupdate.conf", + new String[]{jsonContent}); } } + processCommand(request, timestamp); }else{ if(StringUtils.equals(dataType, MsgEnum.Agent版本更新应答.getValue())){ try { + //更改全局变量 + GlobalConfig.isCollect = false; + //关闭采集任务 + cancelCollect(); //设置检查回滚任务 String SCRIPT_PATH = properties.getScriptPath()+"/rollback-tragent.sh"; AgentDataUtil.chmod(SCRIPT_PATH,"775"); @@ -265,6 +254,40 @@ public class AgentServiceImpl implements AgentService { } } + /** + * 辅助方法:持久化定时更新 + * @param request + * @param timestamp + */ + public void processCommand(SpecificTimeRequest request, long timestamp){ + try { + taskService.createSpecificTimeTask(request); + JSONObject json = new JSONObject(); + json.put("resCode",1); + json.put("resMsg", "执行脚本策略定时任务保存成功"); + json.put("timestamp",timestamp); + // 判定客户端与服务端是否连接 + if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { + Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.执行脚本策略应答.getValue()) + .data(json.toString()).build(); + sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message); + AssertLog.info("发送执行脚本策略应答={}",JSON.toJSONString(message)); + } + } catch (Exception e) { + JSONObject json = new JSONObject(); + json.put("resCode",0); + json.put("resMsg", "执行脚本策略定时任务保存失败"); + json.put("timestamp",timestamp); + // 判定客户端与服务端是否连接 + if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { + Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.执行脚本策略应答.getValue()) + .data(json.toString()).build(); + sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message); + AssertLog.info("发送执行脚本策略应答={}",JSON.toJSONString(message)); + } + } + } + @Override public void cancelTask(String taskId) { dynamicTaskService.cancelTask(taskId); @@ -288,6 +311,8 @@ public class AgentServiceImpl implements AgentService { checkFirewall(); AssertLog.info("启动检查防火墙策略定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 600000); dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000); + AssertLog.info("检测agent更新配置"); + checkAgentUpdate(); } @Override @@ -741,9 +766,9 @@ public class AgentServiceImpl implements AgentService { if(StringUtils.isNotBlank(md5) && StringUtils.isNotBlank(versionUpdateEO.getFileMd5()) && StringUtils.equals(md5,versionUpdateEO.getFileMd5())){ //更改全局变量 - GlobalConfig.isCollect = false; +// GlobalConfig.isCollect = false; //关闭采集任务 - cancelCollect(); +// cancelCollect(); //所有文件下载完成,执行脚本命令 AssertLog.info("版本策略MD5验证成功"); command(policy, GlobalConfig.CLIENT_ID,MsgEnum.Agent版本更新应答.getValue()); @@ -1444,7 +1469,7 @@ public class AgentServiceImpl implements AgentService { if (!netName.equals(GlobalConfig.NETNAME)) { try { - // 1. 将网卡名称写入配置文件 + // 1. 将网卡名称写入配置文件 String[] lines = { "# 业务网卡防火墙策略配置文件", "# 生成时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), @@ -1460,18 +1485,34 @@ public class AgentServiceImpl implements AgentService { if (AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(properties.getConfPath())) { AgentUtil.bufferedWriter(properties.getConfPath() + "/businessnetname.conf", lines); } + // 更新全局变量 GlobalConfig.NETNAME = netName; - // 2. 为每个业务网卡执行iptables命令 + + // 2. 为每个业务网卡执行iptables命令(先检查规则是否存在) for (String interfaceName : netNames) { String iface = interfaceName.trim(); - if (!iface.isEmpty()) { - // 执行IPv4防火墙命令 - executeIptablesCommand("iptables", "-I", "INPUT", "1", "-i", iface, "-j", "ACCEPT"); - executeIptablesCommand("iptables", "-I", "OUTPUT", "1", "-o", iface, "-j", "ACCEPT"); + if (iface.isEmpty()) { + continue; + } - // 执行IPv6防火墙命令 + // 检查IPv4 INPUT规则是否存在 + if (!checkRuleExists("iptables", "INPUT", "-i", iface, "-j", "ACCEPT")) { + executeIptablesCommand("iptables", "-I", "INPUT", "1", "-i", iface, "-j", "ACCEPT"); + } + + // 检查IPv4 OUTPUT规则是否存在 + if (!checkRuleExists("iptables", "OUTPUT", "-o", iface, "-j", "ACCEPT")) { + executeIptablesCommand("iptables", "-I", "OUTPUT", "1", "-o", iface, "-j", "ACCEPT"); + } + + // 检查IPv6 INPUT规则是否存在 + if (!checkRuleExists("ip6tables", "INPUT", "-i", iface, "-j", "ACCEPT")) { executeIptablesCommand("ip6tables", "-I", "INPUT", "1", "-i", iface, "-j", "ACCEPT"); + } + + // 检查IPv6 OUTPUT规则是否存在 + if (!checkRuleExists("ip6tables", "OUTPUT", "-o", iface, "-j", "ACCEPT")) { executeIptablesCommand("ip6tables", "-I", "OUTPUT", "1", "-o", iface, "-j", "ACCEPT"); } } @@ -1483,6 +1524,7 @@ public class AgentServiceImpl implements AgentService { } } + /** * 执行iptables命令 */ @@ -1547,37 +1589,7 @@ public class AgentServiceImpl implements AgentService { AssertLog.debug("没有配置业务网卡,跳过防火墙检查"); return; } - - String[] netNames = netName.split(";"); - StringBuilder missingNetNames = new StringBuilder(); - - for (String interfaceName : netNames) { - String iface = interfaceName.trim(); - if (iface.isEmpty()) { - continue; - } - - // 检查该网卡是否缺少任何规则 - if (!checkRuleExists("iptables", "INPUT", "-i", iface, "-j", "ACCEPT") || - !checkRuleExists("iptables", "OUTPUT", "-o", iface, "-j", "ACCEPT") || - !checkRuleExists("ip6tables", "INPUT", "-i", iface, "-j", "ACCEPT") || - !checkRuleExists("ip6tables", "OUTPUT", "-o", iface, "-j", "ACCEPT")) { - - if (missingNetNames.length() > 0) { - missingNetNames.append(";"); - } - missingNetNames.append(iface); - } - } - - if (missingNetNames.length() > 0) { - AssertLog.info("检测到以下网卡防火墙策略缺失: {}", missingNetNames.toString()); - // 只对缺失规则的网卡调用addFirewall - addFirewall(missingNetNames.toString()); - } else { - AssertLog.debug("所有业务网卡防火墙策略检查正常,无需添加"); - } - + addFirewall(netName); } catch (Exception e) { AssertLog.error("检查防火墙策略时发生错误: {}", e.getMessage()); } @@ -1662,4 +1674,29 @@ public class AgentServiceImpl implements AgentService { return null; } + + @Override + public void checkAgentUpdate() { + File agentUpdateFile = new File(properties.getConfPath() + "/agentupdate.conf"); + if (agentUpdateFile.exists()) { + Properties props = new Properties(); + try (InputStream input = Files.newInputStream(agentUpdateFile.toPath())) { + props.load(input); + + String updateMsg = props.getProperty("updateMsg"); + if (StringUtils.isNotBlank(updateMsg)) { + long timestamp = System.currentTimeMillis(); + timestamp = Math.round(timestamp / 1000.0); + SpecificTimeRequest request = JSON.parseObject(updateMsg, SpecificTimeRequest.class); + processCommand(request, timestamp); + AssertLog.info("应用启动时加载agent更新配置成功: {}", updateMsg); + } + + } catch (Exception e) { + AssertLog.error("应用启动时加载agent更新配置异常", e); + } + } else { + AssertLog.debug("agent更新配置文件不存在: {}", agentUpdateFile.getPath()); + } + } } diff --git a/src/main/java/com/tongran/agent/client/service/impl/NetServiceImpl.java b/src/main/java/com/tongran/agent/client/service/impl/NetServiceImpl.java index 2000fbf..3d8ffc5 100644 --- a/src/main/java/com/tongran/agent/client/service/impl/NetServiceImpl.java +++ b/src/main/java/com/tongran/agent/client/service/impl/NetServiceImpl.java @@ -17,7 +17,6 @@ import java.util.*; @Service public class NetServiceImpl implements NetService { - @Override public List netList(long timestamp) { List list = new ArrayList<>(); @@ -46,13 +45,21 @@ public class NetServiceImpl implements NetService { String interfaceName = net.getName(); boolean isBusinessInterface = GlobalConfig.NETNAME.contains(interfaceName); + String[] ipv6Arr = net.getIPv6addr(); + String publicIpv6 = ""; + for (String ipv6 : ipv6Arr) { + if(AgentUtil.isPublicIPv6(ipv6)){ + publicIpv6 = ipv6; + break; + } + } System.out.println("接口名称: " + interfaceName + "(" + net.getDisplayName() + ")"); System.out.println("MAC地址: " + net.getMacaddr()); System.out.println("运行状态: " + (net.isConnectorPresent() ? "已连接" : "未连接")); System.out.println("接口类型: " + AgentUtil.getInterfaceType(net)); System.out.println("IPv4地址: " + String.join(", ", net.getIPv4addr())); - System.out.println("IPv6地址: " + String.join(", ", net.getIPv6addr())); + System.out.println("公网IPv6地址: " + publicIpv6); System.out.println("业务网卡: " + (isBusinessInterface ? "是" : "否")); // 获取总流量 @@ -98,7 +105,7 @@ public class NetServiceImpl implements NetService { .status(net.isConnectorPresent() ? "已连接" : "未连接") .type(AgentUtil.getInterfaceType(net)) .ipV4(String.join(", ", net.getIPv4addr())) - .ipV6(String.join(", ", net.getIPv6addr())) + .ipV6(publicIpv6) .inDropped(net.getInDrops()) .outDropped(net.getCollisions()) .inSpeed(totalBytesRecv) @@ -127,58 +134,51 @@ public class NetServiceImpl implements NetService { } /** - * 获取iptables统计信息 + * 获取iptables统计信息 - 使用命令过滤模式优化 */ private Map> getIptablesStatistics() { Map> stats = new HashMap<>(); try { - System.out.println("开始获取iptables统计信息..."); + System.out.println("开始获取iptables统计信息(过滤模式)..."); - // 获取IPv4 INPUT统计 - System.out.println("获取IPv4 INPUT统计..."); - ProcessBuilder pb = new ProcessBuilder("iptables", "-v", "-n", "-x", "-L", "INPUT"); - Process process = pb.start(); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + // 遍历所有业务网卡,为每个网卡分别获取统计信息 + for (String interfaceName : GlobalConfig.NETNAME.split(";")) { + System.out.println("处理业务网卡: " + interfaceName); - String line; - while ((line = reader.readLine()) != null) { - parseIptablesLine(line, stats, "ipv4Recv"); + Map interfaceStats = new HashMap<>(); + + // 获取IPv4 INPUT统计 + Long ipv4Recv = getIptablesStatForInterface("iptables", "INPUT", interfaceName); + if (ipv4Recv != null) { + interfaceStats.put("ipv4Recv", ipv4Recv); + } + + // 获取IPv4 OUTPUT统计 + Long ipv4Sent = getIptablesStatForInterface("iptables", "OUTPUT", interfaceName); + if (ipv4Sent != null) { + interfaceStats.put("ipv4Sent", ipv4Sent); + } + + // 获取IPv6 INPUT统计 + Long ipv6Recv = getIptablesStatForInterface("ip6tables", "INPUT", interfaceName); + if (ipv6Recv != null) { + interfaceStats.put("ipv6Recv", ipv6Recv); + } + + // 获取IPv6 OUTPUT统计 + Long ipv6Sent = getIptablesStatForInterface("ip6tables", "OUTPUT", interfaceName); + if (ipv6Sent != null) { + interfaceStats.put("ipv6Sent", ipv6Sent); + } + + if (!interfaceStats.isEmpty()) { + stats.put(interfaceName, interfaceStats); + System.out.println("网卡 " + interfaceName + " 的统计信息: " + interfaceStats); + } else { + System.out.println("警告: 未找到网卡 " + interfaceName + " 的iptables统计信息"); + } } - process.waitFor(); - - // 获取IPv4 OUTPUT统计 - System.out.println("获取IPv4 OUTPUT统计..."); - pb = new ProcessBuilder("iptables", "-v", "-n", "-x", "-L", "OUTPUT"); - process = pb.start(); - reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - - while ((line = reader.readLine()) != null) { - parseIptablesLine(line, stats, "ipv4Sent"); - } - process.waitFor(); - - // 获取IPv6 INPUT统计 - System.out.println("获取IPv6 INPUT统计..."); - pb = new ProcessBuilder("ip6tables", "-v", "-n", "-x", "-L", "INPUT"); - process = pb.start(); - reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - - while ((line = reader.readLine()) != null) { - parseIptablesLine(line, stats, "ipv6Recv"); - } - process.waitFor(); - - // 获取IPv6 OUTPUT统计 - System.out.println("获取IPv6 OUTPUT统计..."); - pb = new ProcessBuilder("ip6tables", "-v", "-n", "-x", "-L", "OUTPUT"); - process = pb.start(); - reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - - while ((line = reader.readLine()) != null) { - parseIptablesLine(line, stats, "ipv6Sent"); - } - process.waitFor(); System.out.println("最终iptables统计信息: " + stats); @@ -191,63 +191,79 @@ public class NetServiceImpl implements NetService { } /** - * 解析iptables输出行 - 根据实际输出格式修复 + * 获取指定网卡的iptables统计信息 + * @param iptablesCmd iptables或ip6tables + * @param chain 链名(INPUT/OUTPUT) + * @param interfaceName 网卡名 + * @return 字节数,如果没有找到返回null */ - private void parseIptablesLine(String line, Map> stats, String statType) { + private Long getIptablesStatForInterface(String iptablesCmd, String chain, String interfaceName) { + try { + // 构建命令:iptables -v -n -x -L INPUT | grep eth0 + ProcessBuilder pb = new ProcessBuilder("sh", "-c", + iptablesCmd + " -v -n -x -L " + chain + " | grep " + interfaceName); + + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + long totalBytes = 0; + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + System.out.println("解析 " + iptablesCmd + " " + chain + " 行: " + line); + Long bytes = parseIptablesLineForInterface(line); + if (bytes != null) { + totalBytes += bytes; + found = true; + System.out.println("找到匹配的规则: " + bytes + " bytes"); + } + } + + process.waitFor(); + + if (found) { + System.out.println(iptablesCmd + " " + chain + " 接口 " + interfaceName + " 总流量: " + totalBytes + " bytes"); + return totalBytes; + } else { + System.out.println("未找到 " + iptablesCmd + " " + chain + " 接口 " + interfaceName + " 的统计信息"); + return null; + } + + } catch (Exception e) { + System.err.println("获取 " + iptablesCmd + " " + chain + " 统计失败: " + e.getMessage()); + return null; + } + } + + /** + * 解析iptables输出行 + */ + private Long parseIptablesLineForInterface(String line) { line = line.trim(); if (line.isEmpty() || line.startsWith("Chain") || line.startsWith("pkts") || line.startsWith("target")) { - return; + return null; } // 使用空格分割,处理多个连续空格 String[] parts = line.split("\\s+"); - System.out.println("解析行: " + line); - System.out.println("分割部分数量: " + parts.length + ", 内容: " + Arrays.toString(parts)); if (parts.length >= 7) { try { // 字节数在第二列(索引1) long bytes = Long.parseLong(parts[1]); - String interfaceName = null; - // 根据statType确定是输入接口还是输出接口 - if ("ipv4Recv".equals(statType) || "ipv6Recv".equals(statType)) { - // 接收流量:查看输入接口(第6列,索引5) - if (parts.length > 5 && !parts[5].equals("*")) { - interfaceName = parts[5]; - System.out.println("接收流量 - 输入接口: " + interfaceName); - } - } else if ("ipv4Sent".equals(statType) || "ipv6Sent".equals(statType)) { - // 发送流量:查看输出接口(第7列,索引6) - if (parts.length > 6 && !parts[6].equals("*")) { - interfaceName = parts[6]; - System.out.println("发送流量 - 输出接口: " + interfaceName); - } - } - - if (interfaceName != null && GlobalConfig.NETNAME.contains(interfaceName)) { - Map interfaceStats = stats.getOrDefault(interfaceName, new HashMap<>()); - - // 累加相同接口的流量(处理多条规则匹配同一接口的情况) - long currentBytes = interfaceStats.getOrDefault(statType, 0L); - interfaceStats.put(statType, currentBytes + bytes); - stats.put(interfaceName, interfaceStats); - - System.out.println("成功记录iptables统计: " + interfaceName + " - " + statType + " = " + bytes + " bytes (累计: " + (currentBytes + bytes) + ")"); - } else if (interfaceName != null) { - System.out.println("接口 " + interfaceName + " 不是业务网卡,跳过"); - } else { - System.out.println("未找到有效的接口名(可能是通配符*)"); - } + // 由于已经用grep过滤了,直接返回字节数 + System.out.println("找到匹配的规则: " + bytes + " bytes"); + return bytes; } catch (NumberFormatException e) { System.err.println("解析iptables行失败,无法解析数字: " + line); - System.err.println("错误部分: " + parts[1]); } - } else { - System.out.println("行格式不符合预期,跳过: " + line); } + + return null; } public static Map getNetworkMode(String interfaceName) { diff --git a/src/main/java/com/tongran/agent/client/utils/AgentUtil.java b/src/main/java/com/tongran/agent/client/utils/AgentUtil.java index e8e80ed..bd8c84e 100644 --- a/src/main/java/com/tongran/agent/client/utils/AgentUtil.java +++ b/src/main/java/com/tongran/agent/client/utils/AgentUtil.java @@ -814,7 +814,140 @@ public class AgentUtil { int hz = getSystemHz(); return (double) ticks / hz; } + /** + * 判断IPv6地址是否为公网地址 + * @param ipv6 IPv6地址字符串 + * @return true-公网地址, false-私网地址或无效地址 + */ + public static boolean isPublicIPv6(String ipv6) { + if (ipv6 == null || ipv6.trim().isEmpty()) { + return false; + } + try { + // 规范化IPv6地址(处理缩写形式) + String normalized = normalizeIPv6(ipv6); + if (normalized == null) { + return false; // 无效的IPv6地址 + } + + // 私网地址范围: + // 1. fc00::/7 - 唯一本地地址 (ULA) + // 2. fe80::/10 - 链路本地地址 + // 3. ::1/128 - 环回地址 + // 4. ::/128 - 未指定地址 + + if (normalized.startsWith("fc") || normalized.startsWith("fd")) { + return false; // fc00::/7 (ULA) + } + + if (normalized.startsWith("fe8") || normalized.startsWith("fe9") || + normalized.startsWith("fea") || normalized.startsWith("feb")) { + return false; // fe80::/10 (链路本地) + } + + if (normalized.equals("00000000000000000000000000000001") || + normalized.equals("00000000000000000000000000000000")) { + return false; // ::1 和 :: + } + + return true; + + } catch (Exception e) { + return false; // 解析异常,视为无效地址 + } + } + + /** + * 规范化IPv6地址为完整的32字符十六进制形式(无冒号) + */ + private static String normalizeIPv6(String ipv6) { + // 处理环回和未指定地址的特殊情况 + if ("::1".equals(ipv6) || "::".equals(ipv6)) { + return ipv6.equals("::1") ? + "00000000000000000000000000000001" : + "00000000000000000000000000000000"; + } + + // 分离可能的IPv6字面量(如[::1]形式) + ipv6 = ipv6.replaceAll("[\\[\\]]", ""); + + // 分离地址和端口(如果有) + String[] parts = ipv6.split("%", 2); // 处理区域标识符 + String address = parts[0]; + + // 分离IPv4映射的IPv6地址(如::ffff:192.168.1.1) + if (address.contains(".")) { + if (address.startsWith("::ffff:")) { + // IPv4映射地址,暂时按公网处理或根据需求调整 + return "00000000000000000000000000000000"; // 这里可根据需要调整 + } + return null; // 无效的混合格式 + } + + // 检查是否为缩写形式并扩展 + if (address.contains("::")) { + address = expandDoubleColon(address); + } + + // 分割成8个16位块 + String[] blocks = address.split(":"); + if (blocks.length != 8) { + return null; // 无效的IPv6格式 + } + + // 将每个块扩展为4个十六进制字符 + StringBuilder normalized = new StringBuilder(32); + for (String block : blocks) { + if (block.length() > 4) { + return null; // 无效的块长度 + } + // 左侧补零到4位 + String padded = "0000" + block; + normalized.append(padded.substring(padded.length() - 4)); + } + + return normalized.toString(); + } + + /** + * 扩展双冒号缩写 + */ + private static String expandDoubleColon(String ipv6) { + int doubleColonIndex = ipv6.indexOf("::"); + String[] parts = ipv6.split("::", -1); + + if (parts.length != 2) { + return ipv6; // 无效的双冒号格式 + } + + String leftPart = parts[0]; + String rightPart = parts[1]; + + int leftCount = leftPart.isEmpty() ? 0 : leftPart.split(":").length; + int rightCount = rightPart.isEmpty() ? 0 : rightPart.split(":").length; + int zeroCount = 8 - (leftCount + rightCount); + + if (zeroCount <= 0) { + return ipv6; // 无效的缩写 + } + + StringBuilder zeros = new StringBuilder(); + for (int i = 0; i < zeroCount; i++) { + if (i > 0) zeros.append(":"); + zeros.append("0"); + } + + if (leftPart.isEmpty() && rightPart.isEmpty()) { + return zeros.toString(); + } else if (leftPart.isEmpty()) { + return zeros + ":" + rightPart; + } else if (rightPart.isEmpty()) { + return leftPart + ":" + zeros; + } else { + return leftPart + ":" + zeros + ":" + rightPart; + } + } }