新架构
This commit is contained in:
101
cap.go
101
cap.go
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
@@ -14,7 +13,7 @@ import (
|
||||
"github.com/google/gopacket/pcapgo"
|
||||
)
|
||||
|
||||
// 打印可用的网络接口信息
|
||||
// 打印可用的网络接口信息 (保持不变)
|
||||
func printAvailableInterfaces() {
|
||||
devices, err := pcap.FindAllDevs()
|
||||
if err != nil {
|
||||
@@ -27,54 +26,61 @@ func printAvailableInterfaces() {
|
||||
}
|
||||
}
|
||||
|
||||
// 判断 IP 是否在链表中
|
||||
func isIPInList(ip net.IP) bool {
|
||||
for e := IpList.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(net.IP).Equal(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
// ----------------------------------------------------------------------
|
||||
// 移除 isIPInList 函数 (不再需要)
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// 打印捕获到的每个数据包的信息
|
||||
func printPacketInfo(packet gopacket.Packet) {
|
||||
// 处理捕获到的数据包 (原 printPacketInfo 的优化版)
|
||||
func handlePacket(packet gopacket.Packet) {
|
||||
// 1. 快速提取网络层 (IPv4)
|
||||
// 使用 Layer() 比 Layers() 稍微快一点点,但如果追求极致性能,建议使用 ZeroCopy 模式
|
||||
ipLayer := packet.Layer(layers.LayerTypeIPv4)
|
||||
|
||||
if ipLayer != nil {
|
||||
ip, _ := ipLayer.(*layers.IPv4)
|
||||
IpMutex.Lock()
|
||||
defer IpMutex.Unlock()
|
||||
if !isIPInList(ip.SrcIP) {
|
||||
IpList.PushBack(ip.SrcIP)
|
||||
log.Printf("\033[31m 已添加源 IP: %s 到链表 \033[0m\n", ip.SrcIP)
|
||||
|
||||
}
|
||||
if ipLayer == nil {
|
||||
return
|
||||
}
|
||||
ip, ok := ipLayer.(*layers.IPv4)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 提取源 IP 字符串
|
||||
srcIP := ip.SrcIP.String()
|
||||
|
||||
// 3. 将 IP 推入全局处理队列
|
||||
// 这里不再加锁,也不判断是否存在链表中,直接交给 PushIPToQueue 的非阻塞逻辑处理
|
||||
// PushIPToQueue 会负责去重 (PendingIPs) 和限流
|
||||
PushIPToQueue(srcIP)
|
||||
|
||||
// 注意:为了性能,去掉了这里的日志打印。
|
||||
// 在高流量攻击下,每秒打印几千行日志会导致 IO 阻塞,拖慢抓包速度。
|
||||
// 具体的封禁日志会在 processIP (消费者) 中打印。
|
||||
}
|
||||
|
||||
// startPacketCapture 启动数据包捕获
|
||||
func startPacketCapture() {
|
||||
if *InterfaceName == "" {
|
||||
log.Fatal("未指定网络接口,请使用 -i 参数")
|
||||
}
|
||||
|
||||
// 打开指定的网络接口进行实时数据包捕获
|
||||
handle, err := pcap.OpenLive(*InterfaceName, 65535, true, pcap.BlockForever)
|
||||
// snaplen 设置为 1600 足够捕获头部信息,没必要设为 65535,可以稍微节省内存
|
||||
handle, err := pcap.OpenLive(*InterfaceName, 1600, true, pcap.BlockForever)
|
||||
if err != nil {
|
||||
log.Fatalf("打开网络接口 %s 出错: %v", *InterfaceName, err)
|
||||
}
|
||||
// 确保在函数退出时关闭句柄,释放资源
|
||||
defer func() {
|
||||
fmt.Println("清理资源...")
|
||||
if handle != nil {
|
||||
handle.Close()
|
||||
}
|
||||
fmt.Println("清理抓包资源...")
|
||||
handle.Close()
|
||||
}()
|
||||
|
||||
// 设置 BPF(Berkeley Packet Filter)过滤器,以便只捕获指定协议的数据包
|
||||
err = handle.SetBPFFilter(*Protocol)
|
||||
if err != nil {
|
||||
log.Fatalf("设置 BPF 过滤器出错: %v", err)
|
||||
// 设置 BPF 过滤器
|
||||
if *Protocol != "" {
|
||||
if err = handle.SetBPFFilter(*Protocol); err != nil {
|
||||
log.Fatalf("设置 BPF 过滤器出错: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果指定了输出文件,则创建文件并初始化 pcapgo.Writer
|
||||
// --- PCAP 文件保存逻辑 (如果不需要存包,这部分其实是最大的性能瓶颈) ---
|
||||
var pcapWriter *pcapgo.Writer
|
||||
if *PcapFile != "" {
|
||||
file, err := os.Create(*PcapFile)
|
||||
@@ -84,28 +90,35 @@ func startPacketCapture() {
|
||||
defer file.Close()
|
||||
|
||||
pcapWriter = pcapgo.NewWriter(file)
|
||||
// 写入 pcap 文件头部,指定最大捕获长度和链路层类型
|
||||
err = pcapWriter.WriteFileHeader(65535, layers.LinkTypeEthernet)
|
||||
if err != nil {
|
||||
// 写入文件头
|
||||
if err = pcapWriter.WriteFileHeader(1600, layers.LinkTypeEthernet); err != nil {
|
||||
log.Fatalf("写入全局头部出错: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建数据包源,用于从网络接口读取数据包
|
||||
// 使用 ZeroCopyPacketDataSource 可以减少内存拷贝,提升性能 (Linux Only)
|
||||
// 如果在非 Linux 环境编译报错,请改回 gopacket.NewPacketSource
|
||||
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
|
||||
// packetSource.NoCopy = true // 开启 NoCopy 模式 (可选,稍微危险但更快)
|
||||
|
||||
log.Printf(" 正在监听网络接口 %s, 使用过滤器 '%s'...\n", *InterfaceName, *Protocol)
|
||||
|
||||
// 创建信号通道,用于捕获中断信号
|
||||
// 创建信号通道
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// 启动一个 goroutine 来处理捕获到的数据包
|
||||
// 启动 Goroutine 处理数据包
|
||||
go func() {
|
||||
// 直接遍历 channel
|
||||
for packet := range packetSource.Packets() {
|
||||
// 打印数据包信息
|
||||
printPacketInfo(packet)
|
||||
// 如果指定了输出文件,则将数据包写入文件
|
||||
|
||||
// 1. 核心业务:提取 IP 进队列
|
||||
handlePacket(packet)
|
||||
|
||||
// 2. 可选业务:存盘
|
||||
// 警告:在高并发攻击下,写文件 IO 会成为瓶颈。建议生产环境非必要不开启 -o 参数
|
||||
if pcapWriter != nil {
|
||||
// 注意:CaptureInfo 和 Data 必须在 packet 被复用前读取
|
||||
err := pcapWriter.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
|
||||
if err != nil {
|
||||
log.Printf("写入数据包出错: %v", err)
|
||||
@@ -114,7 +127,7 @@ func startPacketCapture() {
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待中断信号,收到信号后停止捕获
|
||||
// 阻塞等待信号
|
||||
<-sigChan
|
||||
fmt.Println("\n停止抓包...")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user