package service import ( "bytes" "encoding/binary" "fmt" "net" "os" "path/filepath" "runtime" "time" "be.ems/src/modules/trace/model" ) // TraceMsgToOamTraceData 结构体定义 type TraceMsgToOamTraceData struct { NfTraceMsg *model.TraceMsg TimestampStr string TracePayloadLen uint16 TracePayload []byte } // traceHandler 处理跟踪数据 func traceHandler(data []byte) (*TraceMsgToOamTraceData, error) { decodeData, err := decodeTraceData(data) if err != nil { return decodeData, err } fmt.Printf("TraceHandler get oamData: %s,%+v\n, payload=len(%d,%d)", decodeData.TimestampStr, decodeData.NfTraceMsg, decodeData.TracePayloadLen, len(decodeData.TracePayload)) // Return parsed message and payload if len(decodeData.TracePayload) != int(decodeData.TracePayloadLen) { return decodeData, fmt.Errorf("trace payload is bad, len=%d, shall be:%d", len(decodeData.TracePayload), int(decodeData.TracePayloadLen)) } // 输出到文件 filePath := fmt.Sprintf("/tmp/omc/trace/task_%d.pcap", decodeData.NfTraceMsg.TraceId) if runtime.GOOS == "windows" { filePath = fmt.Sprintf("C:%s", filePath) } err = writePCAP(filePath, decodeData.NfTraceMsg.Timestamp, decodeData.TracePayload) return decodeData, err } // decodeTraceData 解析跟踪数据 func decodeTraceData(data []byte) (*TraceMsgToOamTraceData, error) { buf := bytes.NewBuffer(data) oamData := new(TraceMsgToOamTraceData) msg := new(model.TraceMsg) // 1. Parse Flag (1 byte) var flag uint8 if err := binary.Read(buf, binary.BigEndian, &flag); err != nil { return nil, err } // Parse message type (bits 1-2) switch flag & 0x3 { case 0x1: msg.MsgType = model.MSG_TYPE_REQ case 0x2: msg.MsgType = model.MSG_TYPE_RSP } // Parse message direction (bits 3-4) switch flag & 0xc { case 0x4: msg.MsgDirect = model.MSG_DIRECT_SEND case 0x8: msg.MsgDirect = model.MSG_DIRECT_RECV } // 2. Parse TraceId (4 bytes) var traceId uint32 if err := binary.Read(buf, binary.BigEndian, &traceId); err != nil { return nil, err } msg.TraceId = int(traceId) // 3. Parse IMSI (15 bytes fixed length) imsi := make([]byte, 15) if _, err := buf.Read(imsi); err != nil { return nil, err } msg.IMSI = string(imsi) // 4. Parse IfType (1 byte) ifTypeByte, err := buf.ReadByte() if err != nil { return nil, err } msg.IfType = byte2Iftype(ifTypeByte) // 5. Parse Timestamp (8 bytes) if err := binary.Read(buf, binary.BigEndian, &msg.Timestamp); err != nil { return nil, err } // 6. Parse SrcIp (4 bytes IPv4) srcIp := make([]byte, 4) if flag&0x20 != 0 { srcIp = make([]byte, 16) } if _, err := buf.Read(srcIp); err != nil { return nil, err } msg.SrcIpStr = net.IP(srcIp).String() // 7. Parse DstIp (4 bytes IPv4) dstIp := make([]byte, 4) if flag&0x20 != 0 { dstIp = make([]byte, 16) } if _, err := buf.Read(dstIp); err != nil { return nil, err } msg.DstIpStr = net.IP(dstIp).String() // 8. Parse NfName (variable length) nfNameLen, err := buf.ReadByte() if err != nil { return nil, err } nfName := make([]byte, nfNameLen) if _, err := buf.Read(nfName); err != nil { return nil, err } msg.NfName = string(nfName) // 9. Parse MsgEvent (variable length) msgEventLen, err := buf.ReadByte() if err != nil { return nil, err } msgEvent := make([]byte, msgEventLen) if _, err := buf.Read(msgEvent); err != nil { return nil, err } msg.MsgEvent = string(msgEvent) // 10. Parse ExtenBuf (variable length) extenBufLen, err := buf.ReadByte() if err != nil { return nil, err } if extenBufLen > 0 { extenBuf := make([]byte, extenBufLen) if _, err := buf.Read(extenBuf); err != nil { return nil, err } // TODO: Parse extended fields according to actual protocol } // 11. len of payload(2 byte) var len uint16 if err := binary.Read(buf, binary.BigEndian, &len); err != nil { return nil, err } oamData.NfTraceMsg = msg oamData.TimestampStr = time.Unix(0, msg.Timestamp).Format("2006-01-02 15:04:05.999999999") oamData.TracePayloadLen = len oamData.TracePayload = buf.Bytes() return oamData, nil } // byte2Iftype 将字节转换为接口类型字符串 // 参考:3GPP TS 32.422 V17.0.0 (2022-06) 5G; 3GPP TS 29.244 V17.0.0 (2022-06) 5G func byte2Iftype(val uint8) string { ret := "" switch val { case 1: ret = "N1" case 2: ret = "N2" case 3: ret = "N1/N2" case 4: ret = "N4" case 7: ret = "N7" case 8: ret = "N8" case 10: ret = "N10" case 11: ret = "N11" case 12: ret = "N12" case 13: ret = "N13" case 15: ret = "N15" } return ret } // writePCAP 写入PCAP文件 func writePCAP(filename string, timeStamp int64, data []byte) error { var err error var file *os.File // 1. 创建目录(含权限控制) if err := os.MkdirAll(filepath.Dir(filename), 0775); err != nil { return err } // 2. 智能打开文件(原子操作避免竞态) if _, err = os.Stat(filename); os.IsNotExist(err) { file, err = os.Create(filename) if err != nil { return err } // 写入PCAP全局头(严格兼容Wireshark) fileHeader := [24]byte{ 0xD4, 0xC3, 0xB2, 0xA1, // magic_number (微秒级) 0x02, 0x00, 0x04, 0x00, // version_major(2) + version_minor(4) 0x00, 0x00, 0x00, 0x00, // thiszone (UTC) 0x00, 0x00, 0x00, 0x00, // sigfigs (固定0) 0x00, 0x00, 0x04, 0x00, // snaplen (1024) 0x71, 0x00, 0x00, 0x00, // network (LINKTYPE_LINUX_SLL) } if _, err := file.Write(fileHeader[:]); err != nil { return err } } else { // 追加模式打开(避免截断已有内容) file, err = os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666) if err != nil { return err } } defer file.Close() // 3. 构造Linux cooked头(RFC 3580规范) linuxSLLHeaderLen := 16 // Linux cooked模式头长度 cookedHeader := make([]byte, linuxSLLHeaderLen) binary.BigEndian.PutUint16(cookedHeader[0:2], 0x0000) // 数据包类型(主机→网络) binary.BigEndian.PutUint16(cookedHeader[2:4], 0x0304) // 地址类型(ARPHRD_ETHER) binary.BigEndian.PutUint16(cookedHeader[4:6], 0x0008) // 协议类型(ETH_P_IP) binary.BigEndian.PutUint16(cookedHeader[14:16], 0x0800) // 数据包类型(PACKET_HOST) // 4. 合并链路层头与数据(兼容IPv4选项) fullData := append(cookedHeader, data...) pktLen := len(fullData) // 5. 生成精确时间戳(处理闰秒和时区) t := time.Unix(0, timeStamp).UTC() if t.IsZero() { t = time.Now().UTC() } secs := t.Unix() usecs := t.Nanosecond() / 1000 // 微秒级时间戳 // 6. 构造PCAP报文头(16字节) var packetHeader [16]byte binary.LittleEndian.PutUint32(packetHeader[0:4], uint32(secs)) // 时间戳秒 binary.LittleEndian.PutUint32(packetHeader[4:8], uint32(usecs)) // 时间戳微秒 binary.LittleEndian.PutUint32(packetHeader[8:12], uint32(pktLen)) // 捕获长度 binary.LittleEndian.PutUint32(packetHeader[12:16], uint32(pktLen)) // 原始长度 // 7. 原子写入操作(避免部分写入) buf := new(bytes.Buffer) buf.Write(packetHeader[:]) buf.Write(fullData) if _, err := file.Write(buf.Bytes()); err != nil { return err } // 8. 强制刷盘并验证写入 return file.Sync() }