Files
be.ems/src/modules/trace/service/trace_task_udp_data.go
2025-04-22 14:25:55 +08:00

272 lines
7.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()
}