ref: 重构packet跟踪解析

This commit is contained in:
TsMask
2025-04-17 19:18:00 +08:00
parent 94cab3965e
commit 243312e5fa
5 changed files with 397 additions and 1442 deletions

View File

@@ -2,6 +2,8 @@ package controller
import (
"fmt"
"path/filepath"
"runtime"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
@@ -125,3 +127,23 @@ func (s *PacketController) KeepAlive(c *gin.Context) {
}
c.JSON(200, resp.Ok(nil))
}
// 信令跟踪文件
//
// GET /filePull
func (s *PacketController) FilePull(c *gin.Context) {
var querys struct {
TaskNo string `json:"taskNo" form:"taskNo" binding:"required"` // 任务编号
}
if err := c.ShouldBindQuery(&querys); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(40422, errMsgs))
return
}
fileName := fmt.Sprintf("%s.pcap", querys.TaskNo)
localFilePath := filepath.Join("/tmp/omc/packet", fileName)
if runtime.GOOS == "windows" {
localFilePath = fmt.Sprintf("C:%s", localFilePath)
}
c.FileAttachment(localFilePath, fileName)
}

View File

@@ -1,245 +0,0 @@
package packet_task
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"be.ems/src/framework/logger"
wsService "be.ems/src/modules/ws/service"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
"github.com/gopacket/gopacket/pcap"
"github.com/gopacket/gopacket/pcapgo"
)
// 捕获任务
var taskMap sync.Map
// task 任务信息
type task struct {
TaskNo string // 任务编号
Handle *pcap.Handle // 捕获句柄
File *os.File // 捕获信息输出文件句柄
Writer *pcapgo.Writer // 捕获信息输出句柄
Filter string // 过滤表达式
Ticker *time.Ticker // 任务失效定时器
}
// NetworkDevices 获取网卡设备信息
func NetworkDevices() ([]pcap.Interface, error) {
devices, err := pcap.FindAllDevs()
if err != nil {
logger.Errorf("interfaces find all devices err: %s", err.Error())
return nil, err
}
return devices, nil
}
// verifyDevice 检查网卡设备是否存在
func verifyDevice(str string) (string, bool) {
devices, err := pcap.FindAllDevs()
if err != nil {
logger.Errorf("interfaces find all devices err: %s", err.Error())
return "", false
}
for _, device := range devices {
if len(device.Addresses) == 0 {
continue
}
if device.Name == str {
return device.Name, true
}
for _, address := range device.Addresses {
if address.IP.String() == str {
return device.Name, true
}
}
}
return "", false
}
// outputPCAP 输出 pcap 文件
// 新文件时需要 snaplen 最大长度 linktype 链路类型
func outputPCAP(snaplen uint32, linktype layers.LinkType, outputFile string) (*os.File, *pcapgo.Writer, error) {
var err error
var f *os.File
if err := os.MkdirAll(filepath.Dir(outputFile), 0775); err != nil {
return nil, nil, err
}
// 检查文件是否存在
if _, err = os.Stat(outputFile); os.IsNotExist(err) {
f, err = os.Create(outputFile)
if err != nil {
return nil, nil, err
}
w := pcapgo.NewWriter(f)
w.WriteFileHeader(snaplen, linktype) // new file, must do this.
return f, w, nil
}
f, err = os.OpenFile(outputFile, os.O_APPEND, 0700)
if err != nil {
return nil, nil, err
}
w := pcapgo.NewWriter(f)
return f, w, nil
}
// capturePacketSource 捕获数据
func capturePacketSource(taskInfo *task) {
// capture packets
packetSource := gopacket.NewPacketSource(taskInfo.Handle, taskInfo.Handle.LinkType())
packetSource.Lazy = false
packetSource.NoCopy = true
packetSource.DecodeStreamsAsDatagrams = true
// 协程停止后关闭句柄并移除任务信息
defer func() {
taskInfo.Ticker.Stop()
taskInfo.Handle.Close()
if taskInfo.File != nil {
taskInfo.File.Close()
}
taskMap.Delete(taskInfo.TaskNo)
}()
frameNumber := 0 // 帧编号
frameTime := 0.000000 // 时间
var startTimestamp time.Time // 开始时间
for {
select {
case <-taskInfo.Ticker.C:
return
case packet := <-packetSource.Packets():
if packet == nil {
continue
}
// if packet.Metadata().Timestamp.Before(time.Now()) {
// continue
// }
if taskInfo.Writer != nil {
taskInfo.Writer.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
}
fmt.Println("---------- packet.Layers() ", len(packet.Layers()))
frameNumber++ // 帧编号
currentTimestamp := packet.Metadata().Timestamp
if !startTimestamp.IsZero() {
// 计算时间差转换为秒
frameTime = currentTimestamp.Sub(startTimestamp).Seconds()
} else {
startTimestamp = currentTimestamp
}
// 数据
frameMeta := parsePacketFrame(frameNumber, frameTime, packet)
// 推送到ws订阅组
wsService.NewWSSend.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_TRACE_PACKET, taskInfo.TaskNo), frameMeta)
}
}
}
// LiveStart 开始捕获数据
func LiveStart(taskNo, deviceName, filterBPF string, outputFile bool) (string, error) {
if _, ok := taskMap.Load(taskNo); ok {
return "", fmt.Errorf("task no. %s already exist", taskNo)
}
// Verify the specified network interface exists
device, deviceOk := verifyDevice(deviceName)
if !deviceOk {
return "", fmt.Errorf("network device not exist: %s", deviceName)
}
snapshotLength := 262144
// open device
handle, err := pcap.OpenLive(device, int32(snapshotLength), true, pcap.BlockForever)
if err != nil {
logger.Errorf("open live err: %s", err.Error())
if strings.Contains(err.Error(), "operation not permitted") {
return "", fmt.Errorf("you don't have permission to capture on that/these device(s)")
}
return "", err
}
// write a new file
var w *pcapgo.Writer
var f *os.File
if outputFile {
// 网管本地路径
localFilePath := fmt.Sprintf("/tmp/omc/packet/%s.pcap", taskNo)
if runtime.GOOS == "windows" {
localFilePath = fmt.Sprintf("C:%s", localFilePath)
}
f, w, err = outputPCAP(uint32(snapshotLength), handle.LinkType(), localFilePath)
if err != nil {
return "", err
}
}
// set filter
if filterBPF != "" {
if err = handle.SetBPFFilter(filterBPF); err != nil {
logger.Errorf("packet BPF Filter %s => %s", filterBPF, err.Error())
filterBPF = ""
}
}
// save tasks
taskInfo := &task{
TaskNo: taskNo,
Handle: handle,
File: f,
Writer: w,
Filter: filterBPF,
Ticker: time.NewTicker(time.Second * 120),
}
go capturePacketSource(taskInfo)
taskMap.Store(taskNo, taskInfo)
return fmt.Sprintf("task no. %s initiated", taskNo), nil
}
// LiveFilter 捕获过滤
func LiveFilter(taskNo, expr string) error {
info, ok := taskMap.Load(taskNo)
if !ok {
return fmt.Errorf("task no. %s not exist", taskNo)
}
task := info.(*task)
task.Filter = expr
err := task.Handle.SetBPFFilter(expr)
if err != nil {
logger.Errorf("packet BPF Filter %s => %s", expr, err.Error())
return fmt.Errorf("can't parse filter expression")
}
return nil
}
// LiveTimeout 更新捕获失效时间
func LiveTimeout(taskNo string, seconds int) error {
info, ok := taskMap.Load(taskNo)
if !ok {
return fmt.Errorf("task no. %s not exist", taskNo)
}
info.(*task).Ticker.Reset(time.Duration(seconds) * time.Second)
return nil
}
// LiveStop 停止捕获数据
func LiveStop(taskNo string) error {
info, ok := taskMap.Load(taskNo)
if !ok {
return fmt.Errorf("task no. %s not exist", taskNo)
}
info.(*task).Ticker.Reset(time.Millisecond)
return nil
}

