gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/header/ndp_test.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package header 16 17 import ( 18 "bytes" 19 "encoding/binary" 20 "errors" 21 "fmt" 22 "io" 23 "regexp" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/google/go-cmp/cmp" 29 "gvisor.dev/gvisor/pkg/tcpip" 30 "gvisor.dev/gvisor/pkg/tcpip/testutil" 31 ) 32 33 // TestNDPNeighborSolicit tests the functions of NDPNeighborSolicit. 34 func TestNDPNeighborSolicit(t *testing.T) { 35 b := []byte{ 36 0, 0, 0, 0, 37 1, 2, 3, 4, 38 5, 6, 7, 8, 39 9, 10, 11, 12, 40 13, 14, 15, 16, 41 } 42 43 // Test getting the Target Address. 44 ns := NDPNeighborSolicit(b) 45 addr := testutil.MustParse6("102:304:506:708:90a:b0c:d0e:f10") 46 if got := ns.TargetAddress(); got != addr { 47 t.Errorf("got ns.TargetAddress = %s, want %s", got, addr) 48 } 49 50 // Test updating the Target Address. 51 addr2 := testutil.MustParse6("1112:1314:1516:1718:191a:1b1c:1d1e:1f11") 52 ns.SetTargetAddress(addr2) 53 if got := ns.TargetAddress(); got != addr2 { 54 t.Errorf("got ns.TargetAddress = %s, want %s", got, addr2) 55 } 56 // Make sure the address got updated in the backing buffer. 57 if got := tcpip.AddrFrom16Slice(b[ndpNSTargetAddessOffset:][:IPv6AddressSize]); got != addr2 { 58 t.Errorf("got targetaddress buffer = %s, want %s", got, addr2) 59 } 60 } 61 62 func TestNDPRouteInformationOption(t *testing.T) { 63 tests := []struct { 64 name string 65 66 length uint8 67 prefixLength uint8 68 prf NDPRoutePreference 69 lifetimeS uint32 70 prefixBytes []byte 71 expectedPrefix tcpip.Subnet 72 73 expectedErr error 74 }{ 75 { 76 name: "Length=1 with Prefix Length = 0", 77 length: 1, 78 prefixLength: 0, 79 prf: MediumRoutePreference, 80 lifetimeS: 1, 81 prefixBytes: nil, 82 expectedPrefix: IPv6EmptySubnet, 83 }, 84 { 85 name: "Length=1 but Prefix Length > 0", 86 length: 1, 87 prefixLength: 1, 88 prf: MediumRoutePreference, 89 lifetimeS: 1, 90 prefixBytes: nil, 91 expectedErr: ErrNDPOptMalformedBody, 92 }, 93 { 94 name: "Length=2 with Prefix Length = 0", 95 length: 2, 96 prefixLength: 0, 97 prf: MediumRoutePreference, 98 lifetimeS: 1, 99 prefixBytes: nil, 100 expectedPrefix: IPv6EmptySubnet, 101 }, 102 { 103 name: "Length=2 with Prefix Length in [1, 64] (1)", 104 length: 2, 105 prefixLength: 1, 106 prf: LowRoutePreference, 107 lifetimeS: 1, 108 prefixBytes: nil, 109 expectedPrefix: tcpip.AddressWithPrefix{ 110 Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))), 111 PrefixLen: 1, 112 }.Subnet(), 113 }, 114 { 115 name: "Length=2 with Prefix Length in [1, 64] (64)", 116 length: 2, 117 prefixLength: 64, 118 prf: HighRoutePreference, 119 lifetimeS: 1, 120 prefixBytes: nil, 121 expectedPrefix: tcpip.AddressWithPrefix{ 122 Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))), 123 PrefixLen: 64, 124 }.Subnet(), 125 }, 126 { 127 name: "Length=2 with Prefix Length > 64", 128 length: 2, 129 prefixLength: 65, 130 prf: HighRoutePreference, 131 lifetimeS: 1, 132 prefixBytes: nil, 133 expectedErr: ErrNDPOptMalformedBody, 134 }, 135 { 136 name: "Length=3 with Prefix Length = 0", 137 length: 3, 138 prefixLength: 0, 139 prf: MediumRoutePreference, 140 lifetimeS: 1, 141 prefixBytes: nil, 142 expectedPrefix: IPv6EmptySubnet, 143 }, 144 { 145 name: "Length=3 with Prefix Length in [1, 64] (1)", 146 length: 3, 147 prefixLength: 1, 148 prf: LowRoutePreference, 149 lifetimeS: 1, 150 prefixBytes: nil, 151 expectedPrefix: tcpip.AddressWithPrefix{ 152 Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))), 153 PrefixLen: 1, 154 }.Subnet(), 155 }, 156 { 157 name: "Length=3 with Prefix Length in [1, 64] (64)", 158 length: 3, 159 prefixLength: 64, 160 prf: HighRoutePreference, 161 lifetimeS: 1, 162 prefixBytes: nil, 163 expectedPrefix: tcpip.AddressWithPrefix{ 164 Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))), 165 PrefixLen: 64, 166 }.Subnet(), 167 }, 168 { 169 name: "Length=3 with Prefix Length in [65, 128] (65)", 170 length: 3, 171 prefixLength: 65, 172 prf: HighRoutePreference, 173 lifetimeS: 1, 174 prefixBytes: nil, 175 expectedPrefix: tcpip.AddressWithPrefix{ 176 Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))), 177 PrefixLen: 65, 178 }.Subnet(), 179 }, 180 { 181 name: "Length=3 with Prefix Length in [65, 128] (128)", 182 length: 3, 183 prefixLength: 128, 184 prf: HighRoutePreference, 185 lifetimeS: 1, 186 prefixBytes: nil, 187 expectedPrefix: tcpip.AddressWithPrefix{ 188 Address: tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))), 189 PrefixLen: 128, 190 }.Subnet(), 191 }, 192 { 193 name: "Length=3 with (invalid) Prefix Length > 128", 194 length: 3, 195 prefixLength: 129, 196 prf: HighRoutePreference, 197 lifetimeS: 1, 198 prefixBytes: nil, 199 expectedErr: ErrNDPOptMalformedBody, 200 }, 201 } 202 203 for _, test := range tests { 204 t.Run(test.name, func(t *testing.T) { 205 expectedRouteInformationBytes := [...]byte{ 206 // Type, Length 207 24, test.length, 208 209 // Prefix Length, Prf 210 uint8(test.prefixLength), uint8(test.prf) << 3, 211 212 // Route Lifetime 213 0, 0, 0, 0, 214 215 0, 0, 0, 0, 216 0, 0, 0, 0, 217 0, 0, 0, 0, 218 0, 0, 0, 0, 219 } 220 binary.BigEndian.PutUint32(expectedRouteInformationBytes[4:], test.lifetimeS) 221 _ = copy(expectedRouteInformationBytes[8:], test.prefixBytes) 222 223 opts := NDPOptions(expectedRouteInformationBytes[:test.length*lengthByteUnits]) 224 it, err := opts.Iter(false) 225 if err != nil { 226 t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err) 227 } 228 opt, done, err := it.Next() 229 if !errors.Is(err, test.expectedErr) { 230 t.Fatalf("got Next() = (_, _, %s), want = (_, _, %s)", err, test.expectedErr) 231 } 232 if want := test.expectedErr != nil; done != want { 233 t.Fatalf("got Next() = (_, %t, _), want = (_, %t, _)", done, want) 234 } 235 if test.expectedErr != nil { 236 return 237 } 238 239 if got := opt.kind(); got != ndpRouteInformationType { 240 t.Errorf("got kind() = %d, want = %d", got, ndpRouteInformationType) 241 } 242 243 ri, ok := opt.(NDPRouteInformation) 244 if !ok { 245 t.Fatalf("got opt = %T, want = NDPRouteInformation", opt) 246 } 247 248 if got := ri.PrefixLength(); got != test.prefixLength { 249 t.Errorf("got PrefixLength() = %d, want = %d", got, test.prefixLength) 250 } 251 if got := ri.RoutePreference(); got != test.prf { 252 t.Errorf("got RoutePreference() = %d, want = %d", got, test.prf) 253 } 254 if got, want := ri.RouteLifetime(), time.Duration(test.lifetimeS)*time.Second; got != want { 255 t.Errorf("got RouteLifetime() = %s, want = %s", got, want) 256 } 257 if got, err := ri.Prefix(); err != nil { 258 t.Errorf("Prefix(): %s", err) 259 } else if got != test.expectedPrefix { 260 t.Errorf("got Prefix() = %s, want = %s", got, test.expectedPrefix) 261 } 262 263 // Iterator should not return anything else. 264 { 265 next, done, err := it.Next() 266 if err != nil { 267 t.Errorf("got Next() = (_, _, %s), want = (_, _, nil)", err) 268 } 269 if !done { 270 t.Error("got Next() = (_, false, _), want = (_, true, _)") 271 } 272 if next != nil { 273 t.Errorf("got Next() = (%x, _, _), want = (nil, _, _)", next) 274 } 275 } 276 }) 277 } 278 } 279 280 // TestNDPNeighborAdvert tests the functions of NDPNeighborAdvert. 281 func TestNDPNeighborAdvert(t *testing.T) { 282 b := []byte{ 283 160, 0, 0, 0, 284 1, 2, 3, 4, 285 5, 6, 7, 8, 286 9, 10, 11, 12, 287 13, 14, 15, 16, 288 } 289 290 // Test getting the Target Address. 291 na := NDPNeighborAdvert(b) 292 addr := testutil.MustParse6("102:304:506:708:90a:b0c:d0e:f10") 293 if got := na.TargetAddress(); got != addr { 294 t.Errorf("got TargetAddress = %s, want %s", got, addr) 295 } 296 297 // Test getting the Router Flag. 298 if got := na.RouterFlag(); !got { 299 t.Errorf("got RouterFlag = false, want = true") 300 } 301 302 // Test getting the Solicited Flag. 303 if got := na.SolicitedFlag(); got { 304 t.Errorf("got SolicitedFlag = true, want = false") 305 } 306 307 // Test getting the Override Flag. 308 if got := na.OverrideFlag(); !got { 309 t.Errorf("got OverrideFlag = false, want = true") 310 } 311 312 // Test updating the Target Address. 313 addr2 := testutil.MustParse6("1112:1314:1516:1718:191a:1b1c:1d1e:1f11") 314 na.SetTargetAddress(addr2) 315 if got := na.TargetAddress(); got != addr2 { 316 t.Errorf("got TargetAddress = %s, want %s", got, addr2) 317 } 318 // Make sure the address got updated in the backing buffer. 319 if got := tcpip.AddrFrom16Slice(b[ndpNATargetAddressOffset:][:IPv6AddressSize]); got != addr2 { 320 t.Errorf("got targetaddress buffer = %s, want %s", got, addr2) 321 } 322 323 // Test updating the Router Flag. 324 na.SetRouterFlag(false) 325 if got := na.RouterFlag(); got { 326 t.Errorf("got RouterFlag = true, want = false") 327 } 328 329 // Test updating the Solicited Flag. 330 na.SetSolicitedFlag(true) 331 if got := na.SolicitedFlag(); !got { 332 t.Errorf("got SolicitedFlag = false, want = true") 333 } 334 335 // Test updating the Override Flag. 336 na.SetOverrideFlag(false) 337 if got := na.OverrideFlag(); got { 338 t.Errorf("got OverrideFlag = true, want = false") 339 } 340 341 // Make sure flags got updated in the backing buffer. 342 if got := b[ndpNAFlagsOffset]; got != 64 { 343 t.Errorf("got flags byte = %d, want = 64", got) 344 } 345 } 346 347 func TestNDPRouterAdvert(t *testing.T) { 348 tests := []struct { 349 hopLimit uint8 350 managedFlag, otherConfFlag bool 351 prf NDPRoutePreference 352 routerLifetimeS uint16 353 reachableTimeMS, retransTimerMS uint32 354 }{ 355 { 356 hopLimit: 1, 357 managedFlag: false, 358 otherConfFlag: true, 359 prf: HighRoutePreference, 360 routerLifetimeS: 2, 361 reachableTimeMS: 3, 362 retransTimerMS: 4, 363 }, 364 { 365 hopLimit: 64, 366 managedFlag: true, 367 otherConfFlag: false, 368 prf: LowRoutePreference, 369 routerLifetimeS: 258, 370 reachableTimeMS: 78492, 371 retransTimerMS: 13213, 372 }, 373 } 374 375 for i, test := range tests { 376 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 377 flags := uint8(0) 378 if test.managedFlag { 379 flags |= 1 << 7 380 } 381 if test.otherConfFlag { 382 flags |= 1 << 6 383 } 384 flags |= uint8(test.prf) << 3 385 386 b := []byte{ 387 test.hopLimit, flags, 1, 2, 388 3, 4, 5, 6, 389 7, 8, 9, 10, 390 } 391 binary.BigEndian.PutUint16(b[2:], test.routerLifetimeS) 392 binary.BigEndian.PutUint32(b[4:], test.reachableTimeMS) 393 binary.BigEndian.PutUint32(b[8:], test.retransTimerMS) 394 395 ra := NDPRouterAdvert(b) 396 397 if got := ra.CurrHopLimit(); got != test.hopLimit { 398 t.Errorf("got ra.CurrHopLimit() = %d, want = %d", got, test.hopLimit) 399 } 400 401 if got := ra.ManagedAddrConfFlag(); got != test.managedFlag { 402 t.Errorf("got ManagedAddrConfFlag() = %t, want = %t", got, test.managedFlag) 403 } 404 405 if got := ra.OtherConfFlag(); got != test.otherConfFlag { 406 t.Errorf("got OtherConfFlag() = %t, want = %t", got, test.otherConfFlag) 407 } 408 409 if got := ra.DefaultRouterPreference(); got != test.prf { 410 t.Errorf("got DefaultRouterPreference() = %d, want = %d", got, test.prf) 411 } 412 413 if got, want := ra.RouterLifetime(), time.Second*time.Duration(test.routerLifetimeS); got != want { 414 t.Errorf("got ra.RouterLifetime() = %d, want = %d", got, want) 415 } 416 417 if got, want := ra.ReachableTime(), time.Millisecond*time.Duration(test.reachableTimeMS); got != want { 418 t.Errorf("got ra.ReachableTime() = %d, want = %d", got, want) 419 } 420 421 if got, want := ra.RetransTimer(), time.Millisecond*time.Duration(test.retransTimerMS); got != want { 422 t.Errorf("got ra.RetransTimer() = %d, want = %d", got, want) 423 } 424 }) 425 } 426 } 427 428 // TestNDPSourceLinkLayerAddressOptionEthernetAddress tests getting the 429 // Ethernet address from an NDPSourceLinkLayerAddressOption. 430 func TestNDPSourceLinkLayerAddressOptionEthernetAddress(t *testing.T) { 431 tests := []struct { 432 name string 433 buf []byte 434 expected tcpip.LinkAddress 435 }{ 436 { 437 "ValidMAC", 438 []byte{1, 2, 3, 4, 5, 6}, 439 tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), 440 }, 441 { 442 "SLLBodyTooShort", 443 []byte{1, 2, 3, 4, 5}, 444 tcpip.LinkAddress([]byte(nil)), 445 }, 446 { 447 "SLLBodyLargerThanNeeded", 448 []byte{1, 2, 3, 4, 5, 6, 7, 8}, 449 tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), 450 }, 451 } 452 453 for _, test := range tests { 454 t.Run(test.name, func(t *testing.T) { 455 sll := NDPSourceLinkLayerAddressOption(test.buf) 456 if got := sll.EthernetAddress(); got != test.expected { 457 t.Errorf("got sll.EthernetAddress = %s, want = %s", got, test.expected) 458 } 459 }) 460 } 461 } 462 463 // TestNDPTargetLinkLayerAddressOptionEthernetAddress tests getting the 464 // Ethernet address from an NDPTargetLinkLayerAddressOption. 465 func TestNDPTargetLinkLayerAddressOptionEthernetAddress(t *testing.T) { 466 tests := []struct { 467 name string 468 buf []byte 469 expected tcpip.LinkAddress 470 }{ 471 { 472 "ValidMAC", 473 []byte{1, 2, 3, 4, 5, 6}, 474 tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), 475 }, 476 { 477 "TLLBodyTooShort", 478 []byte{1, 2, 3, 4, 5}, 479 tcpip.LinkAddress([]byte(nil)), 480 }, 481 { 482 "TLLBodyLargerThanNeeded", 483 []byte{1, 2, 3, 4, 5, 6, 7, 8}, 484 tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"), 485 }, 486 } 487 488 for _, test := range tests { 489 t.Run(test.name, func(t *testing.T) { 490 tll := NDPTargetLinkLayerAddressOption(test.buf) 491 if got := tll.EthernetAddress(); got != test.expected { 492 t.Errorf("got tll.EthernetAddress = %s, want = %s", got, test.expected) 493 } 494 }) 495 } 496 } 497 498 func TestOpts(t *testing.T) { 499 const optionHeaderLen = 2 500 501 checkNonce := func(expectedNonce []byte) func(*testing.T, NDPOption) { 502 return func(t *testing.T, opt NDPOption) { 503 if got := opt.kind(); got != ndpNonceOptionType { 504 t.Errorf("got kind() = %d, want = %d", got, ndpNonceOptionType) 505 } 506 nonce, ok := opt.(NDPNonceOption) 507 if !ok { 508 t.Fatalf("got nonce = %T, want = NDPNonceOption", opt) 509 } 510 if diff := cmp.Diff(expectedNonce, nonce.Nonce()); diff != "" { 511 t.Errorf("nonce mismatch (-want +got):\n%s", diff) 512 } 513 } 514 } 515 516 checkTLL := func(expectedAddr tcpip.LinkAddress) func(*testing.T, NDPOption) { 517 return func(t *testing.T, opt NDPOption) { 518 if got := opt.kind(); got != ndpTargetLinkLayerAddressOptionType { 519 t.Errorf("got kind() = %d, want = %d", got, ndpTargetLinkLayerAddressOptionType) 520 } 521 tll, ok := opt.(NDPTargetLinkLayerAddressOption) 522 if !ok { 523 t.Fatalf("got tll = %T, want = NDPTargetLinkLayerAddressOption", opt) 524 } 525 if got, want := tll.EthernetAddress(), expectedAddr; got != want { 526 t.Errorf("got tll.EthernetAddress = %s, want = %s", got, want) 527 } 528 } 529 } 530 531 checkSLL := func(expectedAddr tcpip.LinkAddress) func(*testing.T, NDPOption) { 532 return func(t *testing.T, opt NDPOption) { 533 if got := opt.kind(); got != ndpSourceLinkLayerAddressOptionType { 534 t.Errorf("got kind() = %d, want = %d", got, ndpSourceLinkLayerAddressOptionType) 535 } 536 sll, ok := opt.(NDPSourceLinkLayerAddressOption) 537 if !ok { 538 t.Fatalf("got sll = %T, want = NDPSourceLinkLayerAddressOption", opt) 539 } 540 if got, want := sll.EthernetAddress(), expectedAddr; got != want { 541 t.Errorf("got sll.EthernetAddress = %s, want = %s", got, want) 542 } 543 } 544 } 545 546 const validLifetimeSeconds = 16909060 547 address := testutil.MustParse6("90a:b0c:d0e:f10:1112:1314:1516:1718") 548 549 expectedRDNSSBytes := [...]byte{ 550 // Type, Length 551 25, 3, 552 553 // Reserved 554 0, 0, 555 556 // Lifetime 557 1, 2, 4, 8, 558 559 // Address 560 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 561 } 562 binary.BigEndian.PutUint32(expectedRDNSSBytes[4:], validLifetimeSeconds) 563 if n := copy(expectedRDNSSBytes[8:], address.AsSlice()); n != IPv6AddressSize { 564 t.Fatalf("got copy(...) = %d, want = %d", n, IPv6AddressSize) 565 } 566 // Update reserved fields to non zero values to make sure serializing sets 567 // them to zero. 568 rdnssBytes := expectedRDNSSBytes 569 rdnssBytes[1] = 1 570 rdnssBytes[2] = 2 571 572 const searchListPaddingBytes = 3 573 const domainName = "abc.abcd.e" 574 expectedSearchListBytes := [...]byte{ 575 // Type, Length 576 31, 3, 577 578 // Reserved 579 0, 0, 580 581 // Lifetime 582 1, 0, 0, 0, 583 584 // Domain names 585 3, 'a', 'b', 'c', 586 4, 'a', 'b', 'c', 'd', 587 1, 'e', 588 0, 589 0, 0, 0, 0, 590 } 591 binary.BigEndian.PutUint32(expectedSearchListBytes[4:], validLifetimeSeconds) 592 // Update reserved fields to non zero values to make sure serializing sets 593 // them to zero. 594 searchListBytes := expectedSearchListBytes 595 searchListBytes[2] = 1 596 searchListBytes[3] = 2 597 598 const prefixLength = 43 599 const onLinkFlag = false 600 const slaacFlag = true 601 const preferredLifetimeSeconds = 84281096 602 const onLinkFlagBit = 7 603 const slaacFlagBit = 6 604 boolToByte := func(v bool) byte { 605 if v { 606 return 1 607 } 608 return 0 609 } 610 flags := boolToByte(onLinkFlag)<<onLinkFlagBit | boolToByte(slaacFlag)<<slaacFlagBit 611 expectedPrefixInformationBytes := [...]byte{ 612 // Type, Length 613 3, 4, 614 615 prefixLength, flags, 616 617 // Valid Lifetime 618 1, 2, 3, 4, 619 620 // Preferred Lifetime 621 5, 6, 7, 8, 622 623 // Reserved2 624 0, 0, 0, 0, 625 626 // Address 627 9, 10, 11, 12, 628 13, 14, 15, 16, 629 17, 18, 19, 20, 630 21, 22, 23, 24, 631 } 632 binary.BigEndian.PutUint32(expectedPrefixInformationBytes[4:], validLifetimeSeconds) 633 binary.BigEndian.PutUint32(expectedPrefixInformationBytes[8:], preferredLifetimeSeconds) 634 if n := copy(expectedPrefixInformationBytes[16:], address.AsSlice()); n != IPv6AddressSize { 635 t.Fatalf("got copy(...) = %d, want = %d", n, IPv6AddressSize) 636 } 637 // Update reserved fields to non zero values to make sure serializing sets 638 // them to zero. 639 prefixInformationBytes := expectedPrefixInformationBytes 640 prefixInformationBytes[3] |= (1 << slaacFlagBit) - 1 641 binary.BigEndian.PutUint32(prefixInformationBytes[12:], validLifetimeSeconds+1) 642 tests := []struct { 643 name string 644 buf []byte 645 opt NDPOption 646 expectedBuf []byte 647 check func(*testing.T, NDPOption) 648 }{ 649 { 650 name: "Nonce", 651 buf: make([]byte, 8), 652 opt: NDPNonceOption([]byte{1, 2, 3, 4, 5, 6}), 653 expectedBuf: []byte{14, 1, 1, 2, 3, 4, 5, 6}, 654 check: checkNonce([]byte{1, 2, 3, 4, 5, 6}), 655 }, 656 { 657 name: "Nonce with padding", 658 buf: []byte{1, 1, 1, 1, 1, 1, 1, 1}, 659 opt: NDPNonceOption([]byte{1, 2, 3, 4, 5}), 660 expectedBuf: []byte{14, 1, 1, 2, 3, 4, 5, 0}, 661 check: checkNonce([]byte{1, 2, 3, 4, 5, 0}), 662 }, 663 664 { 665 name: "TLL Ethernet", 666 buf: make([]byte, 8), 667 opt: NDPTargetLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06"), 668 expectedBuf: []byte{2, 1, 1, 2, 3, 4, 5, 6}, 669 check: checkTLL("\x01\x02\x03\x04\x05\x06"), 670 }, 671 { 672 name: "TLL Padding", 673 buf: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 674 opt: NDPTargetLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06\x07\x08"), 675 expectedBuf: []byte{2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0}, 676 check: checkTLL("\x01\x02\x03\x04\x05\x06"), 677 }, 678 { 679 name: "TLL Empty", 680 buf: nil, 681 opt: NDPTargetLinkLayerAddressOption(""), 682 expectedBuf: nil, 683 }, 684 685 { 686 name: "SLL Ethernet", 687 buf: make([]byte, 8), 688 opt: NDPSourceLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06"), 689 expectedBuf: []byte{1, 1, 1, 2, 3, 4, 5, 6}, 690 check: checkSLL("\x01\x02\x03\x04\x05\x06"), 691 }, 692 { 693 name: "SLL Padding", 694 buf: []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 695 opt: NDPSourceLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06\x07\x08"), 696 expectedBuf: []byte{1, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0}, 697 check: checkSLL("\x01\x02\x03\x04\x05\x06"), 698 }, 699 { 700 name: "SLL Empty", 701 buf: nil, 702 opt: NDPSourceLinkLayerAddressOption(""), 703 expectedBuf: nil, 704 }, 705 706 { 707 name: "RDNSS", 708 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}, 709 // NDPRecursiveDNSServer holds the option after the header bytes. 710 opt: NDPRecursiveDNSServer(rdnssBytes[optionHeaderLen:]), 711 expectedBuf: expectedRDNSSBytes[:], 712 check: func(t *testing.T, opt NDPOption) { 713 if got := opt.kind(); got != ndpRecursiveDNSServerOptionType { 714 t.Errorf("got kind() = %d, want = %d", got, ndpRecursiveDNSServerOptionType) 715 } 716 rdnss, ok := opt.(NDPRecursiveDNSServer) 717 if !ok { 718 t.Fatalf("got opt = %T, want = NDPRecursiveDNSServer", opt) 719 } 720 if got, want := rdnss.length(), len(expectedRDNSSBytes[optionHeaderLen:]); got != want { 721 t.Errorf("got length() = %d, want = %d", got, want) 722 } 723 if got, want := rdnss.Lifetime(), validLifetimeSeconds*time.Second; got != want { 724 t.Errorf("got Lifetime() = %s, want = %s", got, want) 725 } 726 if addrs, err := rdnss.Addresses(); err != nil { 727 t.Errorf("Addresses(): %s", err) 728 } else if diff := cmp.Diff([]tcpip.Address{address}, addrs); diff != "" { 729 t.Errorf("mismatched addresses (-want +got):\n%s", diff) 730 } 731 }, 732 }, 733 734 { 735 name: "Search list", 736 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}, 737 opt: NDPDNSSearchList(searchListBytes[optionHeaderLen:]), 738 expectedBuf: expectedSearchListBytes[:], 739 check: func(t *testing.T, opt NDPOption) { 740 if got := opt.kind(); got != ndpDNSSearchListOptionType { 741 t.Errorf("got kind() = %d, want = %d", got, ndpDNSSearchListOptionType) 742 } 743 744 dnssl, ok := opt.(NDPDNSSearchList) 745 if !ok { 746 t.Fatalf("got opt = %T, want = NDPDNSSearchList", opt) 747 } 748 if got, want := dnssl.length(), len(expectedRDNSSBytes[optionHeaderLen:]); got != want { 749 t.Errorf("got length() = %d, want = %d", got, want) 750 } 751 if got, want := dnssl.Lifetime(), validLifetimeSeconds*time.Second; got != want { 752 t.Errorf("got Lifetime() = %s, want = %s", got, want) 753 } 754 755 if domainNames, err := dnssl.DomainNames(); err != nil { 756 t.Errorf("DomainNames(): %s", err) 757 } else if diff := cmp.Diff([]string{domainName}, domainNames); diff != "" { 758 t.Errorf("domain names mismatch (-want +got):\n%s", diff) 759 } 760 }, 761 }, 762 763 { 764 name: "Prefix Information", 765 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}, 766 // NDPPrefixInformation holds the option after the header bytes. 767 opt: NDPPrefixInformation(prefixInformationBytes[optionHeaderLen:]), 768 expectedBuf: expectedPrefixInformationBytes[:], 769 check: func(t *testing.T, opt NDPOption) { 770 if got := opt.kind(); got != ndpPrefixInformationType { 771 t.Errorf("got kind() = %d, want = %d", got, ndpPrefixInformationType) 772 } 773 774 pi, ok := opt.(NDPPrefixInformation) 775 if !ok { 776 t.Fatalf("got opt = %T, want = NDPPrefixInformation", opt) 777 } 778 779 if got, want := pi.length(), len(expectedPrefixInformationBytes[optionHeaderLen:]); got != want { 780 t.Errorf("got length() = %d, want = %d", got, want) 781 } 782 if got := pi.PrefixLength(); got != prefixLength { 783 t.Errorf("got PrefixLength() = %d, want = %d", got, prefixLength) 784 } 785 if got := pi.OnLinkFlag(); got != onLinkFlag { 786 t.Errorf("got OnLinkFlag() = %t, want = %t", got, onLinkFlag) 787 } 788 if got := pi.AutonomousAddressConfigurationFlag(); got != slaacFlag { 789 t.Errorf("got AutonomousAddressConfigurationFlag() = %t, want = %t", got, slaacFlag) 790 } 791 if got, want := pi.ValidLifetime(), validLifetimeSeconds*time.Second; got != want { 792 t.Errorf("got ValidLifetime() = %s, want = %s", got, want) 793 } 794 if got, want := pi.PreferredLifetime(), preferredLifetimeSeconds*time.Second; got != want { 795 t.Errorf("got PreferredLifetime() = %s, want = %s", got, want) 796 } 797 if got := pi.Prefix(); got != address { 798 t.Errorf("got Prefix() = %s, want = %s", got, address) 799 } 800 }, 801 }, 802 } 803 804 for _, test := range tests { 805 t.Run(test.name, func(t *testing.T) { 806 opts := NDPOptions(test.buf) 807 serializer := NDPOptionsSerializer{ 808 test.opt, 809 } 810 if got, want := int(serializer.Length()), len(test.expectedBuf); got != want { 811 t.Fatalf("got Length() = %d, want = %d", got, want) 812 } 813 opts.Serialize(serializer) 814 if diff := cmp.Diff(test.expectedBuf, test.buf); diff != "" { 815 t.Fatalf("serialized buffer mismatch (-want +got):\n%s", diff) 816 } 817 818 it, err := opts.Iter(true) 819 if err != nil { 820 t.Fatalf("got Iter(true) = (_, %s), want = (_, nil)", err) 821 } 822 823 if len(test.expectedBuf) > 0 { 824 next, done, err := it.Next() 825 if err != nil { 826 t.Fatalf("got Next() = (_, _, %s), want = (_, _, nil)", err) 827 } 828 if done { 829 t.Fatal("got Next() = (_, true, _), want = (_, false, _)") 830 } 831 test.check(t, next) 832 } 833 834 // Iterator should not return anything else. 835 next, done, err := it.Next() 836 if err != nil { 837 t.Errorf("got Next() = (_, _, %s), want = (_, _, nil)", err) 838 } 839 if !done { 840 t.Error("got Next() = (_, false, _), want = (_, true, _)") 841 } 842 if next != nil { 843 t.Errorf("got Next() = (%x, _, _), want = (nil, _, _)", next) 844 } 845 }) 846 } 847 } 848 849 func TestNDPRecursiveDNSServerOption(t *testing.T) { 850 tests := []struct { 851 name string 852 buf []byte 853 lifetime time.Duration 854 addrs []tcpip.Address 855 }{ 856 { 857 "Valid1Addr", 858 []byte{ 859 25, 3, 0, 0, 860 0, 0, 0, 0, 861 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 862 }, 863 0, 864 []tcpip.Address{ 865 tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")), 866 }, 867 }, 868 { 869 "Valid2Addr", 870 []byte{ 871 25, 5, 0, 0, 872 0, 0, 0, 0, 873 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 874 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 875 }, 876 0, 877 []tcpip.Address{ 878 tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")), 879 tcpip.AddrFrom16Slice([]byte("\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10")), 880 }, 881 }, 882 { 883 "Valid3Addr", 884 []byte{ 885 25, 7, 0, 0, 886 0, 0, 0, 0, 887 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 888 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 889 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 890 }, 891 0, 892 []tcpip.Address{ 893 tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")), 894 tcpip.AddrFrom16Slice([]byte("\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10")), 895 tcpip.AddrFrom16Slice([]byte("\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x11")), 896 }, 897 }, 898 } 899 900 for _, test := range tests { 901 t.Run(test.name, func(t *testing.T) { 902 opts := NDPOptions(test.buf) 903 it, err := opts.Iter(true) 904 if err != nil { 905 t.Fatalf("got Iter = (_, %s), want = (_, nil)", err) 906 } 907 908 // Iterator should get our option. 909 next, done, err := it.Next() 910 if err != nil { 911 t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) 912 } 913 if done { 914 t.Fatal("got Next = (_, true, _), want = (_, false, _)") 915 } 916 if got := next.kind(); got != ndpRecursiveDNSServerOptionType { 917 t.Fatalf("got Type = %d, want = %d", got, ndpRecursiveDNSServerOptionType) 918 } 919 920 comparer := cmp.Comparer(func(addrA, addrB tcpip.Address) bool { 921 return addrA == addrB 922 }) 923 924 opt, ok := next.(NDPRecursiveDNSServer) 925 if !ok { 926 t.Fatalf("next (type = %T) cannot be casted to an NDPRecursiveDNSServer", next) 927 } 928 if got := opt.Lifetime(); got != test.lifetime { 929 t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime) 930 } 931 addrs, err := opt.Addresses() 932 if err != nil { 933 t.Errorf("opt.Addresses() = %s", err) 934 } 935 if diff := cmp.Diff(addrs, test.addrs, comparer); diff != "" { 936 t.Errorf("mismatched addresses (-want +got):\n%s", diff) 937 } 938 939 // Iterator should not return anything else. 940 next, done, err = it.Next() 941 if err != nil { 942 t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err) 943 } 944 if !done { 945 t.Error("got Next = (_, false, _), want = (_, true, _)") 946 } 947 if next != nil { 948 t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next) 949 } 950 }) 951 } 952 } 953 954 // TestNDPDNSSearchListOption tests the getters of NDPDNSSearchList. 955 func TestNDPDNSSearchListOption(t *testing.T) { 956 tests := []struct { 957 name string 958 buf []byte 959 lifetime time.Duration 960 domainNames []string 961 err error 962 }{ 963 { 964 name: "Valid1Label", 965 buf: []byte{ 966 0, 0, 967 0, 0, 0, 1, 968 3, 'a', 'b', 'c', 969 0, 970 0, 0, 0, 971 }, 972 lifetime: time.Second, 973 domainNames: []string{ 974 "abc", 975 }, 976 err: nil, 977 }, 978 { 979 name: "Valid2Label", 980 buf: []byte{ 981 0, 0, 982 0, 0, 0, 5, 983 3, 'a', 'b', 'c', 984 4, 'a', 'b', 'c', 'd', 985 0, 986 0, 0, 0, 0, 0, 0, 987 }, 988 lifetime: 5 * time.Second, 989 domainNames: []string{ 990 "abc.abcd", 991 }, 992 err: nil, 993 }, 994 { 995 name: "Valid3Label", 996 buf: []byte{ 997 0, 0, 998 1, 0, 0, 0, 999 3, 'a', 'b', 'c', 1000 4, 'a', 'b', 'c', 'd', 1001 1, 'e', 1002 0, 1003 0, 0, 0, 0, 1004 }, 1005 lifetime: 16777216 * time.Second, 1006 domainNames: []string{ 1007 "abc.abcd.e", 1008 }, 1009 err: nil, 1010 }, 1011 { 1012 name: "Valid2Domains", 1013 buf: []byte{ 1014 0, 0, 1015 1, 2, 3, 4, 1016 3, 'a', 'b', 'c', 1017 0, 1018 2, 'd', 'e', 1019 3, 'x', 'y', 'z', 1020 0, 1021 0, 0, 0, 1022 }, 1023 lifetime: 16909060 * time.Second, 1024 domainNames: []string{ 1025 "abc", 1026 "de.xyz", 1027 }, 1028 err: nil, 1029 }, 1030 { 1031 name: "Valid3DomainsMixedCase", 1032 buf: []byte{ 1033 0, 0, 1034 0, 0, 0, 0, 1035 3, 'a', 'B', 'c', 1036 0, 1037 2, 'd', 'E', 1038 3, 'X', 'y', 'z', 1039 0, 1040 1, 'J', 1041 0, 1042 }, 1043 lifetime: 0, 1044 domainNames: []string{ 1045 "abc", 1046 "de.xyz", 1047 "j", 1048 }, 1049 err: nil, 1050 }, 1051 { 1052 name: "ValidDomainAfterNULL", 1053 buf: []byte{ 1054 0, 0, 1055 0, 0, 0, 0, 1056 3, 'a', 'B', 'c', 1057 0, 0, 0, 0, 1058 2, 'd', 'E', 1059 3, 'X', 'y', 'z', 1060 0, 1061 }, 1062 lifetime: 0, 1063 domainNames: []string{ 1064 "abc", 1065 "de.xyz", 1066 }, 1067 err: nil, 1068 }, 1069 { 1070 name: "Valid0Domains", 1071 buf: []byte{ 1072 0, 0, 1073 0, 0, 0, 0, 1074 0, 1075 0, 0, 0, 0, 0, 0, 0, 1076 }, 1077 lifetime: 0, 1078 domainNames: nil, 1079 err: nil, 1080 }, 1081 { 1082 name: "NoTrailingNull", 1083 buf: []byte{ 1084 0, 0, 1085 0, 0, 0, 0, 1086 7, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 1087 }, 1088 lifetime: 0, 1089 domainNames: nil, 1090 err: io.ErrUnexpectedEOF, 1091 }, 1092 { 1093 name: "IncorrectLength", 1094 buf: []byte{ 1095 0, 0, 1096 0, 0, 0, 0, 1097 8, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 1098 }, 1099 lifetime: 0, 1100 domainNames: nil, 1101 err: io.ErrUnexpectedEOF, 1102 }, 1103 { 1104 name: "IncorrectLengthWithNULL", 1105 buf: []byte{ 1106 0, 0, 1107 0, 0, 0, 0, 1108 7, 'a', 'b', 'c', 'd', 'e', 'f', 1109 0, 1110 }, 1111 lifetime: 0, 1112 domainNames: nil, 1113 err: ErrNDPOptMalformedBody, 1114 }, 1115 { 1116 name: "LabelOfLength63", 1117 buf: []byte{ 1118 0, 0, 1119 0, 0, 0, 0, 1120 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1121 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1122 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1123 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1124 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1125 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1126 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1127 'i', 'j', 'k', 1128 0, 1129 }, 1130 lifetime: 0, 1131 domainNames: []string{ 1132 "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", 1133 }, 1134 err: nil, 1135 }, 1136 { 1137 name: "LabelOfLength64", 1138 buf: []byte{ 1139 0, 0, 1140 0, 0, 0, 0, 1141 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1142 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1143 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1144 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1145 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1146 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1147 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1148 'i', 'j', 'k', 'l', 1149 0, 1150 }, 1151 lifetime: 0, 1152 domainNames: nil, 1153 err: ErrNDPOptMalformedBody, 1154 }, 1155 { 1156 name: "DomainNameOfLength255", 1157 buf: []byte{ 1158 0, 0, 1159 0, 0, 0, 0, 1160 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1161 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1162 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1163 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1164 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1165 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1166 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1167 'i', 'j', 'k', 1168 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1169 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1170 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1171 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1172 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1173 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1174 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1175 'i', 'j', 'k', 1176 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1177 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1178 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1179 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1180 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1181 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1182 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1183 'i', 'j', 'k', 1184 62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1185 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1186 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1187 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1188 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1189 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1190 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1191 'i', 'j', 1192 0, 1193 }, 1194 lifetime: 0, 1195 domainNames: []string{ 1196 "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij", 1197 }, 1198 err: nil, 1199 }, 1200 { 1201 name: "DomainNameOfLength256", 1202 buf: []byte{ 1203 0, 0, 1204 0, 0, 0, 0, 1205 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1206 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1207 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1208 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1209 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1210 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1211 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1212 'i', 'j', 'k', 1213 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1214 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1215 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1216 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1217 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1218 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1219 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1220 'i', 'j', 'k', 1221 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1222 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1223 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1224 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1225 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1226 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1227 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1228 'i', 'j', 'k', 1229 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1230 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1231 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1232 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1233 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1234 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1235 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1236 'i', 'j', 'k', 1237 0, 1238 }, 1239 lifetime: 0, 1240 domainNames: nil, 1241 err: ErrNDPOptMalformedBody, 1242 }, 1243 { 1244 name: "StartingDigitForLabel", 1245 buf: []byte{ 1246 0, 0, 1247 0, 0, 0, 1, 1248 3, '9', 'b', 'c', 1249 0, 1250 0, 0, 0, 1251 }, 1252 lifetime: time.Second, 1253 domainNames: nil, 1254 err: ErrNDPOptMalformedBody, 1255 }, 1256 { 1257 name: "StartingHyphenForLabel", 1258 buf: []byte{ 1259 0, 0, 1260 0, 0, 0, 1, 1261 3, '-', 'b', 'c', 1262 0, 1263 0, 0, 0, 1264 }, 1265 lifetime: time.Second, 1266 domainNames: nil, 1267 err: ErrNDPOptMalformedBody, 1268 }, 1269 { 1270 name: "EndingHyphenForLabel", 1271 buf: []byte{ 1272 0, 0, 1273 0, 0, 0, 1, 1274 3, 'a', 'b', '-', 1275 0, 1276 0, 0, 0, 1277 }, 1278 lifetime: time.Second, 1279 domainNames: nil, 1280 err: ErrNDPOptMalformedBody, 1281 }, 1282 { 1283 name: "EndingDigitForLabel", 1284 buf: []byte{ 1285 0, 0, 1286 0, 0, 0, 1, 1287 3, 'a', 'b', '9', 1288 0, 1289 0, 0, 0, 1290 }, 1291 lifetime: time.Second, 1292 domainNames: []string{ 1293 "ab9", 1294 }, 1295 err: nil, 1296 }, 1297 } 1298 1299 for _, test := range tests { 1300 t.Run(test.name, func(t *testing.T) { 1301 opt := NDPDNSSearchList(test.buf) 1302 1303 if got := opt.Lifetime(); got != test.lifetime { 1304 t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime) 1305 } 1306 domainNames, err := opt.DomainNames() 1307 if !errors.Is(err, test.err) { 1308 t.Errorf("opt.DomainNames() = %s", err) 1309 } 1310 if diff := cmp.Diff(domainNames, test.domainNames); diff != "" { 1311 t.Errorf("mismatched domain names (-want +got):\n%s", diff) 1312 } 1313 }) 1314 } 1315 } 1316 1317 func TestNDPSearchListOptionDomainNameLabelInvalidSymbols(t *testing.T) { 1318 for r := rune(0); r <= 255; r++ { 1319 t.Run(fmt.Sprintf("RuneVal=%d", r), func(t *testing.T) { 1320 buf := []byte{ 1321 0, 0, 1322 0, 0, 0, 0, 1323 3, 'a', 0 /* will be replaced */, 'c', 1324 0, 1325 0, 0, 0, 1326 } 1327 buf[8] = uint8(r) 1328 opt := NDPDNSSearchList(buf) 1329 1330 // As per RFC 1035 section 2.3.1, the label must only include ASCII 1331 // letters, digits and hyphens (a-z, A-Z, 0-9, -). 1332 var expectedErr error 1333 re := regexp.MustCompile(`[a-zA-Z0-9-]`) 1334 if !re.Match([]byte{byte(r)}) { 1335 expectedErr = ErrNDPOptMalformedBody 1336 } 1337 1338 if domainNames, err := opt.DomainNames(); !errors.Is(err, expectedErr) { 1339 t.Errorf("got opt.DomainNames() = (%s, %v), want = (_, %v)", domainNames, err, ErrNDPOptMalformedBody) 1340 } 1341 }) 1342 } 1343 } 1344 1345 // TestNDPOptionsIterCheck tests that Iter will return false if the NDPOptions 1346 // the iterator was returned for is malformed. 1347 func TestNDPOptionsIterCheck(t *testing.T) { 1348 tests := []struct { 1349 name string 1350 buf []byte 1351 expectedErr error 1352 }{ 1353 { 1354 name: "ZeroLengthField", 1355 buf: []byte{0, 0, 0, 0, 0, 0, 0, 0}, 1356 expectedErr: ErrNDPOptMalformedHeader, 1357 }, 1358 { 1359 name: "ValidSourceLinkLayerAddressOption", 1360 buf: []byte{1, 1, 1, 2, 3, 4, 5, 6}, 1361 expectedErr: nil, 1362 }, 1363 { 1364 name: "TooSmallSourceLinkLayerAddressOption", 1365 buf: []byte{1, 1, 1, 2, 3, 4, 5}, 1366 expectedErr: io.ErrUnexpectedEOF, 1367 }, 1368 { 1369 name: "ValidTargetLinkLayerAddressOption", 1370 buf: []byte{2, 1, 1, 2, 3, 4, 5, 6}, 1371 expectedErr: nil, 1372 }, 1373 { 1374 name: "TooSmallTargetLinkLayerAddressOption", 1375 buf: []byte{2, 1, 1, 2, 3, 4, 5}, 1376 expectedErr: io.ErrUnexpectedEOF, 1377 }, 1378 { 1379 name: "ValidPrefixInformation", 1380 buf: []byte{ 1381 3, 4, 43, 64, 1382 1, 2, 3, 4, 1383 5, 6, 7, 8, 1384 0, 0, 0, 0, 1385 9, 10, 11, 12, 1386 13, 14, 15, 16, 1387 17, 18, 19, 20, 1388 21, 22, 23, 24, 1389 }, 1390 expectedErr: nil, 1391 }, 1392 { 1393 name: "TooSmallPrefixInformation", 1394 buf: []byte{ 1395 3, 4, 43, 64, 1396 1, 2, 3, 4, 1397 5, 6, 7, 8, 1398 0, 0, 0, 0, 1399 9, 10, 11, 12, 1400 13, 14, 15, 16, 1401 17, 18, 19, 20, 1402 21, 22, 23, 1403 }, 1404 expectedErr: io.ErrUnexpectedEOF, 1405 }, 1406 { 1407 name: "InvalidPrefixInformationLength", 1408 buf: []byte{ 1409 3, 3, 43, 64, 1410 1, 2, 3, 4, 1411 5, 6, 7, 8, 1412 0, 0, 0, 0, 1413 9, 10, 11, 12, 1414 13, 14, 15, 16, 1415 }, 1416 expectedErr: ErrNDPOptMalformedBody, 1417 }, 1418 { 1419 name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformation", 1420 buf: []byte{ 1421 // Source Link-Layer Address. 1422 1, 1, 1, 2, 3, 4, 5, 6, 1423 1424 // Target Link-Layer Address. 1425 2, 1, 7, 8, 9, 10, 11, 12, 1426 1427 // Prefix information. 1428 3, 4, 43, 64, 1429 1, 2, 3, 4, 1430 5, 6, 7, 8, 1431 0, 0, 0, 0, 1432 9, 10, 11, 12, 1433 13, 14, 15, 16, 1434 17, 18, 19, 20, 1435 21, 22, 23, 24, 1436 }, 1437 expectedErr: nil, 1438 }, 1439 { 1440 name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformationWithUnrecognized", 1441 buf: []byte{ 1442 // Source Link-Layer Address. 1443 1, 1, 1, 2, 3, 4, 5, 6, 1444 1445 // Target Link-Layer Address. 1446 2, 1, 7, 8, 9, 10, 11, 12, 1447 1448 // 255 is an unrecognized type. If 255 ends up 1449 // being the type for some recognized type, 1450 // update 255 to some other unrecognized value. 1451 255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8, 1452 1453 // Prefix information. 1454 3, 4, 43, 64, 1455 1, 2, 3, 4, 1456 5, 6, 7, 8, 1457 0, 0, 0, 0, 1458 9, 10, 11, 12, 1459 13, 14, 15, 16, 1460 17, 18, 19, 20, 1461 21, 22, 23, 24, 1462 }, 1463 expectedErr: nil, 1464 }, 1465 { 1466 name: "InvalidRecursiveDNSServerCutsOffAddress", 1467 buf: []byte{ 1468 25, 4, 0, 0, 1469 0, 0, 0, 0, 1470 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1471 0, 1, 2, 3, 4, 5, 6, 7, 1472 }, 1473 expectedErr: ErrNDPOptMalformedBody, 1474 }, 1475 { 1476 name: "InvalidRecursiveDNSServerInvalidLengthField", 1477 buf: []byte{ 1478 25, 2, 0, 0, 1479 0, 0, 0, 0, 1480 0, 1, 2, 3, 4, 5, 6, 7, 8, 1481 }, 1482 expectedErr: io.ErrUnexpectedEOF, 1483 }, 1484 { 1485 name: "RecursiveDNSServerTooSmall", 1486 buf: []byte{ 1487 25, 1, 0, 0, 1488 0, 0, 0, 1489 }, 1490 expectedErr: io.ErrUnexpectedEOF, 1491 }, 1492 { 1493 name: "RecursiveDNSServerMulticast", 1494 buf: []byte{ 1495 25, 3, 0, 0, 1496 0, 0, 0, 0, 1497 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1498 }, 1499 expectedErr: ErrNDPOptMalformedBody, 1500 }, 1501 { 1502 name: "RecursiveDNSServerUnspecified", 1503 buf: []byte{ 1504 25, 3, 0, 0, 1505 0, 0, 0, 0, 1506 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1507 }, 1508 expectedErr: ErrNDPOptMalformedBody, 1509 }, 1510 { 1511 name: "DNSSearchListLargeCompliantRFC1035", 1512 buf: []byte{ 1513 31, 33, 0, 0, 1514 0, 0, 0, 0, 1515 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1516 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1517 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1518 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1519 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1520 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1521 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1522 'i', 'j', 'k', 1523 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1524 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1525 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1526 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1527 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1528 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1529 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1530 'i', 'j', 'k', 1531 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1532 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1533 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1534 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1535 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1536 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1537 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1538 'i', 'j', 'k', 1539 62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1540 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1541 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1542 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1543 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1544 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1545 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1546 'i', 'j', 1547 0, 1548 }, 1549 expectedErr: nil, 1550 }, 1551 { 1552 name: "DNSSearchListNonCompliantRFC1035", 1553 buf: []byte{ 1554 31, 33, 0, 0, 1555 0, 0, 0, 0, 1556 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1557 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1558 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1559 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1560 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1561 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1562 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1563 'i', 'j', 'k', 1564 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1565 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1566 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1567 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1568 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1569 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1570 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1571 'i', 'j', 'k', 1572 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1573 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1574 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1575 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1576 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1577 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1578 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1579 'i', 'j', 'k', 1580 63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1581 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1582 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1583 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1584 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 1585 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1586 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 1587 'i', 'j', 'k', 1588 0, 1589 0, 0, 0, 0, 0, 0, 0, 0, 1590 }, 1591 expectedErr: ErrNDPOptMalformedBody, 1592 }, 1593 { 1594 name: "DNSSearchListValidSmall", 1595 buf: []byte{ 1596 31, 2, 0, 0, 1597 0, 0, 0, 0, 1598 6, 'a', 'b', 'c', 'd', 'e', 'f', 1599 0, 1600 }, 1601 expectedErr: nil, 1602 }, 1603 { 1604 name: "DNSSearchListTooSmall", 1605 buf: []byte{ 1606 31, 1, 0, 0, 1607 0, 0, 0, 1608 }, 1609 expectedErr: io.ErrUnexpectedEOF, 1610 }, 1611 } 1612 1613 for _, test := range tests { 1614 t.Run(test.name, func(t *testing.T) { 1615 opts := NDPOptions(test.buf) 1616 1617 if _, err := opts.Iter(true); !errors.Is(err, test.expectedErr) { 1618 t.Fatalf("got Iter(true) = (_, %v), want = (_, %v)", err, test.expectedErr) 1619 } 1620 1621 // test.buf may be malformed but we chose not to check 1622 // the iterator so it must return true. 1623 if _, err := opts.Iter(false); err != nil { 1624 t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err) 1625 } 1626 }) 1627 } 1628 } 1629 1630 // TestNDPOptionsIter tests that we can iterator over a valid NDPOptions. Note, 1631 // this test does not actually check any of the option's getters, it simply 1632 // checks the option Type and Body. We have other tests that tests the option 1633 // field gettings given an option body and don't need to duplicate those tests 1634 // here. 1635 func TestNDPOptionsIter(t *testing.T) { 1636 buf := []byte{ 1637 // Source Link-Layer Address. 1638 1, 1, 1, 2, 3, 4, 5, 6, 1639 1640 // Target Link-Layer Address. 1641 2, 1, 7, 8, 9, 10, 11, 12, 1642 1643 // 255 is an unrecognized type. If 255 ends up being the type 1644 // for some recognized type, update 255 to some other 1645 // unrecognized value. Note, this option should be skipped when 1646 // iterating. 1647 255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8, 1648 1649 // Prefix information. 1650 3, 4, 43, 64, 1651 1, 2, 3, 4, 1652 5, 6, 7, 8, 1653 0, 0, 0, 0, 1654 9, 10, 11, 12, 1655 13, 14, 15, 16, 1656 17, 18, 19, 20, 1657 21, 22, 23, 24, 1658 } 1659 1660 opts := NDPOptions(buf) 1661 it, err := opts.Iter(true) 1662 if err != nil { 1663 t.Fatalf("got Iter = (_, %s), want = (_, nil)", err) 1664 } 1665 1666 // Test the first (Source Link-Layer) option. 1667 next, done, err := it.Next() 1668 if err != nil { 1669 t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) 1670 } 1671 if done { 1672 t.Fatal("got Next = (_, true, _), want = (_, false, _)") 1673 } 1674 if got, want := []byte(next.(NDPSourceLinkLayerAddressOption)), buf[2:][:6]; !bytes.Equal(got, want) { 1675 t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want) 1676 } 1677 if got := next.kind(); got != ndpSourceLinkLayerAddressOptionType { 1678 t.Errorf("got Type = %d, want = %d", got, ndpSourceLinkLayerAddressOptionType) 1679 } 1680 1681 // Test the next (Target Link-Layer) option. 1682 next, done, err = it.Next() 1683 if err != nil { 1684 t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) 1685 } 1686 if done { 1687 t.Fatal("got Next = (_, true, _), want = (_, false, _)") 1688 } 1689 if got, want := []byte(next.(NDPTargetLinkLayerAddressOption)), buf[10:][:6]; !bytes.Equal(got, want) { 1690 t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want) 1691 } 1692 if got := next.kind(); got != ndpTargetLinkLayerAddressOptionType { 1693 t.Errorf("got Type = %d, want = %d", got, ndpTargetLinkLayerAddressOptionType) 1694 } 1695 1696 // Test the next (Prefix Information) option. 1697 // Note, the unrecognized option should be skipped. 1698 next, done, err = it.Next() 1699 if err != nil { 1700 t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err) 1701 } 1702 if done { 1703 t.Fatal("got Next = (_, true, _), want = (_, false, _)") 1704 } 1705 if got, want := next.(NDPPrefixInformation), buf[34:][:30]; !bytes.Equal(got, want) { 1706 t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want) 1707 } 1708 if got := next.kind(); got != ndpPrefixInformationType { 1709 t.Errorf("got Type = %d, want = %d", got, ndpPrefixInformationType) 1710 } 1711 1712 // Iterator should not return anything else. 1713 next, done, err = it.Next() 1714 if err != nil { 1715 t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err) 1716 } 1717 if !done { 1718 t.Error("got Next = (_, false, _), want = (_, true, _)") 1719 } 1720 if next != nil { 1721 t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next) 1722 } 1723 } 1724 1725 func TestNDPRoutePreferenceStringer(t *testing.T) { 1726 p := NDPRoutePreference(0) 1727 for { 1728 var wantStr string 1729 switch p { 1730 case 0b01: 1731 wantStr = "HighRoutePreference" 1732 case 0b00: 1733 wantStr = "MediumRoutePreference" 1734 case 0b11: 1735 wantStr = "LowRoutePreference" 1736 case 0b10: 1737 wantStr = "ReservedRoutePreference" 1738 default: 1739 wantStr = fmt.Sprintf("NDPRoutePreference(%d)", p) 1740 } 1741 1742 if gotStr := p.String(); gotStr != wantStr { 1743 t.Errorf("got NDPRoutePreference(%d).String() = %s, want = %s", p, gotStr, wantStr) 1744 } 1745 1746 p++ 1747 if p == 0 { 1748 // Overflowed, we hit all values. 1749 break 1750 } 1751 } 1752 }