Skip to content
Erlang Makefile
Latest commit 9d7c37f May 19, 2016 @msantos 802.1q: store next header's type
To process the frame as a stream of bytes, put the 802.1q TPID into the
ethernet header and store the next header type as part of the 802.1q
protocol header.

The 802.1q header is inserted into the original frame between the source
MAC address and the ethernet type. Multiple 802.1q headers may be
inserted into the frame:

    original frame:
        |dst MAC|src MAC|ethernet type|

    with 802.1q header:
        ethernet header type = 802.1q

        |dst MAC|src MAC|16#8100 (802.1q TPID)|Prio, CFI, VLAN ID|ethernet type|

    with 802.1qinq header:
        ethernet header type = 802.1q QinQ

        |dst MAC|src MAC
        |16#88a8 (802.1q QinQ TPID)|Prio, CFI, VLAN ID
        |16#8100 (802.1q TPID)|Prio, CFI, VLAN ID
        |ethernet type|

Thanks to Alexander Shavelev (@alexshavelev) for reporting!
Failed to load latest commit information.
include 802.1q: store next header's type May 19, 2016
src
test 802.1q: store next header's type May 19, 2016
.gitignore Change rebar2 to rebar3 Mar 18, 2016
LICENSE Add LICENSE Apr 15, 2016
Makefile Remove rebar3 binary Mar 18, 2016
README.md fix readme May 13, 2016
rebar.config Change rebar2 to rebar3 Mar 18, 2016
rebar.lock Change rebar2 to rebar3 Mar 18, 2016

README.md

An Erlang network protocol library.

Originally part of epcap: http://github.com/msantos/epcap

EXPORTS

pkt:decapsulate(Data) -> Packet
pkt:decapsulate(Proto, Data) -> Packet

    Types   Data = binary()
            Proto = atom() | integer()
            Packet = [ Header | Payload ]
            Header = #ether{} | #arp{} | #null{} | #linux_cooked{} |
                #ipv4{} | #ipv6{} | #tcp{} | #udp{} | #sctp{} | #icmp{} |
                #icmp6{} | #igmp{} | #gre{} | #llc{} | #vrrp{} | #'802.1q'{}
            Payload = binary()

    Convert network protocols from binary data to a list of Erlang
    records followed by the payload.

    decapsulate/1,2 works on valid packets. If the packet is malformed
    or unsupported, decapsulate/1 will crash.

    decapsulate/1 parses the data as an ethernet frame.

    decapsulate/2 allows specifying the protocol for decoding the
    packet. If the protocol is specified as an integer, the integer
    is treated as a datalink type.

pkt:decode(Data) -> {ok, Packet} | {error, SoFar, {FailedProto, binary()}}
pkt:decode(Proto, Data) -> {ok, Packet} | {error, SoFar, {FailedProto, binary()}}

    Types   Data = binary()
            Proto = FailedProto = atom()
            Packet = {Headers, Payload}
            Headers = [Header]
            Header = #ether{} | #arp{} | #null{} | #linux_cooked{} |
                #ipv4{} | #ipv6{} | #tcp{} | #udp{} | #sctp{} | #icmp{} |
                #icmp6{} | #igmp{} | #gre{} | #llc{} | #vrrp{} | #'802.1q'{}
            SoFar = Headers | []
            Payload = binary()

    Similar to decapsulate/1 but, on error, returns any part of the
    packet that has been successfully converted to Erlang term format.

The following functions create the protocol headers, converting between records and binaries. See include/pkt.hrl for definition of the record types.

