diff --git a/src/modules/trace/controller/packet.go b/src/modules/trace/controller/packet.go index db8d8b3f..2fb07ee9 100644 --- a/src/modules/trace/controller/packet.go +++ b/src/modules/trace/controller/packet.go @@ -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) +} diff --git a/src/modules/trace/packet_task/packet.go b/src/modules/trace/packet_task/packet.go deleted file mode 100644 index 420b5a39..00000000 --- a/src/modules/trace/packet_task/packet.go +++ /dev/null @@ -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 -} diff --git a/src/modules/trace/packet_task/packet_frame.go b/src/modules/trace/packet_task/packet_frame.go deleted file mode 100644 index d060149d..00000000 --- a/src/modules/trace/packet_task/packet_frame.go +++ /dev/null @@ -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 -} diff --git a/src/modules/trace/packet_task/packet_frame_util.go b/src/modules/trace/packet_task/packet_frame_util.go deleted file mode 100644 index 77d86a9f..00000000 --- a/src/modules/trace/packet_task/packet_frame_util.go +++ /dev/null @@ -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 -} diff --git a/src/modules/trace/service/packet.go b/src/modules/trace/service/packet.go index 8091f356..cb715cca 100644 --- a/src/modules/trace/service/packet.go +++ b/src/modules/trace/service/packet.go @@ -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 }