mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
The important change here is in tcpip/tcpip.go, where tcpip.Address is defined. The rest is updating uses of tcpip.Address. This is preparation for netip.Addr or []byte based addresses, which should save us a bunch of allocations. Currently, we allocate every time we want to, say, get a tcpip.Address from a header. This is because the header is a byte slice, but Address is a string. Strings are immutable, so Go allocates and copies. PiperOrigin-RevId: 532284732
1753 lines
48 KiB
Go
1753 lines
48 KiB
Go
// 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"
|
|
"strings"
|
|
"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.AddrFrom16Slice(b[ndpNSTargetAddessOffset:][:IPv6AddressSize]); got != addr2 {
|
|
t.Errorf("got targetaddress buffer = %s, want %s", got, addr2)
|
|
}
|
|
}
|
|
|
|
func TestNDPRouteInformationOption(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
|
|
length uint8
|
|
prefixLength uint8
|
|
prf NDPRoutePreference
|
|
lifetimeS uint32
|
|
prefixBytes []byte
|
|
expectedPrefix tcpip.Subnet
|
|
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "Length=1 with Prefix Length = 0",
|
|
length: 1,
|
|
prefixLength: 0,
|
|
prf: MediumRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: IPv6EmptySubnet,
|
|
},
|
|
{
|
|
name: "Length=1 but Prefix Length > 0",
|
|
length: 1,
|
|
prefixLength: 1,
|
|
prf: MediumRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedErr: ErrNDPOptMalformedBody,
|
|
},
|
|
{
|
|
name: "Length=2 with Prefix Length = 0",
|
|
length: 2,
|
|
prefixLength: 0,
|
|
prf: MediumRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: IPv6EmptySubnet,
|
|
},
|
|
{
|
|
name: "Length=2 with Prefix Length in [1, 64] (1)",
|
|
length: 2,
|
|
prefixLength: 1,
|
|
prf: LowRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
|
|
PrefixLen: 1,
|
|
}.Subnet(),
|
|
},
|
|
{
|
|
name: "Length=2 with Prefix Length in [1, 64] (64)",
|
|
length: 2,
|
|
prefixLength: 64,
|
|
prf: HighRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
|
|
PrefixLen: 64,
|
|
}.Subnet(),
|
|
},
|
|
{
|
|
name: "Length=2 with Prefix Length > 64",
|
|
length: 2,
|
|
prefixLength: 65,
|
|
prf: HighRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedErr: ErrNDPOptMalformedBody,
|
|
},
|
|
{
|
|
name: "Length=3 with Prefix Length = 0",
|
|
length: 3,
|
|
prefixLength: 0,
|
|
prf: MediumRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: IPv6EmptySubnet,
|
|
},
|
|
{
|
|
name: "Length=3 with Prefix Length in [1, 64] (1)",
|
|
length: 3,
|
|
prefixLength: 1,
|
|
prf: LowRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
|
|
PrefixLen: 1,
|
|
}.Subnet(),
|
|
},
|
|
{
|
|
name: "Length=3 with Prefix Length in [1, 64] (64)",
|
|
length: 3,
|
|
prefixLength: 64,
|
|
prf: HighRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
|
|
PrefixLen: 64,
|
|
}.Subnet(),
|
|
},
|
|
{
|
|
name: "Length=3 with Prefix Length in [65, 128] (65)",
|
|
length: 3,
|
|
prefixLength: 65,
|
|
prf: HighRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
|
|
PrefixLen: 65,
|
|
}.Subnet(),
|
|
},
|
|
{
|
|
name: "Length=3 with Prefix Length in [65, 128] (128)",
|
|
length: 3,
|
|
prefixLength: 128,
|
|
prf: HighRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
|
|
PrefixLen: 128,
|
|
}.Subnet(),
|
|
},
|
|
{
|
|
name: "Length=3 with (invalid) Prefix Length > 128",
|
|
length: 3,
|
|
prefixLength: 129,
|
|
prf: HighRoutePreference,
|
|
lifetimeS: 1,
|
|
prefixBytes: nil,
|
|
expectedErr: ErrNDPOptMalformedBody,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
expectedRouteInformationBytes := [...]byte{
|
|
// Type, Length
|
|
24, test.length,
|
|
|
|
// Prefix Length, Prf
|
|
uint8(test.prefixLength), uint8(test.prf) << 3,
|
|
|
|
// Route Lifetime
|
|
0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
}
|
|
binary.BigEndian.PutUint32(expectedRouteInformationBytes[4:], test.lifetimeS)
|
|
_ = copy(expectedRouteInformationBytes[8:], test.prefixBytes)
|
|
|
|
opts := NDPOptions(expectedRouteInformationBytes[:test.length*lengthByteUnits])
|
|
it, err := opts.Iter(false)
|
|
if err != nil {
|
|
t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err)
|
|
}
|
|
opt, done, err := it.Next()
|
|
if !errors.Is(err, test.expectedErr) {
|
|
t.Fatalf("got Next() = (_, _, %s), want = (_, _, %s)", err, test.expectedErr)
|
|
}
|
|
if want := test.expectedErr != nil; done != want {
|
|
t.Fatalf("got Next() = (_, %t, _), want = (_, %t, _)", done, want)
|
|
}
|
|
if test.expectedErr != nil {
|
|
return
|
|
}
|
|
|
|
if got := opt.kind(); got != ndpRouteInformationType {
|
|
t.Errorf("got kind() = %d, want = %d", got, ndpRouteInformationType)
|
|
}
|
|
|
|
ri, ok := opt.(NDPRouteInformation)
|
|
if !ok {
|
|
t.Fatalf("got opt = %T, want = NDPRouteInformation", opt)
|
|
}
|
|
|
|
if got := ri.PrefixLength(); got != test.prefixLength {
|
|
t.Errorf("got PrefixLength() = %d, want = %d", got, test.prefixLength)
|
|
}
|
|
if got := ri.RoutePreference(); got != test.prf {
|
|
t.Errorf("got RoutePreference() = %d, want = %d", got, test.prf)
|
|
}
|
|
if got, want := ri.RouteLifetime(), time.Duration(test.lifetimeS)*time.Second; got != want {
|
|
t.Errorf("got RouteLifetime() = %s, want = %s", got, want)
|
|
}
|
|
if got, err := ri.Prefix(); err != nil {
|
|
t.Errorf("Prefix(): %s", err)
|
|
} else if got != test.expectedPrefix {
|
|
t.Errorf("got Prefix() = %s, want = %s", got, test.expectedPrefix)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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.AddrFrom16Slice(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.AsSlice()); 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.AsSlice()); 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{
|
|
tcpip.AddrFrom16Slice([]byte("\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{
|
|
tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")),
|
|
tcpip.AddrFrom16Slice([]byte("\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{
|
|
tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")),
|
|
tcpip.AddrFrom16Slice([]byte("\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10")),
|
|
tcpip.AddrFrom16Slice([]byte("\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)
|
|
}
|
|
|
|
comparer := cmp.Comparer(func(addrA, addrB tcpip.Address) bool {
|
|
return addrA == addrB
|
|
})
|
|
|
|
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, comparer); 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)
|
|
}
|
|
}
|
|
|
|
func TestNDPRoutePreferenceStringer(t *testing.T) {
|
|
p := NDPRoutePreference(0)
|
|
for {
|
|
var wantStr string
|
|
switch p {
|
|
case 0b01:
|
|
wantStr = "HighRoutePreference"
|
|
case 0b00:
|
|
wantStr = "MediumRoutePreference"
|
|
case 0b11:
|
|
wantStr = "LowRoutePreference"
|
|
case 0b10:
|
|
wantStr = "ReservedRoutePreference"
|
|
default:
|
|
wantStr = fmt.Sprintf("NDPRoutePreference(%d)", p)
|
|
}
|
|
|
|
if gotStr := p.String(); gotStr != wantStr {
|
|
t.Errorf("got NDPRoutePreference(%d).String() = %s, want = %s", p, gotStr, wantStr)
|
|
}
|
|
|
|
p++
|
|
if p == 0 {
|
|
// Overflowed, we hit all values.
|
|
break
|
|
}
|
|
}
|
|
}
|