View File

@@ -1,843 +0,0 @@
package packet_task
import (
"encoding/base64"
"fmt"
"strings"
"be.ems/src/framework/logger"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
)
// FrameMeta 数据帧元信息
type FrameMeta struct {
Number int `json:"number"`
Comments bool `json:"comments"`
Ignored bool `json:"ignored"`
Marked bool `json:"marked"`
Bg int `json:"bg"` // 背景色 数值转字符串16进制 15007687->e4ffc7
Fg int `json:"fg"` // 前景色 文字
Columns [7]string `json:"columns"` // 长度对应字段 ['No.', 'Time', 'Source', 'Destination', 'Protocol', 'Length', 'Info']
Frame Frame `json:"frame"`
}
// Frame 数据帧信息
type Frame struct {
Number int `json:"number"`
Comments []string `json:"comments"`
DataSource []map[string]string `json:"data_sources"`
Tree []ProtoTree `json:"tree"`
Follow [][]string `json:"follow"`
}
// ProtoTree 数据帧协议树
type ProtoTree struct {
Label string `json:"label"` // 显示的文本
Filter string `json:"filter"` // 过滤条件
Severity string `json:"severity"`
Type string `json:"type"`
URL string `json:"url"`
Fnum int `json:"fnum"`
Start int `json:"start"` // 开始位置
Length int `json:"length"` // 长度
DataSourceIdx int `json:"data_source_idx"`
Tree []ProtoTree `json:"tree"` // 子节点
}
// parsePacketFrame 解析数据包帧信息
// frameNumber 帧编号 i++
// frameTime 时间秒 0.000000
func parsePacketFrame(frameNumber int, frameTime float64, packet gopacket.Packet) FrameMeta {
frameSrcHost := "" // 源主机IP
frameDstHost := "" // 目的主机IP
frameProtocol := "" // 协议
frameLength := fmt.Sprintf("%d", packet.Metadata().Length) // 长度
frameInfo := "" // 信息
fg, bg := colorRuleFB(packet) // 背景色 数值转字符串16进制 15007687->e4ffc7
frame := Frame{
Number: frameNumber,
Comments: []string{},
DataSource: []map[string]string{
{
"name": fmt.Sprintf("Frame (%d bytes)", packet.Metadata().Length),
"data": base64.StdEncoding.EncodeToString(packet.Data()),
},
},
Tree: []ProtoTree{}, // 各层的数据
Follow: [][]string{}, // {"TCP", "tcp.stream eq 0"}
}
// 连接层
// fmt.Println(packet.LinkLayer())
if linkLayer := packet.LinkLayer(); linkLayer != nil {
linkTree := linkLayerTree(linkLayer)
frame.Tree = append(frame.Tree, linkTree)
}
// 网络层
// fmt.Println(packet.NetworkLayer())
if networkLayer := packet.NetworkLayer(); networkLayer != nil {
networkTree := networkLayerTree(networkLayer)
frame.Tree = append(frame.Tree, networkTree)
src, dst := networkLayer.NetworkFlow().Endpoints()
frameSrcHost = src.String()
frameDstHost = dst.String()
if frameDstHost == "ff:ff:ff:ff" {
frameDstHost = "Broadcast"
}
}
// 传输层
// fmt.Println(packet.TransportLayer())
if transportLayer := packet.TransportLayer(); transportLayer != nil {
info, transportTree := transportLayerTree(transportLayer)
frame.Tree = append(frame.Tree, transportTree)
frameProtocol = transportLayer.LayerType().String()
frameInfo += info
frame.Follow = append(frame.Follow, []string{
frameProtocol,
fmt.Sprintf("%s.stream eq 0", strings.ToLower(frameProtocol)),
})
}
// 应用层
// fmt.Println(packet.ApplicationLayer())
if applicationLayer := packet.ApplicationLayer(); applicationLayer != nil {
applicationTree := applicationLayerTree(applicationLayer)
frame.Tree = append(frame.Tree, applicationTree)
}
return FrameMeta{
Number: frameNumber,
Comments: false,
Ignored: false,
Marked: false,
Bg: fg,
Fg: bg,
Columns: [7]string{
fmt.Sprintf("%d", frameNumber),
fmt.Sprintf("%.6f", frameTime), // 格式化为 0.000000
frameSrcHost,
frameDstHost,
frameProtocol,
frameLength,
frameInfo,
},
Frame: frame,
}
}
// linkLayerTree 连接层
func linkLayerTree(linkLayer gopacket.LinkLayer) ProtoTree {
var protoTree ProtoTree
switch layer := linkLayer.(type) {
case *layers.Ethernet: // 最常见的链路层协议用于局域网LAN中。
srcMAC := layer.SrcMAC
dstMAC := layer.DstMAC
ethernetLayerLen := len(layer.Contents)
protoTree = ProtoTree{
Label: fmt.Sprintf("%s II, Src: %s, Dst: %s", layer.LayerType(), srcMAC, dstMAC),
Filter: "eth",
Start: 0,
Length: ethernetLayerLen,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf("Destination: %s", dstMAC.String()),
Filter: fmt.Sprintf("eth.dst == %s", dstMAC.String()),
Start: 0,
Length: ethernetLayerLen,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf("Address: %s", dstMAC.String()),
Filter: fmt.Sprintf("eth.addr == %s", dstMAC.String()),
Start: 0,
Length: 6,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: ".... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)",
Filter: "eth.dst.lg == 0",
Start: 0,
Length: 3,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
Filter: "eth.dst.ig == 0",
Start: 0,
Length: 3,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Source: %s", srcMAC.String()),
Filter: fmt.Sprintf("eth.src == %s", srcMAC.String()),
Start: ethernetLayerLen,
Length: ethernetLayerLen,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf("Address: %s", srcMAC.String()),
Filter: fmt.Sprintf("eth.addr == %s", dstMAC.String()),
Start: ethernetLayerLen,
Length: ethernetLayerLen,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: ".... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)",
Filter: "eth.src.lg == 0",
Start: len(srcMAC),
Length: len(srcMAC) / 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: ".... ...0 .... .... .... .... = IG bit: Individual address (unicast)",
Filter: "eth.src.ig == 0",
Start: len(srcMAC),
Length: len(srcMAC) / 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: "Type: IPv4 (0x0800)",
Filter: "eth.type == 0x0800",
Start: len(dstMAC) + len(srcMAC),
Length: len(layer.LayerContents()) - (len(dstMAC) + len(srcMAC)),
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
},
Severity: "",
Type: "proto",
Fnum: 1052,
URL: "",
}
case *layers.PPP: // 点对点协议,通常用于拨号连接。
protoTree = ProtoTree{
Label: fmt.Sprintf("%s ", layer.LayerType()),
Filter: "ppp",
Start: 0,
Length: len(layer.LayerContents()),
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "proto",
Fnum: 1052,
URL: "",
}
}
return protoTree
}
// networkLayerTree 网络层
func networkLayerTree(networkLayer gopacket.NetworkLayer) ProtoTree {
var protoTree ProtoTree
switch layer := networkLayer.(type) {
case *layers.IPv4: // 第四版因特网协议,广泛使用。
// 偏移量取连接层的长度Length
linkLayerLen := 14
networkLayerLen := len(layer.Contents)
version := layer.Version
length := layer.Length
srcIP := layer.SrcIP
dstIP := layer.DstIP
ihl := layer.IHL
headerLength := ihl * 4 // 提取头部长度
tos := layer.TOS
dscp, ecn := networkDSCPAndECN(tos)
identification := layer.Id
flags := layer.Flags // 提取标志位
// 生成标志描述
flagsDesc := networkFlagsDesc(flags)
rb, rbDesc := networkFlagsEvil(flags)
df, dfDesc := networkFlagsDF(flags)
mf, mfDesc := networkFlagsMF(flags)
fragOffset := layer.FragOffset
fragOffsetDesc := networkOffset(fragOffset)
ttl := layer.TTL
proto := layer.Protocol
checksum := layer.Checksum
protoTree = ProtoTree{
Label: fmt.Sprintf("Internet Protocol Version %d, Src: %s, Dst: %s", version, srcIP, dstIP),
Filter: "ip",
Start: linkLayerLen,
Length: networkLayerLen,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf("%04b .... = Version: %d", version, version),
Filter: fmt.Sprintf("ip.version == %d", version),
Start: linkLayerLen,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf(".... 0101 = Header Length: %d bytes (%d)", headerLength, ihl),
Filter: fmt.Sprintf("ip.hdr_len == %d", headerLength),
Start: linkLayerLen,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Differentiated Services Field: 0x%02x (DSCP: %s, ECN: %s)", tos, dscp, ecn),
Filter: fmt.Sprintf("ip.dsfield == 0x%02x", tos),
Start: linkLayerLen + 1,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf("0000 00.. = Differentiated Services Codepoint: %s (%d)", dscp, tos),
Filter: fmt.Sprintf("ip.dsfield.dscp == %d", tos>>2),
Start: linkLayerLen + 1,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: fmt.Sprintf(".... ..00 = Explicit Congestion Notification: %s Capable Transport (%d)", ecn, tos),
Filter: fmt.Sprintf("ip.dsfield.ecn == %d", tos&0x03),
Start: linkLayerLen + 1,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Total Length: %d", length),
Filter: fmt.Sprintf("ip.len == %d", length),
Start: linkLayerLen + 2,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Identification: 0x%X (%d)", identification, identification),
Filter: fmt.Sprintf("ip.id == 0x%X", identification),
Start: linkLayerLen + 4,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("%03b. .... = Flags: %s", flags, flagsDesc),
Filter: fmt.Sprintf("ip.flags == 0x%X", flags),
Start: linkLayerLen + 6,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf("0... .... = Reserved bit: %s", rbDesc),
Filter: fmt.Sprintf("ip.flags.rb == %d", rb),
Start: linkLayerLen + 6,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: fmt.Sprintf(".1.. .... = Don't fragment: %s", dfDesc),
Filter: fmt.Sprintf("ip.flags.df == %d", df),
Start: linkLayerLen + 6,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: fmt.Sprintf("..0. .... = More fragments: %s", mfDesc),
Filter: fmt.Sprintf("ip.flags.mf == %d", mf),
Start: linkLayerLen + 6,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("%s = Fragment Offset: %d", fragOffsetDesc, fragOffset),
Filter: fmt.Sprintf("ip.frag_offset == %d", fragOffset),
Start: linkLayerLen + 6,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Time to Live: %d", ttl),
Filter: fmt.Sprintf("ip.ttl == %d", ttl),
Start: linkLayerLen + 8,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Protocol: TCP (%d)", proto),
Filter: fmt.Sprintf("ip.proto == %d", proto),
Start: linkLayerLen + 9,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Header Checksum: 0x%x [validation disabled]", checksum),
Filter: fmt.Sprintf("ip.checksum == 0x%x", checksum),
Start: linkLayerLen + 10,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: "Header checksum status: Unverified",
Filter: "ip.checksum.status == \"Unverified\"",
Start: 0,
Length: 0,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Source Address: %s", srcIP),
Filter: fmt.Sprintf("ip.src == %s", srcIP),
Start: linkLayerLen + 12,
Length: 4,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Destination Address: %s", dstIP),
Filter: fmt.Sprintf("ip.dst == %s", dstIP),
Start: linkLayerLen + 16,
Length: 4,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
},
Severity: "",
Type: "proto",
Fnum: 1052,
URL: "",
}
logger.Infof("-> (tos 0x%x, ttl %d, id %d, offset %d, flags [%s], proto %s (%d), length %d)\n", tos, ttl, identification, fragOffset, flags, proto, proto, len(layer.Contents)+len(layer.Payload))
case *layers.IPv6: // 第六版因特网协议,逐渐取代 IPv4。
logger.Infof("-> (flowlabel 0x%x, hlim %d, next-header %s (%d), payload length: %d)\n", layer.FlowLabel, layer.HopLimit, layer.NextHeader, layer.NextHeader, len(layer.Payload))
}
return protoTree
}
// transportLayerTree 传输层
func transportLayerTree(transportLayer gopacket.TransportLayer) (string, ProtoTree) {
var info string
var tree ProtoTree
switch layer := transportLayer.(type) {
case *layers.TCP: // 传输控制协议,提供可靠的数据传输。
// 偏移量取连接层加网络层的长度Length
linkLayerAndNetworkLayerLen := 14 + 20
transportLayerLen := len(layer.Contents)
payloadrLen := len(layer.Payload)
seq := layer.Seq
ack := layer.Ack
srcPort := layer.SrcPort
dstPort := layer.DstPort
dataOffset := layer.DataOffset
hdrLen := dataOffset * 4
flags, flagsDesc := transportFlagsDesc(layer)
flagsACK, flagsACKDesc := transportFlagsStatus(layer.ACK)
flagsPSH, flagsPSHDesc := transportFlagsStatus(layer.PSH)
window := layer.Window
checksum := layer.Checksum
urgent := layer.Urgent
optionsLen, optionsDesc := transportOptions(layer.Options)
payloadStr := bytesToHexString(layer.Payload)
tree = ProtoTree{
Label: fmt.Sprintf("Transmission Control Protocol, Src Port: %s, Dst Port: %s, Seq: %d, Ack: %d, Len: %d", srcPort, dstPort, seq, ack, payloadrLen),
Filter: "tcp",
Start: linkLayerAndNetworkLayerLen,
Length: transportLayerLen,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf("Source Port: %s", srcPort),
Filter: fmt.Sprintf("tcp.srcport == %d", srcPort),
Start: linkLayerAndNetworkLayerLen,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Destination Port: %s", dstPort),
Filter: fmt.Sprintf("tcp.dstport == %d", dstPort),
Start: linkLayerAndNetworkLayerLen + 2,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("TCP Segment Len: %d", payloadrLen),
Filter: fmt.Sprintf("tcp.len == %d", payloadrLen),
Start: linkLayerAndNetworkLayerLen + 12,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Sequence Number: %d (relative sequence number)", seq),
Filter: fmt.Sprintf("tcp.seq == %d", seq),
Start: linkLayerAndNetworkLayerLen + 4,
Length: 4,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Acknowledgment Number: %d (relative ack number)", ack),
Filter: fmt.Sprintf("tcp.ack == %d", ack),
Start: linkLayerAndNetworkLayerLen + 8,
Length: 4,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("%04b .... = Header Length: %d bytes (%d)", dataOffset, hdrLen, dataOffset),
Filter: fmt.Sprintf("tcp.hdr_len == %d", hdrLen),
Start: linkLayerAndNetworkLayerLen + 12,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Flags: 0x%03X (%s)", flags, flagsDesc),
Filter: fmt.Sprintf("ip.frag_offset == 0x%03X", flags),
Start: linkLayerAndNetworkLayerLen + 12,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{
{
Label: fmt.Sprintf(".... ...%d .... = Acknowledgment: %s", flagsACK, flagsACKDesc),
Filter: fmt.Sprintf("tcp.flags.ack == %d", flagsACK),
Start: linkLayerAndNetworkLayerLen + 13,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
{
Label: fmt.Sprintf(".... .... %d... = Push: %s", flagsPSH, flagsPSHDesc),
Filter: fmt.Sprintf("tcp.flags.push == %d", flagsPSH),
Start: linkLayerAndNetworkLayerLen + 13,
Length: 1,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 926233912,
URL: "",
},
},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Window: %d", window),
Filter: fmt.Sprintf("tcp.window_size_value == %d", window),
Start: linkLayerAndNetworkLayerLen + 14,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Calculated window size: %d", window),
Filter: fmt.Sprintf("tcp.window_size == %d", window),
Start: linkLayerAndNetworkLayerLen + 14,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Checksum: 0x%04x [unverified]", checksum),
Filter: fmt.Sprintf("tcp.checksum == 0x%04x", checksum),
Start: linkLayerAndNetworkLayerLen + 16,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: "Checksum Status: Unverified",
Filter: "tcp.checksum.status == \"Unverified\"",
Start: 0,
Length: 0,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Urgent Pointer: %d", urgent),
Filter: fmt.Sprintf("tcp.urgent_pointer == %d", urgent),
Start: linkLayerAndNetworkLayerLen + 18,
Length: 2,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("Options: (%d bytes), %s", optionsLen, optionsDesc),
Filter: fmt.Sprintf("tcp.options == %d", optionsLen),
Start: linkLayerAndNetworkLayerLen + 20,
Length: int(optionsLen),
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
{
Label: fmt.Sprintf("TCP payload (%d bytes)", payloadrLen),
Filter: fmt.Sprintf("tcp.payload == %s", payloadStr),
Start: linkLayerAndNetworkLayerLen + 32,
Length: payloadrLen,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "",
Fnum: 0,
URL: "",
},
},
Severity: "",
Type: "proto",
Fnum: 1052,
URL: "",
}
info = fmt.Sprintf("%v -> %v [%s], Seq=%d Ack=%d Win=%d Len=1%d ", srcPort, dstPort, flagsDesc, seq, ack, window, payloadrLen)
logger.Infof("-> TCP, %s", info)
case *layers.UDP: // 用户数据报协议,提供无连接的快速数据传输。
logger.Infof("-> UDP, length %d", len(layer.Payload))
case *layers.UDPLite:
logger.Infof("-> UDPLite, length %d", len(layer.Payload))
case *layers.SCTP: // 流控制传输协议,支持多流和多宿主机。
logger.Infof("-> SCTP, length %d", len(layer.Payload))
}
return info, tree
}
// applicationLayerTree 应用层
func applicationLayerTree(applicationLayer gopacket.ApplicationLayer) ProtoTree {
var protoTree ProtoTree
switch layer := applicationLayer.(type) {
case *layers.DNS:
logger.Infof("-> DNS, %d", layer.ID)
case *layers.SIP:
logger.Infof("-> SIP, %s", layer.RequestURI)
default:
logger.Infof("-> %s, length %d", layer.LayerType(), layer.Payload())
if applicationHTTP(layer.LayerContents()) {
logger.Infof("-> HTTP, %s", layer.LayerContents())
// 偏移量取连接层加网络层加协议层的长度Length
linkLayerAndNetworkLayerAndTransportLayerLen := 14 + 20 + 32
length := len(layer.LayerContents())
protoTree = ProtoTree{
Label: "Hypertext Transfer Protocol",
Filter: "http",
Start: linkLayerAndNetworkLayerAndTransportLayerLen,
Length: length,
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "Chat",
Fnum: 1052,
URL: "",
}
result := applicationHTTPProcess(string(layer.LayerContents()))
for _, v := range result {
protoTree.Tree = append(protoTree.Tree, ProtoTree{
Label: v["label"].(string),
Filter: fmt.Sprintf("http.%s == %s", v["key"].(string), v["value"].(string)),
Start: linkLayerAndNetworkLayerAndTransportLayerLen + v["length"].(int),
Length: v["length"].(int),
DataSourceIdx: 0,
Tree: []ProtoTree{},
Severity: "",
Type: "Chat",
Fnum: 1052,
URL: "",
})
}
}
}
return protoTree
}

