Skip to content

Commit

Permalink
Merge pull request #10 from negbie/master
Browse files Browse the repository at this point in the history
Better LinkTypeLinuxSLL + VLAN handling
  • Loading branch information
negbie authored Dec 1, 2017
2 parents e83204f + 89e2fae commit a24a2f0
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 47 deletions.
26 changes: 12 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Windows: Download [heplify.exe](https://github.com/sipcapture/heplify/releases)

### Usage
```bash
-i Listen on interface
-i Listen on interface (default "any")
-t Capture types are [pcap, af_packet] (default "pcap")
-m Capture modes [DNS, LOG, SIP, SIPRTCP, TLS] (default "SIP")
-m Capture modes [SIPDNS, SIPLOG, SIPRTCP, SIP, TLS] (default "SIPRTCP")
-pr Portrange to capture SIP (default "5060-5090")
-hs HEP Server address (default "127.0.0.1:9060")
-di Discard uninteresting packets
Expand All @@ -25,26 +25,24 @@ Windows: Download [heplify.exe](https://github.com/sipcapture/heplify/releases)
-wf Write packets to pcap file
-e Log to stderr and disable syslog/file output
-l Log level [debug, info, warning, error] (default "info")
-d Enable certain debug selectors [layer, fragment, sdp, rtcp, rtcpfail]
```

### Examples
```bash
# Capture SIP packets on eth2 and send them to 192.168.1.1:9060
./heplify -i eth2 -hs 192.168.1.1:9060 &
# Capture SIP and RTCP packets on any interface and send them to 127.0.0.1:9060
./heplify

# 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
# Capture SIP and RTCP packets on any interface and send them to 192.168.1.1:9060. Print debug to stdout
./heplify -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 SIP and RTCP 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

# 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 -t af_packet &
# Capture SIP and RTCP packets on eth2 and save them to pcap into current folder
./heplify -i eth2 -wf capture.pcap

# 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 &
./heplify -rf example/rtp_rtcp_sip.pcap -hs 192.168.1.1:9060

```
43 changes: 31 additions & 12 deletions decoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

type Decoder struct {
Host string
LayerType gopacket.LayerType
defragger *ip4defrag.IPv4Defragmenter
fragCount int
dupCount int
Expand All @@ -40,6 +41,7 @@ type Packet struct {
HEPType byte
Tsec uint32
Tmsec uint32
Vlan uint16
Version uint8
Protocol uint8
SrcIP uint32
Expand All @@ -50,11 +52,21 @@ type Packet struct {
Payload []byte
}

func NewDecoder() *Decoder {
func NewDecoder(datalink layers.LinkType) *Decoder {
host, err := os.Hostname()
if err != nil {
host = "sniffer"
}
var lt gopacket.LayerType

switch datalink {
case layers.LinkTypeEthernet:
lt = layers.LayerTypeEthernet
case layers.LinkTypeLinuxSLL:
lt = layers.LayerTypeLinuxSLL
default:
lt = layers.LayerTypeEthernet
}

cSIP := freecache.NewCache(20 * 1024 * 1024) // 20MB
cSDP := freecache.NewCache(20 * 1024 * 1024) // 20MB
Expand All @@ -63,6 +75,7 @@ func NewDecoder() *Decoder {

d := &Decoder{
Host: host,
LayerType: lt,
defragger: ip4defrag.NewIPv4Defragmenter(),
fragCount: 0,
dupCount: 0,
Expand All @@ -87,8 +100,8 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error
Tmsec: uint32(ci.Timestamp.Nanosecond() / 1000),
}

packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
logp.Debug("layers", "\n%v", packet)
packet := gopacket.NewPacket(data, d.LayerType, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
logp.Debug("layer", "\n%v", packet)

if config.Cfg.Dedup {
if appLayer := packet.ApplicationLayer(); appLayer != nil {
Expand All @@ -105,6 +118,14 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error
}
}

if dot1qLayer := packet.Layer(layers.LayerTypeDot1Q); dot1qLayer != nil {
dot1q, ok := dot1qLayer.(*layers.Dot1Q)
if !ok {
return nil, nil
}
pkt.Vlan = dot1q.VLANIdentifier
}

if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {
ip4, ok := ipLayer.(*layers.IPv4)
ip4Len := ip4.Length
Expand Down Expand Up @@ -196,16 +217,14 @@ func (d *Decoder) Process(data []byte, ci *gopacket.CaptureInfo) (*Packet, error
}

// TODO: add more layers like DHCP, NTP
if config.Cfg.Mode == "DNS" {
if dnsLayer := packet.Layer(layers.LayerTypeDNS); dnsLayer != nil {
dns, ok := dnsLayer.(*layers.DNS)
if !ok {
return nil, nil
}
d.dnsCount++
pkt.Payload = protos.ParseDNS(dns)
pkt.HEPType = 100
if dnsLayer := packet.Layer(layers.LayerTypeDNS); dnsLayer != nil {
dns, ok := dnsLayer.(*layers.DNS)
if !ok {
return nil, nil
}
d.dnsCount++
pkt.Payload = protos.ParseDNS(dns)
pkt.HEPType = 53
}

if config.Cfg.Mode == "TLS" {
Expand Down
2 changes: 2 additions & 0 deletions decoder/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (p *Packet) MarshalJSON() ([]byte, error) {
HEPType byte
Tsec uint32
Tmsec uint32
Vlan uint16
Version uint8
Protocol uint8
SrcIP net.IP
Expand All @@ -62,6 +63,7 @@ func (p *Packet) MarshalJSON() ([]byte, error) {
HEPType: p.HEPType,
Tsec: p.Tsec,
Tmsec: p.Tmsec,
Vlan: p.Vlan,
Version: p.Version,
Protocol: p.Protocol,
SrcIP: int2ip(p.SrcIP),
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func parseFlags() {
var rotateEveryKB uint64
var keepLogFiles int

flag.StringVar(&ifaceConfig.Device, "i", "", "Listen on interface")
flag.StringVar(&ifaceConfig.Device, "i", "any", "Listen on interface")
flag.StringVar(&ifaceConfig.Type, "t", "pcap", "Capture types are [pcap, af_packet]")
flag.StringVar(&ifaceConfig.ReadFile, "rf", "", "Read packets from pcap file")
flag.StringVar(&ifaceConfig.WriteFile, "wf", "", "Write packets to pcap file")
Expand All @@ -38,7 +38,7 @@ func parseFlags() {
flag.StringVar(&fileRotator.Path, "p", "./", "Log filepath")
flag.StringVar(&fileRotator.Name, "n", "heplify.log", "Log filename")
flag.Uint64Var(&rotateEveryKB, "r", 16384, "Log filesize (KB)")
flag.StringVar(&config.Cfg.Mode, "m", "SIP", "Capture modes [DNS, LOG, SIP, SIPRTCP, TLS]")
flag.StringVar(&config.Cfg.Mode, "m", "SIPRTCP", "Capture modes [SIPDNS, SIPLOG, SIPRTCP, SIP, TLS]")
flag.BoolVar(&config.Cfg.Dedup, "dd", true, "Deduplicate packets")
flag.StringVar(&config.Cfg.Filter, "fi", "", "Filter interesting packets")
flag.StringVar(&config.Cfg.Discard, "di", "", "Discard uninteresting packets")
Expand Down Expand Up @@ -76,7 +76,7 @@ func main() {
}

capture := &sniffer.SnifferSetup{}
err = capture.Init(false, config.Cfg.Mode, sniffer.NewWorker, config.Cfg.Iface)
err = capture.Init(false, config.Cfg.Mode, config.Cfg.Iface)
checkCritErr(err)
defer capture.Close()
err = capture.Run()
Expand Down
7 changes: 7 additions & 0 deletions publish/hep.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ func makeChunck(chunckVen uint16, chunckType uint16, h *decoder.Packet) []byte {
chunck[6] = 1 // SIP
case 5:
chunck[6] = 5 // RTCP
case 53:
chunck[6] = 53 // DNS
case 100:
chunck[6] = 100 // LOG
default:
Expand Down Expand Up @@ -217,6 +219,11 @@ func makeChunck(chunckVen uint16, chunckType uint16, h *decoder.Packet) []byte {
chunck = make([]byte, len(h.CorrelationID)+6)
copy(chunck[6:], h.CorrelationID)

// Chunk VLAN
case 0x0012:
chunck = make([]byte, 6+2)
binary.BigEndian.PutUint16(chunck[6:], h.Vlan)

// Chunk MOS only
case 0x0020:
//chunck = make([]byte, 6+2)
Expand Down
2 changes: 1 addition & 1 deletion sniffer/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func deviceNameFromIndex(index int, devices []string) (string, error) {

func filterDeviceName(name []string) {
for _, d := range name {
if strings.HasPrefix(d, "any") || strings.HasPrefix(d, "bluetooth") || strings.HasPrefix(d, "dbus") || strings.HasPrefix(d, "nf") || strings.HasPrefix(d, "usb") {
if strings.HasPrefix(d, "bluetooth") || strings.HasPrefix(d, "dbus") || strings.HasPrefix(d, "nf") || strings.HasPrefix(d, "usb") {
continue
}
fmt.Printf("./heplify -i %s\n", d)
Expand Down
34 changes: 17 additions & 17 deletions sniffer/sniffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type Worker interface {

type WorkerFactory func(layers.LinkType) (Worker, error)

func NewWorker(dl layers.LinkType) (Worker, error) {
func NewWorker(lt layers.LinkType) (Worker, error) {
var o publish.Outputer
var err error

Expand All @@ -60,7 +60,7 @@ func NewWorker(dl layers.LinkType) (Worker, error) {
}

p := publish.NewPublisher(o)
d := decoder.NewDecoder()
d := decoder.NewDecoder(lt)
w := &MainWorker{publisher: p, decoder: d}
return w, nil
}
Expand Down Expand Up @@ -91,20 +91,20 @@ func (sniffer *SnifferSetup) setFromConfig(cfg *config.InterfacesConfig) error {

switch sniffer.mode {
case "SIP":
sniffer.filter = "(greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0) or (vlan and (greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0))"
sniffer.filter = "greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0"
case "SIPDNS":
sniffer.filter = "(greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0) or (greater 32 and ip and dst port 53)"
case "RTCP":
sniffer.filter = "(ip and ip[6] & 0x2 = 0 and ip[6:2] & 0x1fff = 0 and udp and udp[8] & 0xc0 = 0x80 and udp[9] >= 0xc8 && udp[9] <= 0xcc)"
case "SIPRTCP":
sniffer.filter = "(greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0) or (ip and ip[6] & 0x2 = 0 and ip[6:2] & 0x1fff = 0 and udp and udp[8] & 0xc0 = 0x80 and udp[9] >= 0xc8 && udp[9] <= 0xcc)"
case "LOG":
sniffer.filter = "greater 128 and port 514"
case "DNS":
sniffer.filter = "greater 32 and ip and dst port 53"
case "SIPLOG":
sniffer.filter = "(greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0) or (greater 128 and ip and dst port 514)"
case "TLS":
sniffer.filter = "tcp and port 443 and tcp[(((tcp[12:1] & 0xf0) >> 2)):1] = 0x16 and ((tcp[(((tcp[12:1] & 0xf0) >> 2)+5):1] = 0x01) or (tcp[(((tcp[12:1] & 0xf0) >> 2)+5):1] = 0x02))"
default:
sniffer.mode = "SIP"
sniffer.filter = "(greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0) or (vlan and (greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0))"
sniffer.filter = "greater 256 and portrange " + sniffer.config.PortRange + " or ip[6:2] & 0x1fff != 0"
}

logp.Info("Sniffer type: [%s] device: [%s] mode: [%s]", sniffer.config.Type, sniffer.config.Device, sniffer.mode)
Expand Down Expand Up @@ -162,29 +162,27 @@ func (sniffer *SnifferSetup) setFromConfig(cfg *config.InterfacesConfig) error {
return nil
}

func (sniffer *SnifferSetup) Init(testMode bool, mode string, factory WorkerFactory, interfaces *config.InterfacesConfig) error {
func (sniffer *SnifferSetup) Init(testMode bool, mode string, interfaces *config.InterfacesConfig) error {
var err error
sniffer.mode = mode

if interfaces.ReadFile == "" {
if interfaces.Device == "any" {
// OS X or Windows
// Windows or MacOS
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
return fmt.Errorf("'-i any' is not supported on %s", runtime.GOOS)
}
}
} else if interfaces.Device == "" && interfaces.ReadFile == "" {
}

if interfaces.Device == "" && interfaces.ReadFile == "" {
fmt.Printf("Please use one of the following devices:\n\n")
_, err := ListDeviceNames(false, false)
if err != nil {
return fmt.Errorf("getting devices list: %v", err)
}
fmt.Println("")
os.Exit(1)
} else if interfaces.Device == "any" && interfaces.Type == "pcap" {
fmt.Println("Interface 'any' and capture type 'pcap' will break VLAN capture!")
fmt.Println("To listen on interface 'any' please use 'af_packet' capture type!")
os.Exit(1)
}

if !testMode {
Expand All @@ -194,7 +192,7 @@ func (sniffer *SnifferSetup) Init(testMode bool, mode string, factory WorkerFact
}
}

sniffer.worker, err = factory(sniffer.Datalink())
sniffer.worker, err = NewWorker(sniffer.Datalink())
if err != nil {
return fmt.Errorf("creating decoder: %v", err)
}
Expand Down Expand Up @@ -240,7 +238,7 @@ func (sniffer *SnifferSetup) Run() error {
}

if err == pcap.NextErrorTimeoutExpired || err == syscall.EINTR {
logp.Debug("sniffer", "Idle")
//logp.Debug("sniffer", "Idle")
continue
}

Expand Down Expand Up @@ -342,6 +340,8 @@ func (sniffer *SnifferSetup) Stop() error {
func (sniffer *SnifferSetup) Datalink() layers.LinkType {
if sniffer.config.Type == "pcap" {
return sniffer.pcapHandle.LinkType()
} else if sniffer.config.Type == "af_packet" {
return sniffer.afpacketHandle.LinkType()
}
return layers.LinkTypeEthernet
}
Expand Down

0 comments on commit a24a2f0

Please sign in to comment.