using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NewLife;
using NewLife.Log;
using NewLife.Threading;
using Rainbow.Entity;
namespace Rainbow.Services;
/// <summary>防火墙/NAT 管理器。支持时段策略</summary>
public class FirewallManager
{
public ShellExecutor Shell { get; set; }
public ConfigBackup Backup { get; set; }
public ILog Log { get; set; } = Logger.Null;
private readonly IFirewallAdapter? _adapter;
private TimerX _scheduleTimer;
private readonly ConcurrentDictionary<Int32, (String StartTime, String EndTime, Int32 WeekDays)> _schedules = new();
private Int32 _scheduling;
public FirewallManager(ShellExecutor shell = null, ConfigBackup backup = null, IFirewallAdapter? adapter = null) { Shell = shell; Backup = backup; _adapter = adapter; }
public void SetSchedule(Int32 ruleId, String startTime, String endTime, Int32 weekDays = 127)
{
if (startTime.IsNullOrEmpty() || endTime.IsNullOrEmpty()) { _schedules.TryRemove(ruleId, out _); return; }
_schedules[ruleId] = (startTime, endTime, weekDays);
StartScheduleTimer();
Log.Info("规则[{0}] 时段设置:{1}-{2}", ruleId, startTime, endTime);
}
public void RemoveSchedule(Int32 ruleId) { _schedules.TryRemove(ruleId, out _); }
private void StartScheduleTimer()
{
if (_scheduleTimer != null) return;
_scheduleTimer = new TimerX(OnScheduleCheck, null, 10000, 60_000, "Firewall") { Async = true };
}
private void OnScheduleCheck(Object state)
{
if (Interlocked.CompareExchange(ref _scheduling, 1, 0) != 0) return;
try
{
var now = DateTime.Now;
var currentTime = now.ToString("HH:mm");
var dayOfWeek = (Int32)now.DayOfWeek;
var dayBit = dayOfWeek == 0 ? 64 : 1 << (dayOfWeek - 1);
foreach (var kv in _schedules)
{
var (startTime, endTime, weekDays) = kv.Value;
var inRange = String.Compare(currentTime, startTime, StringComparison.Ordinal) >= 0 && String.Compare(currentTime, endTime, StringComparison.Ordinal) <= 0;
var shouldEnable = (weekDays & dayBit) != 0 && inRange;
var rule = FirewallRule.FindById(kv.Key);
if (rule == null) { _schedules.TryRemove(kv.Key, out _); continue; }
if (rule.Enable != shouldEnable) { rule.Enable = shouldEnable; rule.Update(); Log.Info("规则[{0}] 时段调度:{1}", kv.Key, shouldEnable ? "启用" : "停用"); }
}
}
catch (Exception ex) { Log.Error("时段调度异常:{0}", ex.Message); }
finally { Interlocked.Exchange(ref _scheduling, 0); }
}
public IReadOnlyDictionary<Int32, (String StartTime, String EndTime, Int32 WeekDays)> GetSchedules() => _schedules;
public async Task<ShellResult> SaveRulesAsync()
{
if (_adapter != null)
{
var ok = await _adapter.SaveAsync();
return new ShellResult { ExitCode = ok ? 0 : -1, Stdout = ok ? "防火墙规则已保存" : null, Stderr = ok ? null : "保存防火墙规则失败" };
}
// 无适配器时回退到 shell 命令(Linux iptables-save)
if (Shell != null)
{
var result = await Shell.ExecuteAsync("iptables-save", "", true);
return result;
}
return ShellResult.Fail("无可用防火墙适配器");
}
}
|