From 5190bddab0aa6126afbcbf0e6567a0b4308290ee Mon Sep 17 00:00:00 2001 From: negbie Date: Thu, 9 Nov 2017 18:48:57 +0100 Subject: [PATCH 1/4] More error checks, use lru instead fifo --- decoder/decoder.go | 71 +++++++++++++++++++---------------------- protos/rtcp.go | 79 +++++++++++++++++++++++++--------------------- sniffer/sniffer.go | 4 +++ 3 files changed, 79 insertions(+), 75 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index 75885bf..e5ef608 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -5,9 +5,7 @@ import ( "hash" "os" "strconv" - "time" - "github.com/allegro/bigcache" "github.com/cespare/xxhash" "github.com/google/gopacket" "github.com/google/gopacket/layers" @@ -32,7 +30,8 @@ type Decoder struct { UDPFlow gopacket.Flow SIPHash hash.Hash64 SIPCache *lru.Cache - RTCPCache *bigcache.BigCache + SDPCache *lru.Cache + RTCPCache *lru.Cache } type Packet struct { @@ -56,37 +55,21 @@ func NewDecoder() *Decoder { host = "sniffer" } - sh := xxhash.New() + hSIP := xxhash.New() - sc, err := lru.New(8000) + cSIP, err := lru.New(2000) if err != nil { - logp.Err("lru %v", err) + logp.Err("SIPCache %v", err) } - rcConf := bigcache.Config{ - // number of shards (must be a power of 2) - Shards: 1024, - // time after which entry can be evicted - LifeWindow: 180 * time.Minute, - // rps * lifeWindow, used only in initial memory allocation - MaxEntriesInWindow: 1000 * 180 * 60, - // max entry size in bytes, used only in initial memory allocation - MaxEntrySize: 300, - // prints information about additional memory allocation - Verbose: false, - // cache will not allocate more memory than this limit, value in MB - // if value is reached then the oldest entries can be overridden for the new ones - // 0 value means no size limit - HardMaxCacheSize: 512, - // callback fired when the oldest entry is removed because of its - // expiration time or no space left for the new entry. Default value is nil which - // means no callback and it prevents from unwrapping the oldest entry. - OnRemove: nil, + cSDP, err := lru.New(10000) + if err != nil { + logp.Err("SDPCache %v", err) } - rc, err := bigcache.NewBigCache(rcConf) + cRTCP, err := lru.New(10000) if err != nil { - logp.Err("bigcache %v", err) + logp.Err("RTCPCache %v", err) } d := &Decoder{ @@ -99,9 +82,10 @@ func NewDecoder() *Decoder { tcpCount: 0, dnsCount: 0, unknownCount: 0, - SIPHash: sh, - SIPCache: sc, - RTCPCache: rc, + SIPHash: hSIP, + SIPCache: cSIP, + SDPCache: cSDP, + RTCPCache: cRTCP, } go d.flushFrag() go d.printStats() @@ -190,7 +174,7 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error if config.Cfg.Mode == "SIPRTCP" { d.cacheSDPIPPort(udp.Payload) - if (udp.Payload[0]&0xc0)>>6 == 2 && (udp.Payload[1] == 200 || udp.Payload[1] == 201) { + if (udp.Payload[0]&0xc0)>>6 == 2 && udp.SrcPort%2 != 0 && udp.DstPort%2 != 0 && (udp.Payload[1] == 200 || udp.Payload[1] == 201) { pkt.Payload, pkt.CorrelationID, pkt.Type = d.correlateRTCP(udp.Payload) } } @@ -276,7 +260,7 @@ func (d *Decoder) cacheSDPIPPort(payload []byte) { logp.Warn("Couldn't find end of Call-ID in '%s'", string(restID)) } } - d.RTCPCache.Set(SDPIP+RTCPPort, callID) + d.SDPCache.Add(SDPIP+RTCPPort, callID) } } @@ -284,15 +268,24 @@ func (d *Decoder) correlateRTCP(payload []byte) ([]byte, []byte, byte) { jsonRTCP, err := protos.ParseRTCP(payload) if err != nil { logp.Warn("%v", err) - return nil, nil, 0 + if jsonRTCP == nil { + return nil, nil, 0 + } } - corrID, err := d.RTCPCache.Get(d.IPFlow.Src().String() + d.UDPFlow.Src().String()) - if err != nil { - logp.Warn("%v", err) - return nil, nil, 0 + if corrID, ok := d.SDPCache.Get(d.IPFlow.Src().String() + d.UDPFlow.Src().String()); ok { + logp.Debug("decoder", "SDPCache RTCP JSON payload: %s", string(jsonRTCP)) + d.RTCPCache.Add(d.IPFlow.Src().String()+d.UDPFlow.Src().String(), corrID) + //fmt.Println(string(jsonRTCP)) + //fmt.Println(string(corrID.([]byte))) + return jsonRTCP, corrID.([]byte), 5 + } else if corrID, ok := d.RTCPCache.Get(d.IPFlow.Src().String() + d.UDPFlow.Src().String()); ok { + logp.Debug("decoder", "RTCPCache RTCP JSON payload: %s", string(jsonRTCP)) + d.RTCPCache.Add(d.IPFlow.Src().String()+d.UDPFlow.Src().String(), corrID) + return jsonRTCP, corrID.([]byte), 5 + } else { + logp.Warn("Couldn't find RTCP correlation value for key=%v", d.IPFlow.Src().String()+d.UDPFlow.Src().String()) } - logp.Debug("decoder", "RTCP JSON payload: %s", string(jsonRTCP)) - return jsonRTCP, corrID, 5 + return nil, nil, 0 } diff --git a/protos/rtcp.go b/protos/rtcp.go index 30cdf28..4f1bf3c 100644 --- a/protos/rtcp.go +++ b/protos/rtcp.go @@ -2,6 +2,7 @@ package protos import ( "encoding/binary" + "encoding/hex" "encoding/json" "fmt" @@ -177,32 +178,37 @@ func (rp *RTCP_Packet) MarshalJSON() ([]byte, error) { func ParseRTCP(data []byte) ([]byte, error) { dataLen := len(data) if dataLen < 28 { - return nil, fmt.Errorf("Useless data inside RTCP packet='%s' length=%d", string(data), dataLen) + return nil, fmt.Errorf("Fishy RTCP packet length=%d in packet:\n%v\n", dataLen, hex.Dump(data)) } + var err error pkt := &RTCP_Packet{} + rtcpPkt := []byte{} offset := 0 for dataLen > 0 { if dataLen < 4 || dataLen > 576 { - return nil, fmt.Errorf("Fishy RTCP packet=%v length=%d", data, dataLen) + return nil, fmt.Errorf("Fishy RTCP packet length=%d in packet:\n%v\n", dataLen, hex.Dump(data)) } //version := (data[offset] & 0xc0) >> 6 //padding := (data[offset] & 0x20) >> 5 - receptionReportCount := data[offset] & 0x1f - RTCPType := data[offset+1] - RTCPLength := binary.BigEndian.Uint16(data[offset+2:]) * 4 - + receptionReportCount := int(data[offset] & 0x1f) + RTCPType := int(data[offset+1]) + RTCPLength := int(binary.BigEndian.Uint16(data[offset+2:]) * 4) offset += 4 if receptionReportCount < 0 || receptionReportCount > 4 { - return nil, fmt.Errorf("Fishy RTCP receptionReportCount=%d", receptionReportCount) + return rtcpPkt, fmt.Errorf("Fishy RTCP receptionReportCount=%v type=%d length=%d offset=%d in packet:\n%v", receptionReportCount, RTCPType, dataLen, offset, hex.Dump(data)) + } else if RTCPLength > dataLen { + return rtcpPkt, fmt.Errorf("Fishy RTCP report length=%d in packet:\n%v", RTCPLength, hex.Dump(data)) + } else if RTCPType < 200 || RTCPType > 207 { + return rtcpPkt, fmt.Errorf("Fishy RTCP type=%d in packet:\n%v", RTCPType, hex.Dump(data)) } switch RTCPType { case TYPE_RTCP_SR: - if RTCPLength < 24 { - return nil, fmt.Errorf("To small RTCP packet=%v length=%d type=%d", data[offset:RTCPLength], RTCPLength, RTCPType) + if RTCPLength < 24 || offset+24 > len(data) { + return rtcpPkt, fmt.Errorf("Fishy RTCP packet=%v length=%d type=%d offset=%d", data, RTCPLength, RTCPType, offset) } pkt.Ssrc = binary.BigEndian.Uint32(data[offset:]) @@ -213,9 +219,9 @@ func ParseRTCP(data []byte) ([]byte, error) { pkt.SenderInformation.Octet_count = binary.BigEndian.Uint32(data[offset+20:]) offset += 24 - if receptionReportCount > 0 && RTCPLength >= 24 { + if receptionReportCount > 0 && RTCPLength >= 24 && offset+24 <= len(data) { tmpReportBlocks := make([]RTCP_report_block, receptionReportCount) - for i := 0; i < int(receptionReportCount); i++ { + for i := 0; i < receptionReportCount; i++ { tmpReportBlocks[i].SourceSsrc = binary.BigEndian.Uint32(data[offset:]) tmpReportBlocks[i].Fraction_lost = data[offset+4] var cumBuf [4]byte @@ -225,25 +231,29 @@ func ParseRTCP(data []byte) ([]byte, error) { tmpReportBlocks[i].Jitter = binary.BigEndian.Uint32(data[offset+12:]) tmpReportBlocks[i].LastSR = binary.BigEndian.Uint32(data[offset+16:]) tmpReportBlocks[i].Delay_last_SR = binary.BigEndian.Uint32(data[offset+20:]) - tmpReportBlocks[i].ReportCount = receptionReportCount - tmpReportBlocks[i].RTCPType = RTCPType + tmpReportBlocks[i].ReportCount = uint8(receptionReportCount) + tmpReportBlocks[i].RTCPType = uint8(RTCPType) offset += 24 RTCPLength -= 24 pkt.ReportBlocks = pkt.AddReportBlock(tmpReportBlocks[i]) } } + rtcpPkt, err = pkt.MarshalJSON() + if err != nil { + return nil, err + } case TYPE_RTCP_RR: - if RTCPLength < 4 { - return nil, fmt.Errorf("To small RTCP packet=%v length=%d type=%d", data[offset:RTCPLength], RTCPLength, RTCPType) + if RTCPLength < 4 || offset+4 > len(data) { + return rtcpPkt, fmt.Errorf("Fishy RTCP packet=%v length=%d type=%d offset=%d", data, RTCPLength, RTCPType, offset) } pkt.Ssrc = binary.BigEndian.Uint32(data[offset:]) offset += 4 - if receptionReportCount > 0 && RTCPLength >= 24 { + if receptionReportCount > 0 && RTCPLength >= 24 && offset+24 <= len(data) { tmpReportBlocks := make([]RTCP_report_block, receptionReportCount) - for i := 0; i < int(receptionReportCount); i++ { + for i := 0; i < receptionReportCount; i++ { tmpReportBlocks[i].SourceSsrc = binary.BigEndian.Uint32(data[offset:]) tmpReportBlocks[i].Fraction_lost = data[offset+4] var cumBuf [4]byte @@ -253,39 +263,36 @@ func ParseRTCP(data []byte) ([]byte, error) { tmpReportBlocks[i].Jitter = binary.BigEndian.Uint32(data[offset+12:]) tmpReportBlocks[i].LastSR = binary.BigEndian.Uint32(data[offset+16:]) tmpReportBlocks[i].Delay_last_SR = binary.BigEndian.Uint32(data[offset+20:]) - tmpReportBlocks[i].ReportCount = receptionReportCount - tmpReportBlocks[i].RTCPType = RTCPType + tmpReportBlocks[i].ReportCount = uint8(receptionReportCount) + tmpReportBlocks[i].RTCPType = uint8(RTCPType) offset += 24 RTCPLength -= 24 pkt.ReportBlocks = pkt.AddReportBlock(tmpReportBlocks[i]) } } + rtcpPkt, err = pkt.MarshalJSON() + if err != nil { + return nil, err + } case TYPE_RTCP_SDES: - logp.Debug("rtcp", "Discard RTCP_SDES packet type: %d", RTCPType) - offset += int(RTCPLength) + logp.Debug("rtcp", "Discard RTCP_SDES packet type=%d", RTCPType) + offset += RTCPLength case TYPE_RTCP_APP: - logp.Debug("rtcp", "Discard RTCP_APP packet type: %d", RTCPType) - offset += int(RTCPLength) + logp.Debug("rtcp", "Discard RTCP_APP packet type=%d", RTCPType) + offset += RTCPLength case TYPE_RTCP_BYE: - logp.Debug("rtcp", "Discard RTCP_BYE packet type: %d", RTCPType) - offset += int(RTCPLength) + logp.Debug("rtcp", "Discard RTCP_BYE packet type=%d", RTCPType) + offset += RTCPLength case TYPE_RTCP_XR: - logp.Debug("rtcp", "Discard RTCP_XR packet type: %d", RTCPType) - offset += int(RTCPLength) + logp.Debug("rtcp", "Discard RTCP_XR packet type=%d", RTCPType) + offset += RTCPLength default: - logp.Debug("rtcp", "Discard unsupported packet type: %d", RTCPType) - offset += int(RTCPLength) + logp.Warn("rtcp", "Discard unsupported packet type=%d length=%d offset=%d in packet:\n%v", RTCPType, dataLen, offset, hex.Dump(data)) + return nil, fmt.Errorf("Discard unsupported packet type: %d", RTCPType) } dataLen -= offset - } - - rtcpPkt, err := pkt.MarshalJSON() - if err != nil { - return nil, err - } - return rtcpPkt, nil } diff --git a/sniffer/sniffer.go b/sniffer/sniffer.go index 501d064..ad1845c 100644 --- a/sniffer/sniffer.go +++ b/sniffer/sniffer.go @@ -120,6 +120,10 @@ func (sniffer *SnifferSetup) setFromConfig(cfg *config.InterfacesConfig) error { if err != nil { return fmt.Errorf("couldn't open file %v! %v", sniffer.config.ReadFile, err) } + err = sniffer.pcapHandle.SetBPFFilter(sniffer.filter) + if err != nil { + return fmt.Errorf("SetBPFFilter '%s' for ReadFile pcap: %v", sniffer.filter, err) + } } else { sniffer.pcapHandle, err = pcap.OpenLive(sniffer.config.Device, int32(sniffer.config.Snaplen), true, pcap.BlockForever) if err != nil { From d686aaee7aed3c28b3823b350f52a57d5699cf08 Mon Sep 17 00:00:00 2001 From: Eugen Biegler Date: Fri, 10 Nov 2017 01:12:00 +0100 Subject: [PATCH 2/4] Simple LRU cache, more checks --- decoder/decoder.go | 60 +++++++++++++++----------------------- decoder/util.go | 67 ++++++++++++++++++++++++++++++++++++++++++ protos/rtcp.go | 72 +++++++++++++++++++++------------------------- 3 files changed, 123 insertions(+), 76 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index e5ef608..62ddae5 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -2,6 +2,7 @@ package decoder import ( "bytes" + "fmt" "hash" "os" "strconv" @@ -9,7 +10,6 @@ import ( "github.com/cespare/xxhash" "github.com/google/gopacket" "github.com/google/gopacket/layers" - "github.com/hashicorp/golang-lru" "github.com/negbie/heplify/config" "github.com/negbie/heplify/ip4defrag" "github.com/negbie/heplify/logp" @@ -26,12 +26,12 @@ type Decoder struct { tcpCount int dnsCount int unknownCount int - IPFlow gopacket.Flow - UDPFlow gopacket.Flow + FlowSrcIP string + FlowSrcPort string SIPHash hash.Hash64 - SIPCache *lru.Cache - SDPCache *lru.Cache - RTCPCache *lru.Cache + SIPCache *Cache + SDPCache *Cache + RTCPCache *Cache } type Packet struct { @@ -56,21 +56,9 @@ func NewDecoder() *Decoder { } hSIP := xxhash.New() - - cSIP, err := lru.New(2000) - if err != nil { - logp.Err("SIPCache %v", err) - } - - cSDP, err := lru.New(10000) - if err != nil { - logp.Err("SDPCache %v", err) - } - - cRTCP, err := lru.New(10000) - if err != nil { - logp.Err("RTCPCache %v", err) - } + cSIP := NewLRUCache(4000) + cSDP := NewLRUCache(10000) + cRTCP := NewLRUCache(100000) d := &Decoder{ Host: host, @@ -112,7 +100,7 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error if config.Cfg.Dedup { d.SIPHash.Write(ip4.Payload) //key := fastHash(ip4.Payload) - key := d.SIPHash.Sum64() + key := strconv.FormatUint(d.SIPHash.Sum64(), 10) d.SIPHash.Reset() _, dup := d.SIPCache.Get(key) d.SIPCache.Add(key, nil) @@ -122,7 +110,7 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error } } - d.IPFlow = ip4.NetworkFlow() + d.FlowSrcIP = ip4.NetworkFlow().Src().String() d.ip4Count++ pkt.Version = ip4.Version @@ -164,7 +152,7 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error return nil, nil } - d.UDPFlow = udp.TransportFlow() + d.FlowSrcPort = udp.TransportFlow().Src().String() d.udpCount++ pkt.Sport = uint16(udp.SrcPort) @@ -265,27 +253,25 @@ func (d *Decoder) cacheSDPIPPort(payload []byte) { } func (d *Decoder) correlateRTCP(payload []byte) ([]byte, []byte, byte) { - jsonRTCP, err := protos.ParseRTCP(payload) - if err != nil { - logp.Warn("%v", err) + jsonRTCP, info := protos.ParseRTCP(payload) + if info != "" { + logp.Info("%v", info) if jsonRTCP == nil { return nil, nil, 0 } } - if corrID, ok := d.SDPCache.Get(d.IPFlow.Src().String() + d.UDPFlow.Src().String()); ok { + if corrID, ok := d.SDPCache.Get(d.FlowSrcIP + d.FlowSrcPort); ok { logp.Debug("decoder", "SDPCache RTCP JSON payload: %s", string(jsonRTCP)) - d.RTCPCache.Add(d.IPFlow.Src().String()+d.UDPFlow.Src().String(), corrID) - //fmt.Println(string(jsonRTCP)) - //fmt.Println(string(corrID.([]byte))) - return jsonRTCP, corrID.([]byte), 5 - } else if corrID, ok := d.RTCPCache.Get(d.IPFlow.Src().String() + d.UDPFlow.Src().String()); ok { + d.RTCPCache.Add(d.FlowSrcIP+d.FlowSrcPort, corrID) + fmt.Println(string(jsonRTCP)) + fmt.Println(string(corrID)) + return jsonRTCP, corrID, 5 + } else if corrID, ok := d.RTCPCache.Get(d.FlowSrcIP + d.FlowSrcPort); ok { logp.Debug("decoder", "RTCPCache RTCP JSON payload: %s", string(jsonRTCP)) - d.RTCPCache.Add(d.IPFlow.Src().String()+d.UDPFlow.Src().String(), corrID) - return jsonRTCP, corrID.([]byte), 5 - } else { - logp.Warn("Couldn't find RTCP correlation value for key=%v", d.IPFlow.Src().String()+d.UDPFlow.Src().String()) + return jsonRTCP, corrID, 5 } + logp.Info("Couldn't find RTCP correlation value for key=%v", d.FlowSrcIP+d.FlowSrcPort) return nil, nil, 0 } diff --git a/decoder/util.go b/decoder/util.go index 17070c2..d711e1c 100644 --- a/decoder/util.go +++ b/decoder/util.go @@ -1,8 +1,10 @@ package decoder import ( + "container/list" "encoding/binary" "net" + "sync" "time" "github.com/negbie/heplify/logp" @@ -46,3 +48,68 @@ func (d *Decoder) printStats() { }() } } + +type cacheValue struct { + key string + bytes []byte +} + +// Just an estimate +func (v *cacheValue) size() uint64 { + return uint64(len([]byte(v.key)) + len(v.bytes)) +} + +type Cache struct { + sync.Mutex + Size uint64 + capacity uint64 + list *list.List + table map[string]*list.Element +} + +// NewLRUCache with a maximum size of capacity bytes. +func NewLRUCache(capacity uint64) *Cache { + return &Cache{ + capacity: capacity, + list: list.New(), + table: make(map[string]*list.Element), + } +} + +// Set some {key, document} into the cache. Doesn't do anything if the key is already present. +func (c *Cache) Add(key string, document []byte) { + c.Lock() + defer c.Unlock() + + _, ok := c.table[key] + if ok { + return + } + v := &cacheValue{key, document} + elt := c.list.PushFront(v) + c.table[key] = elt + c.Size += v.size() + for c.Size > c.capacity { + elt := c.list.Back() + if elt == nil { + return + } + v := c.list.Remove(elt).(*cacheValue) + delete(c.table, v.key) + c.Size -= v.size() + } +} + +// Get retrieves a value from the cache and returns the value and an indicator boolean to show whether it was +// present. +func (c *Cache) Get(key string) (document []byte, ok bool) { + c.Lock() + defer c.Unlock() + + elt, ok := c.table[key] + if !ok { + return nil, false + } + c.list.MoveToFront(elt) + return elt.Value.(*cacheValue).bytes, true +} diff --git a/protos/rtcp.go b/protos/rtcp.go index 4f1bf3c..4c97d0f 100644 --- a/protos/rtcp.go +++ b/protos/rtcp.go @@ -5,8 +5,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - - "github.com/negbie/heplify/logp" ) /* RTCP header @@ -134,11 +132,11 @@ const ( ) type RTCP_header struct { - Version uint8 `json:"version"` // 2 bit - Padding uint8 `json:"padding"` // 1 bit - ReceptionReportCount uint8 `json:"report_count"` // 5 bit - RTCPType uint8 `json:"type"` // 8 bit - Length uint16 `json:"length"` // 16 bit + Version uint8 `json:"version"` // 2 bit + Padding uint8 `json:"padding"` // 1 bit + ReportCount uint8 `json:"report_count"` // 5 bit + RTCPType uint8 `json:"type"` // 8 bit + Length uint16 `json:"length"` // 16 bit } type RTCP_Packet struct { @@ -175,40 +173,37 @@ func (rp *RTCP_Packet) MarshalJSON() ([]byte, error) { return bytes, err } -func ParseRTCP(data []byte) ([]byte, error) { +func ParseRTCP(data []byte) (rtcpPkt []byte, infoMsg string) { dataLen := len(data) if dataLen < 28 { - return nil, fmt.Errorf("Fishy RTCP packet length=%d in packet:\n%v\n", dataLen, hex.Dump(data)) + return nil, fmt.Sprintf("Fishy RTCP dataLen=%d in packet:\n%v", dataLen, hex.Dump(data)) } var err error pkt := &RTCP_Packet{} - rtcpPkt := []byte{} offset := 0 for dataLen > 0 { if dataLen < 4 || dataLen > 576 { - return nil, fmt.Errorf("Fishy RTCP packet length=%d in packet:\n%v\n", dataLen, hex.Dump(data)) + return rtcpPkt, fmt.Sprintf("Fishy RTCP dataLen=%d in packet:\n%v", dataLen, hex.Dump(data)) } - //version := (data[offset] & 0xc0) >> 6 + RTCPVersion := int((data[offset] & 0xc0) >> 6) //padding := (data[offset] & 0x20) >> 5 - receptionReportCount := int(data[offset] & 0x1f) + RTCPReportCount := int(data[offset] & 0x1f) RTCPType := int(data[offset+1]) RTCPLength := int(binary.BigEndian.Uint16(data[offset+2:]) * 4) offset += 4 - if receptionReportCount < 0 || receptionReportCount > 4 { - return rtcpPkt, fmt.Errorf("Fishy RTCP receptionReportCount=%v type=%d length=%d offset=%d in packet:\n%v", receptionReportCount, RTCPType, dataLen, offset, hex.Dump(data)) - } else if RTCPLength > dataLen { - return rtcpPkt, fmt.Errorf("Fishy RTCP report length=%d in packet:\n%v", RTCPLength, hex.Dump(data)) - } else if RTCPType < 200 || RTCPType > 207 { - return rtcpPkt, fmt.Errorf("Fishy RTCP type=%d in packet:\n%v", RTCPType, hex.Dump(data)) + if RTCPVersion != 2 || RTCPReportCount < 0 || RTCPReportCount > 4 || RTCPType < 200 || RTCPType > 207 || RTCPLength > dataLen { + return rtcpPkt, fmt.Sprintf("Fishy RTCPVersion=%d, RTCPReportCount=%d, RTCPType=%d, RTCPLength=%d, dataLen=%d, offset=%d in packet:\n%v", + RTCPVersion, RTCPReportCount, RTCPType, RTCPLength, dataLen, offset, hex.Dump(data)) } switch RTCPType { case TYPE_RTCP_SR: if RTCPLength < 24 || offset+24 > len(data) { - return rtcpPkt, fmt.Errorf("Fishy RTCP packet=%v length=%d type=%d offset=%d", data, RTCPLength, RTCPType, offset) + return rtcpPkt, fmt.Sprintf("Fishy RTCPVersion=%d, RTCPReportCount=%d, RTCPType=%d, RTCPLength=%d, dataLen=%d, offset=%d in packet:\n%v", + RTCPVersion, RTCPReportCount, RTCPType, RTCPLength, dataLen, offset, hex.Dump(data)) } pkt.Ssrc = binary.BigEndian.Uint32(data[offset:]) @@ -219,9 +214,9 @@ func ParseRTCP(data []byte) ([]byte, error) { pkt.SenderInformation.Octet_count = binary.BigEndian.Uint32(data[offset+20:]) offset += 24 - if receptionReportCount > 0 && RTCPLength >= 24 && offset+24 <= len(data) { - tmpReportBlocks := make([]RTCP_report_block, receptionReportCount) - for i := 0; i < receptionReportCount; i++ { + if RTCPReportCount > 0 && RTCPLength >= 24 && offset+24 <= len(data) { + tmpReportBlocks := make([]RTCP_report_block, RTCPReportCount) + for i := 0; i < RTCPReportCount; i++ { tmpReportBlocks[i].SourceSsrc = binary.BigEndian.Uint32(data[offset:]) tmpReportBlocks[i].Fraction_lost = data[offset+4] var cumBuf [4]byte @@ -231,7 +226,7 @@ func ParseRTCP(data []byte) ([]byte, error) { tmpReportBlocks[i].Jitter = binary.BigEndian.Uint32(data[offset+12:]) tmpReportBlocks[i].LastSR = binary.BigEndian.Uint32(data[offset+16:]) tmpReportBlocks[i].Delay_last_SR = binary.BigEndian.Uint32(data[offset+20:]) - tmpReportBlocks[i].ReportCount = uint8(receptionReportCount) + tmpReportBlocks[i].ReportCount = uint8(RTCPReportCount) tmpReportBlocks[i].RTCPType = uint8(RTCPType) offset += 24 RTCPLength -= 24 @@ -240,20 +235,21 @@ func ParseRTCP(data []byte) ([]byte, error) { } rtcpPkt, err = pkt.MarshalJSON() if err != nil { - return nil, err + return nil, fmt.Sprintf("RTCP MarshalJSON %v", err) } case TYPE_RTCP_RR: if RTCPLength < 4 || offset+4 > len(data) { - return rtcpPkt, fmt.Errorf("Fishy RTCP packet=%v length=%d type=%d offset=%d", data, RTCPLength, RTCPType, offset) + return rtcpPkt, fmt.Sprintf("Fishy RTCPVersion=%d, RTCPReportCount=%d, RTCPType=%d, RTCPLength=%d, dataLen=%d, offset=%d in packet:\n%v", + RTCPVersion, RTCPReportCount, RTCPType, RTCPLength, dataLen, offset, hex.Dump(data)) } pkt.Ssrc = binary.BigEndian.Uint32(data[offset:]) offset += 4 - if receptionReportCount > 0 && RTCPLength >= 24 && offset+24 <= len(data) { - tmpReportBlocks := make([]RTCP_report_block, receptionReportCount) - for i := 0; i < receptionReportCount; i++ { + if RTCPReportCount > 0 && RTCPLength >= 24 && offset+24 <= len(data) { + tmpReportBlocks := make([]RTCP_report_block, RTCPReportCount) + for i := 0; i < RTCPReportCount; i++ { tmpReportBlocks[i].SourceSsrc = binary.BigEndian.Uint32(data[offset:]) tmpReportBlocks[i].Fraction_lost = data[offset+4] var cumBuf [4]byte @@ -263,7 +259,7 @@ func ParseRTCP(data []byte) ([]byte, error) { tmpReportBlocks[i].Jitter = binary.BigEndian.Uint32(data[offset+12:]) tmpReportBlocks[i].LastSR = binary.BigEndian.Uint32(data[offset+16:]) tmpReportBlocks[i].Delay_last_SR = binary.BigEndian.Uint32(data[offset+20:]) - tmpReportBlocks[i].ReportCount = uint8(receptionReportCount) + tmpReportBlocks[i].ReportCount = uint8(RTCPReportCount) tmpReportBlocks[i].RTCPType = uint8(RTCPType) offset += 24 RTCPLength -= 24 @@ -272,27 +268,25 @@ func ParseRTCP(data []byte) ([]byte, error) { } rtcpPkt, err = pkt.MarshalJSON() if err != nil { - return nil, err + return nil, fmt.Sprintf("RTCP MarshalJSON %v", err) } case TYPE_RTCP_SDES: - logp.Debug("rtcp", "Discard RTCP_SDES packet type=%d", RTCPType) + infoMsg = fmt.Sprintf("Discard RTCP_SDES packet type=%d", RTCPType) offset += RTCPLength case TYPE_RTCP_APP: - logp.Debug("rtcp", "Discard RTCP_APP packet type=%d", RTCPType) + infoMsg = fmt.Sprintf("Discard RTCP_APP packet type=%d", RTCPType) offset += RTCPLength case TYPE_RTCP_BYE: - logp.Debug("rtcp", "Discard RTCP_BYE packet type=%d", RTCPType) + infoMsg = fmt.Sprintf("Discard RTCP_BYE packet type=%d", RTCPType) offset += RTCPLength case TYPE_RTCP_XR: - logp.Debug("rtcp", "Discard RTCP_XR packet type=%d", RTCPType) + infoMsg = fmt.Sprintf("Discard RTCP_XR packet type=%d", RTCPType) offset += RTCPLength - default: - logp.Warn("rtcp", "Discard unsupported packet type=%d length=%d offset=%d in packet:\n%v", RTCPType, dataLen, offset, hex.Dump(data)) - return nil, fmt.Errorf("Discard unsupported packet type: %d", RTCPType) } dataLen -= offset } - return rtcpPkt, nil + + return } From a70b0c90d4ce6d5a198a3bb2a88ad4348d9c44b6 Mon Sep 17 00:00:00 2001 From: negbie Date: Fri, 10 Nov 2017 13:20:01 +0100 Subject: [PATCH 3/4] Remove debug println --- decoder/decoder.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/decoder/decoder.go b/decoder/decoder.go index 62ddae5..ce4c385 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -2,7 +2,6 @@ package decoder import ( "bytes" - "fmt" "hash" "os" "strconv" @@ -99,7 +98,6 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error if config.Cfg.Dedup { d.SIPHash.Write(ip4.Payload) - //key := fastHash(ip4.Payload) key := strconv.FormatUint(d.SIPHash.Sum64(), 10) d.SIPHash.Reset() _, dup := d.SIPCache.Get(key) @@ -264,8 +262,6 @@ func (d *Decoder) correlateRTCP(payload []byte) ([]byte, []byte, byte) { if corrID, ok := d.SDPCache.Get(d.FlowSrcIP + d.FlowSrcPort); ok { logp.Debug("decoder", "SDPCache RTCP JSON payload: %s", string(jsonRTCP)) d.RTCPCache.Add(d.FlowSrcIP+d.FlowSrcPort, corrID) - fmt.Println(string(jsonRTCP)) - fmt.Println(string(corrID)) return jsonRTCP, corrID, 5 } else if corrID, ok := d.RTCPCache.Get(d.FlowSrcIP + d.FlowSrcPort); ok { logp.Debug("decoder", "RTCPCache RTCP JSON payload: %s", string(jsonRTCP)) From d4cff294d2516b1109d32bd477d25d10f70215ad Mon Sep 17 00:00:00 2001 From: negbie Date: Fri, 10 Nov 2017 13:39:17 +0100 Subject: [PATCH 4/4] Readme --- README.md | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 6764405..e99be48 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,11 @@ # heplify heplify is captagents little brother. While it offers a compareable performance the design goal was simplicity. -It's a single binary which you can place on your linux or windows machine. Just run it to capture packets and -send them to Homer. Right now heplify is able to send SIP, DNS, LOG or TLS handshakes into homer. It's able to +It's a single binary which you can run to capture packets and send them to Homer. +Right now heplify is able to send SIP, correlated RTCP and very basic DNS, LOG or TLS handshakes into homer. It's able to handle fragmented and duplicate packets out of the box. ### Requirements -* libpcap - -On Debian/Ubuntu: sudo apt-get install libpcap-dev -On CentOS/RHEL: yum install libpcap-devel -On Windows: install WinPcap +* None if you use the binary from the [releases](https://github.com/sipcapture/heplify/releases) ### Installation Simply grab it from the [releases](https://github.com/sipcapture/heplify/releases) @@ -18,35 +14,36 @@ chmod +x heplify ### Usage ```bash -i Listen on interface - -t Capture types are [af_packet, pcap, file] (default "pcap") - -m Capture modes [DNS, LOG, SIP, TLS] (default "SIP") + -t Capture types are [pcap, af_packet] (default "pcap") + -m Capture modes [DNS, LOG, SIP, SIPRTCP, TLS] (default "SIP") + -pr Portrange to capture SIP (default "5060-5090") -hs HEP Server address (default "127.0.0.1:9060") - -di Discard uninteresting packets like SIP OPTIONS, HTTP Requests ... - -fi Filter out interesting packets like SIP INVITES, Handshakes ... - -rf Read packets from file. Please use -t file - -wf Write packets to file + -di Discard uninteresting packets + -fi Filter interesting packets + -rf Read packets from pcap file + -wf Write packets to pcap file -e Log to stderr and disable syslog/file output -l Log level [debug, info, warning, error] (default "info") ``` ### Examples ```bash -# Capture SIP packets on eth2 and send them to Homer under 192.168.1.1:9060 -./heplify -i eth2 -hs "192.168.1.1:9060" +# Capture SIP packets on eth2 and send them to 192.168.1.1:9060 +./heplify -i eth2 -hs 192.168.1.1:9060 & -# Print default log level to stdout -./heplify -i eth2 -hs "192.168.1.1:9060" -e +# Capture SIP packets on eth2 and send them to 192.168.1.1:9060. Print debug log level to stdout +./heplify -i eth2 -hs 192.168.1.1:9060 -e -l debug -# Print debug log level to stdout -./heplify -i eth2 -hs "192.168.1.1:9060" -e -l debug +# Capture SIP packets with custom port range on eth2 and send them to 192.168.1.1:9060 +./heplify -i eth2 -pr 6000-6010 -hs 192.168.1.1:9060 & -# Capture LOG packets on eth2 and send them to Homer under 192.168.1.1:9060 -./heplify -i eth2 -hs "192.168.1.1:9060" -m LOG +# Use af_packet to capture SIP and correlated RTCP packets on eth2 and send them to 192.168.1.1:9060 +./heplify -i eth2 -hs 192.168.1.1:9060 -t af_packet -m SIPRTCP & # Capture SIP packets on eth2 and save them to pcap into current folder -./heplify -i eth2 -wf capture.pcap +./heplify -i eth2 -wf capture.pcap -t af_packet & -# Read pcap file from current folder and send it's content to Homer under 192.168.1.1:9060 -./heplify -i eth2 -t file -rf capture.pcap -hs "192.168.1.1:9060" +# Read example/rtp_rtcp_sip.pcap and send SIP and correlated RTCP packets to 192.168.1.1:9060 +./heplify -rf example/rtp_rtcp_sip.pcap -m SIPRTCP -hs 192.168.1.1:9060 & ``` \ No newline at end of file