summaryrefslogtreecommitdiffhomepage
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/tcpip/checker/checker.go4
-rw-r--r--pkg/tcpip/header/ipv4.go45
-rw-r--r--pkg/tcpip/header/ipversion_test.go2
-rw-r--r--pkg/tcpip/network/ip_test.go29
-rw-r--r--pkg/tcpip/network/ipv4/ipv4.go19
-rw-r--r--pkg/tcpip/network/ipv4/ipv4_test.go130
-rw-r--r--pkg/tcpip/stack/registration.go19
-rw-r--r--pkg/tcpip/stack/transport_demuxer_test.go1
-rw-r--r--pkg/tcpip/tests/integration/multicast_broadcast_test.go2
-rw-r--r--pkg/tcpip/transport/tcp/testing/context/context.go2
-rw-r--r--pkg/tcpip/transport/udp/udp_test.go1
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,