package pcap

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/lacework/agent/datacollector/dlog"
)

type encapType uint32

const (
	NoEncap encapType = iota
	IP_IN_IP
	VXLAN
	GRE
)

const MAX_HEADERS uint32 = 4

type PacketTime struct {
	Sec  int32
	Usec int32
}

type Packet struct {

	Time    time.Time 
	Caplen  uint32    
	Len     uint32    
	Partial uint32    
	Seq     uint32    

	Data []byte 

	Type     int 
	LinkType int 
	DestMac  uint64
	SrcMac   uint64

	Headers     [MAX_HEADERS]interface{} 
	Headers_cnt int
	EncapType   encapType
	Payload     []byte   
	VxLanPorts  []uint16 

	data    []byte
	iphdr   *Iphdr
	ip6hdr  *Ip6hdr
	tcphdr  *Tcphdr
	udphdr  *Udphdr
	icmphdr *Icmphdr
}

func (p *Packet) setHeader(header interface{}) error {

	if p.Headers_cnt >= len(p.Headers) {
		return fmt.Errorf("Too many headers. max = %v", MAX_HEADERS)
	}
	p.Headers[p.Headers_cnt] = header
	p.Headers_cnt++

	return nil
}

func (p *Packet) resetHeaders() error {
	p.Headers_cnt = 0
	return nil
}

func (p *Packet) IsRaw() bool {
	if p.LinkType == LINKTYPE_RAW {
		return true
	}
	return false
}

func (p *Packet) Decode() {

	if p.Payload == nil {
		p.Payload = p.Data
	}
	if p.IsRaw() == false {

		p.decodeEther(true )
	} else {
		p.Type = TYPE_IP
		if p.Payload[0]&0xf0 == 6 {
			p.Type = TYPE_IP6
		}
		p.Payload = p.Payload[0:]
		p.decodeL3()
	}
}

func (p *Packet) verifyEther() (int, []byte, bool) {
	tData := p.Payload
	tData_len := len(tData)
	if tData_len < 14 {
		return 0, nil, false
	}
	eType := int(binary.BigEndian.Uint16(tData[12:14]))

	l3start := tData[14:]
	switch eType {
	case 0x8100:

		if tData_len < 18 {
			return 0, nil, false
		}
		eType = int(binary.BigEndian.Uint16(tData[16:18]))
		l3start = tData[18:]
	case 0x88A8:

		if tData_len < 22 {
			return 0, nil, false
		}
		eType = int(binary.BigEndian.Uint16(tData[20:22]))
		l3start = tData[22:]
	}
	return eType, l3start, true
}

func (p *Packet) decodeEther(outerMac bool) {
	tData := p.Payload
	tData_len := len(tData)
	if tData_len < 14 {
		return
	}
	p.Type = int(binary.BigEndian.Uint16(tData[12:14]))
	if outerMac {
		p.DestMac = decodemac(tData[0:6])
		p.SrcMac = decodemac(tData[6:12])
	}
	p.Payload = tData[14:]
	switch p.Type {
	case 0x8100:

		if tData_len < 18 {
			return
		}
		p.Type = int(binary.BigEndian.Uint16(tData[16:18]))
		p.Payload = tData[18:]
	case 0x88A8:

		if tData_len < 22 {
			return
		}
		p.Type = int(binary.BigEndian.Uint16(tData[20:22]))
		p.Payload = tData[22:]
	}

	p.decodeL3()
	return
}

func (p *Packet) decodeL3() {

	p.resetHeaders()

	switch p.Type {
	case TYPE_IP:
		p.decodeIp(0)
	case TYPE_IP6:
		p.decodeIp6(0)
	case TYPE_ARP:

	}
}

func (p *Packet) headerString(headers []interface{}) string {

	if len(headers) == 1 {
		if hdr, ok := headers[0].(fmt.Stringer); ok {
			return hdr.String()
		}
	}

	if len(headers) == 2 {

		if addr, ok := p.Headers[0].(addrHdr); ok {
			if hdr, ok := p.Headers[1].(addrStringer); ok {
				return fmt.Sprintf("%s %s", p.Time, hdr.String(addr))
			}
		}
	}

	if len(headers) >= 2 {
		if addr, ok := headers[0].(addrHdr); ok {
			if _, ok := headers[1].(addrHdr); ok {
				return fmt.Sprintf("%s > %s IP in IP: %v",
					addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:]))
			}
		}
	}

	var typeNames []string
	for _, hdr := range headers {
		typeNames = append(typeNames, reflect.TypeOf(hdr).String())
	}

	return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ","))
}

