From afcf562bc5752a1c54388e1253eb9ab254d71ac6 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 30 Sep 2024 21:04:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=A1=E4=BB=A4=E8=B7=9F=E8=B8=AA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/trace/controller/packet.go | 38 +- src/modules/trace/packet_task/packet.go | 245 +++++ src/modules/trace/packet_task/packet_frame.go | 843 ++++++++++++++++++ .../trace/packet_task/packet_frame_util.go | 346 +++++++ src/modules/trace/service/packet.go | 150 +--- src/modules/trace/trace.go | 1 - 6 files changed, 1471 insertions(+), 152 deletions(-) create mode 100644 src/modules/trace/packet_task/packet.go create mode 100644 src/modules/trace/packet_task/packet_frame.go create mode 100644 src/modules/trace/packet_task/packet_frame_util.go diff --git a/src/modules/trace/controller/packet.go b/src/modules/trace/controller/packet.go index a89cacc6..ac54aa2b 100644 --- a/src/modules/trace/controller/packet.go +++ b/src/modules/trace/controller/packet.go @@ -21,7 +21,7 @@ type PacketController struct { packetService *traceService.Packet // 信令跟踪服务 } -// 网元跟踪网卡设备列表 +// 信令跟踪网卡设备列表 // // GET /devices func (s *PacketController) Devices(c *gin.Context) { @@ -29,22 +29,23 @@ func (s *PacketController) Devices(c *gin.Context) { c.JSON(200, result.OkData(data)) } -// 网元跟踪开始 +// 信令跟踪开始 // // POST /start func (s *PacketController) Start(c *gin.Context) { language := ctx.AcceptLanguage(c) var body struct { - Device string `json:"device" binding:"required"` // 网卡设备 - TaskNo string `json:"taskNo" binding:"required"` // 任务编号 - Output string `json:"output" ` // 输出PCAP文件路径,为空则不输出 + TaskNo string `json:"taskNo" binding:"required"` // 任务编号 + Device string `json:"device" binding:"required"` // 网卡设备 + Filter string `json:"filter" ` // 过滤表达式(port 33030 or 33040) + OutputPCAP bool `json:"outputPCAP" ` // 输出PCAP文件 (默认false) } if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - msg, err := s.packetService.LiveStart(body.TaskNo, body.Device, body.Output) + msg, err := s.packetService.LiveStart(body.TaskNo, body.Device, body.Filter, body.OutputPCAP) if err != nil { c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error()))) return @@ -52,7 +53,7 @@ func (s *PacketController) Start(c *gin.Context) { c.JSON(200, result.OkData(msg)) } -// 网元跟踪结束 +// 信令跟踪结束 // // POST /stop func (s *PacketController) Stop(c *gin.Context) { @@ -72,14 +73,14 @@ func (s *PacketController) Stop(c *gin.Context) { c.JSON(200, result.Ok(nil)) } -// 网元跟踪过滤 +// 信令跟踪过滤 // // PUT /filter func (s *PacketController) Filter(c *gin.Context) { language := ctx.AcceptLanguage(c) var body struct { TaskNo string `json:"taskNo" binding:"required"` // 任务编号 - Expr string `json:"expr" binding:"required"` // 过滤表达式(port 33030 or 33040) + Expr string `json:"expr" ` // 过滤表达式(port 33030 or 33040) } if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) @@ -93,19 +94,28 @@ func (s *PacketController) Filter(c *gin.Context) { c.JSON(200, result.Ok(nil)) } -// 网元跟踪续期保活 +// 信令跟踪续期保活 // // PUT /keep-alive func (s *PacketController) KeepAlive(c *gin.Context) { language := ctx.AcceptLanguage(c) var body struct { - IntervalIn int `json:"intervalIn" ` // 服务续约的频率,默认设置为60秒 - Duration int `json:"duration" ` // 服务失效的时间,默认设置为120秒 + TaskNo string `json:"taskNo" binding:"required"` // 任务编号 + Duration int `json:"duration" ` // 服务失效的时间,默认设置为120秒 } - err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil { + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } + + // 默认设置为120秒 + if body.Duration <= 1 { + body.Duration = 120 + } + + if err := s.packetService.LiveTimeout(body.TaskNo, body.Duration); err != nil { + c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error()))) + return + } c.JSON(200, result.Ok(nil)) } diff --git a/src/modules/trace/packet_task/packet.go b/src/modules/trace/packet_task/packet.go new file mode 100644 index 00000000..5df723b3 --- /dev/null +++ b/src/modules/trace/packet_task/packet.go @@ -0,0 +1,245 @@ +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 +} diff --git a/src/modules/trace/packet_task/packet_frame.go b/src/modules/trace/packet_task/packet_frame.go new file mode 100644 index 00000000..d060149d --- /dev/null +++ b/src/modules/trace/packet_task/packet_frame.go @@ -0,0 +1,843 @@ +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 +} diff --git a/src/modules/trace/packet_task/packet_frame_util.go b/src/modules/trace/packet_task/packet_frame_util.go new file mode 100644 index 00000000..77d86a9f --- /dev/null +++ b/src/modules/trace/packet_task/packet_frame_util.go @@ -0,0 +1,346 @@ +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 +} diff --git a/src/modules/trace/service/packet.go b/src/modules/trace/service/packet.go index 9b6e8f8a..64cc6430 100644 --- a/src/modules/trace/service/packet.go +++ b/src/modules/trace/service/packet.go @@ -1,46 +1,21 @@ package service import ( - "context" - "fmt" - "os" - "strings" - "sync" - "time" - - "be.ems/src/framework/logger" "be.ems/src/framework/vo" - "github.com/gopacket/gopacket" - "github.com/gopacket/gopacket/pcap" - "github.com/gopacket/gopacket/pcapgo" + packetTask "be.ems/src/modules/trace/packet_task" ) // 实例化服务层 Packet 结构体 -var NewPacket = &Packet{ - taskMap: sync.Map{}, -} +var NewPacket = &Packet{} // 信令跟踪 服务层处理 -type Packet struct { - taskMap sync.Map // 捕获任务 -} - -// task 任务信息 -type task struct { - TaskNo string // 任务编号 - Handle *pcap.Handle // 捕获句柄 - File *os.File // 捕获信息输出文件句柄 - Writer *pcapgo.Writer // 捕获信息输出句柄 - Expire time.Time // 过期时间 - context context.Context // 上下文 控制完成结束 -} +type Packet struct{} // NetworkDevices 获取网卡设备信息 func (s *Packet) NetworkDevices() []vo.TreeSelect { arr := make([]vo.TreeSelect, 0) - devices, err := pcap.FindAllDevs() + devices, err := packetTask.NetworkDevices() if err != nil { - logger.Errorf("interfaces find all devices err: %s", err.Error()) return arr } @@ -71,121 +46,22 @@ func (s *Packet) NetworkDevices() []vo.TreeSelect { return arr } -// 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 -} - // LiveStart 开始捕获数据 -func (s *Packet) LiveStart(taskNo, deviceName, outputFile string) (string, error) { - 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) - } - - 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 != "" { - f, err = os.Create(outputFile) - if err != nil { - return "", err - } - w = pcapgo.NewWriter(f) - w.WriteFileHeader(uint32(snapshotLength), handle.LinkType()) - } - - // capture packets - packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) - packetSource.Lazy = false - packetSource.NoCopy = true - packetSource.DecodeStreamsAsDatagrams = true - - // save tasks - ctx := context.Background() - task := &task{ - TaskNo: taskNo, - Handle: handle, - File: f, - Writer: w, - Expire: time.Now().Add(time.Second * 120), - context: ctx, - } - s.taskMap.Store(taskNo, task) - - // start capture - go func() { - for packet := range packetSource.PacketsCtx(ctx) { - if packet == nil { - continue - } - if packet.Metadata().Timestamp.Before(time.Now()) { - continue - } - if w != nil { - w.WritePacket(packet.Metadata().CaptureInfo, packet.Data()) - } - fmt.Println(packet.Dump()) - } - }() - return "task initiated", nil +func (s *Packet) LiveStart(taskNo, deviceName, filterBPF string, outputPCAP bool) (string, error) { + return packetTask.LiveStart(taskNo, deviceName, filterBPF, outputPCAP) } // LiveFilter 捕获过滤 func (s *Packet) LiveFilter(taskNo, expr string) error { - info, ok := s.taskMap.Load(taskNo) - if !ok { - return fmt.Errorf("task no. %s not exist", taskNo) - } - return info.(*task).Handle.SetBPFFilter(expr) + return packetTask.LiveFilter(taskNo, expr) +} + +// LiveTimeout 更新捕获失效时间 +func (s *Packet) LiveTimeout(taskNo string, seconds int) error { + return packetTask.LiveTimeout(taskNo, seconds) } // LiveStop 停止捕获数据 func (s *Packet) LiveStop(taskNo string) error { - info, ok := s.taskMap.Load(taskNo) - if !ok { - return fmt.Errorf("task no. %s not exist", taskNo) - } - info.(*task).context.Done() - info.(*task).Handle.Close() - if info.(task).File != nil { - info.(task).File.Close() - } - s.taskMap.Delete(taskNo) - return nil + return packetTask.LiveStop(taskNo) } diff --git a/src/modules/trace/trace.go b/src/modules/trace/trace.go index 98ff72da..3ffb0ab2 100644 --- a/src/modules/trace/trace.go +++ b/src/modules/trace/trace.go @@ -65,7 +65,6 @@ func Setup(router *gin.Engine) { ) packetGroup.PUT("/keep-alive", middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.packet", collectlogs.BUSINESS_TYPE_OTHER)), controller.NewPacket.KeepAlive, ) }