package inet import ( "encoding/binary" "fmt" "pcap-web-tools/internal/pcap" ) const ( etherTypeIPv4 = 0x0800 etherTypeIPv6 = 0x86dd etherTypeVLAN = 0x8100 etherTypeQinQ = 0x88a8 ) // FindIP returns the offset of the IP header within a link-layer frame of the // given pcap link type, by parsing the link-layer header. func FindIP(frame []byte, linkType uint32) (int, error) { switch linkType { case pcap.LinkTypeRaw: return 0, nil case pcap.LinkTypeEthernet: off := 12 for { if off+2 > len(frame) { return 0, fmt.Errorf("Ethernet header truncated") } et := binary.BigEndian.Uint16(frame[off:]) if et == etherTypeVLAN || et == etherTypeQinQ { off += 4 continue } return etherIP(et, off+2) } case pcap.LinkTypeLinuxSLL: if len(frame) < 16 { return 0, fmt.Errorf("Linux cooked (SLL) header truncated") } return etherIP(binary.BigEndian.Uint16(frame[14:]), 16) case pcap.LinkTypeLinuxSLL2: if len(frame) < 20 { return 0, fmt.Errorf("Linux cooked v2 (SLL2) header truncated") } return etherIP(binary.BigEndian.Uint16(frame[0:]), 20) } return 0, fmt.Errorf("cannot locate the IP header for link type %s", pcap.LinkTypeName(linkType)) } func etherIP(etherType uint16, off int) (int, error) { if etherType != etherTypeIPv4 && etherType != etherTypeIPv6 { return 0, fmt.Errorf("link-layer payload is not IP (EtherType 0x%04x)", etherType) } return off, nil }