diff options
Diffstat (limited to 'pkg/tcpip/header')
-rw-r--r-- | pkg/tcpip/header/BUILD | 76 | ||||
-rw-r--r-- | pkg/tcpip/header/checksum_test.go | 258 | ||||
-rw-r--r-- | pkg/tcpip/header/eth_test.go | 150 | ||||
-rw-r--r-- | pkg/tcpip/header/header_state_autogen.go | 74 | ||||
-rw-r--r-- | pkg/tcpip/header/igmp_test.go | 110 | ||||
-rw-r--r-- | pkg/tcpip/header/ipv4_test.go | 254 | ||||
-rw-r--r-- | pkg/tcpip/header/ipv6_extension_headers_test.go | 1346 | ||||
-rw-r--r-- | pkg/tcpip/header/ipv6_test.go | 457 | ||||
-rw-r--r-- | pkg/tcpip/header/ipversion_test.go | 67 | ||||
-rw-r--r-- | pkg/tcpip/header/mld_test.go | 61 | ||||
-rw-r--r-- | pkg/tcpip/header/ndp_test.go | 1500 | ||||
-rw-r--r-- | pkg/tcpip/header/parse/BUILD | 15 | ||||
-rw-r--r-- | pkg/tcpip/header/parse/parse_state_autogen.go | 3 | ||||
-rw-r--r-- | pkg/tcpip/header/tcp_test.go | 168 |
14 files changed, 77 insertions, 4462 deletions
diff --git a/pkg/tcpip/header/BUILD b/pkg/tcpip/header/BUILD deleted file mode 100644 index 01240f5d0..000000000 --- a/pkg/tcpip/header/BUILD +++ /dev/null @@ -1,76 +0,0 @@ -load("//tools:defs.bzl", "go_library", "go_test") - -package(licenses = ["notice"]) - -go_library( - name = "header", - srcs = [ - "arp.go", - "checksum.go", - "eth.go", - "gue.go", - "icmpv4.go", - "icmpv6.go", - "igmp.go", - "interfaces.go", - "ipv4.go", - "ipv6.go", - "ipv6_extension_headers.go", - "ipv6_fragment.go", - "mld.go", - "ndp_neighbor_advert.go", - "ndp_neighbor_solicit.go", - "ndp_options.go", - "ndp_router_advert.go", - "ndp_router_solicit.go", - "ndpoptionidentifier_string.go", - "tcp.go", - "udp.go", - ], - visibility = ["//visibility:public"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/seqnum", - "@com_github_google_btree//:go_default_library", - ], -) - -go_test( - name = "header_x_test", - size = "small", - srcs = [ - "checksum_test.go", - "igmp_test.go", - "ipv4_test.go", - "ipv6_test.go", - "ipversion_test.go", - "tcp_test.go", - ], - deps = [ - ":header", - "//pkg/rand", - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/testutil", - "@com_github_google_go_cmp//cmp:go_default_library", - ], -) - -go_test( - name = "header_test", - size = "small", - srcs = [ - "eth_test.go", - "ipv6_extension_headers_test.go", - "mld_test.go", - "ndp_test.go", - ], - library = ":header", - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/testutil", - "@com_github_google_go_cmp//cmp:go_default_library", - ], -) diff --git a/pkg/tcpip/header/checksum_test.go b/pkg/tcpip/header/checksum_test.go deleted file mode 100644 index d267dabd0..000000000 --- a/pkg/tcpip/header/checksum_test.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package header provides the implementation of the encoding and decoding of -// network protocol headers. -package header_test - -import ( - "bytes" - "fmt" - "math/rand" - "sync" - "testing" - - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/header" -) - -func TestChecksumer(t *testing.T) { - testCases := []struct { - name string - data [][]byte - want uint16 - }{ - { - name: "empty", - want: 0, - }, - { - name: "OneOddView", - data: [][]byte{ - []byte{1, 9, 0, 5, 4}, - }, - want: 1294, - }, - { - name: "TwoOddViews", - data: [][]byte{ - []byte{1, 9, 0, 5, 4}, - []byte{4, 3, 7, 1, 2, 123}, - }, - want: 33819, - }, - { - name: "OneEvenView", - data: [][]byte{ - []byte{1, 9, 0, 5}, - }, - want: 270, - }, - { - name: "TwoEvenViews", - data: [][]byte{ - buffer.NewViewFromBytes([]byte{98, 1, 9, 0}), - buffer.NewViewFromBytes([]byte{9, 0, 5, 4}), - }, - want: 30981, - }, - { - name: "ThreeViews", - data: [][]byte{ - []byte{77, 11, 33, 0, 55, 44}, - []byte{98, 1, 9, 0, 5, 4}, - []byte{4, 3, 7, 1, 2, 123, 99}, - }, - want: 34236, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var all bytes.Buffer - var c header.Checksumer - for _, b := range tc.data { - c.Add(b) - // Append to the buffer. We will check the checksum as a whole later. - if _, err := all.Write(b); err != nil { - t.Fatalf("all.Write(b) = _, %s; want _, nil", err) - } - } - if got, want := c.Checksum(), tc.want; got != want { - t.Errorf("c.Checksum() = %d, want %d", got, want) - } - if got, want := header.Checksum(all.Bytes(), 0 /* initial */), tc.want; got != want { - t.Errorf("Checksum(flatten tc.data) = %d, want %d", got, want) - } - }) - } -} - -func TestChecksum(t *testing.T) { - var bufSizes = []int{0, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128, 255, 256, 257, 1023, 1024} - type testCase struct { - buf []byte - initial uint16 - csumOrig uint16 - csumNew uint16 - } - testCases := make([]testCase, 100000) - // Ensure same buffer generation for test consistency. - rnd := rand.New(rand.NewSource(42)) - for i := range testCases { - testCases[i].buf = make([]byte, bufSizes[i%len(bufSizes)]) - testCases[i].initial = uint16(rnd.Intn(65536)) - rnd.Read(testCases[i].buf) - } - - for i := range testCases { - testCases[i].csumOrig = header.ChecksumOld(testCases[i].buf, testCases[i].initial) - testCases[i].csumNew = header.Checksum(testCases[i].buf, testCases[i].initial) - if got, want := testCases[i].csumNew, testCases[i].csumOrig; got != want { - t.Fatalf("new checksum for (buf = %x, initial = %d) does not match old got: %d, want: %d", testCases[i].buf, testCases[i].initial, got, want) - } - } -} - -func BenchmarkChecksum(b *testing.B) { - var bufSizes = []int{64, 128, 256, 512, 1024, 1500, 2048, 4096, 8192, 16384, 32767, 32768, 65535, 65536} - - checkSumImpls := []struct { - fn func([]byte, uint16) uint16 - name string - }{ - {header.ChecksumOld, fmt.Sprintf("checksum_old")}, - {header.Checksum, fmt.Sprintf("checksum")}, - } - - for _, csumImpl := range checkSumImpls { - // Ensure same buffer generation for test consistency. - rnd := rand.New(rand.NewSource(42)) - for _, bufSz := range bufSizes { - b.Run(fmt.Sprintf("%s_%d", csumImpl.name, bufSz), func(b *testing.B) { - tc := struct { - buf []byte - initial uint16 - csum uint16 - }{ - buf: make([]byte, bufSz), - initial: uint16(rnd.Intn(65536)), - } - rnd.Read(tc.buf) - b.ResetTimer() - for i := 0; i < b.N; i++ { - tc.csum = csumImpl.fn(tc.buf, tc.initial) - } - }) - } - } -} - -func testICMPChecksum(t *testing.T, headerChecksum func() uint16, icmpChecksum func() uint16, want uint16, pktStr string) { - // icmpChecksum should not do any modifications of the header to - // calculate its checksum. Let's call it from a few go-routines and the - // race detector will trigger a warning if there are any concurrent - // read/write accesses. - - const concurrency = 5 - start := make(chan int) - ready := make(chan bool, concurrency) - var wg sync.WaitGroup - wg.Add(concurrency) - defer wg.Wait() - - for i := 0; i < concurrency; i++ { - go func() { - defer wg.Done() - - ready <- true - <-start - - if got := headerChecksum(); want != got { - t.Errorf("new checksum for %s does not match old got: %x, want: %x", pktStr, got, want) - } - if got := icmpChecksum(); want != got { - t.Errorf("new checksum for %s does not match old got: %x, want: %x", pktStr, got, want) - } - }() - } - for i := 0; i < concurrency; i++ { - <-ready - } - close(start) -} - -func TestICMPv4Checksum(t *testing.T) { - rnd := rand.New(rand.NewSource(42)) - - h := header.ICMPv4(make([]byte, header.ICMPv4MinimumSize)) - if _, err := rnd.Read(h); err != nil { - t.Fatalf("rnd.Read failed: %v", err) - } - h.SetChecksum(0) - - buf := make([]byte, 13) - if _, err := rnd.Read(buf); err != nil { - t.Fatalf("rnd.Read failed: %v", err) - } - vv := buffer.NewVectorisedView(len(buf), []buffer.View{ - buffer.NewViewFromBytes(buf[:5]), - buffer.NewViewFromBytes(buf[5:]), - }) - - want := header.Checksum(vv.ToView(), 0) - want = ^header.Checksum(h, want) - h.SetChecksum(want) - - testICMPChecksum(t, h.Checksum, func() uint16 { - return header.ICMPv4Checksum(h, header.ChecksumVV(vv, 0)) - }, want, fmt.Sprintf("header: {% x} data {% x}", h, vv.ToView())) -} - -func TestICMPv6Checksum(t *testing.T) { - rnd := rand.New(rand.NewSource(42)) - - h := header.ICMPv6(make([]byte, header.ICMPv6MinimumSize)) - if _, err := rnd.Read(h); err != nil { - t.Fatalf("rnd.Read failed: %v", err) - } - h.SetChecksum(0) - - buf := make([]byte, 13) - if _, err := rnd.Read(buf); err != nil { - t.Fatalf("rnd.Read failed: %v", err) - } - vv := buffer.NewVectorisedView(len(buf), []buffer.View{ - buffer.NewViewFromBytes(buf[:7]), - buffer.NewViewFromBytes(buf[7:10]), - buffer.NewViewFromBytes(buf[10:]), - }) - - dst := header.IPv6Loopback - src := header.IPv6Loopback - - want := header.PseudoHeaderChecksum(header.ICMPv6ProtocolNumber, src, dst, uint16(len(h)+vv.Size())) - want = header.Checksum(vv.ToView(), want) - want = ^header.Checksum(h, want) - h.SetChecksum(want) - - testICMPChecksum(t, h.Checksum, func() uint16 { - return header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ - Header: h, - Src: src, - Dst: dst, - PayloadCsum: header.ChecksumVV(vv, 0), - PayloadLen: vv.Size(), - }) - }, want, fmt.Sprintf("header: {% x} data {% x}", h, vv.ToView())) -} diff --git a/pkg/tcpip/header/eth_test.go b/pkg/tcpip/header/eth_test.go deleted file mode 100644 index bf9ccbf1a..000000000 --- a/pkg/tcpip/header/eth_test.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header - -import ( - "testing" - - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/testutil" -) - -func TestIsValidUnicastEthernetAddress(t *testing.T) { - tests := []struct { - name string - addr tcpip.LinkAddress - expected bool - }{ - { - "Nil", - tcpip.LinkAddress([]byte(nil)), - false, - }, - { - "Empty", - tcpip.LinkAddress(""), - false, - }, - { - "InvalidLength", - tcpip.LinkAddress("\x01\x02\x03"), - false, - }, - { - "Unspecified", - unspecifiedEthernetAddress, - false, - }, - { - "Multicast", - tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), - false, - }, - { - "Valid", - tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06"), - true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := IsValidUnicastEthernetAddress(test.addr); got != test.expected { - t.Fatalf("got IsValidUnicastEthernetAddress = %t, want = %t", got, test.expected) - } - }) - } -} - -func TestIsMulticastEthernetAddress(t *testing.T) { - tests := []struct { - name string - addr tcpip.LinkAddress - expected bool - }{ - { - "Nil", - tcpip.LinkAddress([]byte(nil)), - false, - }, - { - "Empty", - tcpip.LinkAddress(""), - false, - }, - { - "InvalidLength", - tcpip.LinkAddress("\x01\x02\x03"), - false, - }, - { - "Unspecified", - unspecifiedEthernetAddress, - false, - }, - { - "Multicast", - tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), - true, - }, - { - "Unicast", - tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06"), - false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := IsMulticastEthernetAddress(test.addr); got != test.expected { - t.Fatalf("got IsMulticastEthernetAddress = %t, want = %t", got, test.expected) - } - }) - } -} - -func TestEthernetAddressFromMulticastIPv4Address(t *testing.T) { - tests := []struct { - name string - addr tcpip.Address - expectedLinkAddr tcpip.LinkAddress - }{ - { - name: "IPv4 Multicast without 24th bit set", - addr: "\xe0\x7e\xdc\xba", - expectedLinkAddr: "\x01\x00\x5e\x7e\xdc\xba", - }, - { - name: "IPv4 Multicast with 24th bit set", - addr: "\xe0\xfe\xdc\xba", - expectedLinkAddr: "\x01\x00\x5e\x7e\xdc\xba", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := EthernetAddressFromMulticastIPv4Address(test.addr); got != test.expectedLinkAddr { - t.Fatalf("got EthernetAddressFromMulticastIPv4Address(%s) = %s, want = %s", test.addr, got, test.expectedLinkAddr) - } - }) - } -} - -func TestEthernetAddressFromMulticastIPv6Address(t *testing.T) { - addr := testutil.MustParse6("ff02:304:506:708:90a:b0c:d0e:f1a") - if got, want := EthernetAddressFromMulticastIPv6Address(addr), tcpip.LinkAddress("\x33\x33\x0d\x0e\x0f\x1a"); got != want { - t.Fatalf("got EthernetAddressFromMulticastIPv6Address(%s) = %s, want = %s", addr, got, want) - } -} diff --git a/pkg/tcpip/header/header_state_autogen.go b/pkg/tcpip/header/header_state_autogen.go new file mode 100644 index 000000000..d6dd58874 --- /dev/null +++ b/pkg/tcpip/header/header_state_autogen.go @@ -0,0 +1,74 @@ +// automatically generated by stateify. + +package header + +import ( + "gvisor.dev/gvisor/pkg/state" +) + +func (r *SACKBlock) StateTypeName() string { + return "pkg/tcpip/header.SACKBlock" +} + +func (r *SACKBlock) StateFields() []string { + return []string{ + "Start", + "End", + } +} + +func (r *SACKBlock) beforeSave() {} + +// +checklocksignore +func (r *SACKBlock) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.Start) + stateSinkObject.Save(1, &r.End) +} + +func (r *SACKBlock) afterLoad() {} + +// +checklocksignore +func (r *SACKBlock) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.Start) + stateSourceObject.Load(1, &r.End) +} + +func (t *TCPOptions) StateTypeName() string { + return "pkg/tcpip/header.TCPOptions" +} + +func (t *TCPOptions) StateFields() []string { + return []string{ + "TS", + "TSVal", + "TSEcr", + "SACKBlocks", + } +} + +func (t *TCPOptions) beforeSave() {} + +// +checklocksignore +func (t *TCPOptions) StateSave(stateSinkObject state.Sink) { + t.beforeSave() + stateSinkObject.Save(0, &t.TS) + stateSinkObject.Save(1, &t.TSVal) + stateSinkObject.Save(2, &t.TSEcr) + stateSinkObject.Save(3, &t.SACKBlocks) +} + +func (t *TCPOptions) afterLoad() {} + +// +checklocksignore +func (t *TCPOptions) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &t.TS) + stateSourceObject.Load(1, &t.TSVal) + stateSourceObject.Load(2, &t.TSEcr) + stateSourceObject.Load(3, &t.SACKBlocks) +} + +func init() { + state.Register((*SACKBlock)(nil)) + state.Register((*TCPOptions)(nil)) +} diff --git a/pkg/tcpip/header/igmp_test.go b/pkg/tcpip/header/igmp_test.go deleted file mode 100644 index 575604928..000000000 --- a/pkg/tcpip/header/igmp_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header_test - -import ( - "testing" - "time" - - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/testutil" -) - -// TestIGMPHeader tests the functions within header.igmp -func TestIGMPHeader(t *testing.T) { - const maxRespTimeTenthSec = 0xF0 - b := []byte{ - 0x11, // IGMP Type, Membership Query - maxRespTimeTenthSec, // Maximum Response Time - 0xC0, 0xC0, // Checksum - 0x01, 0x02, 0x03, 0x04, // Group Address - } - - igmpHeader := header.IGMP(b) - - if got, want := igmpHeader.Type(), header.IGMPMembershipQuery; got != want { - t.Errorf("got igmpHeader.Type() = %x, want = %x", got, want) - } - - if got, want := igmpHeader.MaxRespTime(), header.DecisecondToDuration(maxRespTimeTenthSec); got != want { - t.Errorf("got igmpHeader.MaxRespTime() = %s, want = %s", got, want) - } - - if got, want := igmpHeader.Checksum(), uint16(0xC0C0); got != want { - t.Errorf("got igmpHeader.Checksum() = %x, want = %x", got, want) - } - - if got, want := igmpHeader.GroupAddress(), testutil.MustParse4("1.2.3.4"); got != want { - t.Errorf("got igmpHeader.GroupAddress() = %s, want = %s", got, want) - } - - igmpType := header.IGMPv2MembershipReport - igmpHeader.SetType(igmpType) - if got := igmpHeader.Type(); got != igmpType { - t.Errorf("got igmpHeader.Type() = %x, want = %x", got, igmpType) - } - if got := header.IGMPType(b[0]); got != igmpType { - t.Errorf("got IGMPtype in backing buffer = %x, want %x", got, igmpType) - } - - respTime := byte(0x02) - igmpHeader.SetMaxRespTime(respTime) - if got, want := igmpHeader.MaxRespTime(), header.DecisecondToDuration(respTime); got != want { - t.Errorf("got igmpHeader.MaxRespTime() = %s, want = %s", got, want) - } - - checksum := uint16(0x0102) - igmpHeader.SetChecksum(checksum) - if got := igmpHeader.Checksum(); got != checksum { - t.Errorf("got igmpHeader.Checksum() = %x, want = %x", got, checksum) - } - - groupAddress := testutil.MustParse4("4.3.2.1") - igmpHeader.SetGroupAddress(groupAddress) - if got := igmpHeader.GroupAddress(); got != groupAddress { - t.Errorf("got igmpHeader.GroupAddress() = %s, want = %s", got, groupAddress) - } -} - -// TestIGMPChecksum ensures that the checksum calculator produces the expected -// checksum. -func TestIGMPChecksum(t *testing.T) { - b := []byte{ - 0x11, // IGMP Type, Membership Query - 0xF0, // Maximum Response Time - 0xC0, 0xC0, // Checksum - 0x01, 0x02, 0x03, 0x04, // Group Address - } - - igmpHeader := header.IGMP(b) - - // Calculate the initial checksum after setting the checksum temporarily to 0 - // to avoid checksumming the checksum. - initialChecksum := igmpHeader.Checksum() - igmpHeader.SetChecksum(0) - checksum := ^header.Checksum(b, 0) - igmpHeader.SetChecksum(initialChecksum) - - if got := header.IGMPCalculateChecksum(igmpHeader); got != checksum { - t.Errorf("got IGMPCalculateChecksum = %x, want %x", got, checksum) - } -} - -func TestDecisecondToDuration(t *testing.T) { - const valueInDeciseconds = 5 - if got, want := header.DecisecondToDuration(valueInDeciseconds), valueInDeciseconds*time.Second/10; got != want { - t.Fatalf("got header.DecisecondToDuration(%d) = %s, want = %s", valueInDeciseconds, got, want) - } -} diff --git a/pkg/tcpip/header/ipv4_test.go b/pkg/tcpip/header/ipv4_test.go deleted file mode 100644 index c02fe898b..000000000 --- a/pkg/tcpip/header/ipv4_test.go +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header_test - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/header" -) - -func TestIPv4OptionsSerializer(t *testing.T) { - optCases := []struct { - name string - option []header.IPv4SerializableOption - expect []byte - }{ - { - name: "NOP", - option: []header.IPv4SerializableOption{ - &header.IPv4SerializableNOPOption{}, - }, - expect: []byte{1, 0, 0, 0}, - }, - { - name: "ListEnd", - option: []header.IPv4SerializableOption{ - &header.IPv4SerializableListEndOption{}, - }, - expect: []byte{0, 0, 0, 0}, - }, - { - name: "RouterAlert", - option: []header.IPv4SerializableOption{ - &header.IPv4SerializableRouterAlertOption{}, - }, - expect: []byte{148, 4, 0, 0}, - }, { - name: "NOP and RouterAlert", - option: []header.IPv4SerializableOption{ - &header.IPv4SerializableNOPOption{}, - &header.IPv4SerializableRouterAlertOption{}, - }, - expect: []byte{1, 148, 4, 0, 0, 0, 0, 0}, - }, - } - - for _, opt := range optCases { - t.Run(opt.name, func(t *testing.T) { - s := header.IPv4OptionsSerializer(opt.option) - l := s.Length() - if got := len(opt.expect); got != int(l) { - t.Fatalf("s.Length() = %d, want = %d", got, l) - } - b := make([]byte, l) - for i := range b { - // Fill the buffer with full bytes to ensure padding is being set - // correctly. - b[i] = 0xFF - } - if serializedLength := s.Serialize(b); serializedLength != l { - t.Fatalf("s.Serialize(_) = %d, want %d", serializedLength, l) - } - if diff := cmp.Diff(opt.expect, b); diff != "" { - t.Errorf("mismatched serialized option (-want +got):\n%s", diff) - } - }) - } -} - -// TestIPv4Encode checks that ipv4.Encode correctly fills out the requested -// fields when options are supplied. -func TestIPv4EncodeOptions(t *testing.T) { - tests := []struct { - name string - numberOfNops int - encodedOptions header.IPv4Options // reply should look like this - wantIHL int - }{ - { - name: "valid no options", - wantIHL: header.IPv4MinimumSize, - }, - { - name: "one byte options", - numberOfNops: 1, - encodedOptions: header.IPv4Options{1, 0, 0, 0}, - wantIHL: header.IPv4MinimumSize + 4, - }, - { - name: "two byte options", - numberOfNops: 2, - encodedOptions: header.IPv4Options{1, 1, 0, 0}, - wantIHL: header.IPv4MinimumSize + 4, - }, - { - name: "three byte options", - numberOfNops: 3, - encodedOptions: header.IPv4Options{1, 1, 1, 0}, - wantIHL: header.IPv4MinimumSize + 4, - }, - { - name: "four byte options", - numberOfNops: 4, - encodedOptions: header.IPv4Options{1, 1, 1, 1}, - wantIHL: header.IPv4MinimumSize + 4, - }, - { - name: "five byte options", - numberOfNops: 5, - encodedOptions: header.IPv4Options{1, 1, 1, 1, 1, 0, 0, 0}, - wantIHL: header.IPv4MinimumSize + 8, - }, - { - name: "thirty nine byte options", - numberOfNops: 39, - encodedOptions: header.IPv4Options{ - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, - }, - wantIHL: header.IPv4MinimumSize + 40, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - serializeOpts := header.IPv4OptionsSerializer(make([]header.IPv4SerializableOption, test.numberOfNops)) - for i := range serializeOpts { - serializeOpts[i] = &header.IPv4SerializableNOPOption{} - } - paddedOptionLength := serializeOpts.Length() - ipHeaderLength := int(header.IPv4MinimumSize + paddedOptionLength) - if ipHeaderLength > header.IPv4MaximumHeaderSize { - t.Fatalf("IP header length too large: got = %d, want <= %d ", ipHeaderLength, header.IPv4MaximumHeaderSize) - } - totalLen := uint16(ipHeaderLength) - hdr := buffer.NewPrependable(int(totalLen)) - ip := header.IPv4(hdr.Prepend(ipHeaderLength)) - // To check the padding works, poison the last byte of the options space. - if paddedOptionLength != serializeOpts.Length() { - ip.SetHeaderLength(uint8(ipHeaderLength)) - ip.Options()[paddedOptionLength-1] = 0xff - ip.SetHeaderLength(0) - } - ip.Encode(&header.IPv4Fields{ - Options: serializeOpts, - }) - options := ip.Options() - wantOptions := test.encodedOptions - if got, want := int(ip.HeaderLength()), test.wantIHL; got != want { - t.Errorf("got IHL of %d, want %d", got, want) - } - - // cmp.Diff does not consider nil slices equal to empty slices, but we do. - if len(wantOptions) == 0 && len(options) == 0 { - return - } - - if diff := cmp.Diff(wantOptions, options); diff != "" { - t.Errorf("options mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func TestIsV4LinkLocalUnicastAddress(t *testing.T) { - tests := []struct { - name string - addr tcpip.Address - expected bool - }{ - { - name: "Valid (lowest)", - addr: "\xa9\xfe\x00\x00", - expected: true, - }, - { - name: "Valid (highest)", - addr: "\xa9\xfe\xff\xff", - expected: true, - }, - { - name: "Invalid (before subnet)", - addr: "\xa9\xfd\xff\xff", - expected: false, - }, - { - name: "Invalid (after subnet)", - addr: "\xa9\xff\x00\x00", - expected: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := header.IsV4LinkLocalUnicastAddress(test.addr); got != test.expected { - t.Errorf("got header.IsV4LinkLocalUnicastAddress(%s) = %t, want = %t", test.addr, got, test.expected) - } - }) - } -} - -func TestIsV4LinkLocalMulticastAddress(t *testing.T) { - tests := []struct { - name string - addr tcpip.Address - expected bool - }{ - { - name: "Valid (lowest)", - addr: "\xe0\x00\x00\x00", - expected: true, - }, - { - name: "Valid (highest)", - addr: "\xe0\x00\x00\xff", - expected: true, - }, - { - name: "Invalid (before subnet)", - addr: "\xdf\xff\xff\xff", - expected: false, - }, - { - name: "Invalid (after subnet)", - addr: "\xe0\x00\x01\x00", - expected: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := header.IsV4LinkLocalMulticastAddress(test.addr); got != test.expected { - t.Errorf("got header.IsV4LinkLocalMulticastAddress(%s) = %t, want = %t", test.addr, got, test.expected) - } - }) - } -} diff --git a/pkg/tcpip/header/ipv6_extension_headers_test.go b/pkg/tcpip/header/ipv6_extension_headers_test.go deleted file mode 100644 index 65adc6250..000000000 --- a/pkg/tcpip/header/ipv6_extension_headers_test.go +++ /dev/null @@ -1,1346 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header - -import ( - "bytes" - "errors" - "io" - "testing" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" -) - -// Equal returns true of a and b are equivalent. -// -// Note, Equal will return true if a and b hold the same Identifier value and -// contain the same bytes in Buf, even if the bytes are split across views -// differently. -// -// Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported -// fields. -func (a IPv6RawPayloadHeader) Equal(b IPv6RawPayloadHeader) bool { - return a.Identifier == b.Identifier && bytes.Equal(a.Buf.ToView(), b.Buf.ToView()) -} - -// Equal returns true of a and b are equivalent. -// -// Note, Equal will return true if a and b hold equivalent ipv6OptionsExtHdrs. -// -// Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported -// fields. -func (a IPv6HopByHopOptionsExtHdr) Equal(b IPv6HopByHopOptionsExtHdr) bool { - return bytes.Equal(a.ipv6OptionsExtHdr, b.ipv6OptionsExtHdr) -} - -// Equal returns true of a and b are equivalent. -// -// Note, Equal will return true if a and b hold equivalent ipv6OptionsExtHdrs. -// -// Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported -// fields. -func (a IPv6DestinationOptionsExtHdr) Equal(b IPv6DestinationOptionsExtHdr) bool { - return bytes.Equal(a.ipv6OptionsExtHdr, b.ipv6OptionsExtHdr) -} - -func TestIPv6UnknownExtHdrOption(t *testing.T) { - tests := []struct { - name string - identifier IPv6ExtHdrOptionIdentifier - expectedUnknownAction IPv6OptionUnknownAction - }{ - { - name: "Skip with zero LSBs", - identifier: 0, - expectedUnknownAction: IPv6OptionUnknownActionSkip, - }, - { - name: "Discard with zero LSBs", - identifier: 64, - expectedUnknownAction: IPv6OptionUnknownActionDiscard, - }, - { - name: "Discard and ICMP with zero LSBs", - identifier: 128, - expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMP, - }, - { - name: "Discard and ICMP for non multicast destination with zero LSBs", - identifier: 192, - expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, - }, - { - name: "Skip with non-zero LSBs", - identifier: 63, - expectedUnknownAction: IPv6OptionUnknownActionSkip, - }, - { - name: "Discard with non-zero LSBs", - identifier: 127, - expectedUnknownAction: IPv6OptionUnknownActionDiscard, - }, - { - name: "Discard and ICMP with non-zero LSBs", - identifier: 191, - expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMP, - }, - { - name: "Discard and ICMP for non multicast destination with non-zero LSBs", - identifier: 255, - expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - opt := &IPv6UnknownExtHdrOption{Identifier: test.identifier, Data: []byte{1, 2, 3, 4}} - if a := opt.UnknownAction(); a != test.expectedUnknownAction { - t.Fatalf("got UnknownAction() = %d, want = %d", a, test.expectedUnknownAction) - } - }) - } - -} - -func TestIPv6OptionsExtHdrIterErr(t *testing.T) { - tests := []struct { - name string - bytes []byte - err error - }{ - { - name: "Single unknown with zero length", - bytes: []byte{255, 0}, - }, - { - name: "Single unknown with non-zero length", - bytes: []byte{255, 3, 1, 2, 3}, - }, - { - name: "Two options", - bytes: []byte{ - 255, 0, - 254, 1, 1, - }, - }, - { - name: "Three options", - bytes: []byte{ - 255, 0, - 254, 1, 1, - 253, 4, 2, 3, 4, 5, - }, - }, - { - name: "Single unknown only identifier", - bytes: []byte{255}, - err: io.ErrUnexpectedEOF, - }, - { - name: "Single unknown too small with length = 1", - bytes: []byte{255, 1}, - err: io.ErrUnexpectedEOF, - }, - { - name: "Single unknown too small with length = 2", - bytes: []byte{255, 2, 1}, - err: io.ErrUnexpectedEOF, - }, - { - name: "Valid first with second unknown only identifier", - bytes: []byte{ - 255, 0, - 254, - }, - err: io.ErrUnexpectedEOF, - }, - { - name: "Valid first with second unknown missing data", - bytes: []byte{ - 255, 0, - 254, 1, - }, - err: io.ErrUnexpectedEOF, - }, - { - name: "Valid first with second unknown too small", - bytes: []byte{ - 255, 0, - 254, 2, 1, - }, - err: io.ErrUnexpectedEOF, - }, - { - name: "One Pad1", - bytes: []byte{0}, - }, - { - name: "Multiple Pad1", - bytes: []byte{0, 0, 0}, - }, - { - name: "Multiple PadN", - bytes: []byte{ - // Pad3 - 1, 1, 1, - - // Pad5 - 1, 3, 1, 2, 3, - }, - }, - { - name: "Pad5 too small middle of data buffer", - bytes: []byte{1, 3, 1, 2}, - err: io.ErrUnexpectedEOF, - }, - { - name: "Pad5 no data", - bytes: []byte{1, 3}, - err: io.ErrUnexpectedEOF, - }, - { - name: "Router alert without data", - bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 0}, - err: ErrMalformedIPv6ExtHdrOption, - }, - { - name: "Router alert with partial data", - bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1, 1}, - err: ErrMalformedIPv6ExtHdrOption, - }, - { - name: "Router alert with partial data and Pad1", - bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1, 1, 0}, - err: ErrMalformedIPv6ExtHdrOption, - }, - { - name: "Router alert with extra data", - bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 3, 1, 2, 3}, - err: ErrMalformedIPv6ExtHdrOption, - }, - { - name: "Router alert with missing data", - bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1}, - err: io.ErrUnexpectedEOF, - }, - } - - check := func(t *testing.T, it IPv6OptionsExtHdrOptionsIterator, expectedErr error) { - for i := 0; ; i++ { - _, done, err := it.Next() - if err != nil { - // If we encountered a non-nil error while iterating, make sure it is - // is the same error as expectedErr. - if !errors.Is(err, expectedErr) { - t.Fatalf("got %d-th Next() = %v, want = %v", i, err, expectedErr) - } - - return - } - if done { - // If we are done (without an error), make sure that we did not expect - // an error. - if expectedErr != nil { - t.Fatalf("expected error when iterating; want = %s", expectedErr) - } - - return - } - } - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Run("Hop By Hop", func(t *testing.T) { - extHdr := IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: test.bytes} - check(t, extHdr.Iter(), test.err) - }) - - t.Run("Destination", func(t *testing.T) { - extHdr := IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: test.bytes} - check(t, extHdr.Iter(), test.err) - }) - }) - } -} - -func TestIPv6OptionsExtHdrIter(t *testing.T) { - tests := []struct { - name string - bytes []byte - expected []IPv6ExtHdrOption - }{ - { - name: "Single unknown with zero length", - bytes: []byte{255, 0}, - expected: []IPv6ExtHdrOption{ - &IPv6UnknownExtHdrOption{Identifier: 255, Data: []byte{}}, - }, - }, - { - name: "Single unknown with non-zero length", - bytes: []byte{255, 3, 1, 2, 3}, - expected: []IPv6ExtHdrOption{ - &IPv6UnknownExtHdrOption{Identifier: 255, Data: []byte{1, 2, 3}}, - }, - }, - { - name: "Single Pad1", - bytes: []byte{0}, - }, - { - name: "Two Pad1", - bytes: []byte{0, 0}, - }, - { - name: "Single Pad3", - bytes: []byte{1, 1, 1}, - }, - { - name: "Single Pad5", - bytes: []byte{1, 3, 1, 2, 3}, - }, - { - name: "Multiple Pad", - bytes: []byte{ - // Pad1 - 0, - - // Pad2 - 1, 0, - - // Pad3 - 1, 1, 1, - - // Pad4 - 1, 2, 1, 2, - - // Pad5 - 1, 3, 1, 2, 3, - }, - }, - { - name: "Multiple options", - bytes: []byte{ - // Pad1 - 0, - - // Unknown - 255, 0, - - // Pad2 - 1, 0, - - // Unknown - 254, 1, 1, - - // Pad3 - 1, 1, 1, - - // Unknown - 253, 4, 2, 3, 4, 5, - - // Pad4 - 1, 2, 1, 2, - }, - expected: []IPv6ExtHdrOption{ - &IPv6UnknownExtHdrOption{Identifier: 255, Data: []byte{}}, - &IPv6UnknownExtHdrOption{Identifier: 254, Data: []byte{1}}, - &IPv6UnknownExtHdrOption{Identifier: 253, Data: []byte{2, 3, 4, 5}}, - }, - }, - } - - checkIter := func(t *testing.T, it IPv6OptionsExtHdrOptionsIterator, expected []IPv6ExtHdrOption) { - for i, e := range expected { - opt, done, err := it.Next() - if err != nil { - t.Errorf("(i=%d) Next(): %s", i, err) - } - if done { - t.Errorf("(i=%d) unexpectedly done iterating", i) - } - if diff := cmp.Diff(e, opt); diff != "" { - t.Errorf("(i=%d) got option mismatch (-want +got):\n%s", i, diff) - } - - if t.Failed() { - t.FailNow() - } - } - - opt, done, err := it.Next() - if err != nil { - t.Errorf("(last) Next(): %s", err) - } - if !done { - t.Errorf("(last) iterator unexpectedly not done") - } - if opt != nil { - t.Errorf("(last) got Next() = %T, want = nil", opt) - } - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - t.Run("Hop By Hop", func(t *testing.T) { - extHdr := IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: test.bytes} - checkIter(t, extHdr.Iter(), test.expected) - }) - - t.Run("Destination", func(t *testing.T) { - extHdr := IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: test.bytes} - checkIter(t, extHdr.Iter(), test.expected) - }) - }) - } -} - -func TestIPv6RoutingExtHdr(t *testing.T) { - tests := []struct { - name string - bytes []byte - segmentsLeft uint8 - }{ - { - name: "Zeroes", - bytes: []byte{0, 0, 0, 0, 0, 0}, - segmentsLeft: 0, - }, - { - name: "Ones", - bytes: []byte{1, 1, 1, 1, 1, 1}, - segmentsLeft: 1, - }, - { - name: "Mixed", - bytes: []byte{1, 2, 3, 4, 5, 6}, - segmentsLeft: 2, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - extHdr := IPv6RoutingExtHdr(test.bytes) - if got := extHdr.SegmentsLeft(); got != test.segmentsLeft { - t.Errorf("got SegmentsLeft() = %d, want = %d", got, test.segmentsLeft) - } - }) - } -} - -func TestIPv6FragmentExtHdr(t *testing.T) { - tests := []struct { - name string - bytes [6]byte - fragmentOffset uint16 - more bool - id uint32 - }{ - { - name: "Zeroes", - bytes: [6]byte{0, 0, 0, 0, 0, 0}, - fragmentOffset: 0, - more: false, - id: 0, - }, - { - name: "Ones", - bytes: [6]byte{0, 9, 0, 0, 0, 1}, - fragmentOffset: 1, - more: true, - id: 1, - }, - { - name: "Mixed", - bytes: [6]byte{68, 9, 128, 4, 2, 1}, - fragmentOffset: 2177, - more: true, - id: 2147746305, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - extHdr := IPv6FragmentExtHdr(test.bytes) - if got := extHdr.FragmentOffset(); got != test.fragmentOffset { - t.Errorf("got FragmentOffset() = %d, want = %d", got, test.fragmentOffset) - } - if got := extHdr.More(); got != test.more { - t.Errorf("got More() = %t, want = %t", got, test.more) - } - if got := extHdr.ID(); got != test.id { - t.Errorf("got ID() = %d, want = %d", got, test.id) - } - }) - } -} - -func makeVectorisedViewFromByteBuffers(bs ...[]byte) buffer.VectorisedView { - size := 0 - var vs []buffer.View - - for _, b := range bs { - vs = append(vs, buffer.View(b)) - size += len(b) - } - - return buffer.NewVectorisedView(size, vs) -} - -func TestIPv6ExtHdrIterErr(t *testing.T) { - tests := []struct { - name string - firstNextHdr IPv6ExtensionHeaderIdentifier - payload buffer.VectorisedView - err error - }{ - { - name: "Upper layer only without data", - firstNextHdr: 255, - }, - { - name: "Upper layer only with data", - firstNextHdr: 255, - payload: makeVectorisedViewFromByteBuffers([]byte{1, 2, 3, 4}), - }, - { - name: "No next header", - firstNextHdr: IPv6NoNextHeaderIdentifier, - }, - { - name: "No next header with data", - firstNextHdr: IPv6NoNextHeaderIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{1, 2, 3, 4}), - }, - { - name: "Valid single hop by hop", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3, 4}), - }, - { - name: "Hop by hop too small", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3}), - err: io.ErrUnexpectedEOF, - }, - { - name: "Valid single fragment", - firstNextHdr: IPv6FragmentExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 68, 9, 128, 4, 2, 1}), - }, - { - name: "Fragment too small", - firstNextHdr: IPv6FragmentExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 68, 9, 128, 4, 2}), - err: io.ErrUnexpectedEOF, - }, - { - name: "Valid single destination", - firstNextHdr: IPv6DestinationOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3, 4}), - }, - { - name: "Destination too small", - firstNextHdr: IPv6DestinationOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3}), - err: io.ErrUnexpectedEOF, - }, - { - name: "Valid single routing", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2, 3, 4, 5, 6}), - }, - { - name: "Valid single routing across views", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2}, []byte{3, 4, 5, 6}), - }, - { - name: "Routing too small with zero length field", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2, 3, 4, 5}), - err: io.ErrUnexpectedEOF, - }, - { - name: "Valid routing with non-zero length field", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8}), - }, - { - name: "Valid routing with non-zero length field across views", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7, 8}), - }, - { - name: "Routing too small with non-zero length field", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7}), - err: io.ErrUnexpectedEOF, - }, - { - name: "Routing too small with non-zero length field across views", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7}), - err: io.ErrUnexpectedEOF, - }, - { - name: "Mixed", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Hop By Hop Options extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, - - // (Atomic) Fragment extension header. - // - // Reserved bits are 1 which should not affect anything. - uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, - - // Routing extension header. - uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, - - // Destination Options extension header. - 255, 0, 255, 4, 1, 2, 3, 4, - - // Upper layer data. - 1, 2, 3, 4, - }), - }, - { - name: "Mixed without upper layer data", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Hop By Hop Options extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, - - // (Atomic) Fragment extension header. - // - // Reserved bits are 1 which should not affect anything. - uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, - - // Routing extension header. - uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, - - // Destination Options extension header. - 255, 0, 255, 4, 1, 2, 3, 4, - }), - }, - { - name: "Mixed without upper layer data but last ext hdr too small", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Hop By Hop Options extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, - - // (Atomic) Fragment extension header. - // - // Reserved bits are 1 which should not affect anything. - uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, - - // Routing extension header. - uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, - - // Destination Options extension header. - 255, 0, 255, 4, 1, 2, 3, - }), - err: io.ErrUnexpectedEOF, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - it := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload) - - for i := 0; ; i++ { - _, done, err := it.Next() - if err != nil { - // If we encountered a non-nil error while iterating, make sure it is - // is the same error as test.err. - if !errors.Is(err, test.err) { - t.Fatalf("got %d-th Next() = %v, want = %v", i, err, test.err) - } - - return - } - if done { - // If we are done (without an error), make sure that we did not expect - // an error. - if test.err != nil { - t.Fatalf("expected error when iterating; want = %s", test.err) - } - - return - } - } - }) - } -} - -func TestIPv6ExtHdrIter(t *testing.T) { - routingExtHdrWithUpperLayerData := buffer.View([]byte{255, 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4}) - upperLayerData := buffer.View([]byte{1, 2, 3, 4}) - tests := []struct { - name string - firstNextHdr IPv6ExtensionHeaderIdentifier - payload buffer.VectorisedView - expected []IPv6PayloadHeader - }{ - // With a non-atomic fragment that is not the first fragment, the payload - // after the fragment will not be parsed because the payload is expected to - // only hold upper layer data. - { - name: "hopbyhop - fragment (not first) - routing - upper", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Hop By Hop extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, - - // Fragment extension header. - // - // More = 1, Fragment Offset = 2117, ID = 2147746305 - uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1, - - // Routing extension header. - // - // Even though we have a routing ext header here, it should be - // be interpretted as raw bytes as only the first fragment is expected - // to hold headers. - 255, 0, 1, 2, 3, 4, 5, 6, - - // Upper layer data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}}, - IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}), - IPv6RawPayloadHeader{ - Identifier: IPv6RoutingExtHdrIdentifier, - Buf: routingExtHdrWithUpperLayerData.ToVectorisedView(), - }, - }, - }, - { - name: "hopbyhop - fragment (first) - routing - upper", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Hop By Hop extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, - - // Fragment extension header. - // - // More = 1, Fragment Offset = 0, ID = 2147746305 - uint8(IPv6RoutingExtHdrIdentifier), 0, 0, 1, 128, 4, 2, 1, - - // Routing extension header. - 255, 0, 1, 2, 3, 4, 5, 6, - - // Upper layer data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}}, - IPv6FragmentExtHdr([6]byte{0, 1, 128, 4, 2, 1}), - IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}), - IPv6RawPayloadHeader{ - Identifier: 255, - Buf: upperLayerData.ToVectorisedView(), - }, - }, - }, - { - name: "fragment - routing - upper (across views)", - firstNextHdr: IPv6FragmentExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Fragment extension header. - uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1, - - // Routing extension header. - 255, 0, 1, 2}, []byte{3, 4, 5, 6, - - // Upper layer data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}), - IPv6RawPayloadHeader{ - Identifier: IPv6RoutingExtHdrIdentifier, - Buf: routingExtHdrWithUpperLayerData.ToVectorisedView(), - }, - }, - }, - - // If we have an atomic fragment, the payload following the fragment - // extension header should be parsed normally. - { - name: "atomic fragment - routing - destination - upper", - firstNextHdr: IPv6FragmentExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Fragment extension header. - // - // Reserved bits are 1 which should not affect anything. - uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, - - // Routing extension header. - uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, - - // Destination Options extension header. - 255, 0, 1, 4, 1, 2, 3, 4, - - // Upper layer data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), - IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}), - IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}}, - IPv6RawPayloadHeader{ - Identifier: 255, - Buf: upperLayerData.ToVectorisedView(), - }, - }, - }, - { - name: "atomic fragment - routing - upper (across views)", - firstNextHdr: IPv6FragmentExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Fragment extension header. - // - // Reserved bits are 1 which should not affect anything. - uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1, - - // Routing extension header. - 255, 0, 1, 2}, []byte{3, 4, 5, 6, - - // Upper layer data. - 1, 2}, []byte{3, 4}), - expected: []IPv6PayloadHeader{ - IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), - IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}), - IPv6RawPayloadHeader{ - Identifier: 255, - Buf: makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]), - }, - }, - }, - { - name: "atomic fragment - destination - no next header", - firstNextHdr: IPv6FragmentExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Fragment extension header. - // - // Res (Reserved) bits are 1 which should not affect anything. - uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 0, 6, 128, 4, 2, 1, - - // Destination Options extension header. - uint8(IPv6NoNextHeaderIdentifier), 0, 1, 4, 1, 2, 3, 4, - - // Random data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), - IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}}, - }, - }, - { - name: "routing - atomic fragment - no next header", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Routing extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, - - // Fragment extension header. - // - // Reserved bits are 1 which should not affect anything. - uint8(IPv6NoNextHeaderIdentifier), 0, 0, 6, 128, 4, 2, 1, - - // Random data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}), - IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), - }, - }, - { - name: "routing - atomic fragment - no next header (across views)", - firstNextHdr: IPv6RoutingExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Routing extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, - - // Fragment extension header. - // - // Reserved bits are 1 which should not affect anything. - uint8(IPv6NoNextHeaderIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1, - - // Random data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}), - IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), - }, - }, - { - name: "hopbyhop - routing - fragment - no next header", - firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, - payload: makeVectorisedViewFromByteBuffers([]byte{ - // Hop By Hop Options extension header. - uint8(IPv6RoutingExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, - - // Routing extension header. - uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, - - // Fragment extension header. - // - // Fragment Offset = 32; Res = 6. - uint8(IPv6NoNextHeaderIdentifier), 0, 1, 6, 128, 4, 2, 1, - - // Random data. - 1, 2, 3, 4, - }), - expected: []IPv6PayloadHeader{ - IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}}, - IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}), - IPv6FragmentExtHdr([6]byte{1, 6, 128, 4, 2, 1}), - IPv6RawPayloadHeader{ - Identifier: IPv6NoNextHeaderIdentifier, - Buf: upperLayerData.ToVectorisedView(), - }, - }, - }, - - // Test the raw payload for common transport layer protocol numbers. - { - name: "TCP raw payload", - firstNextHdr: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber), - payload: makeVectorisedViewFromByteBuffers(upperLayerData), - expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ - Identifier: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber), - Buf: upperLayerData.ToVectorisedView(), - }}, - }, - { - name: "UDP raw payload", - firstNextHdr: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber), - payload: makeVectorisedViewFromByteBuffers(upperLayerData), - expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ - Identifier: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber), - Buf: upperLayerData.ToVectorisedView(), - }}, - }, - { - name: "ICMPv4 raw payload", - firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber), - payload: makeVectorisedViewFromByteBuffers(upperLayerData), - expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ - Identifier: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber), - Buf: upperLayerData.ToVectorisedView(), - }}, - }, - { - name: "ICMPv6 raw payload", - firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber), - payload: makeVectorisedViewFromByteBuffers(upperLayerData), - expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ - Identifier: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber), - Buf: upperLayerData.ToVectorisedView(), - }}, - }, - { - name: "Unknwon next header raw payload", - firstNextHdr: 255, - payload: makeVectorisedViewFromByteBuffers(upperLayerData), - expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ - Identifier: 255, - Buf: upperLayerData.ToVectorisedView(), - }}, - }, - { - name: "Unknwon next header raw payload (across views)", - firstNextHdr: 255, - payload: makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]), - expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ - Identifier: 255, - Buf: makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]), - }}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - it := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload) - - for i, e := range test.expected { - extHdr, done, err := it.Next() - if err != nil { - t.Errorf("(i=%d) Next(): %s", i, err) - } - if done { - t.Errorf("(i=%d) unexpectedly done iterating", i) - } - if diff := cmp.Diff(e, extHdr); diff != "" { - t.Errorf("(i=%d) got ext hdr mismatch (-want +got):\n%s", i, diff) - } - - if t.Failed() { - t.FailNow() - } - } - - extHdr, done, err := it.Next() - if err != nil { - t.Errorf("(last) Next(): %s", err) - } - if !done { - t.Errorf("(last) iterator unexpectedly not done") - } - if extHdr != nil { - t.Errorf("(last) got Next() = %T, want = nil", extHdr) - } - }) - } -} - -var _ IPv6SerializableHopByHopOption = (*dummyHbHOptionSerializer)(nil) - -// dummyHbHOptionSerializer provides a generic implementation of -// IPv6SerializableHopByHopOption for use in tests. -type dummyHbHOptionSerializer struct { - id IPv6ExtHdrOptionIdentifier - payload []byte - align int - alignOffset int -} - -// identifier implements IPv6SerializableHopByHopOption. -func (s *dummyHbHOptionSerializer) identifier() IPv6ExtHdrOptionIdentifier { - return s.id -} - -// length implements IPv6SerializableHopByHopOption. -func (s *dummyHbHOptionSerializer) length() uint8 { - return uint8(len(s.payload)) -} - -// alignment implements IPv6SerializableHopByHopOption. -func (s *dummyHbHOptionSerializer) alignment() (int, int) { - align := 1 - if s.align != 0 { - align = s.align - } - return align, s.alignOffset -} - -// serializeInto implements IPv6SerializableHopByHopOption. -func (s *dummyHbHOptionSerializer) serializeInto(b []byte) uint8 { - return uint8(copy(b, s.payload)) -} - -func TestIPv6HopByHopSerializer(t *testing.T) { - validateDummies := func(t *testing.T, serializable IPv6SerializableHopByHopOption, deserialized IPv6ExtHdrOption) { - t.Helper() - dummy, ok := serializable.(*dummyHbHOptionSerializer) - if !ok { - t.Fatalf("got serializable = %T, want = *dummyHbHOptionSerializer", serializable) - } - unknown, ok := deserialized.(*IPv6UnknownExtHdrOption) - if !ok { - t.Fatalf("got deserialized = %T, want = %T", deserialized, &IPv6UnknownExtHdrOption{}) - } - if dummy.id != unknown.Identifier { - t.Errorf("got deserialized identifier = %d, want = %d", unknown.Identifier, dummy.id) - } - if diff := cmp.Diff(dummy.payload, unknown.Data); diff != "" { - t.Errorf("option payload deserialization mismatch (-want +got):\n%s", diff) - } - } - tests := []struct { - name string - nextHeader uint8 - options []IPv6SerializableHopByHopOption - expect []byte - validate func(*testing.T, IPv6SerializableHopByHopOption, IPv6ExtHdrOption) - }{ - { - name: "single option", - nextHeader: 13, - options: []IPv6SerializableHopByHopOption{ - &dummyHbHOptionSerializer{ - id: 15, - payload: []byte{9, 8, 7, 6}, - }, - }, - expect: []byte{13, 0, 15, 4, 9, 8, 7, 6}, - validate: validateDummies, - }, - { - name: "short option padN zero", - nextHeader: 88, - options: []IPv6SerializableHopByHopOption{ - &dummyHbHOptionSerializer{ - id: 22, - payload: []byte{4, 5}, - }, - }, - expect: []byte{88, 0, 22, 2, 4, 5, 1, 0}, - validate: validateDummies, - }, - { - name: "short option pad1", - nextHeader: 11, - options: []IPv6SerializableHopByHopOption{ - &dummyHbHOptionSerializer{ - id: 33, - payload: []byte{1, 2, 3}, - }, - }, - expect: []byte{11, 0, 33, 3, 1, 2, 3, 0}, - validate: validateDummies, - }, - { - name: "long option padN", - nextHeader: 55, - options: []IPv6SerializableHopByHopOption{ - &dummyHbHOptionSerializer{ - id: 77, - payload: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }, - }, - expect: []byte{55, 1, 77, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 0, 0}, - validate: validateDummies, - }, - { - name: "two options", - nextHeader: 33, - options: []IPv6SerializableHopByHopOption{ - &dummyHbHOptionSerializer{ - id: 11, - payload: []byte{1, 2, 3}, - }, - &dummyHbHOptionSerializer{ - id: 22, - payload: []byte{4, 5, 6}, - }, - }, - expect: []byte{33, 1, 11, 3, 1, 2, 3, 22, 3, 4, 5, 6, 1, 2, 0, 0}, - validate: validateDummies, - }, - { - name: "two options align 2n", - nextHeader: 33, - options: []IPv6SerializableHopByHopOption{ - &dummyHbHOptionSerializer{ - id: 11, - payload: []byte{1, 2, 3}, - }, - &dummyHbHOptionSerializer{ - id: 22, - payload: []byte{4, 5, 6}, - align: 2, - }, - }, - expect: []byte{33, 1, 11, 3, 1, 2, 3, 0, 22, 3, 4, 5, 6, 1, 1, 0}, - validate: validateDummies, - }, - { - name: "two options align 8n+1", - nextHeader: 33, - options: []IPv6SerializableHopByHopOption{ - &dummyHbHOptionSerializer{ - id: 11, - payload: []byte{1, 2}, - }, - &dummyHbHOptionSerializer{ - id: 22, - payload: []byte{4, 5, 6}, - align: 8, - alignOffset: 1, - }, - }, - expect: []byte{33, 1, 11, 2, 1, 2, 1, 1, 0, 22, 3, 4, 5, 6, 1, 0}, - validate: validateDummies, - }, - { - name: "no options", - nextHeader: 33, - options: []IPv6SerializableHopByHopOption{}, - expect: []byte{33, 0, 1, 4, 0, 0, 0, 0}, - }, - { - name: "Router Alert", - nextHeader: 33, - options: []IPv6SerializableHopByHopOption{&IPv6RouterAlertOption{Value: IPv6RouterAlertMLD}}, - expect: []byte{33, 0, 5, 2, 0, 0, 1, 0}, - validate: func(t *testing.T, _ IPv6SerializableHopByHopOption, deserialized IPv6ExtHdrOption) { - t.Helper() - routerAlert, ok := deserialized.(*IPv6RouterAlertOption) - if !ok { - t.Fatalf("got deserialized = %T, want = *IPv6RouterAlertOption", deserialized) - } - if routerAlert.Value != IPv6RouterAlertMLD { - t.Errorf("got routerAlert.Value = %d, want = %d", routerAlert.Value, IPv6RouterAlertMLD) - } - }, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := IPv6SerializableHopByHopExtHdr(test.options) - length := s.length() - if length != len(test.expect) { - t.Fatalf("got s.length() = %d, want = %d", length, len(test.expect)) - } - b := make([]byte, length) - for i := range b { - // Fill the buffer with ones to ensure all padding is correctly set. - b[i] = 0xFF - } - if got := s.serializeInto(test.nextHeader, b); got != length { - t.Fatalf("got s.serializeInto(..) = %d, want = %d", got, length) - } - if diff := cmp.Diff(test.expect, b); diff != "" { - t.Fatalf("serialization mismatch (-want +got):\n%s", diff) - } - - // Deserialize the options and verify them. - optLen := (b[ipv6HopByHopExtHdrLengthOffset] + ipv6HopByHopExtHdrUnaccountedLenWords) * ipv6ExtHdrLenBytesPerUnit - iter := ipv6OptionsExtHdr(b[ipv6HopByHopExtHdrOptionsOffset:optLen]).Iter() - for _, testOpt := range test.options { - opt, done, err := iter.Next() - if err != nil { - t.Fatalf("iter.Next(): %s", err) - } - if done { - t.Fatalf("got iter.Next() = (%T, %t, _), want = (_, false, _)", opt, done) - } - test.validate(t, testOpt, opt) - } - opt, done, err := iter.Next() - if err != nil { - t.Fatalf("iter.Next(): %s", err) - } - if !done { - t.Fatalf("got iter.Next() = (%T, %t, _), want = (_, true, _)", opt, done) - } - }) - } -} - -var _ IPv6SerializableExtHdr = (*dummyIPv6ExtHdrSerializer)(nil) - -// dummyIPv6ExtHdrSerializer provides a generic implementation of -// IPv6SerializableExtHdr for use in tests. -// -// The dummy header always carries the nextHeader value in the first byte. -type dummyIPv6ExtHdrSerializer struct { - id IPv6ExtensionHeaderIdentifier - headerContents []byte -} - -// identifier implements IPv6SerializableExtHdr. -func (s *dummyIPv6ExtHdrSerializer) identifier() IPv6ExtensionHeaderIdentifier { - return s.id -} - -// length implements IPv6SerializableExtHdr. -func (s *dummyIPv6ExtHdrSerializer) length() int { - return len(s.headerContents) + 1 -} - -// serializeInto implements IPv6SerializableExtHdr. -func (s *dummyIPv6ExtHdrSerializer) serializeInto(nextHeader uint8, b []byte) int { - b[0] = nextHeader - return copy(b[1:], s.headerContents) + 1 -} - -func TestIPv6ExtHdrSerializer(t *testing.T) { - tests := []struct { - name string - headers []IPv6SerializableExtHdr - nextHeader tcpip.TransportProtocolNumber - expectSerialized []byte - expectNextHeader uint8 - }{ - { - name: "one header", - headers: []IPv6SerializableExtHdr{ - &dummyIPv6ExtHdrSerializer{ - id: 15, - headerContents: []byte{1, 2, 3, 4}, - }, - }, - nextHeader: TCPProtocolNumber, - expectSerialized: []byte{byte(TCPProtocolNumber), 1, 2, 3, 4}, - expectNextHeader: 15, - }, - { - name: "two headers", - headers: []IPv6SerializableExtHdr{ - &dummyIPv6ExtHdrSerializer{ - id: 22, - headerContents: []byte{1, 2, 3}, - }, - &dummyIPv6ExtHdrSerializer{ - id: 23, - headerContents: []byte{4, 5, 6}, - }, - }, - nextHeader: ICMPv6ProtocolNumber, - expectSerialized: []byte{ - 23, 1, 2, 3, - byte(ICMPv6ProtocolNumber), 4, 5, 6, - }, - expectNextHeader: 22, - }, - { - name: "no headers", - headers: []IPv6SerializableExtHdr{}, - nextHeader: UDPProtocolNumber, - expectSerialized: []byte{}, - expectNextHeader: byte(UDPProtocolNumber), - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := IPv6ExtHdrSerializer(test.headers) - l := s.Length() - if got, want := l, len(test.expectSerialized); got != want { - t.Fatalf("got serialized length = %d, want = %d", got, want) - } - b := make([]byte, l) - for i := range b { - // Fill the buffer with garbage to make sure we're writing to all bytes. - b[i] = 0xFF - } - nextHeader, serializedLen := s.Serialize(test.nextHeader, b) - if serializedLen != len(test.expectSerialized) || nextHeader != test.expectNextHeader { - t.Errorf( - "got s.Serialize(..) = (%d, %d), want = (%d, %d)", - nextHeader, - serializedLen, - test.expectNextHeader, - len(test.expectSerialized), - ) - } - if diff := cmp.Diff(test.expectSerialized, b); diff != "" { - t.Errorf("serialization mismatch (-want +got):\n%s", diff) - } - }) - } -} diff --git a/pkg/tcpip/header/ipv6_test.go b/pkg/tcpip/header/ipv6_test.go deleted file mode 100644 index 89be84068..000000000 --- a/pkg/tcpip/header/ipv6_test.go +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header_test - -import ( - "bytes" - "crypto/sha256" - "fmt" - "testing" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/rand" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/testutil" -) - -const linkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06") - -var ( - linkLocalAddr = testutil.MustParse6("fe80::1") - linkLocalMulticastAddr = testutil.MustParse6("ff02::1") - uniqueLocalAddr1 = testutil.MustParse6("fc00::1") - uniqueLocalAddr2 = testutil.MustParse6("fd00::2") - globalAddr = testutil.MustParse6("a000::1") -) - -func TestEthernetAdddressToModifiedEUI64(t *testing.T) { - expectedIID := [header.IIDSize]byte{0, 2, 3, 255, 254, 4, 5, 6} - - if diff := cmp.Diff(expectedIID, header.EthernetAddressToModifiedEUI64(linkAddr)); diff != "" { - t.Errorf("EthernetAddressToModifiedEUI64(%s) mismatch (-want +got):\n%s", linkAddr, diff) - } - - var buf [header.IIDSize]byte - header.EthernetAdddressToModifiedEUI64IntoBuf(linkAddr, buf[:]) - if diff := cmp.Diff(expectedIID, buf); diff != "" { - t.Errorf("EthernetAddressToModifiedEUI64IntoBuf(%s, _) mismatch (-want +got):\n%s", linkAddr, diff) - } -} - -func TestLinkLocalAddr(t *testing.T) { - if got, want := header.LinkLocalAddr(linkAddr), testutil.MustParse6("fe80::2:3ff:fe04:506"); got != want { - t.Errorf("got LinkLocalAddr(%s) = %s, want = %s", linkAddr, got, want) - } -} - -func TestAppendOpaqueInterfaceIdentifier(t *testing.T) { - var secretKeyBuf [header.OpaqueIIDSecretKeyMinBytes * 2]byte - if n, err := rand.Read(secretKeyBuf[:]); err != nil { - t.Fatalf("rand.Read(_): %s", err) - } else if want := header.OpaqueIIDSecretKeyMinBytes * 2; n != want { - t.Fatalf("expected rand.Read to read %d bytes, read %d bytes", want, n) - } - - tests := []struct { - name string - prefix tcpip.Subnet - nicName string - dadCounter uint8 - secretKey []byte - }{ - { - name: "SecretKey of minimum size", - prefix: header.IPv6LinkLocalPrefix.Subnet(), - nicName: "eth0", - dadCounter: 0, - secretKey: secretKeyBuf[:header.OpaqueIIDSecretKeyMinBytes], - }, - { - name: "SecretKey of less than minimum size", - prefix: func() tcpip.Subnet { - addrWithPrefix := tcpip.AddressWithPrefix{ - Address: "\x01\x02\x03\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - PrefixLen: header.IIDOffsetInIPv6Address * 8, - } - return addrWithPrefix.Subnet() - }(), - nicName: "eth10", - dadCounter: 1, - secretKey: secretKeyBuf[:header.OpaqueIIDSecretKeyMinBytes/2], - }, - { - name: "SecretKey of more than minimum size", - prefix: func() tcpip.Subnet { - addrWithPrefix := tcpip.AddressWithPrefix{ - Address: "\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - PrefixLen: header.IIDOffsetInIPv6Address * 8, - } - return addrWithPrefix.Subnet() - }(), - nicName: "eth11", - dadCounter: 2, - secretKey: secretKeyBuf[:header.OpaqueIIDSecretKeyMinBytes*2], - }, - { - name: "Nil SecretKey and empty nicName", - prefix: func() tcpip.Subnet { - addrWithPrefix := tcpip.AddressWithPrefix{ - Address: "\x01\x02\x03\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - PrefixLen: header.IIDOffsetInIPv6Address * 8, - } - return addrWithPrefix.Subnet() - }(), - nicName: "", - dadCounter: 3, - secretKey: nil, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - h := sha256.New() - h.Write([]byte(test.prefix.ID()[:header.IIDOffsetInIPv6Address])) - h.Write([]byte(test.nicName)) - h.Write([]byte{test.dadCounter}) - if k := test.secretKey; k != nil { - h.Write(k) - } - var hashSum [sha256.Size]byte - h.Sum(hashSum[:0]) - want := hashSum[:header.IIDSize] - - // Passing a nil buffer should result in a new buffer returned with the - // IID. - if got := header.AppendOpaqueInterfaceIdentifier(nil, test.prefix, test.nicName, test.dadCounter, test.secretKey); !bytes.Equal(got, want) { - t.Errorf("got AppendOpaqueInterfaceIdentifier(nil, %s, %s, %d, %x) = %x, want = %x", test.prefix, test.nicName, test.dadCounter, test.secretKey, got, want) - } - - // Passing a buffer with sufficient capacity for the IID should populate - // the buffer provided. - var iidBuf [header.IIDSize]byte - if got := header.AppendOpaqueInterfaceIdentifier(iidBuf[:0], test.prefix, test.nicName, test.dadCounter, test.secretKey); !bytes.Equal(got, want) { - t.Errorf("got AppendOpaqueInterfaceIdentifier(iidBuf[:0], %s, %s, %d, %x) = %x, want = %x", test.prefix, test.nicName, test.dadCounter, test.secretKey, got, want) - } - if got := iidBuf[:]; !bytes.Equal(got, want) { - t.Errorf("got iidBuf = %x, want = %x", got, want) - } - }) - } -} - -func TestLinkLocalAddrWithOpaqueIID(t *testing.T) { - var secretKeyBuf [header.OpaqueIIDSecretKeyMinBytes * 2]byte - if n, err := rand.Read(secretKeyBuf[:]); err != nil { - t.Fatalf("rand.Read(_): %s", err) - } else if want := header.OpaqueIIDSecretKeyMinBytes * 2; n != want { - t.Fatalf("expected rand.Read to read %d bytes, read %d bytes", want, n) - } - - prefix := header.IPv6LinkLocalPrefix.Subnet() - - tests := []struct { - name string - prefix tcpip.Subnet - nicName string - dadCounter uint8 - secretKey []byte - }{ - { - name: "SecretKey of minimum size", - nicName: "eth0", - dadCounter: 0, - secretKey: secretKeyBuf[:header.OpaqueIIDSecretKeyMinBytes], - }, - { - name: "SecretKey of less than minimum size", - nicName: "eth10", - dadCounter: 1, - secretKey: secretKeyBuf[:header.OpaqueIIDSecretKeyMinBytes/2], - }, - { - name: "SecretKey of more than minimum size", - nicName: "eth11", - dadCounter: 2, - secretKey: secretKeyBuf[:header.OpaqueIIDSecretKeyMinBytes*2], - }, - { - name: "Nil SecretKey and empty nicName", - nicName: "", - dadCounter: 3, - secretKey: nil, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - addrBytes := [header.IPv6AddressSize]byte{ - 0: 0xFE, - 1: 0x80, - } - - want := tcpip.Address(header.AppendOpaqueInterfaceIdentifier( - addrBytes[:header.IIDOffsetInIPv6Address], - prefix, - test.nicName, - test.dadCounter, - test.secretKey, - )) - - if got := header.LinkLocalAddrWithOpaqueIID(test.nicName, test.dadCounter, test.secretKey); got != want { - t.Errorf("got LinkLocalAddrWithOpaqueIID(%s, %d, %x) = %s, want = %s", test.nicName, test.dadCounter, test.secretKey, got, want) - } - }) - } -} - -func TestIsV6LinkLocalMulticastAddress(t *testing.T) { - tests := []struct { - name string - addr tcpip.Address - expected bool - }{ - { - name: "Valid Link Local Multicast", - addr: linkLocalMulticastAddr, - expected: true, - }, - { - name: "Valid Link Local Multicast with flags", - addr: "\xff\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - expected: true, - }, - { - name: "Link Local Unicast", - addr: linkLocalAddr, - expected: false, - }, - { - name: "IPv4 Multicast", - addr: "\xe0\x00\x00\x01", - expected: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := header.IsV6LinkLocalMulticastAddress(test.addr); got != test.expected { - t.Errorf("got header.IsV6LinkLocalMulticastAddress(%s) = %t, want = %t", test.addr, got, test.expected) - } - }) - } -} - -func TestIsV6LinkLocalUnicastAddress(t *testing.T) { - tests := []struct { - name string - addr tcpip.Address - expected bool - }{ - { - name: "Valid Link Local Unicast", - addr: linkLocalAddr, - expected: true, - }, - { - name: "Link Local Multicast", - addr: linkLocalMulticastAddr, - expected: false, - }, - { - name: "Unique Local", - addr: uniqueLocalAddr1, - expected: false, - }, - { - name: "Global", - addr: globalAddr, - expected: false, - }, - { - name: "IPv4 Link Local", - addr: "\xa9\xfe\x00\x01", - expected: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if got := header.IsV6LinkLocalUnicastAddress(test.addr); got != test.expected { - t.Errorf("got header.IsV6LinkLocalUnicastAddress(%s) = %t, want = %t", test.addr, got, test.expected) - } - }) - } -} - -func TestScopeForIPv6Address(t *testing.T) { - tests := []struct { - name string - addr tcpip.Address - scope header.IPv6AddressScope - err tcpip.Error - }{ - { - name: "Unique Local", - addr: uniqueLocalAddr1, - scope: header.GlobalScope, - err: nil, - }, - { - name: "Link Local Unicast", - addr: linkLocalAddr, - scope: header.LinkLocalScope, - err: nil, - }, - { - name: "Link Local Multicast", - addr: linkLocalMulticastAddr, - scope: header.LinkLocalScope, - err: nil, - }, - { - name: "Global", - addr: globalAddr, - scope: header.GlobalScope, - err: nil, - }, - { - name: "IPv4", - addr: "\x01\x02\x03\x04", - scope: header.GlobalScope, - err: &tcpip.ErrBadAddress{}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got, err := header.ScopeForIPv6Address(test.addr) - if diff := cmp.Diff(test.err, err); diff != "" { - t.Errorf("unexpected error from header.IsV6UniqueLocalAddress(%s), (-want, +got):\n%s", test.addr, diff) - } - if got != test.scope { - t.Errorf("got header.IsV6UniqueLocalAddress(%s) = (%d, _), want = (%d, _)", test.addr, got, test.scope) - } - }) - } -} - -func TestSolicitedNodeAddr(t *testing.T) { - tests := []struct { - addr tcpip.Address - want tcpip.Address - }{ - { - addr: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\xa0", - want: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x0e\x0f\xa0", - }, - { - addr: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\xdd\x0e\x0f\xa0", - want: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x0e\x0f\xa0", - }, - { - addr: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\xdd\x01\x02\x03", - want: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x01\x02\x03", - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("%s", test.addr), func(t *testing.T) { - if got := header.SolicitedNodeAddr(test.addr); got != test.want { - t.Fatalf("got header.SolicitedNodeAddr(%s) = %s, want = %s", test.addr, got, test.want) - } - }) - } -} - -func TestV6MulticastScope(t *testing.T) { - tests := []struct { - addr tcpip.Address - want header.IPv6MulticastScope - }{ - { - addr: "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6Reserved0MulticastScope, - }, - { - addr: "\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6InterfaceLocalMulticastScope, - }, - { - addr: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6LinkLocalMulticastScope, - }, - { - addr: "\xff\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6RealmLocalMulticastScope, - }, - { - addr: "\xff\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6AdminLocalMulticastScope, - }, - { - addr: "\xff\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6SiteLocalMulticastScope, - }, - { - addr: "\xff\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6MulticastScope(6), - }, - { - addr: "\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6MulticastScope(7), - }, - { - addr: "\xff\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6OrganizationLocalMulticastScope, - }, - { - addr: "\xff\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6MulticastScope(9), - }, - { - addr: "\xff\x0a\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6MulticastScope(10), - }, - { - addr: "\xff\x0b\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6MulticastScope(11), - }, - { - addr: "\xff\x0c\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6MulticastScope(12), - }, - { - addr: "\xff\x0d\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6MulticastScope(13), - }, - { - addr: "\xff\x0e\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6GlobalMulticastScope, - }, - { - addr: "\xff\x0f\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", - want: header.IPv6ReservedFMulticastScope, - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("%s", test.addr), func(t *testing.T) { - if got := header.V6MulticastScope(test.addr); got != test.want { - t.Fatalf("got header.V6MulticastScope(%s) = %d, want = %d", test.addr, got, test.want) - } - }) - } -} diff --git a/pkg/tcpip/header/ipversion_test.go b/pkg/tcpip/header/ipversion_test.go deleted file mode 100644 index b5540bf66..000000000 --- a/pkg/tcpip/header/ipversion_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header_test - -import ( - "testing" - - "gvisor.dev/gvisor/pkg/tcpip/header" -) - -func TestIPv4(t *testing.T) { - b := header.IPv4(make([]byte, header.IPv4MinimumSize)) - b.Encode(&header.IPv4Fields{}) - - const want = header.IPv4Version - if v := header.IPVersion(b); v != want { - t.Fatalf("Bad version, want %v, got %v", want, v) - } -} - -func TestIPv6(t *testing.T) { - b := header.IPv6(make([]byte, header.IPv6MinimumSize)) - b.Encode(&header.IPv6Fields{}) - - const want = header.IPv6Version - if v := header.IPVersion(b); v != want { - t.Fatalf("Bad version, want %v, got %v", want, v) - } -} - -func TestOtherVersion(t *testing.T) { - const want = header.IPv4Version + header.IPv6Version - b := make([]byte, 1) - b[0] = want << 4 - - if v := header.IPVersion(b); v != want { - t.Fatalf("Bad version, want %v, got %v", want, v) - } -} - -func TestTooShort(t *testing.T) { - b := make([]byte, 1) - b[0] = (header.IPv4Version + header.IPv6Version) << 4 - - // Get the version of a zero-length slice. - const want = -1 - if v := header.IPVersion(b[:0]); v != want { - t.Fatalf("Bad version, want %v, got %v", want, v) - } - - // Get the version of a nil slice. - if v := header.IPVersion(nil); v != want { - t.Fatalf("Bad version, want %v, got %v", want, v) - } -} diff --git a/pkg/tcpip/header/mld_test.go b/pkg/tcpip/header/mld_test.go deleted file mode 100644 index 0cecf10d4..000000000 --- a/pkg/tcpip/header/mld_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header - -import ( - "encoding/binary" - "testing" - "time" - - "gvisor.dev/gvisor/pkg/tcpip" -) - -func TestMLD(t *testing.T) { - b := []byte{ - // Maximum Response Delay - 0, 0, - - // Reserved - 0, 0, - - // MulticastAddress - 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, - } - - const maxRespDelay = 513 - binary.BigEndian.PutUint16(b, maxRespDelay) - - mld := MLD(b) - - if got, want := mld.MaximumResponseDelay(), maxRespDelay*time.Millisecond; got != want { - t.Errorf("got mld.MaximumResponseDelay() = %s, want = %s", got, want) - } - - const newMaxRespDelay = 1234 - mld.SetMaximumResponseDelay(newMaxRespDelay) - if got, want := mld.MaximumResponseDelay(), newMaxRespDelay*time.Millisecond; got != want { - t.Errorf("got mld.MaximumResponseDelay() = %s, want = %s", got, want) - } - - if got, want := mld.MulticastAddress(), tcpip.Address([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}); got != want { - t.Errorf("got mld.MulticastAddress() = %s, want = %s", got, want) - } - - multicastAddress := tcpip.Address([]byte{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}) - mld.SetMulticastAddress(multicastAddress) - if got := mld.MulticastAddress(); got != multicastAddress { - t.Errorf("got mld.MulticastAddress() = %s, want = %s", got, multicastAddress) - } -} diff --git a/pkg/tcpip/header/ndp_test.go b/pkg/tcpip/header/ndp_test.go deleted file mode 100644 index 8fd1f7d13..000000000 --- a/pkg/tcpip/header/ndp_test.go +++ /dev/null @@ -1,1500 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "regexp" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/testutil" -) - -// TestNDPNeighborSolicit tests the functions of NDPNeighborSolicit. -func TestNDPNeighborSolicit(t *testing.T) { - b := []byte{ - 0, 0, 0, 0, - 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12, - 13, 14, 15, 16, - } - - // Test getting the Target Address. - ns := NDPNeighborSolicit(b) - addr := testutil.MustParse6("102:304:506:708:90a:b0c:d0e:f10") - if got := ns.TargetAddress(); got != addr { - t.Errorf("got ns.TargetAddress = %s, want %s", got, addr) - } - - // Test updating the Target Address. - addr2 := testutil.MustParse6("1112:1314:1516:1718:191a:1b1c:1d1e:1f11") - ns.SetTargetAddress(addr2) - if got := ns.TargetAddress(); got != addr2 { - t.Errorf("got ns.TargetAddress = %s, want %s", got, addr2) - } - // Make sure the address got updated in the backing buffer. - if got := tcpip.Address(b[ndpNSTargetAddessOffset:][:IPv6AddressSize]); got != addr2 { - t.Errorf("got targetaddress buffer = %s, want %s", got, addr2) - } -} - -// TestNDPNeighborAdvert tests the functions of NDPNeighborAdvert. -func TestNDPNeighborAdvert(t *testing.T) { - b := []byte{ - 160, 0, 0, 0, - 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12, - 13, 14, 15, 16, - } - - // Test getting the Target Address. - na := NDPNeighborAdvert(b) - addr := testutil.MustParse6("102:304:506:708:90a:b0c:d0e:f10") - if got := na.TargetAddress(); got != addr { - t.Errorf("got TargetAddress = %s, want %s", got, addr) - } - - // Test getting the Router Flag. - if got := na.RouterFlag(); !got { - t.Errorf("got RouterFlag = false, want = true") - } - - // Test getting the Solicited Flag. - if got := na.SolicitedFlag(); got { - t.Errorf("got SolicitedFlag = true, want = false") - } - - // Test getting the Override Flag. - if got := na.OverrideFlag(); !got { - t.Errorf("got OverrideFlag = false, want = true") - } - - // Test updating the Target Address. - addr2 := testutil.MustParse6("1112:1314:1516:1718:191a:1b1c:1d1e:1f11") - na.SetTargetAddress(addr2) - if got := na.TargetAddress(); got != addr2 { - t.Errorf("got TargetAddress = %s, want %s", got, addr2) - } - // Make sure the address got updated in the backing buffer. - if got := tcpip.Address(b[ndpNATargetAddressOffset:][:IPv6AddressSize]); got != addr2 { - t.Errorf("got targetaddress buffer = %s, want %s", got, addr2) - } - - // Test updating the Router Flag. - na.SetRouterFlag(false) - if got := na.RouterFlag(); got { - t.Errorf("got RouterFlag = true, want = false") - } - - // Test updating the Solicited Flag. - na.SetSolicitedFlag(true) - if got := na.SolicitedFlag(); !got { - t.Errorf("got SolicitedFlag = false, want = true") - } - - // Test updating the Override Flag. - na.SetOverrideFlag(false) - if got := na.OverrideFlag(); got { - t.Errorf("got OverrideFlag = true, want = false") - } - - // Make sure flags got updated in the backing buffer. - if got := b[ndpNAFlagsOffset]; got != 64 { - t.Errorf("got flags byte = %d, want = 64", got) - } -} - -func TestNDPRouterAdvert(t *testing.T) { - tests := []struct { - hopLimit uint8 - managedFlag, otherConfFlag bool - prf NDPRoutePreference - routerLifetimeS uint16 - reachableTimeMS, retransTimerMS uint32 - }{ - { - hopLimit: 1, - managedFlag: false, - otherConfFlag: true, - prf: HighRoutePreference, - routerLifetimeS: 2, - reachableTimeMS: 3, - retransTimerMS: 4, - }, - { - hopLimit: 64, - managedFlag: true, - otherConfFlag: false, - prf: LowRoutePreference, - routerLifetimeS: 258, - reachableTimeMS: 78492, - retransTimerMS: 13213, - }, - } - - for i, test := range tests { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - flags := uint8(0) - if test.managedFlag { - flags |= 1 << 7 - } - if test.otherConfFlag { - flags |= 1 << 6 - } - flags |= uint8(test.prf) << 3 - - b := []byte{ - test.hopLimit, flags, 1, 2, - 3, 4, 5, 6, - 7, 8, 9, 10, - } - binary.BigEndian.PutUint16(b[2:], test.routerLifetimeS) - binary.BigEndian.PutUint32(b[4:], test.reachableTimeMS) - binary.BigEndian.PutUint32(b[8:], test.retransTimerMS) - - ra := NDPRouterAdvert(b) - - if got := ra.CurrHopLimit(); got != test.hopLimit { - t.Errorf("got ra.CurrHopLimit() = %d, want = %d", got, test.hopLimit) - } - - if got := ra.ManagedAddrConfFlag(); got != test.managedFlag { - t.Errorf("got ManagedAddrConfFlag() = %t, want = %t", got, test.managedFlag) - } - - if got := ra.OtherConfFlag(); got != test.otherConfFlag { - t.Errorf("got OtherConfFlag() = %t, want = %t", got, test.otherConfFlag) - } - - if got := ra.DefaultRouterPreference(); got != test.prf { - t.Errorf("got DefaultRouterPreference() = %d, want = %d", got, test.prf) - } - - if got, want := ra.RouterLifetime(), time.Second*time.Duration(test.routerLifetimeS); got != want { - t.Errorf("got ra.RouterLifetime() = %d, want = %d", got, want) - } - - if got, want := ra.ReachableTime(), time.Millisecond*time.Duration(test.reachableTimeMS); got != want { - t.Errorf("got ra.ReachableTime() = %d, want = %d", got, want) - } - - if got, want := ra.RetransTimer(), time.Millisecond*time.Duration(test.retransTimerMS); got != want { - t.Errorf("got ra.RetransTimer() = %d, want = %d", got, want) - } - }) - } -} - -// TestNDPSourceLinkLayerAddressOptionEthernetAddress tests getting the -// Ethernet address from an NDPSourceLinkLayerAddressOption. -func TestNDPSourceLinkLayerAddressOptionEthernetAddress(t *testing.T) { - tests := []struct { - name string - buf []byte - expected tcpip.LinkAddress - }{ - { - "ValidMAC", - []byte{1, 2, 3, 4, 5, 6}, - tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), - }, - { - "SLLBodyTooShort", - []byte{1, 2, 3, 4, 5}, - tcpip.LinkAddress([]byte(nil)), - }, - { - "SLLBodyLargerThanNeeded", - []byte{1, 2, 3, 4, 5, 6, 7, 8}, - tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - sll := NDPSourceLinkLayerAddressOption(test.buf) - if got := sll.EthernetAddress(); got != test.expected { - t.Errorf("got sll.EthernetAddress = %s, want = %s", got, test.expected) - } - }) - } -} - -// TestNDPTargetLinkLayerAddressOptionEthernetAddress tests getting the -// Ethernet address from an NDPTargetLinkLayerAddressOption. -func TestNDPTargetLinkLayerAddressOptionEthernetAddress(t *testing.T) { - tests := []struct { - name string - buf []byte - expected tcpip.LinkAddress - }{ - { - "ValidMAC", - []byte{1, 2, 3, 4, 5, 6}, - tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), - }, - { - "TLLBodyTooShort", - []byte{1, 2, 3, 4, 5}, - tcpip.LinkAddress([]byte(nil)), - }, - { - "TLLBodyLargerThanNeeded", - []byte{1, 2, 3, 4, 5, 6, 7, 8}, - tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - tll := NDPTargetLinkLayerAddressOption(test.buf) - if got := tll.EthernetAddress(); got != test.expected { - t.Errorf("got tll.EthernetAddress = %s, want = %s", got, test.expected) - } - }) - } -} - -func TestOpts(t *testing.T) { - const optionHeaderLen = 2 - - checkNonce := func(expectedNonce []byte) func(*testing.T, NDPOption) { - return func(t *testing.T, opt NDPOption) { - if got := opt.kind(); got != ndpNonceOptionType { - t.Errorf("got kind() = %d, want = %d", got, ndpNonceOptionType) - } - nonce, ok := opt.(NDPNonceOption) - if !ok { - t.Fatalf("got nonce = %T, want = NDPNonceOption", opt) - } - if diff := cmp.Diff(expectedNonce, nonce.Nonce()); diff != "" { - t.Errorf("nonce mismatch (-want +got):\n%s", diff) - } - } - } - - checkTLL := func(expectedAddr tcpip.LinkAddress) func(*testing.T, NDPOption) { - return func(t *testing.T, opt NDPOption) { - if got := opt.kind(); got != ndpTargetLinkLayerAddressOptionType { - t.Errorf("got kind() = %d, want = %d", got, ndpTargetLinkLayerAddressOptionType) - } - tll, ok := opt.(NDPTargetLinkLayerAddressOption) - if !ok { - t.Fatalf("got tll = %T, want = NDPTargetLinkLayerAddressOption", opt) - } - if got, want := tll.EthernetAddress(), expectedAddr; got != want { - t.Errorf("got tll.EthernetAddress = %s, want = %s", got, want) - } - } - } - - checkSLL := func(expectedAddr tcpip.LinkAddress) func(*testing.T, NDPOption) { - return func(t *testing.T, opt NDPOption) { - if got := opt.kind(); got != ndpSourceLinkLayerAddressOptionType { - t.Errorf("got kind() = %d, want = %d", got, ndpSourceLinkLayerAddressOptionType) - } - sll, ok := opt.(NDPSourceLinkLayerAddressOption) - if !ok { - t.Fatalf("got sll = %T, want = NDPSourceLinkLayerAddressOption", opt) - } - if got, want := sll.EthernetAddress(), expectedAddr; got != want { - t.Errorf("got sll.EthernetAddress = %s, want = %s", got, want) - } - } - } - - const validLifetimeSeconds = 16909060 - address := testutil.MustParse6("90a:b0c:d0e:f10:1112:1314:1516:1718") - - expectedRDNSSBytes := [...]byte{ - // Type, Length - 25, 3, - - // Reserved - 0, 0, - - // Lifetime - 1, 2, 4, 8, - - // Address - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - } - binary.BigEndian.PutUint32(expectedRDNSSBytes[4:], validLifetimeSeconds) - if n := copy(expectedRDNSSBytes[8:], address); n != IPv6AddressSize { - t.Fatalf("got copy(...) = %d, want = %d", n, IPv6AddressSize) - } - // Update reserved fields to non zero values to make sure serializing sets - // them to zero. - rdnssBytes := expectedRDNSSBytes - rdnssBytes[1] = 1 - rdnssBytes[2] = 2 - - const searchListPaddingBytes = 3 - const domainName = "abc.abcd.e" - expectedSearchListBytes := [...]byte{ - // Type, Length - 31, 3, - - // Reserved - 0, 0, - - // Lifetime - 1, 0, 0, 0, - - // Domain names - 3, 'a', 'b', 'c', - 4, 'a', 'b', 'c', 'd', - 1, 'e', - 0, - 0, 0, 0, 0, - } - binary.BigEndian.PutUint32(expectedSearchListBytes[4:], validLifetimeSeconds) - // Update reserved fields to non zero values to make sure serializing sets - // them to zero. - searchListBytes := expectedSearchListBytes - searchListBytes[2] = 1 - searchListBytes[3] = 2 - - const prefixLength = 43 - const onLinkFlag = false - const slaacFlag = true - const preferredLifetimeSeconds = 84281096 - const onLinkFlagBit = 7 - const slaacFlagBit = 6 - boolToByte := func(v bool) byte { - if v { - return 1 - } - return 0 - } - flags := boolToByte(onLinkFlag)<<onLinkFlagBit | boolToByte(slaacFlag)<<slaacFlagBit - expectedPrefixInformationBytes := [...]byte{ - // Type, Length - 3, 4, - - prefixLength, flags, - - // Valid Lifetime - 1, 2, 3, 4, - - // Preferred Lifetime - 5, 6, 7, 8, - - // Reserved2 - 0, 0, 0, 0, - - // Address - 9, 10, 11, 12, - 13, 14, 15, 16, - 17, 18, 19, 20, - 21, 22, 23, 24, - } - binary.BigEndian.PutUint32(expectedPrefixInformationBytes[4:], validLifetimeSeconds) - binary.BigEndian.PutUint32(expectedPrefixInformationBytes[8:], preferredLifetimeSeconds) - if n := copy(expectedPrefixInformationBytes[16:], address); n != IPv6AddressSize { - t.Fatalf("got copy(...) = %d, want = %d", n, IPv6AddressSize) - } - // Update reserved fields to non zero values to make sure serializing sets - // them to zero. - prefixInformationBytes := expectedPrefixInformationBytes - prefixInformationBytes[3] |= (1 << slaacFlagBit) - 1 - binary.BigEndian.PutUint32(prefixInformationBytes[12:], validLifetimeSeconds+1) - tests := []struct { - name string - buf []byte - opt NDPOption - expectedBuf []byte - check func(*testing.T, NDPOption) - }{ - { - name: "Nonce", - buf: make([]byte, 8), - opt: NDPNonceOption([]byte{1, 2, 3, 4, 5, 6}), - expectedBuf: []byte{14, 1, 1, 2, 3, 4, 5, 6}, - check: checkNonce([]byte{1, 2, 3, 4, 5, 6}), - }, - { - name: "Nonce with padding", - buf: []byte{1, 1, 1, 1, 1, 1, 1, 1}, - opt: NDPNonceOption([]byte{1, 2, 3, 4, 5}), - expectedBuf: []byte{14, 1, 1, 2, 3, 4, 5, 0}, - check: checkNonce([]byte{1, 2, 3, 4, 5, 0}), - }, - - { - name: "TLL Ethernet", - buf: make([]byte, 8), - opt: NDPTargetLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06"), - expectedBuf: []byte{2, 1, 1, 2, 3, 4, 5, 6}, - check: checkTLL("\x01\x02\x03\x04\x05\x06"), - }, - { - name: "TLL Padding", - buf: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - opt: NDPTargetLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06\x07\x08"), - expectedBuf: []byte{2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0}, - check: checkTLL("\x01\x02\x03\x04\x05\x06"), - }, - { - name: "TLL Empty", - buf: nil, - opt: NDPTargetLinkLayerAddressOption(""), - expectedBuf: nil, - }, - - { - name: "SLL Ethernet", - buf: make([]byte, 8), - opt: NDPSourceLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06"), - expectedBuf: []byte{1, 1, 1, 2, 3, 4, 5, 6}, - check: checkSLL("\x01\x02\x03\x04\x05\x06"), - }, - { - name: "SLL Padding", - buf: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - opt: NDPSourceLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06\x07\x08"), - expectedBuf: []byte{1, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0}, - check: checkSLL("\x01\x02\x03\x04\x05\x06"), - }, - { - name: "SLL Empty", - buf: nil, - opt: NDPSourceLinkLayerAddressOption(""), - expectedBuf: nil, - }, - - { - name: "RDNSS", - buf: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - // NDPRecursiveDNSServer holds the option after the header bytes. - opt: NDPRecursiveDNSServer(rdnssBytes[optionHeaderLen:]), - expectedBuf: expectedRDNSSBytes[:], - check: func(t *testing.T, opt NDPOption) { - if got := opt.kind(); got != ndpRecursiveDNSServerOptionType { - t.Errorf("got kind() = %d, want = %d", got, ndpRecursiveDNSServerOptionType) - } - rdnss, ok := opt.(NDPRecursiveDNSServer) - if !ok { - t.Fatalf("got opt = %T, want = NDPRecursiveDNSServer", opt) - } - if got, want := rdnss.length(), len(expectedRDNSSBytes[optionHeaderLen:]); got != want { - t.Errorf("got length() = %d, want = %d", got, want) - } - if got, want := rdnss.Lifetime(), validLifetimeSeconds*time.Second; got != want { - t.Errorf("got Lifetime() = %s, want = %s", got, want) - } - if addrs, err := rdnss.Addresses(); err != nil { - t.Errorf("Addresses(): %s", err) - } else if diff := cmp.Diff([]tcpip.Address{address}, addrs); diff != "" { - t.Errorf("mismatched addresses (-want +got):\n%s", diff) - } - }, - }, - - { - name: "Search list", - buf: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - opt: NDPDNSSearchList(searchListBytes[optionHeaderLen:]), - expectedBuf: expectedSearchListBytes[:], - check: func(t *testing.T, opt NDPOption) { - if got := opt.kind(); got != ndpDNSSearchListOptionType { - t.Errorf("got kind() = %d, want = %d", got, ndpDNSSearchListOptionType) - } - - dnssl, ok := opt.(NDPDNSSearchList) - if !ok { - t.Fatalf("got opt = %T, want = NDPDNSSearchList", opt) - } - if got, want := dnssl.length(), len(expectedRDNSSBytes[optionHeaderLen:]); got != want { - t.Errorf("got length() = %d, want = %d", got, want) - } - if got, want := dnssl.Lifetime(), validLifetimeSeconds*time.Second; got != want { - t.Errorf("got Lifetime() = %s, want = %s", got, want) - } - - if domainNames, err := dnssl.DomainNames(); err != nil { - t.Errorf("DomainNames(): %s", err) - } else if diff := cmp.Diff([]string{domainName}, domainNames); diff != "" { - t.Errorf("domain names mismatch (-want +got):\n%s", diff) - } - }, - }, - - { - name: "Prefix Information", - buf: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - // NDPPrefixInformation holds the option after the header bytes. - opt: NDPPrefixInformation(prefixInformationBytes[optionHeaderLen:]), - expectedBuf: expectedPrefixInformationBytes[:], - check: func(t *testing.T, opt NDPOption) { - if got := opt.kind(); got != ndpPrefixInformationType { - t.Errorf("got kind() = %d, want = %d", got, ndpPrefixInformationType) - } - - pi, ok := opt.(NDPPrefixInformation) - if !ok { - t.Fatalf("got opt = %T, want = NDPPrefixInformation", opt) - } - - if got, want := pi.length(), len(expectedPrefixInformationBytes[optionHeaderLen:]); got != want { - t.Errorf("got length() = %d, want = %d", got, want) - } - if got := pi.PrefixLength(); got != prefixLength { - t.Errorf("got PrefixLength() = %d, want = %d", got, prefixLength) - } - if got := pi.OnLinkFlag(); got != onLinkFlag { - t.Errorf("got OnLinkFlag() = %t, want = %t", got, onLinkFlag) - } - if got := pi.AutonomousAddressConfigurationFlag(); got != slaacFlag { - t.Errorf("got AutonomousAddressConfigurationFlag() = %t, want = %t", got, slaacFlag) - } - if got, want := pi.ValidLifetime(), validLifetimeSeconds*time.Second; got != want { - t.Errorf("got ValidLifetime() = %s, want = %s", got, want) - } - if got, want := pi.PreferredLifetime(), preferredLifetimeSeconds*time.Second; got != want { - t.Errorf("got PreferredLifetime() = %s, want = %s", got, want) - } - if got := pi.Prefix(); got != address { - t.Errorf("got Prefix() = %s, want = %s", got, address) - } - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - opts := NDPOptions(test.buf) - serializer := NDPOptionsSerializer{ - test.opt, - } - if got, want := int(serializer.Length()), len(test.expectedBuf); got != want { - t.Fatalf("got Length() = %d, want = %d", got, want) - } - opts.Serialize(serializer) - if diff := cmp.Diff(test.expectedBuf, test.buf); diff != "" { - t.Fatalf("serialized buffer mismatch (-want +got):\n%s", diff) - } - - it, err := opts.Iter(true) - if err != nil { - t.Fatalf("got Iter(true) = (_, %s), want = (_, nil)", err) - } - - if len(test.expectedBuf) > 0 { - next, done, err := it.Next() - if err != nil { - t.Fatalf("got Next() = (_, _, %s), want = (_, _, nil)", err) - } - if done { - t.Fatal("got Next() = (_, true, _), want = (_, false, _)") - } - test.check(t, next) - } - - // Iterator should not return anything else. - next, done, err := it.Next() - if err != nil { - t.Errorf("got Next() = (_, _, %s), want = (_, _, nil)", err) - } - if !done { - t.Error("got Next() = (_, false, _), want = (_, true, _)") - } - if next != nil { - t.Errorf("got Next() = (%x, _, _), want = (nil, _, _)", next) - } - }) - } -} - -func TestNDPRecursiveDNSServerOption(t *testing.T) { - tests := []struct { - name string - buf []byte - lifetime time.Duration - addrs []tcpip.Address - }{ - { - "Valid1Addr", - []byte{ - 25, 3, 0, 0, - 0, 0, 0, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - }, - 0, - []tcpip.Address{ - "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - }, - }, - { - "Valid2Addr", - []byte{ - 25, 5, 0, 0, - 0, 0, 0, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, - }, - 0, - []tcpip.Address{ - "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10", - }, - }, - { - "Valid3Addr", - []byte{ - 25, 7, 0, 0, - 0, 0, 0, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, - 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, - }, - 0, - []tcpip.Address{ - "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10", - "\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x11", - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - opts := NDPOptions(test.buf) - it, err := opts.Iter(true) - if err != nil { - t.Fatalf("got Iter = (_, %s), want = (_, nil)", err) - } - - // Iterator should get our option. - next, done, err := it.Next() - if err != nil { - t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) - } - if done { - t.Fatal("got Next = (_, true, _), want = (_, false, _)") - } - if got := next.kind(); got != ndpRecursiveDNSServerOptionType { - t.Fatalf("got Type = %d, want = %d", got, ndpRecursiveDNSServerOptionType) - } - - opt, ok := next.(NDPRecursiveDNSServer) - if !ok { - t.Fatalf("next (type = %T) cannot be casted to an NDPRecursiveDNSServer", next) - } - if got := opt.Lifetime(); got != test.lifetime { - t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime) - } - addrs, err := opt.Addresses() - if err != nil { - t.Errorf("opt.Addresses() = %s", err) - } - if diff := cmp.Diff(addrs, test.addrs); diff != "" { - t.Errorf("mismatched addresses (-want +got):\n%s", diff) - } - - // Iterator should not return anything else. - next, done, err = it.Next() - if err != nil { - t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err) - } - if !done { - t.Error("got Next = (_, false, _), want = (_, true, _)") - } - if next != nil { - t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next) - } - }) - } -} - -// TestNDPDNSSearchListOption tests the getters of NDPDNSSearchList. -func TestNDPDNSSearchListOption(t *testing.T) { - tests := []struct { - name string - buf []byte - lifetime time.Duration - domainNames []string - err error - }{ - { - name: "Valid1Label", - buf: []byte{ - 0, 0, - 0, 0, 0, 1, - 3, 'a', 'b', 'c', - 0, - 0, 0, 0, - }, - lifetime: time.Second, - domainNames: []string{ - "abc", - }, - err: nil, - }, - { - name: "Valid2Label", - buf: []byte{ - 0, 0, - 0, 0, 0, 5, - 3, 'a', 'b', 'c', - 4, 'a', 'b', 'c', 'd', - 0, - 0, 0, 0, 0, 0, 0, - }, - lifetime: 5 * time.Second, - domainNames: []string{ - "abc.abcd", - }, - err: nil, - }, - { - name: "Valid3Label", - buf: []byte{ - 0, 0, - 1, 0, 0, 0, - 3, 'a', 'b', 'c', - 4, 'a', 'b', 'c', 'd', - 1, 'e', - 0, - 0, 0, 0, 0, - }, - lifetime: 16777216 * time.Second, - domainNames: []string{ - "abc.abcd.e", - }, - err: nil, - }, - { - name: "Valid2Domains", - buf: []byte{ - 0, 0, - 1, 2, 3, 4, - 3, 'a', 'b', 'c', - 0, - 2, 'd', 'e', - 3, 'x', 'y', 'z', - 0, - 0, 0, 0, - }, - lifetime: 16909060 * time.Second, - domainNames: []string{ - "abc", - "de.xyz", - }, - err: nil, - }, - { - name: "Valid3DomainsMixedCase", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 3, 'a', 'B', 'c', - 0, - 2, 'd', 'E', - 3, 'X', 'y', 'z', - 0, - 1, 'J', - 0, - }, - lifetime: 0, - domainNames: []string{ - "abc", - "de.xyz", - "j", - }, - err: nil, - }, - { - name: "ValidDomainAfterNULL", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 3, 'a', 'B', 'c', - 0, 0, 0, 0, - 2, 'd', 'E', - 3, 'X', 'y', 'z', - 0, - }, - lifetime: 0, - domainNames: []string{ - "abc", - "de.xyz", - }, - err: nil, - }, - { - name: "Valid0Domains", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 0, - 0, 0, 0, 0, 0, 0, 0, - }, - lifetime: 0, - domainNames: nil, - err: nil, - }, - { - name: "NoTrailingNull", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 7, 'a', 'b', 'c', 'd', 'e', 'f', 'g', - }, - lifetime: 0, - domainNames: nil, - err: io.ErrUnexpectedEOF, - }, - { - name: "IncorrectLength", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 8, 'a', 'b', 'c', 'd', 'e', 'f', 'g', - }, - lifetime: 0, - domainNames: nil, - err: io.ErrUnexpectedEOF, - }, - { - name: "IncorrectLengthWithNULL", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 7, 'a', 'b', 'c', 'd', 'e', 'f', - 0, - }, - lifetime: 0, - domainNames: nil, - err: ErrNDPOptMalformedBody, - }, - { - name: "LabelOfLength63", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 0, - }, - lifetime: 0, - domainNames: []string{ - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", - }, - err: nil, - }, - { - name: "LabelOfLength64", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', - 0, - }, - lifetime: 0, - domainNames: nil, - err: ErrNDPOptMalformedBody, - }, - { - name: "DomainNameOfLength255", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', - 0, - }, - lifetime: 0, - domainNames: []string{ - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij", - }, - err: nil, - }, - { - name: "DomainNameOfLength256", - buf: []byte{ - 0, 0, - 0, 0, 0, 0, - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 0, - }, - lifetime: 0, - domainNames: nil, - err: ErrNDPOptMalformedBody, - }, - { - name: "StartingDigitForLabel", - buf: []byte{ - 0, 0, - 0, 0, 0, 1, - 3, '9', 'b', 'c', - 0, - 0, 0, 0, - }, - lifetime: time.Second, - domainNames: nil, - err: ErrNDPOptMalformedBody, - }, - { - name: "StartingHyphenForLabel", - buf: []byte{ - 0, 0, - 0, 0, 0, 1, - 3, '-', 'b', 'c', - 0, - 0, 0, 0, - }, - lifetime: time.Second, - domainNames: nil, - err: ErrNDPOptMalformedBody, - }, - { - name: "EndingHyphenForLabel", - buf: []byte{ - 0, 0, - 0, 0, 0, 1, - 3, 'a', 'b', '-', - 0, - 0, 0, 0, - }, - lifetime: time.Second, - domainNames: nil, - err: ErrNDPOptMalformedBody, - }, - { - name: "EndingDigitForLabel", - buf: []byte{ - 0, 0, - 0, 0, 0, 1, - 3, 'a', 'b', '9', - 0, - 0, 0, 0, - }, - lifetime: time.Second, - domainNames: []string{ - "ab9", - }, - err: nil, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - opt := NDPDNSSearchList(test.buf) - - if got := opt.Lifetime(); got != test.lifetime { - t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime) - } - domainNames, err := opt.DomainNames() - if !errors.Is(err, test.err) { - t.Errorf("opt.DomainNames() = %s", err) - } - if diff := cmp.Diff(domainNames, test.domainNames); diff != "" { - t.Errorf("mismatched domain names (-want +got):\n%s", diff) - } - }) - } -} - -func TestNDPSearchListOptionDomainNameLabelInvalidSymbols(t *testing.T) { - for r := rune(0); r <= 255; r++ { - t.Run(fmt.Sprintf("RuneVal=%d", r), func(t *testing.T) { - buf := []byte{ - 0, 0, - 0, 0, 0, 0, - 3, 'a', 0 /* will be replaced */, 'c', - 0, - 0, 0, 0, - } - buf[8] = uint8(r) - opt := NDPDNSSearchList(buf) - - // As per RFC 1035 section 2.3.1, the label must only include ASCII - // letters, digits and hyphens (a-z, A-Z, 0-9, -). - var expectedErr error - re := regexp.MustCompile(`[a-zA-Z0-9-]`) - if !re.Match([]byte{byte(r)}) { - expectedErr = ErrNDPOptMalformedBody - } - - if domainNames, err := opt.DomainNames(); !errors.Is(err, expectedErr) { - t.Errorf("got opt.DomainNames() = (%s, %v), want = (_, %v)", domainNames, err, ErrNDPOptMalformedBody) - } - }) - } -} - -// TestNDPOptionsIterCheck tests that Iter will return false if the NDPOptions -// the iterator was returned for is malformed. -func TestNDPOptionsIterCheck(t *testing.T) { - tests := []struct { - name string - buf []byte - expectedErr error - }{ - { - name: "ZeroLengthField", - buf: []byte{0, 0, 0, 0, 0, 0, 0, 0}, - expectedErr: ErrNDPOptMalformedHeader, - }, - { - name: "ValidSourceLinkLayerAddressOption", - buf: []byte{1, 1, 1, 2, 3, 4, 5, 6}, - expectedErr: nil, - }, - { - name: "TooSmallSourceLinkLayerAddressOption", - buf: []byte{1, 1, 1, 2, 3, 4, 5}, - expectedErr: io.ErrUnexpectedEOF, - }, - { - name: "ValidTargetLinkLayerAddressOption", - buf: []byte{2, 1, 1, 2, 3, 4, 5, 6}, - expectedErr: nil, - }, - { - name: "TooSmallTargetLinkLayerAddressOption", - buf: []byte{2, 1, 1, 2, 3, 4, 5}, - expectedErr: io.ErrUnexpectedEOF, - }, - { - name: "ValidPrefixInformation", - buf: []byte{ - 3, 4, 43, 64, - 1, 2, 3, 4, - 5, 6, 7, 8, - 0, 0, 0, 0, - 9, 10, 11, 12, - 13, 14, 15, 16, - 17, 18, 19, 20, - 21, 22, 23, 24, - }, - expectedErr: nil, - }, - { - name: "TooSmallPrefixInformation", - buf: []byte{ - 3, 4, 43, 64, - 1, 2, 3, 4, - 5, 6, 7, 8, - 0, 0, 0, 0, - 9, 10, 11, 12, - 13, 14, 15, 16, - 17, 18, 19, 20, - 21, 22, 23, - }, - expectedErr: io.ErrUnexpectedEOF, - }, - { - name: "InvalidPrefixInformationLength", - buf: []byte{ - 3, 3, 43, 64, - 1, 2, 3, 4, - 5, 6, 7, 8, - 0, 0, 0, 0, - 9, 10, 11, 12, - 13, 14, 15, 16, - }, - expectedErr: ErrNDPOptMalformedBody, - }, - { - name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformation", - buf: []byte{ - // Source Link-Layer Address. - 1, 1, 1, 2, 3, 4, 5, 6, - - // Target Link-Layer Address. - 2, 1, 7, 8, 9, 10, 11, 12, - - // Prefix information. - 3, 4, 43, 64, - 1, 2, 3, 4, - 5, 6, 7, 8, - 0, 0, 0, 0, - 9, 10, 11, 12, - 13, 14, 15, 16, - 17, 18, 19, 20, - 21, 22, 23, 24, - }, - expectedErr: nil, - }, - { - name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformationWithUnrecognized", - buf: []byte{ - // Source Link-Layer Address. - 1, 1, 1, 2, 3, 4, 5, 6, - - // Target Link-Layer Address. - 2, 1, 7, 8, 9, 10, 11, 12, - - // 255 is an unrecognized type. If 255 ends up - // being the type for some recognized type, - // update 255 to some other unrecognized value. - 255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8, - - // Prefix information. - 3, 4, 43, 64, - 1, 2, 3, 4, - 5, 6, 7, 8, - 0, 0, 0, 0, - 9, 10, 11, 12, - 13, 14, 15, 16, - 17, 18, 19, 20, - 21, 22, 23, 24, - }, - expectedErr: nil, - }, - { - name: "InvalidRecursiveDNSServerCutsOffAddress", - buf: []byte{ - 25, 4, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 0, 1, 2, 3, 4, 5, 6, 7, - }, - expectedErr: ErrNDPOptMalformedBody, - }, - { - name: "InvalidRecursiveDNSServerInvalidLengthField", - buf: []byte{ - 25, 2, 0, 0, - 0, 0, 0, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, - }, - expectedErr: io.ErrUnexpectedEOF, - }, - { - name: "RecursiveDNSServerTooSmall", - buf: []byte{ - 25, 1, 0, 0, - 0, 0, 0, - }, - expectedErr: io.ErrUnexpectedEOF, - }, - { - name: "RecursiveDNSServerMulticast", - buf: []byte{ - 25, 3, 0, 0, - 0, 0, 0, 0, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - }, - expectedErr: ErrNDPOptMalformedBody, - }, - { - name: "RecursiveDNSServerUnspecified", - buf: []byte{ - 25, 3, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }, - expectedErr: ErrNDPOptMalformedBody, - }, - { - name: "DNSSearchListLargeCompliantRFC1035", - buf: []byte{ - 31, 33, 0, 0, - 0, 0, 0, 0, - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', - 0, - }, - expectedErr: nil, - }, - { - name: "DNSSearchListNonCompliantRFC1035", - buf: []byte{ - 31, 33, 0, 0, - 0, 0, 0, 0, - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', - 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }, - expectedErr: ErrNDPOptMalformedBody, - }, - { - name: "DNSSearchListValidSmall", - buf: []byte{ - 31, 2, 0, 0, - 0, 0, 0, 0, - 6, 'a', 'b', 'c', 'd', 'e', 'f', - 0, - }, - expectedErr: nil, - }, - { - name: "DNSSearchListTooSmall", - buf: []byte{ - 31, 1, 0, 0, - 0, 0, 0, - }, - expectedErr: io.ErrUnexpectedEOF, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - opts := NDPOptions(test.buf) - - if _, err := opts.Iter(true); !errors.Is(err, test.expectedErr) { - t.Fatalf("got Iter(true) = (_, %v), want = (_, %v)", err, test.expectedErr) - } - - // test.buf may be malformed but we chose not to check - // the iterator so it must return true. - if _, err := opts.Iter(false); err != nil { - t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err) - } - }) - } -} - -// TestNDPOptionsIter tests that we can iterator over a valid NDPOptions. Note, -// this test does not actually check any of the option's getters, it simply -// checks the option Type and Body. We have other tests that tests the option -// field gettings given an option body and don't need to duplicate those tests -// here. -func TestNDPOptionsIter(t *testing.T) { - buf := []byte{ - // Source Link-Layer Address. - 1, 1, 1, 2, 3, 4, 5, 6, - - // Target Link-Layer Address. - 2, 1, 7, 8, 9, 10, 11, 12, - - // 255 is an unrecognized type. If 255 ends up being the type - // for some recognized type, update 255 to some other - // unrecognized value. Note, this option should be skipped when - // iterating. - 255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8, - - // Prefix information. - 3, 4, 43, 64, - 1, 2, 3, 4, - 5, 6, 7, 8, - 0, 0, 0, 0, - 9, 10, 11, 12, - 13, 14, 15, 16, - 17, 18, 19, 20, - 21, 22, 23, 24, - } - - opts := NDPOptions(buf) - it, err := opts.Iter(true) - if err != nil { - t.Fatalf("got Iter = (_, %s), want = (_, nil)", err) - } - - // Test the first (Source Link-Layer) option. - next, done, err := it.Next() - if err != nil { - t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) - } - if done { - t.Fatal("got Next = (_, true, _), want = (_, false, _)") - } - if got, want := []byte(next.(NDPSourceLinkLayerAddressOption)), buf[2:][:6]; !bytes.Equal(got, want) { - t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want) - } - if got := next.kind(); got != ndpSourceLinkLayerAddressOptionType { - t.Errorf("got Type = %d, want = %d", got, ndpSourceLinkLayerAddressOptionType) - } - - // Test the next (Target Link-Layer) option. - next, done, err = it.Next() - if err != nil { - t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) - } - if done { - t.Fatal("got Next = (_, true, _), want = (_, false, _)") - } - if got, want := []byte(next.(NDPTargetLinkLayerAddressOption)), buf[10:][:6]; !bytes.Equal(got, want) { - t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want) - } - if got := next.kind(); got != ndpTargetLinkLayerAddressOptionType { - t.Errorf("got Type = %d, want = %d", got, ndpTargetLinkLayerAddressOptionType) - } - - // Test the next (Prefix Information) option. - // Note, the unrecognized option should be skipped. - next, done, err = it.Next() - if err != nil { - t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) - } - if done { - t.Fatal("got Next = (_, true, _), want = (_, false, _)") - } - if got, want := next.(NDPPrefixInformation), buf[34:][:30]; !bytes.Equal(got, want) { - t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want) - } - if got := next.kind(); got != ndpPrefixInformationType { - t.Errorf("got Type = %d, want = %d", got, ndpPrefixInformationType) - } - - // Iterator should not return anything else. - next, done, err = it.Next() - if err != nil { - t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err) - } - if !done { - t.Error("got Next = (_, false, _), want = (_, true, _)") - } - if next != nil { - t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next) - } -} diff --git a/pkg/tcpip/header/parse/BUILD b/pkg/tcpip/header/parse/BUILD deleted file mode 100644 index 2adee9288..000000000 --- a/pkg/tcpip/header/parse/BUILD +++ /dev/null @@ -1,15 +0,0 @@ -load("//tools:defs.bzl", "go_library") - -package(licenses = ["notice"]) - -go_library( - name = "parse", - srcs = ["parse.go"], - visibility = ["//visibility:public"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/header", - "//pkg/tcpip/stack", - ], -) diff --git a/pkg/tcpip/header/parse/parse_state_autogen.go b/pkg/tcpip/header/parse/parse_state_autogen.go new file mode 100644 index 000000000..ad047be32 --- /dev/null +++ b/pkg/tcpip/header/parse/parse_state_autogen.go @@ -0,0 +1,3 @@ +// automatically generated by stateify. + +package parse diff --git a/pkg/tcpip/header/tcp_test.go b/pkg/tcpip/header/tcp_test.go deleted file mode 100644 index 96db8460f..000000000 --- a/pkg/tcpip/header/tcp_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package header_test - -import ( - "reflect" - "testing" - - "gvisor.dev/gvisor/pkg/tcpip/header" -) - -func TestEncodeSACKBlocks(t *testing.T) { - testCases := []struct { - sackBlocks []header.SACKBlock - want []header.SACKBlock - bufSize int - }{ - { - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}}, - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}}, - 40, - }, - { - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}}, - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}}, - 30, - }, - { - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}}, - []header.SACKBlock{{10, 20}, {22, 30}}, - 20, - }, - { - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}}, - []header.SACKBlock{{10, 20}}, - 10, - }, - { - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}}, - nil, - 8, - }, - { - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}}, - []header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}}, - 60, - }, - } - for _, tc := range testCases { - b := make([]byte, tc.bufSize) - t.Logf("testing: %v", tc) - header.EncodeSACKBlocks(tc.sackBlocks, b) - opts := header.ParseTCPOptions(b) - if got, want := opts.SACKBlocks, tc.want; !reflect.DeepEqual(got, want) { - t.Errorf("header.EncodeSACKBlocks(%v, %v), encoded blocks got: %v, want: %v", tc.sackBlocks, b, got, want) - } - } -} - -func TestTCPParseOptions(t *testing.T) { - type tsOption struct { - tsVal uint32 - tsEcr uint32 - } - - generateOptions := func(tsOpt *tsOption, sackBlocks []header.SACKBlock) []byte { - l := 0 - if tsOpt != nil { - l += 10 - } - if len(sackBlocks) != 0 { - l += len(sackBlocks)*8 + 2 - } - b := make([]byte, l) - offset := 0 - if tsOpt != nil { - offset = header.EncodeTSOption(tsOpt.tsVal, tsOpt.tsEcr, b) - } - header.EncodeSACKBlocks(sackBlocks, b[offset:]) - return b - } - - testCases := []struct { - b []byte - want header.TCPOptions - }{ - // Trivial cases. - {nil, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionNOP}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionNOP, header.TCPOptionNOP}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionEOL}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionNOP, header.TCPOptionEOL, header.TCPOptionTS, 10, 1, 1}, header.TCPOptions{false, 0, 0, nil}}, - - // Test timestamp parsing. - {[]byte{header.TCPOptionNOP, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{true, 1, 1, nil}}, - {[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{true, 1, 1, nil}}, - - // Test malformed timestamp option. - {[]byte{header.TCPOptionTS, 8, 1, 1}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionNOP, header.TCPOptionTS, 8, 1, 1}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionNOP, header.TCPOptionTS, 8, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, nil}}, - - // Test SACKBlock parsing. - {[]byte{header.TCPOptionSACK, 10, 0, 0, 0, 1, 0, 0, 0, 10}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{1, 10}}}}, - {[]byte{header.TCPOptionSACK, 18, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{1, 10}, {11, 12}}}}, - - // Test malformed SACK option. - {[]byte{header.TCPOptionSACK, 0}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionSACK, 8, 0, 0, 0, 1, 0, 0, 0, 10}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionSACK, 11, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionSACK, 17, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionSACK}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionSACK, 10}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionSACK, 10, 0, 0, 0, 1, 0, 0, 0}, header.TCPOptions{false, 0, 0, nil}}, - - // Test Timestamp + SACK block parsing. - {generateOptions(&tsOption{1, 1}, []header.SACKBlock{{1, 10}, {11, 12}}), header.TCPOptions{true, 1, 1, []header.SACKBlock{{1, 10}, {11, 12}}}}, - {generateOptions(&tsOption{1, 2}, []header.SACKBlock{{1, 10}, {11, 12}}), header.TCPOptions{true, 1, 2, []header.SACKBlock{{1, 10}, {11, 12}}}}, - {generateOptions(&tsOption{1, 3}, []header.SACKBlock{{1, 10}, {11, 12}, {13, 14}, {14, 15}, {15, 16}}), header.TCPOptions{true, 1, 3, []header.SACKBlock{{1, 10}, {11, 12}, {13, 14}, {14, 15}}}}, - - // Test valid timestamp + malformed SACK block parsing. - {[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK}, header.TCPOptions{true, 1, 1, nil}}, - {[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK, 10}, header.TCPOptions{true, 1, 1, nil}}, - {[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK, 10, 0, 0, 0}, header.TCPOptions{true, 1, 1, nil}}, - {[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK, 11, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{true, 1, 1, nil}}, - {[]byte{header.TCPOptionSACK, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, nil}}, - {[]byte{header.TCPOptionSACK, 10, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{134873088, 65536}}}}, - {[]byte{header.TCPOptionSACK, 10, 0, 0, 0, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{8, 167772160}}}}, - {[]byte{header.TCPOptionSACK, 11, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, nil}}, - } - for _, tc := range testCases { - if got, want := header.ParseTCPOptions(tc.b), tc.want; !reflect.DeepEqual(got, want) { - t.Errorf("ParseTCPOptions(%v) = %v, want: %v", tc.b, got, tc.want) - } - } -} - -func TestTCPFlags(t *testing.T) { - for _, tt := range []struct { - flags header.TCPFlags - want string - }{ - {header.TCPFlagFin, "F "}, - {header.TCPFlagSyn, " S "}, - {header.TCPFlagRst, " R "}, - {header.TCPFlagPsh, " P "}, - {header.TCPFlagAck, " A "}, - {header.TCPFlagUrg, " U"}, - {header.TCPFlagSyn | header.TCPFlagAck, " S A "}, - {header.TCPFlagFin | header.TCPFlagAck, "F A "}, - } { - if got := tt.flags.String(); got != tt.want { - t.Errorf("got TCPFlags(%#b).String() = %s, want = %s", tt.flags, got, tt.want) - } - } -} |