package service import ( "encoding/base64" "encoding/binary" "fmt" "os" "runtime" "strings" "time" "golang.org/x/net/http/httpguts" "golang.org/x/net/http2/hpack" ) const ( GTPU_V1_VERSION = 1 << 5 GTPU_VER_MASK = 7 << 5 GTPU_PT_GTP = 1 << 4 GTPU_HEADER_LEN = 12 GTPU_E_S_PB_BIT = 7 GTPU_E_BI = 1 << 2 ) const ( GTPU_HEADER_VERSION_INDEX = 0 GTPU_HEADER_MSG_TYPE_INDEX = 1 GTPU_HEADER_LENGTH_INDEX = 2 GTPU_HEADER_TEID_INDEX = 4 ) type ExtHeader struct { TaskId uint32 IMSI string IfType int MsgType int MsgDirect int // 0-recv,1-send TimeStamp int64 SrcIP string DstIP string SrcPort uint16 DstPort uint16 Proto int // Protocol PPI int // only for SCTP DataLen uint16 DataInfo []byte } // parseUDPData 解析UDP数据 func parseUDPData(rvMsg []byte, rvLen int) (ExtHeader, error) { var extHdr ExtHeader // var tr dborm.TraceData var off int msg := rvMsg verFlags := msg[GTPU_HEADER_VERSION_INDEX] gtpuHdrLen := GTPU_HEADER_LEN localTeid := binary.BigEndian.Uint32(msg[GTPU_HEADER_TEID_INDEX:]) extHdr.TaskId = localTeid if (verFlags & GTPU_E_S_PB_BIT) != 0 { if (verFlags & GTPU_E_BI) != 0 { extTypeIndex := GTPU_HEADER_LEN - 1 extType := msg[extTypeIndex] if extType == 0xFE { extHdr.IMSI = string(msg[extTypeIndex+2 : extTypeIndex+17]) extHdr.IfType = int(msg[extTypeIndex+17]) extHdr.MsgType = int(msg[extTypeIndex+18]) extHdr.MsgDirect = int(msg[extTypeIndex+19]) extHdr.TimeStamp = time.Now().UTC().UnixMilli() // extHdr.TimeStamp = int64(binary.BigEndian.Uint64(msg[extTypeIndex+19:])) // fmt.Printf("ext info %v %s %d %d %d \n", msg[(extTypeIndex+2):(extTypeIndex+20)], extHdr.IMSI, extHdr.IfType, extHdr.MsgType, extHdr.MsgDirect) // set offset of IP Packet off = 40 + 4 //src ip: msg+40+12 extHdr.SrcIP = fmt.Sprintf("%d.%d.%d.%d", msg[off+12], msg[off+13], msg[off+14], msg[off+15]) //dst ip: msg+40+12+4 extHdr.DstIP = fmt.Sprintf("%d.%d.%d.%d", msg[off+16], msg[off+17], msg[off+18], msg[off+19]) extHdr.SrcPort = uint16(binary.BigEndian.Uint16(msg[off+20:])) extHdr.DstPort = uint16(binary.BigEndian.Uint16(msg[off+22:])) // fmt.Printf("info %s:%d %s:%d \n", extHdr.SrcIP, extHdr.SrcPort, extHdr.DstIP, extHdr.DstPort) // ip header start msg+40 extHdr.DataLen = uint16(rvLen - off) extHdr.DataInfo = make([]byte, int(rvLen-off)) copy(extHdr.DataInfo, []byte(msg[off:])) // 132 SCTP // 6 TCP // 17 UDP extHdr.Proto = int(msg[off+9]) if extHdr.Proto == 132 { extHdr.PPI = int(msg[off+47]) extHdr.DataLen = uint16(binary.BigEndian.Uint16(msg[(off+34):]) - 16) // fmt.Printf("dat len %d %d \n", extHdr.DataLen, extHdr.PPI) } } for extType != 0 && extTypeIndex < rvLen { extLen := msg[extTypeIndex+1] << 2 if extLen == 0 { return extHdr, fmt.Errorf("error, extLen is zero") } gtpuHdrLen += int(extLen) extTypeIndex += int(extLen) extType = msg[extTypeIndex] } } } else { gtpuHdrLen -= 4 } return extHdr, nil } // UDPDataHandler UDP数据处理 func UDPDataHandler(data []byte, n int) (map[string]any, error) { extHdr, err := parseUDPData(data, n) if err != nil { return nil, err } if extHdr.TaskId == 0 || extHdr.DataLen < 1 { return nil, fmt.Errorf("data error") } m := map[string]any{ "taskId": extHdr.TaskId, "imsi": extHdr.IMSI, "ifType": extHdr.IfType, "srcAddr": fmt.Sprintf("%s:%d", extHdr.SrcIP, extHdr.SrcPort), "dstAddr": fmt.Sprintf("%s:%d", extHdr.DstIP, extHdr.DstPort), "msgType": extHdr.MsgType, "msgDirect": extHdr.MsgDirect, "timestamp": extHdr.TimeStamp, "dataLen": extHdr.DataLen, // "dataInfo": extHdr.DataInfo, "decMsg": "", } // Base64 编码 m["dataInfo"] = base64.StdEncoding.EncodeToString(extHdr.DataInfo) if extHdr.Proto == 6 { // TCP // 取响应数据 iplen := uint16(binary.BigEndian.Uint16(extHdr.DataInfo[2:])) tcplen := uint16(iplen - 32 - 20) hdrlen := uint16(binary.BigEndian.Uint16(extHdr.DataInfo[20+32+1:])) offset := uint16(52) // fmt.Printf("HTTP %d %d %d \n", iplen, tcplen, hdrlen) if tcplen > (hdrlen + 9) { // has data doffset := uint16(offset + hdrlen + 9) datlen := uint16(binary.BigEndian.Uint16(extHdr.DataInfo[doffset+1:])) // fmt.Printf("HTTP datlen %d \n", datlen) m["decMsg"], _ = httpDataMsg(extHdr.DataInfo[offset+9:offset+9+hdrlen], extHdr.DataInfo[doffset+9:doffset+datlen+9]) } else { m["decMsg"], _ = httpDataMsg(extHdr.DataInfo[offset+9:hdrlen], nil) } } // pcap文件 m["pcapFile"] = writePcap(extHdr) return m, nil } // =========== TCP协议Body =========== // httpDataMsg Http数据信息处理 func httpDataMsg(header []byte, data []byte) (string, error) { var remainSize = uint32(16 << 20) var sawRegular bool var invalid bool // pseudo header field errors var Fields []hpack.HeaderField invalid = false hdec := hpack.NewDecoder(4096, nil) hdec.SetEmitEnabled(true) hdec.SetMaxStringLength(int(16 << 20)) hdec.SetEmitFunc(func(hf hpack.HeaderField) { if !httpguts.ValidHeaderFieldValue(hf.Value) { // Don't include the value in the error, because it may be sensitive. invalid = true } isPseudo := strings.HasPrefix(hf.Name, ":") if isPseudo { if sawRegular { invalid = true } } else { sawRegular = true if !validWireHeaderFieldName(hf.Name) { invalid = true } } if invalid { hdec.SetEmitEnabled(false) return } size := hf.Size() if size > remainSize { hdec.SetEmitEnabled(false) //mh.Truncated = true return } remainSize -= size Fields = append(Fields, hf) }) // defer hdec.SetEmitFunc(func(hf hpack.HeaderField) {}) frag := header if _, err := hdec.Write(frag); err != nil { return "", err } if err := hdec.Close(); err != nil { return "", err } // hdec.SetEmitFunc(func(hf hpack.HeaderField) {}) var headers []byte var line string for i := range Fields { line = fmt.Sprintf("\"%s\":\"%s\",", Fields[i].Name, Fields[i].Value) headers = append(headers, []byte(line)...) } if len(data) > 0 { return fmt.Sprintf("{ %s \"content\":%s }", string(headers), string(data)), nil } else { return fmt.Sprintf("{ %s }", string(headers)), nil } } // validWireHeaderFieldName 校验报文头字段名称 func validWireHeaderFieldName(v string) bool { if len(v) == 0 { return false } for _, r := range v { if !httpguts.IsTokenRune(r) { return false } if 'A' <= r && r <= 'Z' { return false } } return true } // =========== writePcap 写Pcap文件 =========== const magicMicroseconds = 0xA1B2C3D4 const versionMajor = 2 const versionMinor = 4 func writeEmptyPcap(filename string, timeStamp int64, length int, data []byte) error { var err error var file *os.File if _, err = os.Stat(filename); os.IsNotExist(err) { file, err = os.Create(filename) // File Header var fileHeaderBuf [24]byte binary.LittleEndian.PutUint32(fileHeaderBuf[0:4], magicMicroseconds) binary.LittleEndian.PutUint16(fileHeaderBuf[4:6], versionMajor) binary.LittleEndian.PutUint16(fileHeaderBuf[6:8], versionMinor) // bytes 8:12 stay 0 (timezone = UTC) // bytes 12:16 stay 0 (sigfigs is always set to zero, according to // http://wiki.wireshark.org/Development/LibpcapFileFormat binary.LittleEndian.PutUint32(fileHeaderBuf[16:20], 0x00040000) binary.LittleEndian.PutUint32(fileHeaderBuf[20:24], 0x00000071) if _, err := file.Write(fileHeaderBuf[:]); err != nil { return err } } else { file, err = os.OpenFile(filename, os.O_WRONLY|os.O_APPEND, 0666) } if err != nil { return err } defer file.Close() // Packet Header var packetHeaderBuf [24]byte t := time.UnixMilli(timeStamp) if t.IsZero() { t = time.Now() } secs := t.Unix() usecs := t.Nanosecond() / 1000 binary.LittleEndian.PutUint32(packetHeaderBuf[0:4], uint32(secs)) binary.LittleEndian.PutUint32(packetHeaderBuf[4:8], uint32(usecs)) binary.LittleEndian.PutUint32(packetHeaderBuf[8:12], uint32(length+16)) binary.LittleEndian.PutUint32(packetHeaderBuf[12:16], uint32(length+16)) if _, err := file.Write(packetHeaderBuf[:]); err != nil { return err } // 数据包内容的定义 cooked := [...]byte{0x00, 0x00, 0x03, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00} if _, err := file.Write(cooked[:]); err != nil { return err } // Packet Data if _, err := file.Write(data); err != nil { return err } return nil } // writePcap 写Pcap文件并返回文件路径 func writePcap(extHdr ExtHeader) string { filePath := fmt.Sprintf("/tmp/trace_%d .pcap", extHdr.TaskId) if runtime.GOOS == "windows" { filePath = fmt.Sprintf("C:%s", filePath) } err := writeEmptyPcap(filePath, extHdr.TimeStamp, int(extHdr.DataLen), extHdr.DataInfo) if err != nil { return "" } return filePath }