View File

@@ -1,346 +0,0 @@
package packet_task
import (
"encoding/binary"
"fmt"
"net"
"strings"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
)
// networkDSCPAndECN 提取 TOS 字段并获取 DSCP 和 ECN
func networkDSCPAndECN(tos uint8) (string, string) {
// 提取 DSCP 和 ECN
dscp := tos >> 2 // 高 6 位
ecn := tos & 0x03 // 低 2 位
// 定义 DSCP 映射
dscpMapping := map[uint8]string{
0: "Default CS0", // Default Forwarding (DF)
8: "CS1", // Class Selector 1
16: "CS2", // Class Selector 2
24: "CS3", // Class Selector 3
32: "CS4", // Class Selector 4
40: "CS5", // Class Selector 5
48: "CS6", // Class Selector 6
56: "CS7", // Class Selector 7
}
// 定义 ECN 映射
ecnMapping := map[uint8]string{
0: "Not-ECT", // Not ECN-Capable Transport
1: "ECT(1)", // ECN-Capable Transport
2: "ECT(0)", // ECN-Capable Transport
3: "CE", // Congestion Experienced
}
// 返回可读的 DSCP 和 ECN 字符串
return dscpMapping[dscp], ecnMapping[ecn]
}
// networkFlagsDesc 生成标志描述
func networkFlagsDesc(flags layers.IPv4Flag) string {
f := fmt.Sprintf("Flags: 0x%X", flags)
if flags&layers.IPv4DontFragment != 0 {
f += ", Don't fragment"
}
if flags&layers.IPv4MoreFragments != 0 {
f += ", More fragments"
}
return f
}
// networkFlagsEvil 生成标志描述 Evil
func networkFlagsEvil(flags layers.IPv4Flag) (int, string) {
if flags&layers.IPv4EvilBit != 0 {
return 1, "Set"
}
return 0, "Not set"
}
// networkFlagsDF 生成标志描述 DF
func networkFlagsDF(flags layers.IPv4Flag) (int, string) {
if flags&layers.IPv4DontFragment != 0 {
return 1, " Set"
}
return 0, "Not set"
}
// networkFlagsMF 生成标志描述 MF
func networkFlagsMF(flags layers.IPv4Flag) (int, string) {
if flags&layers.IPv4MoreFragments != 0 {
return 1, " Set"
}
return 0, "Not set"
}
// networkOffset 二进制Fragment Offset表示 ...0 0000 0000 0000
func networkOffset(offset uint16) string {
return fmt.Sprintf("...0 %04b %04b %04b %04b",
(offset>>12)&0xF, // 高四位
(offset>>8)&0xF, // 次四位
(offset>>4)&0xF, // 再次四位
offset&0xF, // 低四位
)
}
// transportFlagsDesc 生成标志描述
func transportFlagsDesc(layer *layers.TCP) (byte, string) {
var flags byte
var flagsDesc []string
if layer.FIN {
flags |= 1 << 0 // 0b00000001
flagsDesc = append(flagsDesc, "FIN")
}
if layer.SYN {
flags |= 1 << 1 // 0b00000010
flagsDesc = append(flagsDesc, "SYN")
}
if layer.RST {
flags |= 1 << 2 // 0b00000100
flagsDesc = append(flagsDesc, "RST")
}
if layer.PSH {
flags |= 1 << 3 // 0b00001000
flagsDesc = append(flagsDesc, "PSH")
}
if layer.ACK {
flags |= 1 << 4 // 0b00010000
flagsDesc = append(flagsDesc, "ACK")
}
if layer.URG {
flags |= 1 << 5 // 0b00100000
flagsDesc = append(flagsDesc, "URG")
}
if layer.ECE {
flags |= 1 << 6 // 0b01000000
flagsDesc = append(flagsDesc, "ECE")
}
if layer.CWR {
flags |= 1 << 7 // 0b10000000
flagsDesc = append(flagsDesc, "CWR")
}
if layer.NS {
flagsDesc = append(flagsDesc, "NS")
}
return flags, strings.Join(flagsDesc, ", ")
}
// transportFlagsStatus 生成标志描述状态
func transportFlagsStatus(flag bool) (int, string) {
if flag {
return 1, " Set"
}
return 0, "Not set"
}
// bytesToHexString 转换为十六进制字符串格式
func bytesToHexString(data []byte) string {
var sb strings.Builder
for i, b := range data {
if i > 0 {
sb.WriteString(":")
}
sb.WriteString(fmt.Sprintf("%02x", b))
}
return sb.String()
}
// transportOptions 生成头部选项描述
func transportOptions(options []layers.TCPOption) (uint8, string) {
var opts []string
var optLen uint8
for _, opt := range options {
if opt.OptionType == layers.TCPOptionKindMSS && len(opt.OptionData) == 2 {
optLen += opt.OptionLength
opts = append(opts, fmt.Sprintf("%s val %v",
opt.OptionType.String(),
binary.BigEndian.Uint16(opt.OptionData),
))
} else if opt.OptionType == layers.TCPOptionKindTimestamps && len(opt.OptionData) == 8 {
optLen += opt.OptionLength
opts = append(opts, fmt.Sprintf("%s val %v echo %v",
opt.OptionType.String(),
binary.BigEndian.Uint32(opt.OptionData[:4]),
binary.BigEndian.Uint32(opt.OptionData[4:8]),
))
} else {
optLen += opt.OptionLength
opts = append(opts, opt.OptionType.String())
}
}
return optLen, strings.Join(opts, ", ")
}
// applicationHTTP 辨别 HTTP 数据
func applicationHTTP(data []byte) bool {
if len(data) == 0 {
return false
}
prefixStr := string(data)
return strings.HasPrefix(prefixStr, "GET ") || strings.HasPrefix(prefixStr, "POST ") ||
strings.HasPrefix(prefixStr, "PUT ") || strings.HasPrefix(prefixStr, "DELETE ") ||
strings.HasPrefix(prefixStr, "HEAD ") || strings.HasPrefix(prefixStr, "OPTIONS ") ||
strings.HasPrefix(prefixStr, "HTTP/")
}
// applicationHTTP 处理 HTTP 请求
func applicationHTTPProcess(data string) map[string]map[string]any {
p := make(map[string]map[string]any, 0)
// 按行分割
lines := strings.Split(data, "\r\n")
for i, line := range lines {
if i == 0 {
label := line + "\r\n"
p[label] = map[string]any{
"label": label,
"length": len([]byte(label)),
"key": "",
"value": "",
}
continue
}
// 空行表示头部结束Body开始
if line == "" {
break
}
label := line + "\r\n"
p[label] = map[string]any{
"label": label,
"length": len([]byte(label)),
"key": "",
"value": "",
}
// 分割键值对
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
p[label]["key"] = key
p[label]["value"] = value
}
}
return p
}
// colorRuleFB 着色规则-F前景,B背景
//
// This file was created by Wireshark. Edit with care.
func colorRuleFB(packet gopacket.Packet) (int, int) {
// Ethernet
if ethernetLayer := packet.Layer(layers.LayerTypeEthernet); ethernetLayer != nil {
eth := ethernetLayer.(*layers.Ethernet)
ethData := eth.Contents
// Broadcast 检查第一个字节的最低位
// #babdb6, #ffffff
if len(ethData) > 0 && (ethData[0]&1) == 1 {
return 12238262, 16777215
}
// Routing CDP (Cisco Discovery Protocol) 检查前三个字节
// #12272e, #fff3d6
if ethernetLayer.LayerPayload()[0] == 0x01 && ethernetLayer.LayerPayload()[1] == 0x00 && ethernetLayer.LayerPayload()[2] == 0x0c {
return 1189678, 16774102
}
// Routing CARP (Common Address Redundancy Protocol) uses a specific Ethernet type (0x0800)
// #12272e, #fff3d6
if ethernetLayer.LayerType() == 0x0800 {
return 1189678, 16774102
}
}
// ARP
if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil {
// #12272e, #faf0d7
return 1189678, 16445655
}
// ICMP
if icmpLayer := packet.Layer(layers.LayerTypeICMPv4); icmpLayer != nil {
// #12272e, #fce0ff
return 1189678, 16572671
}
if icmpLayer := packet.Layer(layers.LayerTypeICMPv6); icmpLayer != nil {
// #12272e, #fce0ff
return 1189678, 16572671
}
// SCTP
if sctpLayer := packet.Layer(layers.LayerTypeSCTP); sctpLayer != nil {
sctp := sctpLayer.(*layers.SCTP)
// SCTP ABORT
// #fffc9c, #a40000
if sctp.Checksum == 6 {
return 16776348, 10747904
}
}
// TCP
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
tcp := tcpLayer.(*layers.TCP)
// TCP SYN/FIN
// #12272e, #a0a0a0
if tcp.SYN && tcp.FIN {
return 1189678, 10526880
}
// TCP RST
// #fffc9c, #a40000
if tcp.RST {
return 16776348, 10747904
}
// HTTP
// #12272e, #e4ffc7
if tcp.SrcPort == 80 || tcp.DstPort == 80 || tcp.SrcPort == 443 || tcp.DstPort == 443 {
return 1189678, 15007687
}
// 检查 SMB - 通常基于 TCP 445 或 139
// #12272e, #feffd0
if tcp.SrcPort == 445 || tcp.DstPort == 445 || tcp.SrcPort == 139 || tcp.DstPort == 139 {
return 1189678, 16711632
}
// Routing BGP usually runs on TCP port 179
// #12272e, #fff3d6
if tcp.DstPort == 179 || tcp.SrcPort == 179 {
return 1189678, 16774102
}
}
// UDP
if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
udp := udpLayer.(*layers.UDP)
// 检查 SMB NetBIOS 名称服务 (NBNS) - 端口 53
// 检查 SMB NetBIOS 数据报服务 (NBDS) - 端口 138
if udp.SrcPort == 53 || udp.DstPort == 53 || udp.SrcPort == 138 || udp.DstPort == 138 {
return 1189678, 16711632
}
}
// IPv4
if ipv4Layer := packet.Layer(layers.LayerTypeIPv4); ipv4Layer != nil {
ipv4 := ipv4Layer.(*layers.IPv4)
// TCP(6)
// #12272e, #e7e6ff
if ipv4.Protocol == layers.IPProtocolTCP {
return 1189678, 15197951
}
// UDP(17)
// #12272e, #daeeff
if ipv4.Protocol == layers.IPProtocolUDP || ipv4.Protocol == layers.IPProtocolUDPLite {
return 1189678, 14348031
}
// Routing EIGRP(0x2f) OSPF(89)
// #12272e, #fff3d6
if ipv4.Protocol == 0x2f || ipv4.Protocol == layers.IPProtocolOSPF {
return 1189678, 16774102
}
// Routing
// GVRP (GARP VLAN Registration Protocol)
// GVRP typically utilizes the same multicast address as GARP
// HSRP (Hot Standby Router Protocol) uses multicast IP 224.0.0.2
// VRRP (Virtual Router Redundancy Protocol) uses multicast IP 224.0.0.18
// #12272e, #fff3d6
if ipv4.DstIP.Equal(net.IPv4(224, 0, 0, 2)) || ipv4.DstIP.Equal(net.IPv4(224, 0, 0, 100)) {
return 1189678, 16774102
}
}
return 16222087, 1189678 // 默认颜色值 #f78787, #12272e
}

