Skip to content

Commit

Permalink
Refactor packet parser to support additional layer types
Browse files Browse the repository at this point in the history
This commit also adds an additional statistic to keep track of the
number of successfuly parsed DNS packets.
  • Loading branch information
chazlever committed Apr 12, 2019
1 parent 2b78bf5 commit a407c5a
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 40 deletions.
91 changes: 51 additions & 40 deletions parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,26 +69,17 @@ func ParseDevice(device string, snapshotLen int32, promiscuous bool, timeout tim
func ParseDns(handle *pcap.Handle) {
var (
schema DnsSchema
eth layers.Ethernet
dot1q layers.Dot1Q
ip4 layers.IPv4
ip6 layers.IPv6
tcp layers.TCP
udp layers.UDP
ldns layers.DNS
stats Statistics
ip4 *layers.IPv4
ip6 *layers.IPv6
tcp *layers.TCP
udp *layers.UDP
)

// Keep track of parsing statistics
stats := Statistics{}

// Set the source and sensor for packet source
schema.Sensor = Sensor
schema.Source = Source

// Let's reuse the same layers for performance improvement
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &dot1q, &ip4, &ip6, &tcp, &udp, &ldns)
var decoded []gopacket.LayerType

// Use the handle as a packet source to process all packets
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
packetSource.NoCopy = true
Expand All @@ -97,49 +88,72 @@ PACKETLOOP:
for packet := range packetSource.Packets() {
stats.PacketTotal += 1

if err := parser.DecodeLayers(packet.Data(), &decoded); err != nil {
log.Debugf("Could not decode layers: %v\n", err)
}

// Let's decode different layers
msg := new(dns.Msg)
for _, layerType := range decoded {
switch layerType {
// Let's analyze decoded layers
var msg *dns.Msg
for _, curLayer := range packet.Layers() {
switch curLayer.LayerType() {
case layers.LayerTypeIPv4:
stats.PacketIPv4 += 1
ip4 = curLayer.(*layers.IPv4)
schema.SourceAddress = ip4.SrcIP.String()
schema.DestinationAddress = ip4.DstIP.String()
schema.Ipv4 = true
stats.PacketIPv4 += 1
case layers.LayerTypeIPv6:
stats.PacketIPv6 += 1
ip6 = curLayer.(*layers.IPv6)
schema.SourceAddress = ip6.SrcIP.String()
schema.DestinationAddress = ip6.DstIP.String()
schema.Ipv4 = false
stats.PacketIPv6 += 1
case layers.LayerTypeTCP:
tcp = curLayer.(*layers.TCP)
stats.PacketTcp += 1
schema.SourcePort = uint16(tcp.SrcPort)
schema.DestinationPort = uint16(tcp.DstPort)
schema.Udp = false
schema.Sha256 = fmt.Sprintf("%x", sha256.Sum256(tcp.Payload))

if !DoParseTcp {
continue PACKETLOOP
}

msg = new(dns.Msg)
if err := msg.Unpack(tcp.Payload); err != nil {
log.Errorf("Could not decode DNS: %v\n", err)
stats.PacketErrors += 1
continue PACKETLOOP
}
stats.PacketDns += 1

schema.SourcePort = uint16(tcp.SrcPort)
schema.DestinationPort = uint16(tcp.DstPort)
schema.Udp = false
schema.Sha256 = fmt.Sprintf("%x", sha256.Sum256(tcp.Payload))
case layers.LayerTypeUDP:
udp = curLayer.(*layers.UDP)
stats.PacketUdp += 1
schema.SourcePort = uint16(udp.SrcPort)
schema.DestinationPort = uint16(udp.DstPort)
schema.Udp = true
schema.Sha256 = fmt.Sprintf("%x", sha256.Sum256(udp.Payload))

msg = new(dns.Msg)
if err := msg.Unpack(udp.Payload); err != nil {
log.Errorf("Could not decode DNS: %v\n", err)
stats.PacketErrors += 1
continue PACKETLOOP
}
stats.PacketDns += 1

schema.SourcePort = uint16(udp.SrcPort)
schema.DestinationPort = uint16(udp.DstPort)
schema.Udp = true
schema.Sha256 = fmt.Sprintf("%x", sha256.Sum256(udp.Payload))
}
}

// This means we did not attempt to parse a DNS payload
if msg == nil {
// Let's check if we had any errors decoding any of the packet layers
if err := packet.ErrorLayer(); err != nil {
log.Debugf("Error decoding some part of the packet:", err)
stats.PacketErrors += 1
}

continue PACKETLOOP
}

// Ignore questions unless flag set
if !msg.Response && !DoParseQuestions && !DoParseQuestionsEcs {
continue PACKETLOOP
Expand Down Expand Up @@ -184,9 +198,10 @@ PACKETLOOP:
schema.Qtype = qr.Qtype
}

// 1. Print all questions
// 2. Print only questions with ECS
// 3. Print NXDOMAINs without RRs (i.e., SOA)
// Let's get QUESTION information on if:
// 1. Questions flag is set
// 2. QuestionsEcs flag is set and ECS information in question
// 3. NXDOMAINs without RRs (i.e., SOA)
if (DoParseQuestions && !schema.Response) ||
(DoParseQuestionsEcs && schema.EcsClient != nil && !schema.Response) ||
(schema.Rcode == 3 && len(msg.Ns) < 1) {
Expand All @@ -207,17 +222,13 @@ PACKETLOOP:
for _, rr := range msg.Extra {
schema.ToJson(&rr, DnsAdditional)
}

// Let's check and see if we had any errors decoding any of the packets
if err := packet.ErrorLayer(); err != nil {
log.Debugf("Error decoding some part of the packet:", err)
}
}

log.Infof("Number of TOTAL packets: %v", stats.PacketTotal)
log.Infof("Number of IPv4 packets: %v", stats.PacketIPv4)
log.Infof("Number of IPv6 packets: %v", stats.PacketIPv6)
log.Infof("Number of UDP packets: %v", stats.PacketUdp)
log.Infof("Number of TCP packets: %v", stats.PacketTcp)
log.Infof("Number of DNS packets: %v", stats.PacketDns)
log.Infof("Number of FAILED packets: %v", stats.PacketErrors)
}
1 change: 1 addition & 0 deletions parser/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Statistics struct {
PacketIPv6 uint `json:"packetIPv6"`
PacketTcp uint `json:"packetTcp"`
PacketUdp uint `json:"packetUdp"`
PacketDns uint `json:"packetDns"`
PacketErrors uint `json:"packetErrors"`
}

Expand Down

0 comments on commit a407c5a

Please sign in to comment.