184 lines
4.9 KiB
Go
184 lines
4.9 KiB
Go
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"os/exec"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
// 创建 ipset 集合
|
||
func createIPSet(setName string) error {
|
||
// 建议增加 maxelem,防止频繁扩容。默认 65536 有点小。
|
||
// 加上 -exist 防止报错
|
||
cmd := exec.Command("ipset", "create", setName, "hash:ip", "maxelem", "200000", "-exist")
|
||
var stderr bytes.Buffer
|
||
cmd.Stderr = &stderr
|
||
|
||
err := cmd.Run()
|
||
if err != nil {
|
||
return fmt.Errorf("ipset create failed: %v, stderr: %s", err, stderr.String())
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 添加 IP 到集合
|
||
func AddIPSet(setName string, ip string) error {
|
||
cmd := exec.Command("ipset", "add", setName, ip)
|
||
// 不关注输出,只关注 exit code
|
||
return cmd.Run()
|
||
}
|
||
|
||
// NumIPSet 获取集合中 IP 的数量
|
||
// 注意:频繁调用此函数开销较大(因为它通过 shell 解析文本),建议仅在监控循环中低频调用
|
||
func NumIPSet(setName string) (int, error) {
|
||
// 这里必须用 sh -c 因为涉及管道符 |
|
||
cmd := exec.Command("sh", "-c", fmt.Sprintf("ipset list %s | grep 'Number of entries' | cut -d ':' -f 2", setName))
|
||
|
||
var stdout bytes.Buffer
|
||
cmd.Stdout = &stdout
|
||
|
||
err := cmd.Run()
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
output := strings.TrimSpace(stdout.String())
|
||
numEntries, err := strconv.Atoi(output)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("解析数量失败: %w, 输出: %s", err, output)
|
||
}
|
||
|
||
return numEntries, nil
|
||
}
|
||
|
||
// Is_Name_Ipset 检查集合是否存在
|
||
// 返回 0 表示存在 (ExitCode 0),其他表示不存在
|
||
func Is_Name_Ipset(setName string) int {
|
||
cmd := exec.Command("ipset", "list", setName, "-n") // -n 只输出名字,减少开销
|
||
err := cmd.Run()
|
||
if err != nil {
|
||
return 1 // 不存在或出错
|
||
}
|
||
return 0 // 存在
|
||
}
|
||
|
||
// Is_Ip_Ipset 检查 IP 是否在 ANY root 集合中
|
||
// 优化:不再使用 grep 全局搜索,而是遍历 root0...rootN 使用 ipset test
|
||
func Is_Ip_Ipset(ip string) int {
|
||
// 遍历所有可能的集合名
|
||
for i := 0; i < MAX_IPSET_NAME; i++ {
|
||
setName := fmt.Sprintf("root%d", i)
|
||
|
||
// 快速检查集合是否存在(可选,为了严谨)
|
||
// 如果 ipset test 在集合不存在时会报错,所以我们直接运行 test
|
||
// 如果 ip 在集合中,exit code 为 0
|
||
cmd := exec.Command("ipset", "test", setName, ip)
|
||
err := cmd.Run()
|
||
|
||
if err == nil {
|
||
return 1 // 找到了,IP 在该集合中
|
||
}
|
||
}
|
||
|
||
return 0 // 所有集合都找过了,不在其中
|
||
}
|
||
|
||
// RemoveIPIfInSets 在所有集合中查找并删除某个 IP
|
||
func RemoveIPIfInSets(prefix string, max int, ip string) (string, error) {
|
||
for i := 0; i < max; i++ {
|
||
setName := fmt.Sprintf("%s%d", prefix, i)
|
||
|
||
// 1. 先测试是否存在 (避免无意义的 del 调用报错)
|
||
testCmd := exec.Command("ipset", "test", setName, ip)
|
||
if err := testCmd.Run(); err != nil {
|
||
// exit code != 0 说明不在这个集合里,继续找下一个
|
||
continue
|
||
}
|
||
|
||
// 2. 存在则删除
|
||
delCmd := exec.Command("ipset", "del", setName, ip)
|
||
if err := delCmd.Run(); err != nil {
|
||
return "", fmt.Errorf("从 %s 删除 %s 失败: %v", setName, ip, err)
|
||
}
|
||
|
||
return setName, nil // 成功删除
|
||
}
|
||
|
||
return "", nil // 未找到
|
||
}
|
||
|
||
// iptables_add 添加规则
|
||
// 修正:使用 -I (Insert) 而不是 -A (Append),确保规则在最前面生效
|
||
func iptables_add(setName string) error {
|
||
// 检查规则是否已经存在,避免重复添加
|
||
checkCmd := exec.Command("iptables", "-C", "INPUT", "-m", "set", "--match-set", setName, "src", "-j", "DROP")
|
||
if err := checkCmd.Run(); err == nil {
|
||
// 规则已存在,直接返回
|
||
return nil
|
||
}
|
||
|
||
// 添加规则
|
||
cmd := exec.Command("iptables", "-I", "INPUT", "-m", "set", "--match-set", setName, "src", "-j", "DROP")
|
||
var stderr bytes.Buffer
|
||
cmd.Stderr = &stderr
|
||
|
||
err := cmd.Run()
|
||
if err != nil {
|
||
log.Printf("添加 iptables 规则失败 [%s]: %v, stderr: %s", setName, err, stderr.String())
|
||
}
|
||
return err
|
||
}
|
||
|
||
// iptables_del 删除规则
|
||
func iptables_del(setName string) error {
|
||
// 循环删除,防止有重复规则
|
||
for {
|
||
cmd := exec.Command("iptables", "-D", "INPUT", "-m", "set", "--match-set", setName, "src", "-j", "DROP")
|
||
if err := cmd.Run(); err != nil {
|
||
// 删除失败通常意味着规则不存在了,跳出循环
|
||
break
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// iptables_list 打印规则
|
||
func iptables_list() error {
|
||
cmd := exec.Command("iptables", "-L", "INPUT", "-v", "-n", "--line-numbers")
|
||
cmd.Stdout = os.Stdout
|
||
cmd.Stderr = os.Stderr
|
||
return cmd.Run()
|
||
}
|
||
|
||
/*
|
||
func main() {
|
||
// 创建 IPSet,但即使出错也继续执行
|
||
err := createIPSet("root0")
|
||
if err != nil {
|
||
log.Println("创建 IPSet 出错:", err)
|
||
} else {
|
||
fmt.Println("IPSet 创建成功!")
|
||
}
|
||
|
||
// 添加 IP 到 IPSet,出错时继续执行
|
||
err = AddIPSet("root0", "1.1.1.1")
|
||
if err != nil {
|
||
log.Println("添加 IP 到 IPSet 出错:", err)
|
||
}
|
||
|
||
// 获取 IPSet 条目数,出错时继续执行
|
||
num, err := NumIPSet("root0")
|
||
if err != nil {
|
||
log.Println("获取 IPSet 条目数出错:", err)
|
||
} else {
|
||
fmt.Printf("IPSet 条目数: %d\n", num)
|
||
}
|
||
|
||
iptables("root0")
|
||
}
|
||
*/
|