agent定时更新优化、脚本策略定时执行优化

优化ipv4、ipv6采集
This commit is contained in:
gaoyutao
2025-12-04 18:28:59 +08:00
parent 45840e00e3
commit 5f67018d35
8 changed files with 355 additions and 153 deletions
@@ -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{
@@ -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{
//未注册,发送注册
@@ -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 {
@@ -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);
@@ -36,4 +36,6 @@ public interface AgentService {
void checkFirewall();
void checkAndAddFirewallPeriodically();
void checkAgentUpdate();
}
@@ -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());
}
}
}
@@ -17,7 +17,6 @@ import java.util.*;
@Service
public class NetServiceImpl implements NetService {
@Override
public List<NetVO> netList(long timestamp) {
List<NetVO> 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<String, Map<String, Long>> getIptablesStatistics() {
Map<String, Map<String, Long>> 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<String, Long> 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<String, Map<String, Long>> 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<String, Long> 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<String, String> getNetworkMode(String interfaceName) {
@@ -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;
}
}
}