272 lines
7.0 KiB
Go
272 lines
7.0 KiB
Go
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()
|
||
}
|