func (p *Packet) String() string {

	if p.Headers_cnt == 0 {
		return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type))
	}
	return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers[0:p.Headers_cnt]))
}

func (p *Packet) decodeArp() {
	pkt := p.Payload
	arp := new(Arphdr)
	arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2])
	arp.Protocol = binary.BigEndian.Uint16(pkt[2:4])
	arp.HwAddressSize = pkt[4]
	arp.ProtAddressSize = pkt[5]
	arp.Operation = binary.BigEndian.Uint16(pkt[6:8])
	if arp.HwAddressSize != 0 && len(pkt) >= int(8+2*arp.HwAddressSize+2*arp.ProtAddressSize) {
		arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize]
		arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
		arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
		arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]

		p.setHeader(arp)

		p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:]
	} else {
		dlog.Infof("s: %d L:%d(H:%d P:%d) -> T:%x D:%x S:%x A:%x P:%x %x", p.Seq, len(p.Payload), arp.HwAddressSize, arp.ProtAddressSize, p.Type, p.DestMac, p.SrcMac, arp.Addrtype, arp.Protocol, bytes.Split(p.Data, []byte(",")))
	}
}

func IsIp4HdrCksumOk(iphdr []byte) bool {

	acc := uint32(0)
	for i := 0; i < 10; i++ {
		doubleByte := binary.BigEndian.Uint16(iphdr[2*i : 2*i+2])
		acc += uint32(doubleByte)
	}

	carry := (acc & 0xffff0000) >> 16
	cksum := acc & 0xffff
	cksum += carry
	return cksum == 0xffff
}

func (p *Packet) verifyIp4(l3start []byte) bool {
	iphdr := l3start
	if len(iphdr) < 20 {
		return false
	}
	if (uint8(iphdr[0])>>4) != 4 ||
		!IsIp4HdrCksumOk(iphdr) {
		return false
	}
	return true
}

func (p *Packet) decodeIp(recur int) {
	if len(p.Payload) < 20 {
		return
	}
	if recur > 3 {
		dlog.Infof("more than %v level of IPnIP encapsulation %+v", recur, p)
		return
	}
	pkt := p.Payload
	if p.iphdr == nil {
		p.iphdr = new(Iphdr)
	}
	if recur > 0 {
		p.EncapType = IP_IN_IP

		p.resetHeaders()
	}
	ip := p.iphdr

	ip.Version = uint8(pkt[0]) >> 4
	ip.Ihl = uint8(pkt[0]) & 0x0F
	ip.Tos = pkt[1]
	ip.Length = binary.BigEndian.Uint16(pkt[2:4])
	ip.Id = binary.BigEndian.Uint16(pkt[4:6])
	flagsfrags := binary.BigEndian.Uint16(pkt[6:8])
	ip.Flags = uint8(flagsfrags >> 13)
	ip.FragOffset = flagsfrags & 0x1FFF
	ip.Ttl = pkt[8]
	ip.Protocol = pkt[9]
	ip.Checksum = binary.BigEndian.Uint16(pkt[10:12])
	ip.SrcIp = pkt[12:16]
	ip.DestIp = pkt[16:20]
	pEnd := int(ip.Length)
	if pEnd > len(pkt) {
		pEnd = len(pkt)
	}
	pIhl := int(ip.Ihl) * 4
	if pIhl > pEnd {
		pIhl = pEnd
	}
	p.Payload = pkt[pIhl:pEnd]
	p.setHeader(ip)
	switch ip.Protocol {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp(recur + 1)
	case IP_GRE:
		p.decodeGre()
	}
}

func (p *Packet) decodeTcp() {
	pLenPayload := len(p.Payload)
	if pLenPayload < 20 {
		return
	}
	pkt := p.Payload
	if p.tcphdr == nil {
		p.tcphdr = new(Tcphdr)
	}
	tcp := p.tcphdr

	tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	tcp.Seq = binary.BigEndian.Uint32(pkt[4:8])
	tcp.Ack = binary.BigEndian.Uint32(pkt[8:12])
	tcp.DataOffset = (pkt[12] & 0xF0) >> 4
	tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF
	tcp.Window = binary.BigEndian.Uint16(pkt[14:16])
	tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18])
	tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20])
	pDataOffset := int(tcp.DataOffset * 4)
	if pDataOffset > pLenPayload {
		pDataOffset = pLenPayload
	}
	p.Payload = pkt[pDataOffset:]
	p.setHeader(tcp)
}