ether(Packet) -> {#ether{}, Payload} | binary()
llc(Packet) -> {#llc{}, Payload} | binary()
vrrp(Packet) -> {#vrrp{}, Payload} | binary()
'802.1q'(Packet) -> {#'802.1q'{}, Payload} | binary()
null(Packet) -> {#null{}, Payload} | binary()
linux_cooked(Packet) -> {#linux_cooked{}, Payload} | binary()
gre(Packet) -> {#gre{}, Payload} | binary()
arp(Packet) -> {#arp{}, Payload} | binary()
ipv4(Packet) -> {#ipv4{}, Payload} | binary()
ipv6(Packet) -> {#ipv6{}, Payload} | binary()
tcp(Packet) -> {#tcp{}, Payload} | binary()
sctp(Packet) -> {#sctp{}, Payload} | binary()
udp(Packet) -> {#udp{}, Payload} | binary()
icmp(Packet) -> {#icmp{}, Payload} | binary()
icmp6(Packet) -> {#icmp6{}, Payload} | binary()
igmp(Packet) -> {#igmp{}, Payload} | binary()

    Types   Packet = Header | binary()
            Header = #ether{} | #null{} | #linux_cooked{} | #arp{} |
                #ipv4{} | #ipv6{} | #tcp{} | #sctp{} | #udp{} |
                #icmp{} | #icmp6{} | #igmp{} | #gre{} | #llc{} |
                #vrrp{} | #'802.1q'{}


makesum(Packet) -> integer()

    Types   Packet = IPv4_header | [IPv4_header, Header, Payload]
            IPv4_header = #ipv4{}
            Header = #tcp{} | #udp{}
            Payload = binary()

    Calculate the one's complement checksum of the packet.

    When computing the checksum, the header sum field must be set
    to 0:

        Sum = pkt:makesum([IPv4, TCP#tcp{sum = 0}, Payload]).

    For verifcation, the checksum can be compared to the value in
    the header or:

        0 = pkt:makesum([IPv4, TCP, Payload]).

EXAMPLES

  • decode an ethernet frame, displaying the source and destination of valid packets
Frame = <<224,105,149,59,163,24,0,22,182,181,62,198,8,0,69,0,0,54,2,108,64,
          0,53,6,172,243,173,192,82,195,192,168,213,54,0,80,143,166,75,154,
          212,181,116,33,53,92,128,24,0,126,60,199,0,0,1,1,8,10,92,104,96,
          16,22,69,237,136,137,0>>,

try pkt:decapsulate(Frame) of
    [#ether{}, #ipv4{saddr = Saddr, daddr = Daddr},
        #tcp{sport = Sport, dport = Dport}, _Payload] ->
        {{Saddr, Sport}, {Daddr, Dport}}
catch
    error:_ ->
        ok % ignore invalid packets
end
  • verify the TCP checksum of an ethernet frame
{ok, [#ether{}, IPv4, #tcp{sum = Sum} = TCP, Payload]} = pkt:decode(ether, Frame),

% Re-calculate the checksum and match against the checksum in the header
Sum = pkt:makesum([IPv4, TCP#tcp{sum = 0}, Payload]),

% Or just verify the checksum
0 = pkt:makesum([IPv4, TCP, Payload]).

PADDING OF ETHERNET FRAMES

The minimum size of an ethernet payload is 46 bytes. An ethernet frame containing a TCP/IP packet composed of a 20 byte IPv4 header and 20 byte TCP header and payload will be padded by 6 bytes. To calculate the actual length of the packet, use the length and header length values in the IPv4 header and the offset value in the TCP header:

[#ether{}, #ipv4{len = Len, hl = HL}, #tcp{off = Off}, Payload] = pkt:decapsulate(Frame),
    Size = Len - (HL * 4) - (Off * 4),
    <<Payload:Size/bytes>>.

TODO

  • support RFC 2675 (IPv6 Jumbograms)

  • IPv6 AH and ESP

    • handle alignment differences between IPv4 and IPv6 (IPv4 uses 32 bits, IPv6 uses 64 bits)
  • ICMPv6

    • fix handling of neighbour discovery
    • simplify ICMPv6 header record and add a record for ICMPv6 type or add functions for ICMPv6 variable length payloads
  • IGMP

    • support IGMPv3 variable payloads
  • merge in DLT_IEEE802_11 support from wierl

  • merge in ICMPv6 code from gen_icmp

  • DLTs

    • DLT_SLIP
    • DLT_PPP
    • DLT_RAW
    • DLT_PPP_SERIAL
    • DLT_PPP_ETHER
    • DLT_LOOP
Something went wrong with that request. Please try again.