diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/tcpip/checker/checker.go | 4 | ||||
-rw-r--r-- | pkg/tcpip/header/ipv4.go | 45 | ||||
-rw-r--r-- | pkg/tcpip/header/ipversion_test.go | 2 | ||||
-rw-r--r-- | pkg/tcpip/network/ip_test.go | 29 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv4/ipv4.go | 19 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv4/ipv4_test.go | 130 | ||||
-rw-r--r-- | pkg/tcpip/stack/registration.go | 19 | ||||
-rw-r--r-- | pkg/tcpip/stack/transport_demuxer_test.go | 1 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/multicast_broadcast_test.go | 2 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/testing/context/context.go | 2 | ||||
-rw-r--r-- | pkg/tcpip/transport/udp/udp_test.go | 1 |
11 files changed, 135 insertions, 119 deletions
diff --git a/pkg/tcpip/checker/checker.go b/pkg/tcpip/checker/checker.go index 530f2ae2f..8868cf4e3 100644 --- a/pkg/tcpip/checker/checker.go +++ b/pkg/tcpip/checker/checker.go @@ -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 diff --git a/pkg/tcpip/header/ipv4.go b/pkg/tcpip/header/ipv4.go index 961b77628..7e32b31b4 100644 --- a/pkg/tcpip/header/ipv4.go +++ b/pkg/tcpip/header/ipv4.go @@ -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) diff --git a/pkg/tcpip/header/ipversion_test.go b/pkg/tcpip/header/ipversion_test.go index 17a49d4fa..b5540bf66 100644 --- a/pkg/tcpip/header/ipversion_test.go +++ b/pkg/tcpip/header/ipversion_test.go @@ -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 { diff --git a/pkg/tcpip/network/ip_test.go b/pkg/tcpip/network/ip_test.go index 8873bd91f..c7d26e14f 100644 --- a/pkg/tcpip/network/ip_test.go +++ b/pkg/tcpip/network/ip_test.go @@ -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 }, diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index cfd0c505a..a376cb8ec 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -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 diff --git a/pkg/tcpip/network/ipv4/ipv4_test.go b/pkg/tcpip/network/ipv4/ipv4_test.go index c7f434591..c6e565455 100644 --- a/pkg/tcpip/network/ipv4/ipv4_test.go +++ b/pkg/tcpip/network/ipv4/ipv4_test.go @@ -127,8 +127,8 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength uint16 transportProtocol uint8 TTL uint8 - options []byte - replyOptions []byte // if succeeds, reply should look like this + options header.IPv4Options + replyOptions header.IPv4Options // reply should look like this shouldFail bool expectErrorICMP bool ICMPType header.ICMPv4Type @@ -177,24 +177,24 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{0, 0, 0, 0}, - replyOptions: []byte{0, 0, 0, 0}, + options: header.IPv4Options{0, 0, 0, 0}, + replyOptions: header.IPv4Options{0, 0, 0, 0}, }, { name: "NOP options", maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{1, 1, 1, 1}, - replyOptions: []byte{1, 1, 1, 1}, + options: header.IPv4Options{1, 1, 1, 1}, + replyOptions: header.IPv4Options{1, 1, 1, 1}, }, { name: "NOP and End options", maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{1, 1, 0, 0}, - replyOptions: []byte{1, 1, 0, 0}, + options: header.IPv4Options{1, 1, 0, 0}, + replyOptions: header.IPv4Options{1, 1, 0, 0}, }, { name: "bad header length", @@ -240,12 +240,12 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 12, 13, 0x11, 192, 168, 1, 12, 1, 2, 3, 4, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 68, 12, 13, 0x21, 192, 168, 1, 12, 1, 2, 3, 4, @@ -256,7 +256,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 12, 13, 0xF1, // ^ Counter full (15/0xF) 192, 168, 1, 12, @@ -267,24 +267,24 @@ func TestIPv4Sanity(t *testing.T) { ICMPType: header.ICMPv4ParamProblem, ICMPCode: header.ICMPv4UnusedCode, paramProblemPointer: header.IPv4MinimumSize + 3, - replyOptions: []byte{}, + replyOptions: header.IPv4Options{}, }, { name: "unknown option", maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{10, 4, 9, 0}, + options: header.IPv4Options{10, 4, 9, 0}, // ^^ // The unknown option should be stripped out of the reply. - replyOptions: []byte{}, + replyOptions: header.IPv4Options{}, }, { name: "bad option - length 0", maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 0, 9, 0, // ^ 1, 2, 3, 4, @@ -296,7 +296,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 9, 9, 0, // ^ // There are only 8 bytes allocated to options so 9 bytes of timestamp @@ -315,7 +315,7 @@ func TestIPv4Sanity(t *testing.T) { TTL: ttl, // Timestamps are in multiples of 4 or 8 but never 7. // The option space should be padded out. - options: []byte{ + options: header.IPv4Options{ 68, 7, 5, 0, // ^ ^ Linux points here which is wrong. // | Not a multiple of 4 @@ -332,7 +332,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 24, 21, 0x00, 1, 2, 3, 4, 5, 6, 7, 8, @@ -340,7 +340,7 @@ func TestIPv4Sanity(t *testing.T) { 13, 14, 15, 16, 0, 0, 0, 0, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 68, 24, 25, 0x00, 1, 2, 3, 4, 5, 6, 7, 8, @@ -355,7 +355,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 20, 21, 0x11, // ^ 192, 168, 1, 12, @@ -364,7 +364,7 @@ func TestIPv4Sanity(t *testing.T) { 5, 6, 7, 8, }, // Overflow count is the top nibble of the 4th byte. - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 68, 20, 21, 0x21, // ^ 192, 168, 1, 12, @@ -378,7 +378,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 28, 21, 0x01, 192, 168, 1, 12, 1, 2, 3, 4, @@ -387,7 +387,7 @@ func TestIPv4Sanity(t *testing.T) { 0, 0, 0, 0, 0, 0, 0, 0, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 68, 28, 29, 0x01, 192, 168, 1, 12, 1, 2, 3, 4, @@ -403,7 +403,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 20, 17, 0x01, // ^^ ^^ 20 byte area, next free spot at 17. 192, 168, 1, 12, @@ -423,13 +423,13 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 68, 12, 13, 0x11, 192, 168, 1, 12, 1, 2, 3, 4, 0, 10, 3, 99, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 68, 12, 13, 0x21, 192, 168, 1, 12, 1, 2, 3, 4, @@ -442,7 +442,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{68, 1, 0, 0}, + options: header.IPv4Options{68, 1, 0, 0}, // ^ Smallest possible is 8. shouldFail: true, }, @@ -451,12 +451,12 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 7, 7, 4, // 3 byte header 0, 0, 0, 0, 0, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 7, 7, 8, // 3 byte header 192, 168, 1, 58, // New IP Address. 0, // padding to multiple of 4 bytes. @@ -467,7 +467,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 7, 23, 20, // 3 byte header 1, 2, 3, 4, 5, 6, 7, 8, @@ -476,7 +476,7 @@ func TestIPv4Sanity(t *testing.T) { 0, 0, 0, 0, 0, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 7, 23, 24, 1, 2, 3, 4, 5, 6, 7, 8, @@ -491,12 +491,12 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 7, 7, 8, // 3 byte header 1, 2, 3, 4, 0, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 7, 7, 8, // 3 byte header 1, 2, 3, 4, 0, // padding to multiple of 4 bytes. @@ -508,7 +508,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 7, 23, 24, // 3 byte header 1, 2, 3, 4, 5, 6, 7, 8, @@ -517,7 +517,7 @@ func TestIPv4Sanity(t *testing.T) { 17, 18, 19, 20, 0, }, - replyOptions: []byte{ + replyOptions: header.IPv4Options{ 7, 23, 24, 1, 2, 3, 4, 5, 6, 7, 8, @@ -534,7 +534,7 @@ func TestIPv4Sanity(t *testing.T) { maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 7, 8, 8, // 3 byte header // ^ ^ Linux points here. We must too. // | Not enough room. 1 byte free, need 4. @@ -546,14 +546,14 @@ func TestIPv4Sanity(t *testing.T) { ICMPType: header.ICMPv4ParamProblem, ICMPCode: header.ICMPv4UnusedCode, paramProblemPointer: header.IPv4MinimumSize + 2, - replyOptions: []byte{}, + replyOptions: header.IPv4Options{}, }, { name: "duplicate record route", maxTotalLength: ipv4.MaxTotalSize, transportProtocol: uint8(header.ICMPv4ProtocolNumber), TTL: ttl, - options: []byte{ + options: header.IPv4Options{ 7, 7, 8, // 3 byte header 1, 2, 3, 4, 7, 7, 8, // 3 byte header @@ -565,7 +565,7 @@ func TestIPv4Sanity(t *testing.T) { ICMPType: header.ICMPv4ParamProblem, ICMPCode: header.ICMPv4UnusedCode, paramProblemPointer: header.IPv4MinimumSize + 7, - replyOptions: []byte{}, + replyOptions: header.IPv4Options{}, }, } @@ -599,12 +599,7 @@ func TestIPv4Sanity(t *testing.T) { }, }) - // Round up the header size to the next multiple of 4 as RFC 791, page 11 - // says: "Internet Header Length is the length of the internet header - // in 32 bit words..." and on page 23: "The internet header padding is - // used to ensure that the internet header ends on a 32 bit boundary." - ipHeaderLength := ((header.IPv4MinimumSize + len(test.options)) + header.IPv4IHLStride - 1) & ^(header.IPv4IHLStride - 1) - + ipHeaderLength := header.IPv4MinimumSize + test.options.AllocationSize() if ipHeaderLength > header.IPv4MaximumHeaderSize { t.Fatalf("too many bytes in options: got = %d, want <= %d ", ipHeaderLength, header.IPv4MaximumHeaderSize) } @@ -624,17 +619,13 @@ func TestIPv4Sanity(t *testing.T) { totalLen = test.maxTotalLength } ip.Encode(&header.IPv4Fields{ - IHL: uint8(ipHeaderLength), TotalLength: totalLen, Protocol: test.transportProtocol, TTL: test.TTL, SrcAddr: remoteIPv4Addr, DstAddr: ipv4Addr.Address, + Options: test.options, }) - if n := copy(ip.Options(), test.options); n != len(test.options) { - t.Fatalf("options larger than available space: copied %d/%d bytes", n, len(test.options)) - } - // Override the correct value if the test case specified one. if test.headerLength != 0 { ip.SetHeaderLength(test.headerLength) } @@ -1160,9 +1151,11 @@ func TestInvalidFragments(t *testing.T) { } type fragmentData struct { - ipv4fields header.IPv4Fields + ipv4fields header.IPv4Fields + // 0 means insert the correct IHL. Non 0 means override the correct IHL. + overrideIHL int // For 0 use 1 as it is an int and will be divided by 4. payload []byte - autoChecksum bool // if true, the Checksum field will be overwritten. + autoChecksum bool // If true, the Checksum field will be overwritten. } tests := []struct { @@ -1176,7 +1169,6 @@ func TestInvalidFragments(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: 0, TOS: tos, TotalLength: 0, ID: ident, @@ -1187,6 +1179,7 @@ func TestInvalidFragments(t *testing.T) { SrcAddr: addr1, DstAddr: addr2, }, + overrideIHL: 1, // See note above. payload: payloadGen(12), autoChecksum: true, }, @@ -1199,7 +1192,6 @@ func TestInvalidFragments(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: 0, TOS: tos, TotalLength: 0, ID: ident, @@ -1210,6 +1202,7 @@ func TestInvalidFragments(t *testing.T) { SrcAddr: addr1, DstAddr: addr2, }, + overrideIHL: 1, // See note above. payload: payloadGen(12), autoChecksum: true, }, @@ -1224,7 +1217,6 @@ func TestInvalidFragments(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 17, ID: ident, @@ -1249,7 +1241,6 @@ func TestInvalidFragments(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 16, ID: ident, @@ -1272,7 +1263,6 @@ func TestInvalidFragments(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize - 12, TOS: tos, TotalLength: header.IPv4MinimumSize + 28, ID: ident, @@ -1284,11 +1274,11 @@ func TestInvalidFragments(t *testing.T) { DstAddr: addr2, }, payload: payloadGen(28), + overrideIHL: header.IPv4MinimumSize - 12, autoChecksum: true, }, { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize - 12, TOS: tos, TotalLength: header.IPv4MinimumSize - 12, ID: ident, @@ -1300,6 +1290,7 @@ func TestInvalidFragments(t *testing.T) { DstAddr: addr2, }, payload: payloadGen(28), + overrideIHL: header.IPv4MinimumSize - 12, autoChecksum: true, }, }, @@ -1311,7 +1302,6 @@ func TestInvalidFragments(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize + 4, TOS: tos, TotalLength: header.IPv4MinimumSize + 28, ID: ident, @@ -1323,11 +1313,11 @@ func TestInvalidFragments(t *testing.T) { DstAddr: addr2, }, payload: payloadGen(28), + overrideIHL: header.IPv4MinimumSize + 4, autoChecksum: true, }, { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize + 4, TOS: tos, TotalLength: header.IPv4MinimumSize + 4, ID: ident, @@ -1339,6 +1329,7 @@ func TestInvalidFragments(t *testing.T) { DstAddr: addr2, }, payload: payloadGen(28), + overrideIHL: header.IPv4MinimumSize + 4, autoChecksum: true, }, }, @@ -1350,7 +1341,6 @@ func TestInvalidFragments(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 8, ID: ident, @@ -1366,7 +1356,6 @@ func TestInvalidFragments(t *testing.T) { }, { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 8, ID: ident, @@ -1382,7 +1371,6 @@ func TestInvalidFragments(t *testing.T) { }, { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 8, ID: ident, @@ -1423,6 +1411,11 @@ func TestInvalidFragments(t *testing.T) { ip := header.IPv4(hdr.Prepend(pktSize)) ip.Encode(&f.ipv4fields) + // Encode sets this up correctly. If we want a different value for + // testing then we need to overwrite the good value. + if f.overrideIHL != 0 { + ip.SetHeaderLength(uint8(f.overrideIHL)) + } copy(ip[header.IPv4MinimumSize:], f.payload) if f.autoChecksum { @@ -1474,7 +1467,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 16, ID: ident, @@ -1495,7 +1487,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 16, ID: ident, @@ -1510,7 +1501,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { }, { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 16, ID: ident, @@ -1531,7 +1521,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: uint16(header.IPv4MinimumSize + len(data) - 16), ID: ident, @@ -1552,7 +1541,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 8, ID: ident, @@ -1567,7 +1555,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { }, { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: uint16(header.IPv4MinimumSize + len(data) - 16), ID: ident, @@ -1588,7 +1575,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { fragments: []fragmentData{ { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: uint16(header.IPv4MinimumSize + len(data) - 16), ID: ident, @@ -1603,7 +1589,6 @@ func TestFragmentReassemblyTimeout(t *testing.T) { }, { ipv4fields: header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TOS: tos, TotalLength: header.IPv4MinimumSize + 8, ID: ident, @@ -2104,7 +2089,6 @@ func TestReceiveFragments(t *testing.T) { // Serialize IPv4 fixed header. ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize)) ip.Encode(&header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TotalLength: header.IPv4MinimumSize + uint16(len(frag.payload)), ID: frag.id, Flags: frag.flags, @@ -2370,7 +2354,6 @@ func TestPacketQueing(t *testing.T) { u.SetChecksum(^u.CalculateChecksum(sum)) ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize)) ip.Encode(&header.IPv4Fields{ - IHL: header.IPv4MinimumSize, TotalLength: header.IPv4MinimumSize + header.UDPMinimumSize, TTL: ipv4.DefaultTTL, Protocol: uint8(udp.ProtocolNumber), @@ -2414,7 +2397,6 @@ func TestPacketQueing(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, diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index b8f333057..00e9a82ae 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -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 diff --git a/pkg/tcpip/stack/transport_demuxer_test.go b/pkg/tcpip/stack/transport_demuxer_test.go index 698c8609e..41a8e5ad0 100644 --- a/pkg/tcpip/stack/transport_demuxer_test.go +++ b/pkg/tcpip/stack/transport_demuxer_test.go @@ -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, diff --git a/pkg/tcpip/tests/integration/multicast_broadcast_test.go b/pkg/tcpip/tests/integration/multicast_broadcast_test.go index cdf0459e3..1eecd7957 100644 --- a/pkg/tcpip/tests/integration/multicast_broadcast_test.go +++ b/pkg/tcpip/tests/integration/multicast_broadcast_test.go @@ -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, diff --git a/pkg/tcpip/transport/tcp/testing/context/context.go b/pkg/tcpip/transport/tcp/testing/context/context.go index f791f8f13..e6aa4fc4b 100644 --- a/pkg/tcpip/transport/tcp/testing/context/context.go +++ b/pkg/tcpip/transport/tcp/testing/context/context.go @@ -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), diff --git a/pkg/tcpip/transport/udp/udp_test.go b/pkg/tcpip/transport/udp/udp_test.go index fb7738dda..c09c7aa86 100644 --- a/pkg/tcpip/transport/udp/udp_test.go +++ b/pkg/tcpip/transport/udp/udp_test.go @@ -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, |