func (p *Packet) decodeUdp() {
	if len(p.Payload) < 8 {
		return
	}
	pkt := p.Payload
	if p.udphdr == nil {
		p.udphdr = new(Udphdr)
	}
	udp := p.udphdr
	udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	udp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	udp.Length = binary.BigEndian.Uint16(pkt[4:6])
	udp.Checksum = binary.BigEndian.Uint16(pkt[6:8])
	p.setHeader(udp)
	p.Payload = pkt[8:]

	for i := range p.VxLanPorts {
		if udp.DestPort == p.VxLanPorts[i] && p.decodeVxLAN() {
			break
		}
	}
}

func (p *Packet) decodeIcmp() *Icmphdr {
	if len(p.Payload) < 8 {
		return nil
	}
	pkt := p.Payload
	if p.icmphdr == nil {
		p.icmphdr = new(Icmphdr)
	}
	icmp := p.icmphdr
	icmp.Type = pkt[0]
	icmp.Code = pkt[1]
	icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4])
	icmp.Id = binary.BigEndian.Uint16(pkt[4:6])
	icmp.Seq = binary.BigEndian.Uint16(pkt[6:8])
	p.Payload = pkt[8:]
	p.setHeader(icmp)

	return icmp
}

func (p *Packet) verifyIp6(l3start []byte) bool {
	ip6hdr := l3start
	if len(ip6hdr) < 40 {
		return false
	}
	if (uint8(ip6hdr[0]) >> 4) != 6 {
		return false
	}
	return true
}

func (p *Packet) decodeIp6(recur int) {
	if len(p.Payload) < 40 {
		return
	}
	if recur > 3 {
		dlog.Infof("more than %v level of IP6nIP encapsulation %+v", recur, p)
		return
	}
	pkt := p.Payload
	if p.ip6hdr == nil {
		p.ip6hdr = new(Ip6hdr)
	}
	if recur > 0 {
		p.EncapType = IP_IN_IP
		p.resetHeaders()
	}
	ip6 := p.ip6hdr

	ip6.Version = uint8(pkt[0]) >> 4
	ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF)
	ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF
	ip6.Length = binary.BigEndian.Uint16(pkt[4:6])
	ip6.NextHeader = pkt[6]
	ip6.HopLimit = pkt[7]
	ip6.SrcIp = pkt[8:24]
	ip6.DestIp = pkt[24:40]
	p.Payload = pkt[40:]
	p.setHeader(ip6)

	switch ip6.NextHeader {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp6(recur + 1)
	case IP_GRE:
		p.decodeGre()
	}
}
func (p *Packet) decodeVxLAN() bool {

	if len(p.Payload) < 8 {
		return false
	}
	pkt := p.Payload

	if uint8(pkt[0])&0x08 == 0 {
		return false
	}

	p.Payload = p.Payload[8:]

	if len(p.Payload) < 14+20 {
		p.Payload = pkt
		return false
	}

	ethType, l3start, ok := p.verifyEther()
	if !ok {
		p.Payload = pkt
		return false
	}
	if ethType == TYPE_IP && p.verifyIp4(l3start) ||
		ethType == TYPE_IP6 && p.verifyIp6(l3start) {
		p.decodeEther(false )
		p.EncapType = VXLAN
		return true
	}
	p.Payload = pkt
	return false
}

func (p *Packet) decodeGre() {

	if len(p.Payload) < 4 {
		return
	}

	pkt := p.Payload

	greHdrLen := 4
	if uint8(pkt[0])&0x80 == 1 {

		greHdrLen += 4
	}
	if uint8(pkt[0])&0x20 == 1 {

		greHdrLen += 4
	}
	if uint8(pkt[0])&0x10 == 1 {

		greHdrLen += 4
	}

	if uint8(pkt[1])&0x07 != 0 {
		return
	}

	if len(p.Payload) < greHdrLen {
		return
	}

	p.Type = int(binary.BigEndian.Uint16(pkt[2:4]))
	p.Payload = p.Payload[greHdrLen:]
	p.EncapType = GRE
	p.decodeL3()
}
package pcap

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/lacework/agent/datacollector/dlog"
)

