Teach netstack how to add options to IPv4 packets

Most packets don't have options but they are an integral part of the
standard. Teaching the ipv4 code how to handle them will simplify future
testing and use.  Because Options are so rare it is worth making sure
that the extra work is kept out of the fast path as much as possible.

Prior to this change, all usages of the IHL field of the IPv4Fields/Encode
system set it to the same constant value except in a couple of tests
for bad values. From this change IHL will not be a constant as it will
depend on the size of any Options. Since ipv4.Encode() now handles the
options it becomes a possible source of errors to let the callers set
this value, so remove it entirely and calculate the value from the size
of the Options if present (or not) therefore guaranteeing a correct value.

Fixes #4709
RELNOTES: n/a
PiperOrigin-RevId: 341864765
This commit is contained in:
Julian Elischer
2020-11-11 10:59:35 -08:00
committed by gVisor bot
parent 792cbc06de
commit 9c4102896d
14 changed files with 182 additions and 135 deletions
+2 -2
View File
@@ -197,7 +197,7 @@ func IPPayload(payload []byte) NetworkChecker {
}
// IPv4Options returns a checker that checks the options in an IPv4 packet.
func IPv4Options(want []byte) NetworkChecker {
func IPv4Options(want header.IPv4Options) NetworkChecker {
return func(t *testing.T, h []header.Network) {
t.Helper()
@@ -205,7 +205,7 @@ func IPv4Options(want []byte) NetworkChecker {
if !ok {
t.Fatalf("unexpected network header passed to checker, got = %T, want = header.IPv4", h[0])
}
options := []byte(ip.Options())
options := ip.Options()
// cmp.Diff does not consider nil slices equal to empty slices, but we do.
if len(want) == 0 && len(options) == 0 {
return
+36 -9
View File
@@ -56,12 +56,9 @@ const (
)
// IPv4Fields contains the fields of an IPv4 packet. It is used to describe the
// fields of a packet that needs to be encoded.
// fields of a packet that needs to be encoded. The IHL field is not here as
// it is totally defined by the size of the options.
type IPv4Fields struct {
// IHL is the "internet header length" field of an IPv4 packet. The value
// is in bytes.
IHL uint8
// TOS is the "type of service" field of an IPv4 packet.
TOS uint8
@@ -91,6 +88,9 @@ type IPv4Fields struct {
// DstAddr is the "destination ip address" of an IPv4 packet.
DstAddr tcpip.Address
// Options is between 0 and 40 bytes or nil if empty.
Options IPv4Options
}
// IPv4 is an IPv4 header.
@@ -118,7 +118,7 @@ const (
// Linux limits this to 65,515 octets (the max IP datagram size - the IPv4
// header size). But RFC 791 section 3.2 discusses the design of the IPv4
// fragment "allows 2**13 = 8192 fragments of 8 octets each for a total of
// 65,536 octets. Note that this is consistent with the the datagram total
// 65,536 octets. Note that this is consistent with the datagram total
// length field (of course, the header is counted in the total length and not
// in the fragments)."
IPv4MaximumPayloadSize = 65536
@@ -275,10 +275,22 @@ func (b IPv4) DestinationAddress() tcpip.Address {
// IPv4Options is a buffer that holds all the raw IP options.
type IPv4Options []byte
// Options returns a buffer holding the options.
// AllocationSize implements stack.NetOptions.
// It reports the size to allocate for the Options. RFC 791 page 23 (end of
// section 3.1) says of the padding at the end of the options:
// The internet header padding is used to ensure that the internet
// header ends on a 32 bit boundary.
func (o IPv4Options) AllocationSize() int {
return (len(o) + IPv4IHLStride - 1) & ^(IPv4IHLStride - 1)
}
// Options returns a buffer holding the options or nil.
func (b IPv4) Options() IPv4Options {
hdrLen := b.HeaderLength()
return IPv4Options(b[options:hdrLen:hdrLen])
if hdrLen > IPv4MinimumSize {
return IPv4Options(b[options:hdrLen:hdrLen])
}
return nil
}
// TransportProtocol implements Network.TransportProtocol.
@@ -351,7 +363,22 @@ func (b IPv4) CalculateChecksum() uint16 {
// Encode encodes all the fields of the IPv4 header.
func (b IPv4) Encode(i *IPv4Fields) {
b.SetHeaderLength(i.IHL)
// The size of the options defines the size of the whole header and thus the
// IHL field. Options are rare and this is a heavily used function so it is
// worth a bit of optimisation here to keep the copy out of the fast path.
hdrLen := IPv4MinimumSize
if len(i.Options) != 0 {
// AllocationSize is always >= len(i.Options).
aLen := i.Options.AllocationSize()
hdrLen += aLen
if hdrLen > len(b) {
panic(fmt.Sprintf("encode received %d bytes, wanted >= %d", len(b), hdrLen))
}
if aLen != copy(b[options:], i.Options) {
_ = copy(b[options+len(i.Options):options+aLen], []byte{0, 0, 0, 0})
}
}
b.SetHeaderLength(uint8(hdrLen))
b[tos] = i.TOS
b.SetTotalLength(i.TotalLength)
binary.BigEndian.PutUint16(b[id:], i.ID)
+1 -1
View File
@@ -22,7 +22,7 @@ import (
func TestIPv4(t *testing.T) {
b := header.IPv4(make([]byte, header.IPv4MinimumSize))
b.Encode(&header.IPv4Fields{IHL: header.IPv4MinimumSize})
b.Encode(&header.IPv4Fields{})
const want = header.IPv4Version
if v := header.IPVersion(b); v != want {
+7 -22
View File
@@ -320,7 +320,6 @@ func TestSourceAddressValidation(t *testing.T) {
pkt.SetChecksum(^header.Checksum(pkt, 0))
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(totalLen),
Protocol: uint8(icmp.ProtocolNumber4),
TTL: ipv4.DefaultTTL,
@@ -579,7 +578,6 @@ func TestIPv4Receive(t *testing.T) {
view := buffer.NewView(totalLen)
ip := header.IPv4(view)
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(totalLen),
TTL: 20,
Protocol: 10,
@@ -662,7 +660,6 @@ func TestIPv4ReceiveControl(t *testing.T) {
// Create the outer IPv4 header.
ip := header.IPv4(view)
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(len(view) - c.trunc),
TTL: 20,
Protocol: uint8(header.ICMPv4ProtocolNumber),
@@ -681,7 +678,6 @@ func TestIPv4ReceiveControl(t *testing.T) {
// Create the inner IPv4 header.
ip = header.IPv4(view[header.IPv4MinimumSize+header.ICMPv4MinimumSize:])
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: 100,
TTL: 20,
Protocol: 10,
@@ -740,7 +736,6 @@ func TestIPv4FragmentationReceive(t *testing.T) {
frag1 := buffer.NewView(totalLen)
ip1 := header.IPv4(frag1)
ip1.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(totalLen),
TTL: 20,
Protocol: 10,
@@ -759,7 +754,6 @@ func TestIPv4FragmentationReceive(t *testing.T) {
frag2 := buffer.NewView(totalLen)
ip2 := header.IPv4(frag2)
ip2.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(totalLen),
TTL: 20,
Protocol: 10,
@@ -1052,15 +1046,13 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
nicID = 1
transportProto = 5
dataLen = 4
optionsLen = 4
dataLen = 4
)
dataBuf := [dataLen]byte{1, 2, 3, 4}
data := dataBuf[:]
ipv4OptionsBuf := [optionsLen]byte{0, 1, 0, 1}
ipv4Options := ipv4OptionsBuf[:]
ipv4Options := header.IPv4Options{0, 1, 0, 1}
ipv6FragmentExtHdrBuf := [header.IPv6FragmentExtHdrLength]byte{transportProto, 0, 62, 4, 1, 2, 3, 4}
ipv6FragmentExtHdr := ipv6FragmentExtHdrBuf[:]
@@ -1098,7 +1090,6 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
}
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
@@ -1140,12 +1131,12 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
}
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize - 1,
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
})
ip.SetHeaderLength(header.IPv4MinimumSize - 1)
return hdr.View().ToVectorisedView()
},
expectedErr: tcpip.ErrMalformedHeader,
@@ -1159,7 +1150,6 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
pktGen: func(t *testing.T, src tcpip.Address) buffer.VectorisedView {
ip := header.IPv4(make([]byte, header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
@@ -1178,7 +1168,6 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
pktGen: func(t *testing.T, src tcpip.Address) buffer.VectorisedView {
ip := header.IPv4(make([]byte, header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
@@ -1213,7 +1202,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
nicAddr: localIPv4Addr,
remoteAddr: remoteIPv4Addr,
pktGen: func(t *testing.T, src tcpip.Address) buffer.VectorisedView {
ipHdrLen := header.IPv4MinimumSize + len(ipv4Options)
ipHdrLen := header.IPv4MinimumSize + ipv4Options.AllocationSize()
totalLen := ipHdrLen + len(data)
hdr := buffer.NewPrependable(totalLen)
if n := copy(hdr.Prepend(len(data)), data); n != len(data) {
@@ -1221,15 +1210,12 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
}
ip := header.IPv4(hdr.Prepend(ipHdrLen))
ip.Encode(&header.IPv4Fields{
IHL: uint8(ipHdrLen),
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
Options: ipv4Options,
})
if n := copy(ip.Options(), ipv4Options); n != len(ipv4Options) {
t.Fatalf("copied %d bytes, expected %d bytes", n, len(ipv4Options))
}
return hdr.View().ToVectorisedView()
},
checker: func(t *testing.T, pkt *stack.PacketBuffer, src tcpip.Address) {
@@ -1261,16 +1247,15 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
nicAddr: localIPv4Addr,
remoteAddr: remoteIPv4Addr,
pktGen: func(t *testing.T, src tcpip.Address) buffer.VectorisedView {
ip := header.IPv4(make([]byte, header.IPv4MinimumSize))
ip := header.IPv4(make([]byte, header.IPv4MinimumSize+ipv4Options.AllocationSize()))
ip.Encode(&header.IPv4Fields{
IHL: uint8(header.IPv4MinimumSize + len(ipv4Options)),
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
DstAddr: header.IPv4Any,
Options: ipv4Options,
})
vv := buffer.View(ip).ToVectorisedView()
vv.AppendView(ipv4Options)
vv.AppendView(data)
return vv
},
+17 -2
View File
@@ -199,14 +199,28 @@ func (e *endpoint) NetworkProtocolNumber() tcpip.NetworkProtocolNumber {
}
func (e *endpoint) addIPHeader(r *stack.Route, pkt *stack.PacketBuffer, params stack.NetworkHeaderParams) {
ip := header.IPv4(pkt.NetworkHeader().Push(header.IPv4MinimumSize))
hdrLen := header.IPv4MinimumSize
var opts header.IPv4Options
if params.Options != nil {
var ok bool
if opts, ok = params.Options.(header.IPv4Options); !ok {
panic(fmt.Sprintf("want IPv4Options, got %T", params.Options))
}
hdrLen += opts.AllocationSize()
if hdrLen > header.IPv4MaximumHeaderSize {
// Since we have no way to report an error we must either panic or create
// a packet which is different to what was requested. Choose panic as this
// would be a programming error that should be caught in testing.
panic(fmt.Sprintf("IPv4 Options %d bytes, Max %d", params.Options.AllocationSize(), header.IPv4MaximumOptionsSize))
}
}
ip := header.IPv4(pkt.NetworkHeader().Push(hdrLen))
length := uint16(pkt.Size())
// RFC 6864 section 4.3 mandates uniqueness of ID values for non-atomic
// datagrams. Since the DF bit is never being set here, all datagrams
// are non-atomic and need an ID.
id := atomic.AddUint32(&e.protocol.ids[hashRoute(r, params.Protocol, e.protocol.hashIV)%buckets], 1)
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: length,
ID: uint16(id),
TTL: params.TTL,
@@ -214,6 +228,7 @@ func (e *endpoint) addIPHeader(r *stack.Route, pkt *stack.PacketBuffer, params s
Protocol: uint8(params.Protocol),
SrcAddr: r.LocalAddress,
DstAddr: r.RemoteAddress,
Options: opts,
})
ip.SetChecksum(^ip.CalculateChecksum())
pkt.NetworkProtocolNumber = ProtocolNumber
File diff suppressed because it is too large Load Diff
+16 -3
View File
@@ -138,7 +138,7 @@ type PacketEndpoint interface {
HandlePacket(nicID tcpip.NICID, addr tcpip.LinkAddress, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer)
}
// UnknownDestinationPacketDisposition enumerates the possible return vaues from
// UnknownDestinationPacketDisposition enumerates the possible return values from
// HandleUnknownDestinationPacket().
type UnknownDestinationPacketDisposition int
@@ -263,6 +263,15 @@ const (
PacketLoop
)
// NetOptions is an interface that allows us to pass network protocol specific
// options through the Stack layer code.
type NetOptions interface {
// AllocationSize returns the amount of memory that must be allocated to
// hold the options given that the value must be rounded up to the next
// multiple of 4 bytes.
AllocationSize() int
}
// NetworkHeaderParams are the header parameters given as input by the
// transport endpoint to the network.
type NetworkHeaderParams struct {
@@ -274,6 +283,10 @@ type NetworkHeaderParams struct {
// TOS refers to TypeOfService or TrafficClass field of the IP-header.
TOS uint8
// Options is a set of options to add to a network header (or nil).
// It will be protocol specific opaque information from higher layers.
Options NetOptions
}
// GroupAddressableEndpoint is an endpoint that supports group addressing.
@@ -281,7 +294,7 @@ type NetworkHeaderParams struct {
// An endpoint is considered to support group addressing when one or more
// endpoints may associate themselves with the same identifier (group address).
type GroupAddressableEndpoint interface {
// JoinGroup joins the spcified group.
// JoinGroup joins the specified group.
//
// Returns true if the group was newly joined.
JoinGroup(group tcpip.Address) (bool, *tcpip.Error)
@@ -378,7 +391,7 @@ type AddressEndpoint interface {
SetDeprecated(bool)
}
// AddressKind is the kind of of an address.
// AddressKind is the kind of an address.
//
// See the values of AddressKind for more details.
type AddressKind int
@@ -102,7 +102,6 @@ func (c *testContext) sendV4Packet(payload []byte, h *headers, linkEpID tcpip.NI
// Initialize the IP header.
ip := header.IPv4(buf)
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TOS: 0x80,
TotalLength: uint16(len(buf)),
TTL: 65,
@@ -73,7 +73,6 @@ func TestPingMulticastBroadcast(t *testing.T) {
pkt.SetChecksum(^header.Checksum(pkt, 0))
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(totalLen),
Protocol: uint8(icmp.ProtocolNumber4),
TTL: ttl,
@@ -244,7 +243,6 @@ func TestIncomingMulticastAndBroadcast(t *testing.T) {
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(totalLen),
Protocol: uint8(udp.ProtocolNumber),
TTL: ttl,
@@ -395,7 +395,6 @@ func (c *Context) SendICMPPacket(typ header.ICMPv4Type, code header.ICMPv4Code,
ip := header.IPv4(buf)
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(len(buf)),
TTL: 65,
Protocol: uint8(header.ICMPv4ProtocolNumber),
@@ -437,7 +436,6 @@ func (c *Context) BuildSegmentWithAddrs(payload []byte, h *Headers, src, dst tcp
// Initialize the IP header.
ip := header.IPv4(buf)
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TotalLength: uint16(len(buf)),
TTL: 65,
Protocol: uint8(tcp.ProtocolNumber),
-1
View File
@@ -490,7 +490,6 @@ func (c *testContext) buildV4Packet(payload []byte, h *header4Tuple) buffer.View
// Initialize the IP header.
ip := header.IPv4(buf)
ip.Encode(&header.IPv4Fields{
IHL: header.IPv4MinimumSize,
TOS: testTOS,
TotalLength: uint16(len(buf)),
TTL: 65,
+10 -11
View File
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package testbench has utilities to send and receive packets and also command
// the DUT to run POSIX functions.
package testbench
import (
@@ -74,7 +72,7 @@ func pickPort(domain, typ int) (fd int, port uint16, err error) {
}
sa, err = unix.Getsockname(fd)
if err != nil {
return -1, 0, fmt.Errorf("Getsocketname(%d): %w", fd, err)
return -1, 0, fmt.Errorf("fail in Getsocketname(%d): %w", fd, err)
}
port, err = portFromSockaddr(sa)
if err != nil {
@@ -102,9 +100,9 @@ type layerState interface {
// as it was sent is available.
sent(sent Layer) error
// received updates the layerState based on a Layer that is receieved. The
// received updates the layerState based on a Layer that is received. The
// input is a Layer with all prev and next pointers populated so that the
// entire frame as it was receieved is available.
// entire frame as it was received is available.
received(received Layer) error
// close frees associated resources held by the LayerState.
@@ -475,12 +473,12 @@ func (conn *Connection) Close(t *testing.T) {
}
}
// CreateFrame builds a frame for the connection with defaults overriden
// CreateFrame builds a frame for the connection with defaults overridden
// from the innermost layer out, and additionalLayers added after it.
//
// Note that overrideLayers can have a length that is less than the number
// of layers in this connection, and in such cases the innermost layers are
// overriden first. As an example, valid values of overrideLayers for a TCP-
// overridden first. As an example, valid values of overrideLayers for a TCP-
// over-IPv4-over-Ethernet connection are: nil, [TCP], [IPv4, TCP], and
// [Ethernet, IPv4, TCP].
func (conn *Connection) CreateFrame(t *testing.T, overrideLayers Layers, additionalLayers ...Layer) Layers {
@@ -711,7 +709,7 @@ func (conn *TCPIPv4) ConnectWithOptions(t *testing.T, options []byte) {
}
// ExpectData is a convenient method that expects a Layer and the Layer after
// it. If it doens't arrive in time, it returns nil.
// it. If it doesn't arrive in time, it returns nil.
func (conn *TCPIPv4) ExpectData(t *testing.T, tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) {
t.Helper()
@@ -1046,7 +1044,7 @@ func (conn *UDPIPv4) Expect(t *testing.T, udp UDP, timeout time.Duration) (*UDP,
}
// ExpectData is a convenient method that expects a Layer and the Layer after
// it. If it doens't arrive in time, it returns nil.
// it. If it doesn't arrive in time, it returns nil.
func (conn *UDPIPv4) ExpectData(t *testing.T, udp UDP, payload Payload, timeout time.Duration) (Layers, error) {
t.Helper()
@@ -1174,7 +1172,7 @@ func (conn *UDPIPv6) Expect(t *testing.T, udp UDP, timeout time.Duration) (*UDP,
}
// ExpectData is a convenient method that expects a Layer and the Layer after
// it. If it doens't arrive in time, it returns nil.
// it. If it doesn't arrive in time, it returns nil.
func (conn *UDPIPv6) ExpectData(t *testing.T, udp UDP, payload Payload, timeout time.Duration) (Layers, error) {
t.Helper()
@@ -1234,13 +1232,14 @@ func NewTCPIPv6(t *testing.T, outgoingTCP, incomingTCP TCP) TCPIPv6 {
}
}
// SrcPort returns the source port from the given Connection.
func (conn *TCPIPv6) SrcPort() uint16 {
state := conn.layerStates[2].(*tcpState)
return *state.out.SrcPort
}
// ExpectData is a convenient method that expects a Layer and the Layer after
// it. If it doens't arrive in time, it returns nil.
// it. If it doesn't arrive in time, it returns nil.
func (conn *TCPIPv6) ExpectData(t *testing.T, tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) {
t.Helper()
+35 -4
View File
@@ -286,6 +286,7 @@ type IPv4 struct {
Checksum *uint16
SrcAddr *tcpip.Address
DstAddr *tcpip.Address
Options *header.IPv4Options
}
func (l *IPv4) String() string {
@@ -294,10 +295,22 @@ func (l *IPv4) String() string {
// ToBytes implements Layer.ToBytes.
func (l *IPv4) ToBytes() ([]byte, error) {
b := make([]byte, header.IPv4MinimumSize)
// An IPv4 header is variable length depending on the size of the Options.
hdrLen := header.IPv4MinimumSize
if l.Options != nil {
hdrLen += l.Options.AllocationSize()
if hdrLen > header.IPv4MaximumHeaderSize {
// While ToBytes can be called on packets that were received as well
// as packets locally generated, it is physically impossible for a
// received packet to overflow this value so any such failure must
// be the result of a local programming error and not remotely
// triggered. A panic is therefore appropriate.
panic(fmt.Sprintf("IPv4 Options %d bytes, Max %d", len(*l.Options), header.IPv4MaximumOptionsSize))
}
}
b := make([]byte, hdrLen)
h := header.IPv4(b)
fields := &header.IPv4Fields{
IHL: 20,
TOS: 0,
TotalLength: 0,
ID: 0,
@@ -308,6 +321,11 @@ func (l *IPv4) ToBytes() ([]byte, error) {
Checksum: 0,
SrcAddr: tcpip.Address(""),
DstAddr: tcpip.Address(""),
Options: nil,
}
// Leave an empty options slice as nil.
if hdrLen > header.IPv4MinimumSize {
fields.Options = *l.Options
}
if l.TOS != nil {
fields.TOS = *l.TOS
@@ -362,6 +380,11 @@ func (l *IPv4) ToBytes() ([]byte, error) {
if l.Checksum == nil {
h.SetChecksum(^h.CalculateChecksum())
}
// Encode cannot set this incorrectly so we need to overwrite what it wrote
// in order to test handling of a bad IHL value.
if l.IHL != nil {
h.SetHeaderLength(*l.IHL)
}
return h, nil
}
@@ -377,8 +400,8 @@ func Uint8(v uint8) *uint8 {
return &v
}
// Address is a helper routine that allocates a new tcpip.Address value to store
// v and returns a pointer to it.
// Address is a helper routine that allocates a new tcpip.Address value to
// store v and returns a pointer to it.
func Address(v tcpip.Address) *tcpip.Address {
return &v
}
@@ -387,6 +410,13 @@ func Address(v tcpip.Address) *tcpip.Address {
// continues parsing further encapsulations.
func parseIPv4(b []byte) (Layer, layerParser) {
h := header.IPv4(b)
hdrLen := h.HeaderLength()
// Even if there are no options, we set an empty options field instead of nil
// so that the decision to compare is up to the caller of that comparison.
var options header.IPv4Options
if hdrLen > header.IPv4MinimumSize {
options = append(options, h.Options()...)
}
tos, _ := h.TOS()
ipv4 := IPv4{
IHL: Uint8(h.HeaderLength()),
@@ -400,6 +430,7 @@ func parseIPv4(b []byte) (Layer, layerParser) {
Checksum: Uint16(h.Checksum()),
SrcAddr: Address(h.SourceAddress()),
DstAddr: Address(h.DestinationAddress()),
Options: &options,
}
var nextParser layerParser
// If it is a fragment, don't treat it as having a transport protocol.
+2 -1
View File
@@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package testbench is the packetimpact test API.
// Package testbench has utilities to send and receive packets, and also command
// the DUT to run POSIX functions. It is the packetimpact test API.
package testbench
import (