View File

@@ -1,20 +1,64 @@
package service
import (
packetTask "be.ems/src/modules/trace/packet_task"
"encoding/base64"
"fmt"
"net"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
"github.com/gopacket/gopacket/pcap"
"github.com/gopacket/gopacket/pcapgo"
"be.ems/src/framework/logger"
wsService "be.ems/src/modules/ws/service"
)
// Task 任务信息
type Task struct {
TaskNo string // 任务编号
Handle *pcap.Handle // 捕获句柄
File *os.File // 捕获信息输出文件句柄
Writer *pcapgo.Writer // 捕获信息输出句柄
Filter string // 过滤表达式
Ticker *time.Ticker // 任务失效定时器
}
// FrameMeta 数据帧元信息
type FrameMeta struct {
Number int `json:"number"`
Time int64 `json:"time"`
Source string `json:"source"`
Destination string `json:"destination"`
Protocol string `json:"protocol"`
Length int `json:"length"`
Info string `json:"info"`
Data string `json:"data"` // 原始数据byte[] base64 编码数据
}
// 实例化服务层 Packet 结构体
var NewPacket = &Packet{}
var NewPacket = &Packet{
taskMap: sync.Map{},
}
// 信令跟踪 服务层处理
type Packet struct{}
type Packet struct {
taskMap sync.Map
}
// NetworkDevices 获取网卡设备信息
func (s *Packet) NetworkDevices() []map[string]any {
arr := make([]map[string]any, 0)
devices, err := packetTask.NetworkDevices()
devices, err := pcap.FindAllDevs()
if err != nil {
logger.Errorf("interfaces find all devices err: %s", err.Error())
return arr
}
@@ -47,20 +91,343 @@ func (s *Packet) NetworkDevices() []map[string]any {
// LiveStart 开始捕获数据
func (s *Packet) LiveStart(taskNo, deviceName, filterBPF string, outputPCAP bool) (string, error) {
return packetTask.LiveStart(taskNo, deviceName, filterBPF, outputPCAP)
if _, ok := s.taskMap.Load(taskNo); ok {
return "", fmt.Errorf("task no. %s already exist", taskNo)
}
// Verify the specified network interface exists
device, deviceOk := s.verifyDevice(deviceName)
if !deviceOk {
return "", fmt.Errorf("network device not exist: %s", deviceName)
}
// listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
snapshotLength := 262144
// open device
handle, err := pcap.OpenLive(device, int32(snapshotLength), true, pcap.BlockForever)
if err != nil {
logger.Errorf("open live err: %s", err.Error())
if strings.Contains(err.Error(), "operation not permitted") {
return "", fmt.Errorf("you don't have permission to capture on that/these device(s)")
}
return "", err
}
// write a new file
var w *pcapgo.Writer
var f *os.File
if outputPCAP {
// 网管本地路径
localFilePath := fmt.Sprintf("/tmp/omc/packet/%s.pcap", taskNo)
if runtime.GOOS == "windows" {
localFilePath = fmt.Sprintf("C:%s", localFilePath)
}
f, w, err = s.outputPCAPFile(uint32(snapshotLength), handle.LinkType(), localFilePath)
if err != nil {
return "", err
}
}
// set filter
if filterBPF != "" {
if err = handle.SetBPFFilter(filterBPF); err != nil {
logger.Errorf("packet BPF Filter %s => %s", filterBPF, err.Error())
filterBPF = ""
}
}
// save tasks
taskInfo := &Task{
TaskNo: taskNo,
Handle: handle,
File: f,
Writer: w,
Filter: filterBPF,
Ticker: time.NewTicker(time.Second * 120),
}
go s.capturePacketSource(taskInfo)
s.taskMap.Store(taskNo, taskInfo)
return fmt.Sprintf("task no. %s initiated", taskNo), nil
}
// verifyDevice 检查网卡设备是否存在
func (s *Packet) verifyDevice(str string) (string, bool) {
devices, err := pcap.FindAllDevs()
if err != nil {
logger.Errorf("interfaces find all devices err: %s", err.Error())
return "", false
}
for _, device := range devices {
if len(device.Addresses) == 0 {
continue
}
if device.Name == str {
return device.Name, true
}
for _, address := range device.Addresses {
if address.IP.String() == str {
return device.Name, true
}
}
}
return "", false
}
// capturePacketSource 捕获数据
func (s *Packet) capturePacketSource(taskInfo *Task) {
// capture packets
packetSource := gopacket.NewPacketSource(taskInfo.Handle, taskInfo.Handle.LinkType())
packetSource.Lazy = false
packetSource.NoCopy = true
packetSource.DecodeStreamsAsDatagrams = true
// 协程停止后关闭句柄并移除任务信息
defer func() {
taskInfo.Ticker.Stop()
taskInfo.Handle.Close()
if taskInfo.File != nil {
taskInfo.File.Close()
}
s.taskMap.Delete(taskInfo.TaskNo)
}()
var frameNumber int64 = 0 // 帧编号
for {
select {
case <-taskInfo.Ticker.C:
return
case packet := <-packetSource.Packets():
if packet == nil {
continue
}
if packet.Metadata().Timestamp.Before(time.Now()) {
continue
}
// 使用原子操作获取当前帧号并自增
currentFrameNumber := atomic.AddInt64(&frameNumber, 1)
frameSrc := "" // 源主机IP
frameDst := "" // 目的主机IP
frameProtocol := "" // 协议
frameInfo := "" // 信息
// fmt.Printf("---------- packet %d layers:%d time: %s, len: %d \n", currentFrameNumber, len(packet.Layers()), packet.Metadata().Timestamp, packet.Metadata().Length)
// 网络层
// fmt.Println(packet.NetworkLayer())
if networkLayer := packet.NetworkLayer(); networkLayer != nil {
src, dst := networkLayer.NetworkFlow().Endpoints()
frameSrc = src.String()
frameDst = dst.String()
if frameDst == "ff:ff:ff:ff" {
frameDst = "Broadcast"
}
}
// 传输层
// fmt.Println(packet.TransportLayer())
if transportLayer := packet.TransportLayer(); transportLayer != nil {
frameProtocol = transportLayer.LayerType().String()
switch layer := transportLayer.(type) {
case *layers.TCP: // 传输控制协议,提供可靠的数据传输。
var flagsDesc []string
if layer.FIN {
flagsDesc = append(flagsDesc, "FIN")
}
if layer.SYN {
flagsDesc = append(flagsDesc, "SYN")
}
if layer.RST {
flagsDesc = append(flagsDesc, "RST")
}
if layer.PSH {
flagsDesc = append(flagsDesc, "PSH")
}
if layer.ACK {
flagsDesc = append(flagsDesc, "ACK")
}
if layer.URG {
flagsDesc = append(flagsDesc, "URG")
}
if layer.ECE {
flagsDesc = append(flagsDesc, "ECE")
}
if layer.CWR {
flagsDesc = append(flagsDesc, "CWR")
}
if layer.NS {
flagsDesc = append(flagsDesc, "NS")
}
frameInfo = fmt.Sprintf("%v -> %v [%s], Seq=%d Ack=%d Win=%d Len=%d ", layer.SrcPort, layer.DstPort, strings.Join(flagsDesc, ", "), layer.Seq, layer.Ack, layer.Window, len(layer.Payload))
case *layers.UDP: // 用户数据报协议,提供无连接的快速数据传输。
frameInfo = fmt.Sprintf("%v -> %v Len=%d ", layer.SrcPort, layer.DstPort, len(layer.Payload))
case *layers.UDPLite:
frameInfo = fmt.Sprintf("%v -> %v Len=%d ", layer.SrcPort, layer.DstPort, len(layer.Payload))
case *layers.SCTP: // 流控制传输协议,支持多流和多宿主机。
frameInfo = fmt.Sprintf("%v -> %v Len=%d ", layer.SrcPort, layer.DstPort, len(layer.Payload))
}
}
// 应用协议层判断
switch {
case packet.Layer(layers.LayerTypeARP) != nil:
arp := packet.Layer(layers.LayerTypeARP).(*layers.ARP)
frameSrc = net.IP(arp.SourceProtAddress).String()
frameDst = net.IP(arp.DstProtAddress).String()
frameProtocol = "ARP"
frameInfo = fmt.Sprintf("Who has %s? Tell %s", frameDst, frameSrc)
case packet.Layer(layers.LayerTypeVRRP) != nil:
frameProtocol = "VRRP"
frameInfo = "Announcement (v2)"
case packet.Layer(layers.LayerTypeIGMP) != nil:
switch layer := packet.Layer(layers.LayerTypeIGMP).(type) {
case *layers.IGMP:
frameProtocol = fmt.Sprintf("IGMPv%d", layer.Version)
frameInfo = fmt.Sprintf("%s %s", layer.Type.String(), layer.GroupAddress.String())
case *layers.IGMPv1or2:
frameProtocol = fmt.Sprintf("IGMPv%d", layer.Version)
frameInfo = fmt.Sprintf("%s %s", layer.Type.String(), layer.GroupAddress.String())
}
case packet.Layer(layers.LayerTypeICMPv4) != nil:
icmpv4 := packet.Layer(layers.LayerTypeICMPv4).(*layers.ICMPv4)
frameProtocol = "ICMP"
frameInfo = icmpv4.TypeCode.String()
case packet.Layer(layers.LayerTypeICMPv6) != nil:
icmpv6 := packet.Layer(layers.LayerTypeICMPv6).(*layers.ICMPv6)
frameProtocol = "ICMPv6"
frameInfo = icmpv6.TypeCode.String()
case packet.Layer(layers.LayerTypeSTP) != nil:
stp := packet.Layer(layers.LayerTypeSTP).(*layers.STP)
rootIdentifier := stp.RouteID
frameSrc = rootIdentifier.HwAddr.String()
frameDst = stp.BridgeID.HwAddr.String()
frameProtocol = "STP"
frameInfo = fmt.Sprintf("MST. Root = %d/%d/%s Cost = %d Port = 0x%x", rootIdentifier.Priority, rootIdentifier.SysID, frameSrc, stp.Cost, stp.PortID)
case packet.Layer(layers.LayerTypeSIP) != nil:
sip := packet.Layer(layers.LayerTypeSIP).(*layers.SIP)
frameProtocol = "SIP"
if sip.IsResponse {
frameInfo = fmt.Sprintf("%d %s", sip.ResponseCode, sip.ResponseStatus)
} else {
frameInfo = fmt.Sprintf("%s %s", sip.Method, sip.RequestURI)
}
case packet.Layer(layers.LayerTypeDNS) != nil:
dns := packet.Layer(layers.LayerTypeDNS).(*layers.DNS)
frameProtocol = "DNS"
question := []string{}
if len(dns.Questions) > 0 {
question = append(question, fmt.Sprintf("%s %s", dns.Questions[0].Type, dns.Questions[0].Name))
}
frameInfo = fmt.Sprintf("%s %s 0x%x %s Answers %d", dns.ResponseCode.String(), dns.OpCode.String(), dns.ID, question, len(dns.Answers))
case packet.Layer(layers.LayerTypeDHCPv6) != nil:
dhcpv6 := packet.Layer(layers.LayerTypeDHCPv6).(*layers.DHCPv6)
frameProtocol = "DHCPv6"
frameInfo = fmt.Sprintf("%s XID: 0x%+x", dhcpv6.MsgType.String(), dhcpv6.TransactionID)
case packet.Layer(layers.LayerTypeTLS) != nil:
tls := packet.Layer(layers.LayerTypeTLS).(*layers.TLS)
if len(tls.AppData) > 0 {
item := tls.AppData[0]
frameProtocol = item.Version.String()
frameInfo = item.ContentType.String()
} else if len(tls.Handshake) > 0 {
item := tls.Handshake[0]
frameProtocol = item.ClientHello.ProtocolVersion.String()
frameInfo = "Client Hello"
} else {
frameProtocol = "TLS"
frameInfo = "Unknown"
}
}
if frameProtocol == "" {
continue
}
// 数据
frameMeta := FrameMeta{
Number: int(currentFrameNumber),
Time: packet.Metadata().Timestamp.UnixNano(),
Source: frameSrc,
Destination: frameDst,
Protocol: frameProtocol,
Length: packet.Metadata().Length,
Info: frameInfo,
Data: base64.StdEncoding.EncodeToString(packet.Data()),
}
// 推送到ws订阅组
wsService.NewWSSend.ByGroupID(fmt.Sprintf("%s_%s", wsService.GROUP_TRACE_PACKET, taskInfo.TaskNo), frameMeta)
// 写入文件
if taskInfo.Writer != nil {
taskInfo.Writer.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
}
}
}
}
// outputPCAPFile 输出 pcap 文件
// 新文件时需要 snaplen 最大长度 linktype 链路类型
func (s *Packet) outputPCAPFile(snaplen uint32, linktype layers.LinkType, outputFile string) (*os.File, *pcapgo.Writer, error) {
var err error
var f *os.File
if err := os.MkdirAll(filepath.Dir(outputFile), 0775); err != nil {
return nil, nil, err
}
// 检查文件是否存在
if _, err = os.Stat(outputFile); os.IsNotExist(err) {
f, err = os.Create(outputFile)
if err != nil {
return nil, nil, err
}
w := pcapgo.NewWriter(f)
w.WriteFileHeader(snaplen, linktype) // new file, must do this.
return f, w, nil
}
f, err = os.OpenFile(outputFile, os.O_APPEND, 0700)
if err != nil {
return nil, nil, err
}
w := pcapgo.NewWriter(f)
return f, w, nil
}
// LiveFilter 捕获过滤
func (s *Packet) LiveFilter(taskNo, expr string) error {
return packetTask.LiveFilter(taskNo, expr)
info, ok := s.taskMap.Load(taskNo)
if !ok {
return fmt.Errorf("task no. %s not exist", taskNo)
}
task := info.(*Task)
task.Filter = expr
err := task.Handle.SetBPFFilter(expr)
if err != nil {
logger.Errorf("packet BPF Filter %s => %s", expr, err.Error())
return fmt.Errorf("can't parse filter expression")
}
return nil
}
// LiveTimeout 更新捕获失效时间
func (s *Packet) LiveTimeout(taskNo string, seconds int) error {
return packetTask.LiveTimeout(taskNo, seconds)
info, ok := s.taskMap.Load(taskNo)
if !ok {
return fmt.Errorf("task no. %s not exist", taskNo)
}
info.(*Task).Ticker.Reset(time.Duration(seconds) * time.Second)
return nil
}
// LiveStop 停止捕获数据
func (s *Packet) LiveStop(taskNo string) error {
return packetTask.LiveStop(taskNo)
info, ok := s.taskMap.Load(taskNo)
if !ok {
return fmt.Errorf("task no. %s not exist", taskNo)
}
info.(*Task).Ticker.Reset(time.Millisecond)
return nil
}