type encapType uint32

const (
	NoEncap encapType = iota
	IP_IN_IP
	VXLAN
	GRE
)

const MAX_HEADERS uint32 = 4

// const VxLAN_PORT1 uint16 = 4789
// const VxLAN_PORT2 uint16 = 8472

type PacketTime struct {
	Sec  int32
	Usec int32
}

// Packet is a single packet parsed from a pcap file.
type Packet struct {
	// porting from 'pcap_pkthdr' struct
	Time    time.Time // packet send/receive time
	Caplen  uint32    // bytes stored in the file (caplen <= len)
	Len     uint32    // bytes sent/received
	Partial uint32    // partial bytes clipped
	Seq     uint32    // pkt capture sequence number

	Data []byte // packet data

	Type     int // protocol type, see LINKTYPE_*
	LinkType int // [enhnacement] handle LINKTYPE_ETHERNET or LINKTYPE_RAW
	DestMac  uint64
	SrcMac   uint64

	Headers     [MAX_HEADERS]interface{} // decoded headers, in order
	Headers_cnt int
	EncapType   encapType
	Payload     []byte   // remaining non-header bytes; init-ed to Data[] itself
	VxLanPorts  []uint16 // list of udp ports which may indicate payload as vxlan

	// Pre-allocated memory to reduce allocation overhead
	data    []byte
	iphdr   *Iphdr
	ip6hdr  *Ip6hdr
	tcphdr  *Tcphdr
	udphdr  *Udphdr
	icmphdr *Icmphdr
}

func (p *Packet) setHeader(header interface{}) error {

	if p.Headers_cnt >= len(p.Headers) {
		return fmt.Errorf("Too many headers. max = %v", MAX_HEADERS)
	}
	p.Headers[p.Headers_cnt] = header
	p.Headers_cnt++

	return nil
}

// For encapsulated packets, we are interested in
// inner headers only. We can drop outer headers.
func (p *Packet) resetHeaders() error {
	p.Headers_cnt = 0
	return nil
}

// only handle LINKTYPE_ETHER AND LINKTYPE_RAW (IPv4 and IPv6)
func (p *Packet) IsRaw() bool {
	if p.LinkType == LINKTYPE_RAW {
		return true
	}
	return false
}

// Decode decodes the headers of a Packet.
func (p *Packet) Decode() {

	// Our go tests do not initialize p.Payload.
	if p.Payload == nil {
		p.Payload = p.Data
	}
	if p.IsRaw() == false {
		// we want to set mac addresses of the Packet p using outermost
		// ethernet frame. These are used to determine directionality of the
		// captured packet.
		p.decodeEther(true /* outerMac */)
	} else {
		p.Type = TYPE_IP
		if p.Payload[0]&0xf0 == 6 {
			p.Type = TYPE_IP6
		}
		p.Payload = p.Payload[0:]
		p.decodeL3()
	}
}

// returns false if no valid ethernet header is found; true otherwise.
func (p *Packet) verifyEther() (int, []byte, bool) {
	tData := p.Payload
	tData_len := len(tData)
	if tData_len < 14 {
		return 0, nil, false
	}
	eType := int(binary.BigEndian.Uint16(tData[12:14]))

	// we can add mac address verification here.
	// However, there is no good way to identify one
	// except the length.

	l3start := tData[14:]
	switch eType {
	case 0x8100:
		//VLAN tag skip 802.1Q tag
		if tData_len < 18 {
			return 0, nil, false
		}
		eType = int(binary.BigEndian.Uint16(tData[16:18]))
		l3start = tData[18:]
	case 0x88A8:
		//VLAN tag skip 802.1ad double tag
		if tData_len < 22 {
			return 0, nil, false
		}
		eType = int(binary.BigEndian.Uint16(tData[20:22]))
		l3start = tData[22:]
	}
	return eType, l3start, true
}

