package service import ( "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{ taskMap: sync.Map{}, } // 信令跟踪 服务层处理 type Packet struct { taskMap sync.Map } // NetworkDevices 获取网卡设备信息 func (s *Packet) NetworkDevices() []map[string]any { arr := make([]map[string]any, 0) devices, err := pcap.FindAllDevs() if err != nil { logger.Errorf("interfaces find all devices err: %s", err.Error()) return arr } for _, device := range devices { if len(device.Addresses) == 0 { continue } lable := device.Description if lable == "" { lable = device.Name } item := map[string]any{ "id": device.Name, "label": lable, "children": []map[string]any{}, } for _, address := range device.Addresses { if address.IP != nil { ip := address.IP.String() item["children"] = append(item["children"].([]map[string]any), map[string]any{"id": ip, "label": ip}) } } arr = append(arr, item) } return arr } // LiveStart 开始捕获数据 func (s *Packet) LiveStart(taskNo, deviceName, filterBPF string, outputPCAP bool) (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) } // 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("/usr/local/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 } // 如果延迟超过1秒跳过 timeDiff := time.Since(packet.Metadata().Timestamp) if timeDiff > time.Second { 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 { info, ok := s.taskMap.Load(taskNo) if !ok { return fmt.Errorf("task no: %s does not exist or has stopped", 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 { info, ok := s.taskMap.Load(taskNo) if !ok { return fmt.Errorf("task no: %s does not exist or has stopped", taskNo) } info.(*Task).Ticker.Reset(time.Duration(seconds) * time.Second) return nil } // LiveStop 停止捕获数据 func (s *Packet) LiveStop(taskNo string) error { info, ok := s.taskMap.Load(taskNo) if !ok { return fmt.Errorf("task no: %s does not exist or has stopped", taskNo) } info.(*Task).Ticker.Reset(time.Millisecond) return nil }