diff --git a/test/packetimpact/testbench/layers.go b/test/packetimpact/testbench/layers.go index 2644b3248..174ee07d1 100644 --- a/test/packetimpact/testbench/layers.go +++ b/test/packetimpact/testbench/layers.go @@ -215,23 +215,34 @@ func NetworkProtocolNumber(v tcpip.NetworkProtocolNumber) *tcpip.NetworkProtocol return &v } +// bodySizeHint describes num of bytes left to parse for the rest of layers. +type bodySizeHint int + +const bodySizeUnknown bodySizeHint = -1 + // layerParser parses the input bytes and returns a Layer along with the next // layerParser to run. If there is no more parsing to do, the returned // layerParser is nil. -type layerParser func([]byte) (Layer, layerParser) +type layerParser func([]byte) (Layer, bodySizeHint, layerParser) // parse parses bytes starting with the first layerParser and using successive // layerParsers until all the bytes are parsed. func parse(parser layerParser, b []byte) Layers { var layers Layers for { - var layer Layer - layer, parser = parser(b) + layer, hint, next := parser(b) layers = append(layers, layer) if parser == nil { break } b = b[layer.length():] + if hint != bodySizeUnknown { + b = b[:hint] + } + if next == nil { + break + } + parser = next } layers.linkLayers() return layers @@ -239,7 +250,7 @@ func parse(parser layerParser, b []byte) Layers { // parseEther parses the bytes assuming that they start with an ethernet header // and continues parsing further encapsulations. -func parseEther(b []byte) (Layer, layerParser) { +func parseEther(b []byte) (Layer, bodySizeHint, layerParser) { h := header.Ethernet(b) ether := Ether{ SrcAddr: LinkAddress(h.SourceAddress()), @@ -256,7 +267,7 @@ func parseEther(b []byte) (Layer, layerParser) { // Assume that the rest is a payload. nextParser = parsePayload } - return ðer, nextParser + return ðer, bodySizeUnknown, nextParser } func (l *Ether) match(other Layer) bool { @@ -421,7 +432,7 @@ func Address(v tcpip.Address) *tcpip.Address { // parseIPv4 parses the bytes assuming that they start with an ipv4 header and // continues parsing further encapsulations. -func parseIPv4(b []byte) (Layer, layerParser) { +func parseIPv4(b []byte) (Layer, bodySizeHint, layerParser) { h := header.IPv4(b) options := h.Options() tos, _ := h.TOS() @@ -442,7 +453,7 @@ func parseIPv4(b []byte) (Layer, layerParser) { var nextParser layerParser // If it is a fragment, don't treat it as having a transport protocol. if h.FragmentOffset() != 0 || h.More() { - return &ipv4, parsePayload + return &ipv4, bodySizeHint(h.PayloadLength()), parsePayload } switch h.TransportProtocol() { case header.TCPProtocolNumber: @@ -455,7 +466,7 @@ func parseIPv4(b []byte) (Layer, layerParser) { // Assume that the rest is a payload. nextParser = parsePayload } - return &ipv4, nextParser + return &ipv4, bodySizeHint(h.PayloadLength()), nextParser } func (l *IPv4) match(other Layer) bool { @@ -555,7 +566,7 @@ func nextIPv6PayloadParser(nextHeader uint8) layerParser { // parseIPv6 parses the bytes assuming that they start with an ipv6 header and // continues parsing further encapsulations. -func parseIPv6(b []byte) (Layer, layerParser) { +func parseIPv6(b []byte) (Layer, bodySizeHint, layerParser) { h := header.IPv6(b) tos, flowLabel := h.TOS() ipv6 := IPv6{ @@ -568,7 +579,7 @@ func parseIPv6(b []byte) (Layer, layerParser) { DstAddr: Address(h.DestinationAddress()), } nextParser := nextIPv6PayloadParser(h.NextHeader()) - return &ipv6, nextParser + return &ipv6, bodySizeHint(h.PayloadLength()), nextParser } func (l *IPv6) match(other Layer) bool { @@ -727,16 +738,16 @@ func parseIPv6ExtHdr(b []byte) (header.IPv6ExtensionHeaderIdentifier, []byte, la // parseIPv6HopByHopOptionsExtHdr parses the bytes assuming that they start // with an IPv6 HopByHop Options Extension Header. -func parseIPv6HopByHopOptionsExtHdr(b []byte) (Layer, layerParser) { +func parseIPv6HopByHopOptionsExtHdr(b []byte) (Layer, bodySizeHint, layerParser) { nextHeader, options, nextParser := parseIPv6ExtHdr(b) - return &IPv6HopByHopOptionsExtHdr{NextHeader: &nextHeader, Options: options}, nextParser + return &IPv6HopByHopOptionsExtHdr{NextHeader: &nextHeader, Options: options}, bodySizeUnknown, nextParser } // parseIPv6DestinationOptionsExtHdr parses the bytes assuming that they start // with an IPv6 Destination Options Extension Header. -func parseIPv6DestinationOptionsExtHdr(b []byte) (Layer, layerParser) { +func parseIPv6DestinationOptionsExtHdr(b []byte) (Layer, bodySizeHint, layerParser) { nextHeader, options, nextParser := parseIPv6ExtHdr(b) - return &IPv6DestinationOptionsExtHdr{NextHeader: &nextHeader, Options: options}, nextParser + return &IPv6DestinationOptionsExtHdr{NextHeader: &nextHeader, Options: options}, bodySizeUnknown, nextParser } // Bool is a helper routine that allocates a new @@ -747,7 +758,7 @@ func Bool(v bool) *bool { // parseIPv6FragmentExtHdr parses the bytes assuming that they start // with an IPv6 Fragment Extension Header. -func parseIPv6FragmentExtHdr(b []byte) (Layer, layerParser) { +func parseIPv6FragmentExtHdr(b []byte) (Layer, bodySizeHint, layerParser) { nextHeader := b[0] var extHdr header.IPv6FragmentExtHdr copy(extHdr[:], b[2:]) @@ -759,9 +770,9 @@ func parseIPv6FragmentExtHdr(b []byte) (Layer, layerParser) { } // If it is a fragment, we can't interpret it. if extHdr.FragmentOffset() != 0 || extHdr.More() { - return &fragLayer, parsePayload + return &fragLayer, bodySizeUnknown, parsePayload } - return &fragLayer, nextIPv6PayloadParser(nextHeader) + return &fragLayer, bodySizeUnknown, nextIPv6PayloadParser(nextHeader) } func (l *IPv6HopByHopOptionsExtHdr) length() int { @@ -894,7 +905,7 @@ func ICMPv6Code(v header.ICMPv6Code) *header.ICMPv6Code { } // parseICMPv6 parses the bytes assuming that they start with an ICMPv6 header. -func parseICMPv6(b []byte) (Layer, layerParser) { +func parseICMPv6(b []byte) (Layer, bodySizeHint, layerParser) { h := header.ICMPv6(b) msgType := h.Type() icmpv6 := ICMPv6{ @@ -909,7 +920,7 @@ func parseICMPv6(b []byte) (Layer, layerParser) { case header.ICMPv6ParamProblem: icmpv6.Pointer = Uint32(h.TypeSpecific()) } - return &icmpv6, nil + return &icmpv6, bodySizeUnknown, nil } func (l *ICMPv6) match(other Layer) bool { @@ -995,7 +1006,7 @@ func (l *ICMPv4) ToBytes() ([]byte, error) { // parseICMPv4 parses the bytes as an ICMPv4 header, returning a Layer and a // parser for the encapsulated payload. -func parseICMPv4(b []byte) (Layer, layerParser) { +func parseICMPv4(b []byte) (Layer, bodySizeHint, layerParser) { h := header.ICMPv4(b) msgType := h.Type() @@ -1012,7 +1023,7 @@ func parseICMPv4(b []byte) (Layer, layerParser) { case header.ICMPv4ParamProblem: icmpv4.Pointer = Uint8(h.Pointer()) } - return &icmpv4, nil + return &icmpv4, bodySizeUnknown, nil } func (l *ICMPv4) match(other Layer) bool { @@ -1156,7 +1167,7 @@ func Uint32(v uint32) *uint32 { // parseTCP parses the bytes assuming that they start with a tcp header and // continues parsing further encapsulations. -func parseTCP(b []byte) (Layer, layerParser) { +func parseTCP(b []byte) (Layer, bodySizeHint, layerParser) { h := header.TCP(b) tcp := TCP{ SrcPort: Uint16(h.SourcePort()), @@ -1170,7 +1181,7 @@ func parseTCP(b []byte) (Layer, layerParser) { UrgentPointer: Uint16(h.UrgentPointer()), Options: b[header.TCPMinimumSize:h.DataOffset()], } - return &tcp, parsePayload + return &tcp, bodySizeUnknown, parsePayload } func (l *TCP) match(other Layer) bool { @@ -1245,7 +1256,7 @@ func setUDPChecksum(h *header.UDP, udp *UDP) error { // parseUDP parses the bytes assuming that they start with a udp header and // returns the parsed layer and the next parser to use. -func parseUDP(b []byte) (Layer, layerParser) { +func parseUDP(b []byte) (Layer, bodySizeHint, layerParser) { h := header.UDP(b) udp := UDP{ SrcPort: Uint16(h.SourcePort()), @@ -1253,7 +1264,7 @@ func parseUDP(b []byte) (Layer, layerParser) { Length: Uint16(h.Length()), Checksum: Uint16(h.Checksum()), } - return &udp, parsePayload + return &udp, bodySizeUnknown, parsePayload } func (l *UDP) match(other Layer) bool { @@ -1281,11 +1292,11 @@ func (l *Payload) String() string { // parsePayload parses the bytes assuming that they start with a payload and // continue to the end. There can be no further encapsulations. -func parsePayload(b []byte) (Layer, layerParser) { +func parsePayload(b []byte) (Layer, bodySizeHint, layerParser) { payload := Payload{ Bytes: b, } - return &payload, nil + return &payload, bodySizeUnknown, nil } // ToBytes implements Layer.ToBytes. diff --git a/test/packetimpact/testbench/layers_test.go b/test/packetimpact/testbench/layers_test.go index bc96e0c88..c3c56e293 100644 --- a/test/packetimpact/testbench/layers_test.go +++ b/test/packetimpact/testbench/layers_test.go @@ -16,6 +16,7 @@ package testbench import ( "bytes" + "encoding/hex" "net" "testing" @@ -726,3 +727,52 @@ func TestIPv6ExtHdrOptions(t *testing.T) { }) } } + +func TestEthernetPadding(t *testing.T) { + packet := []byte{ + 0x3a, 0xd6, 0x90, 0x36, 0x18, 0xce, 0x64, 0x4f, 0x16, 0x3f, + 0x5f, 0x0f, 0x08, 0x00, 0x45, 0x00, 0x00, 0x2c, 0xf5, 0x0e, + 0x00, 0x00, 0x40, 0x06, 0x2d, 0xba, 0xac, 0x00, 0x00, 0x02, + 0xac, 0x00, 0x00, 0x01, 0x7c, 0x3e, 0xe3, 0x91, 0x2b, 0xe4, + 0xb0, 0xe7, 0x9a, 0xcb, 0x04, 0x43, 0x60, 0x12, 0x72, 0x00, + 0xf2, 0x67, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x00, 0x00, + } + parsed := parse(parseEther, packet) + wanted := Layers{ + &Ether{ + SrcAddr: LinkAddress(tcpip.LinkAddress("\x64\x4f\x16\x3f\x5f\x0f")), + DstAddr: LinkAddress(tcpip.LinkAddress("\x3a\xd6\x90\x36\x18\xce")), + Type: NetworkProtocolNumber(header.IPv4ProtocolNumber), + }, + &IPv4{ + IHL: Uint8(20), + TOS: Uint8(0), + TotalLength: Uint16(44), + ID: Uint16(0xf50e), + Flags: Uint8(0), + FragmentOffset: Uint16(0), + TTL: Uint8(64), + Protocol: Uint8(uint8(header.TCPProtocolNumber)), + Checksum: Uint16(0x2dba), + SrcAddr: Address(tcpip.Address("\xac\x00\x00\x02")), + DstAddr: Address(tcpip.Address("\xac\x00\x00\x01")), + }, + &TCP{ + SrcPort: Uint16(31806), + DstPort: Uint16(58257), + SeqNum: Uint32(736407783), + AckNum: Uint32(2596996163), + DataOffset: Uint8(24), + Flags: TCPFlags(header.TCPFlagSyn | header.TCPFlagAck), + WindowSize: Uint16(29184), + Checksum: Uint16(0xf267), + UrgentPointer: Uint16(0), + }, + &Payload{ + Bytes: []byte{}, + }, + } + if !parsed.match(wanted) { + t.Fatalf("parse(parseEther, %s) = %s, want %s)", hex.Dump(packet), parsed, wanted) + } +}