// returns false if no valid ethernet header is found; true otherwise.
// Currently, we need return value for vxland decoding.
func (p *Packet) decodeEther(outerMac bool) {
	tData := p.Payload
	tData_len := len(tData)
	if tData_len < 14 {
		return
	}
	p.Type = int(binary.BigEndian.Uint16(tData[12:14]))
	if outerMac {
		p.DestMac = decodemac(tData[0:6])
		p.SrcMac = decodemac(tData[6:12])
	}
	p.Payload = tData[14:]
	switch p.Type {
	case 0x8100:
		//VLAN tag skip 802.1Q tag
		if tData_len < 18 {
			return
		}
		p.Type = int(binary.BigEndian.Uint16(tData[16:18]))
		p.Payload = tData[18:]
	case 0x88A8:
		//VLAN tag skip 802.1ad double tag
		if tData_len < 22 {
			return
		}
		p.Type = int(binary.BigEndian.Uint16(tData[20:22]))
		p.Payload = tData[22:]
	}

	p.decodeL3()
	return
}

// this function assumes p.Type is set to one of three supported L2.5/L3
// protocol types i.e. IP, IP6 or ARP. For other protocols, we don't do
// anything.
func (p *Packet) decodeL3() {

	// We are starting to decode a new set of L3/L2.5 headers. Good place to
	// drop previous such headers, if any.
	p.resetHeaders()

	switch p.Type {
	case TYPE_IP:
		p.decodeIp(0)
	case TYPE_IP6:
		p.decodeIp6(0)
	case TYPE_ARP:
		//p.decodeArp()
	}
}

func (p *Packet) headerString(headers []interface{}) string {
	// If there's just one header, return that.
	if len(headers) == 1 {
		if hdr, ok := headers[0].(fmt.Stringer); ok {
			return hdr.String()
		}
	}
	// If there are two headers (IPv4/IPv6 -> TCP/UDP/IP..)
	if len(headers) == 2 {
		// Commonly the first header is an address.
		if addr, ok := p.Headers[0].(addrHdr); ok {
			if hdr, ok := p.Headers[1].(addrStringer); ok {
				return fmt.Sprintf("%s %s", p.Time, hdr.String(addr))
			}
		}
	}
	// For IP in IP, we do a recursive call.
	if len(headers) >= 2 {
		if addr, ok := headers[0].(addrHdr); ok {
			if _, ok := headers[1].(addrHdr); ok {
				return fmt.Sprintf("%s > %s IP in IP: %v",
					addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:]))
			}
		}
	}

	var typeNames []string
	for _, hdr := range headers {
		typeNames = append(typeNames, reflect.TypeOf(hdr).String())
	}

	return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ","))
}

// String prints a one-line representation of the packet header.
// The output is suitable for use in a tcpdump program.
func (p *Packet) String() string {
	// If there are no headers, print "unsupported protocol".
	if p.Headers_cnt == 0 {
		return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type))
	}
	return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers[0:p.Headers_cnt]))
}

func (p *Packet) decodeArp() {
	pkt := p.Payload
	arp := new(Arphdr)
	arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2])
	arp.Protocol = binary.BigEndian.Uint16(pkt[2:4])
	arp.HwAddressSize = pkt[4]
	arp.ProtAddressSize = pkt[5]
	arp.Operation = binary.BigEndian.Uint16(pkt[6:8])
	if arp.HwAddressSize != 0 && len(pkt) >= int(8+2*arp.HwAddressSize+2*arp.ProtAddressSize) {
		arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize]
		arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
		arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
		arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]

		p.setHeader(arp)

		p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:]
	} else {
		dlog.Infof("s: %d L:%d(H:%d P:%d) -> T:%x D:%x S:%x A:%x P:%x %x", p.Seq, len(p.Payload), arp.HwAddressSize, arp.ProtAddressSize, p.Type, p.DestMac, p.SrcMac, arp.Addrtype, arp.Protocol, bytes.Split(p.Data, []byte(",")))
	}
}

func IsIp4HdrCksumOk(iphdr []byte) bool {
	// Caller makes sure that iphdr has at least 20 bytes.
	acc := uint32(0)
	for i := 0; i < 10; i++ {
		doubleByte := binary.BigEndian.Uint16(iphdr[2*i : 2*i+2])
		acc += uint32(doubleByte)
	}

	carry := (acc & 0xffff0000) >> 16
	cksum := acc & 0xffff
	cksum += carry
	return cksum == 0xffff
}

