Files
DenyIP-go/cap.go
2025-12-23 13:14:53 +08:00

134 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/google/gopacket/pcapgo"
)
// 打印可用的网络接口信息 (保持不变)
func printAvailableInterfaces() {
devices, err := pcap.FindAllDevs()
if err != nil {
log.Fatal(err)
}
fmt.Println("可用的网络接口:")
for _, device := range devices {
fmt.Printf("名称: %s\n描述: %s\n地址: %+v\n\n", device.Name, device.Description, device.Addresses)
}
}
// ----------------------------------------------------------------------
// 移除 isIPInList 函数 (不再需要)
// ----------------------------------------------------------------------
// 处理捕获到的数据包 (原 printPacketInfo 的优化版)
func handlePacket(packet gopacket.Packet) {
// 1. 快速提取网络层 (IPv4)
// 使用 Layer() 比 Layers() 稍微快一点点,但如果追求极致性能,建议使用 ZeroCopy 模式
ipLayer := packet.Layer(layers.LayerTypeIPv4)
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 参数")
}
// 打开指定的网络接口进行实时数据包捕获
// 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("清理抓包资源...")
handle.Close()
}()
// 设置 BPF 过滤器
if *Protocol != "" {
if err = handle.SetBPFFilter(*Protocol); err != nil {
log.Fatalf("设置 BPF 过滤器出错: %v", err)
}
}
// --- PCAP 文件保存逻辑 (如果不需要存包,这部分其实是最大的性能瓶颈) ---
var pcapWriter *pcapgo.Writer
if *PcapFile != "" {
file, err := os.Create(*PcapFile)
if err != nil {
log.Fatalf("创建输出文件出错: %v", err)
}
defer file.Close()
pcapWriter = pcapgo.NewWriter(file)
// 写入文件头
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 处理数据包
go func() {
// 直接遍历 channel
for packet := range packetSource.Packets() {
// 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)
}
}
}
}()
// 阻塞等待信号
<-sigChan
fmt.Println("\n停止抓包...")
}