func (p *Packet) verifyIp4(l3start []byte) bool {
	iphdr := l3start
	if len(iphdr) < 20 {
		return false
	}
	if (uint8(iphdr[0])>>4) != 4 ||
		!IsIp4HdrCksumOk(iphdr) {
		return false
	}
	return true
}

func (p *Packet) decodeIp(recur int) {
	if len(p.Payload) < 20 {
		return
	}
	if recur > 3 {
		dlog.Infof("more than %v level of IPnIP encapsulation %+v", recur, p)
		return
	}
	pkt := p.Payload
	if p.iphdr == nil {
		p.iphdr = new(Iphdr)
	}
	if recur > 0 {
		p.EncapType = IP_IN_IP
		// Drop previous L3 headers
		p.resetHeaders()
	}
	ip := p.iphdr

	ip.Version = uint8(pkt[0]) >> 4
	ip.Ihl = uint8(pkt[0]) & 0x0F
	ip.Tos = pkt[1]
	ip.Length = binary.BigEndian.Uint16(pkt[2:4])
	ip.Id = binary.BigEndian.Uint16(pkt[4:6])
	flagsfrags := binary.BigEndian.Uint16(pkt[6:8])
	ip.Flags = uint8(flagsfrags >> 13)
	ip.FragOffset = flagsfrags & 0x1FFF
	ip.Ttl = pkt[8]
	ip.Protocol = pkt[9]
	ip.Checksum = binary.BigEndian.Uint16(pkt[10:12])
	ip.SrcIp = pkt[12:16]
	ip.DestIp = pkt[16:20]
	pEnd := int(ip.Length)
	if pEnd > len(pkt) {
		pEnd = len(pkt)
	}
	pIhl := int(ip.Ihl) * 4
	if pIhl > pEnd {
		pIhl = pEnd
	}
	p.Payload = pkt[pIhl:pEnd]
	p.setHeader(ip)
	switch ip.Protocol {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp(recur + 1)
	case IP_GRE:
		p.decodeGre()
	}
}

func (p *Packet) decodeTcp() {
	pLenPayload := len(p.Payload)
	if pLenPayload < 20 {
		return
	}
	pkt := p.Payload
	if p.tcphdr == nil {
		p.tcphdr = new(Tcphdr)
	}
	tcp := p.tcphdr

	tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	tcp.Seq = binary.BigEndian.Uint32(pkt[4:8])
	tcp.Ack = binary.BigEndian.Uint32(pkt[8:12])
	tcp.DataOffset = (pkt[12] & 0xF0) >> 4
	tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF
	tcp.Window = binary.BigEndian.Uint16(pkt[14:16])
	tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18])
	tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20])
	pDataOffset := int(tcp.DataOffset * 4)
	if pDataOffset > pLenPayload {
		pDataOffset = pLenPayload
	}
	p.Payload = pkt[pDataOffset:]
	p.setHeader(tcp)
}

func (p *Packet) decodeUdp() {
	if len(p.Payload) < 8 {
		return
	}
	pkt := p.Payload
	if p.udphdr == nil {
		p.udphdr = new(Udphdr)
	}
	udp := p.udphdr
	udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	udp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	udp.Length = binary.BigEndian.Uint16(pkt[4:6])
	udp.Checksum = binary.BigEndian.Uint16(pkt[6:8])
	p.setHeader(udp)
	p.Payload = pkt[8:]

	// potential vxlan packet. vxlan header is 8 bytes.
	// typical ports are IANA assigned 4789 or linux default 8472.
	for i := range p.VxLanPorts {
		if udp.DestPort == p.VxLanPorts[i] && p.decodeVxLAN() {
			break
		}
	}
}

func (p *Packet) decodeIcmp() *Icmphdr {
	if len(p.Payload) < 8 {
		return nil
	}
	pkt := p.Payload
	if p.icmphdr == nil {
		p.icmphdr = new(Icmphdr)
	}
	icmp := p.icmphdr
	icmp.Type = pkt[0]
	icmp.Code = pkt[1]
	icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4])
	icmp.Id = binary.BigEndian.Uint16(pkt[4:6])
	icmp.Seq = binary.BigEndian.Uint16(pkt[6:8])
	p.Payload = pkt[8:]
	p.setHeader(icmp)

	return icmp
}

func (p *Packet) verifyIp6(l3start []byte) bool {
	ip6hdr := l3start
	if len(ip6hdr) < 40 {
		return false
	}
	if (uint8(ip6hdr[0]) >> 4) != 6 {
		return false
	}
	return true
}

func (p *Packet) decodeIp6(recur int) {
	if len(p.Payload) < 40 {
		return
	}
	if recur > 3 {
		dlog.Infof("more than %v level of IP6nIP encapsulation %+v", recur, p)
		return
	}
	pkt := p.Payload
	if p.ip6hdr == nil {
		p.ip6hdr = new(Ip6hdr)
	}
	if recur > 0 {
		p.EncapType = IP_IN_IP
		p.resetHeaders()
	}
	ip6 := p.ip6hdr

	ip6.Version = uint8(pkt[0]) >> 4
	ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF)
	ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF
	ip6.Length = binary.BigEndian.Uint16(pkt[4:6])
	ip6.NextHeader = pkt[6]
	ip6.HopLimit = pkt[7]
	ip6.SrcIp = pkt[8:24]
	ip6.DestIp = pkt[24:40]
	p.Payload = pkt[40:]
	p.setHeader(ip6)

	switch ip6.NextHeader {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp6(recur + 1)
	case IP_GRE:
		p.decodeGre()
	}
}
func (p *Packet) decodeVxLAN() bool {
	// VxLAN decode is a heuristic and not an exact algorithm.

	// VXLAN Header is 8 bytes:
	//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |R|R|R|R|I|R|R|R|            Reserved                           |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |                VXLAN Network Identifier (VNI) |   Reserved    |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	if len(p.Payload) < 8 {
		return false
	}
	pkt := p.Payload

	// if 'I' bit is not set, there is no valid VNI.
	// Which means it is very less likely to be a vxlan
	// packet. We can safely return.
	if uint8(pkt[0])&0x08 == 0 {
		return false
	}

	// Seems like a possible vxlan payload.  We are not interested in vxlan
	// header itself.  We want to know if inner ethernet frame and inner
	// payloads are valid. Skip vxlan header.
	p.Payload = p.Payload[8:]

	// Any other heuristics to detect a valid vxlan packet can be added below.

	// A vxlan will always have a ethernet frame of size 14 bytes.  We are
	// interested in vxlan payload, only of it has IP header too along with
	// ethernet frame. ipv4 and ipv6 headers are minimum 20 and 40 bytes
	// respectively.
	if len(p.Payload) < 14+20 {
		p.Payload = pkt
		return false
	}

	// Set EncapType to vxlan. Since vxlan decoding is not fully deterministic,
	// we should set EncapType to vxlan, once we are sure a valid ethernet
	// frame is present.

	ethType, l3start, ok := p.verifyEther()
	if !ok {
		p.Payload = pkt
		return false
	}
	if ethType == TYPE_IP && p.verifyIp4(l3start) ||
		ethType == TYPE_IP6 && p.verifyIp6(l3start) {
		p.decodeEther(false /* outerMac */)
		p.EncapType = VXLAN
		return true
	}
	p.Payload = pkt
	return false
}

func (p *Packet) decodeGre() {
	// We are following rfc 2784 and rfc 2890

	//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |C| |K|S| Reserved0       | Ver |         Protocol Type         |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |      Checksum (optional)      |       Reserved1 (Optional)    |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |                         Key (optional)                        |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |                 Sequence Number (Optional)                    |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

	if len(p.Payload) < 4 {
		return
	}

	pkt := p.Payload
	// minimum size is 4 bytes
	greHdrLen := 4
	if uint8(pkt[0])&0x80 == 1 {
		// Checksum and Reserved1 are present
		greHdrLen += 4
	}
	if uint8(pkt[0])&0x20 == 1 {
		// Key is present
		greHdrLen += 4
	}
	if uint8(pkt[0])&0x10 == 1 {
		// Sequence Number is present
		greHdrLen += 4
	}

	// We may skip this check, if we want. But rfc 2784 says bits 13-15 MUST to
	// be zero.
	if uint8(pkt[1])&0x07 != 0 {
		return
	}

	if len(p.Payload) < greHdrLen {
		return
	}

	p.Type = int(binary.BigEndian.Uint16(pkt[2:4]))
	p.Payload = p.Payload[greHdrLen:]
	p.EncapType = GRE
	p.decodeL3()
}
