gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/header/ipv6_extension_headers_test.go (about) 1 // Copyright 2020 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 "errors" 20 "io" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 "gvisor.dev/gvisor/pkg/buffer" 25 "gvisor.dev/gvisor/pkg/tcpip" 26 ) 27 28 var ( 29 bufferTransformer = cmp.Transformer("buffer", func(b buffer.Buffer) []byte { 30 return b.Flatten() 31 }) 32 viewTransformer = cmp.Transformer("view", func(v buffer.View) []byte { 33 return v.AsSlice() 34 }) 35 ) 36 37 // Equal returns true of a and b are equivalent. 38 // 39 // Note, Equal will return true if a and b hold the same Identifier value and 40 // contain the same bytes in Buf, even if the bytes are split across views 41 // differently. 42 // 43 // Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported 44 // fields. 45 func (i IPv6RawPayloadHeader) Equal(b IPv6RawPayloadHeader) bool { 46 return i.Identifier == b.Identifier && bytes.Equal(i.Buf.Flatten(), b.Buf.Flatten()) 47 } 48 49 // Equal returns true of a and b are equivalent. 50 // 51 // Note, Equal will return true if a and b hold equivalent ipv6OptionsExtHdrs. 52 // 53 // Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported 54 // fields. 55 func (a IPv6HopByHopOptionsExtHdr) Equal(b IPv6HopByHopOptionsExtHdr) bool { 56 return bytes.Equal(a.ipv6OptionsExtHdr.buf.AsSlice(), b.ipv6OptionsExtHdr.buf.AsSlice()) 57 } 58 59 // Equal returns true of a and b are equivalent. 60 // 61 // Note, Equal will return true if a and b hold equivalent ipv6OptionsExtHdrs. 62 // 63 // Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported 64 // fields. 65 func (a IPv6DestinationOptionsExtHdr) Equal(b IPv6DestinationOptionsExtHdr) bool { 66 return bytes.Equal(a.ipv6OptionsExtHdr.buf.AsSlice(), b.ipv6OptionsExtHdr.buf.AsSlice()) 67 } 68 69 func TestIPv6UnknownExtHdrOption(t *testing.T) { 70 tests := []struct { 71 name string 72 identifier IPv6ExtHdrOptionIdentifier 73 expectedUnknownAction IPv6OptionUnknownAction 74 }{ 75 { 76 name: "Skip with zero LSBs", 77 identifier: 0, 78 expectedUnknownAction: IPv6OptionUnknownActionSkip, 79 }, 80 { 81 name: "Discard with zero LSBs", 82 identifier: 64, 83 expectedUnknownAction: IPv6OptionUnknownActionDiscard, 84 }, 85 { 86 name: "Discard and ICMP with zero LSBs", 87 identifier: 128, 88 expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMP, 89 }, 90 { 91 name: "Discard and ICMP for non multicast destination with zero LSBs", 92 identifier: 192, 93 expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, 94 }, 95 { 96 name: "Skip with non-zero LSBs", 97 identifier: 63, 98 expectedUnknownAction: IPv6OptionUnknownActionSkip, 99 }, 100 { 101 name: "Discard with non-zero LSBs", 102 identifier: 127, 103 expectedUnknownAction: IPv6OptionUnknownActionDiscard, 104 }, 105 { 106 name: "Discard and ICMP with non-zero LSBs", 107 identifier: 191, 108 expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMP, 109 }, 110 { 111 name: "Discard and ICMP for non multicast destination with non-zero LSBs", 112 identifier: 255, 113 expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest, 114 }, 115 } 116 117 for _, test := range tests { 118 t.Run(test.name, func(t *testing.T) { 119 opt := &IPv6UnknownExtHdrOption{Identifier: test.identifier, Data: buffer.NewViewWithData([]byte{1, 2, 3, 4})} 120 if a := opt.UnknownAction(); a != test.expectedUnknownAction { 121 t.Fatalf("got UnknownAction() = %d, want = %d", a, test.expectedUnknownAction) 122 } 123 }) 124 } 125 126 } 127 128 func TestIPv6OptionsExtHdrIterErr(t *testing.T) { 129 tests := []struct { 130 name string 131 bytes []byte 132 err error 133 }{ 134 { 135 name: "Single unknown with zero length", 136 bytes: []byte{255, 0}, 137 }, 138 { 139 name: "Single unknown with non-zero length", 140 bytes: []byte{255, 3, 1, 2, 3}, 141 }, 142 { 143 name: "Two options", 144 bytes: []byte{ 145 255, 0, 146 254, 1, 1, 147 }, 148 }, 149 { 150 name: "Three options", 151 bytes: []byte{ 152 255, 0, 153 254, 1, 1, 154 253, 4, 2, 3, 4, 5, 155 }, 156 }, 157 { 158 name: "Single unknown only identifier", 159 bytes: []byte{255}, 160 err: io.ErrUnexpectedEOF, 161 }, 162 { 163 name: "Single unknown too small with length = 1", 164 bytes: []byte{255, 1}, 165 err: io.ErrUnexpectedEOF, 166 }, 167 { 168 name: "Single unknown too small with length = 2", 169 bytes: []byte{255, 2, 1}, 170 err: io.ErrUnexpectedEOF, 171 }, 172 { 173 name: "Valid first with second unknown only identifier", 174 bytes: []byte{ 175 255, 0, 176 254, 177 }, 178 err: io.ErrUnexpectedEOF, 179 }, 180 { 181 name: "Valid first with second unknown missing data", 182 bytes: []byte{ 183 255, 0, 184 254, 1, 185 }, 186 err: io.ErrUnexpectedEOF, 187 }, 188 { 189 name: "Valid first with second unknown too small", 190 bytes: []byte{ 191 255, 0, 192 254, 2, 1, 193 }, 194 err: io.ErrUnexpectedEOF, 195 }, 196 { 197 name: "One Pad1", 198 bytes: []byte{0}, 199 }, 200 { 201 name: "Multiple Pad1", 202 bytes: []byte{0, 0, 0}, 203 }, 204 { 205 name: "Multiple PadN", 206 bytes: []byte{ 207 // Pad3 208 1, 1, 1, 209 210 // Pad5 211 1, 3, 1, 2, 3, 212 }, 213 }, 214 { 215 name: "Pad5 too small middle of data buffer", 216 bytes: []byte{1, 3, 1, 2}, 217 err: io.ErrUnexpectedEOF, 218 }, 219 { 220 name: "Pad5 no data", 221 bytes: []byte{1, 3}, 222 err: io.ErrUnexpectedEOF, 223 }, 224 { 225 name: "Router alert without data", 226 bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 0}, 227 err: ErrMalformedIPv6ExtHdrOption, 228 }, 229 { 230 name: "Router alert with partial data", 231 bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1, 1}, 232 err: ErrMalformedIPv6ExtHdrOption, 233 }, 234 { 235 name: "Router alert with partial data and Pad1", 236 bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1, 1, 0}, 237 err: ErrMalformedIPv6ExtHdrOption, 238 }, 239 { 240 name: "Router alert with extra data", 241 bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 3, 1, 2, 3}, 242 err: ErrMalformedIPv6ExtHdrOption, 243 }, 244 { 245 name: "Router alert with missing data", 246 bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1}, 247 err: io.ErrUnexpectedEOF, 248 }, 249 } 250 251 check := func(t *testing.T, it IPv6OptionsExtHdrOptionsIterator, expectedErr error) { 252 for i := 0; ; i++ { 253 _, done, err := it.Next() 254 if err != nil { 255 // If we encountered a non-nil error while iterating, make sure it is 256 // is the same error as expectedErr. 257 if !errors.Is(err, expectedErr) { 258 t.Fatalf("got %d-th Next() = %v, want = %v", i, err, expectedErr) 259 } 260 261 return 262 } 263 if done { 264 // If we are done (without an error), make sure that we did not expect 265 // an error. 266 if expectedErr != nil { 267 t.Fatalf("expected error when iterating; want = %s", expectedErr) 268 } 269 270 return 271 } 272 } 273 } 274 275 for _, test := range tests { 276 t.Run(test.name, func(t *testing.T) { 277 t.Run("Hop By Hop", func(t *testing.T) { 278 extHdr := IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData(test.bytes)}} 279 check(t, extHdr.Iter(), test.err) 280 }) 281 282 t.Run("Destination", func(t *testing.T) { 283 extHdr := IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData(test.bytes)}} 284 check(t, extHdr.Iter(), test.err) 285 }) 286 }) 287 } 288 } 289 290 func TestIPv6OptionsExtHdrIter(t *testing.T) { 291 tests := []struct { 292 name string 293 bytes []byte 294 expected []IPv6ExtHdrOption 295 }{ 296 { 297 name: "Single unknown with zero length", 298 bytes: []byte{255, 0}, 299 expected: []IPv6ExtHdrOption{ 300 &IPv6UnknownExtHdrOption{Identifier: 255, Data: buffer.NewViewWithData([]byte{})}, 301 }, 302 }, 303 { 304 name: "Single unknown with non-zero length", 305 bytes: []byte{255, 3, 1, 2, 3}, 306 expected: []IPv6ExtHdrOption{ 307 &IPv6UnknownExtHdrOption{Identifier: 255, Data: buffer.NewViewWithData([]byte{1, 2, 3})}, 308 }, 309 }, 310 { 311 name: "Single Pad1", 312 bytes: []byte{0}, 313 }, 314 { 315 name: "Two Pad1", 316 bytes: []byte{0, 0}, 317 }, 318 { 319 name: "Single Pad3", 320 bytes: []byte{1, 1, 1}, 321 }, 322 { 323 name: "Single Pad5", 324 bytes: []byte{1, 3, 1, 2, 3}, 325 }, 326 { 327 name: "Multiple Pad", 328 bytes: []byte{ 329 // Pad1 330 0, 331 332 // Pad2 333 1, 0, 334 335 // Pad3 336 1, 1, 1, 337 338 // Pad4 339 1, 2, 1, 2, 340 341 // Pad5 342 1, 3, 1, 2, 3, 343 }, 344 }, 345 { 346 name: "Multiple options", 347 bytes: []byte{ 348 // Pad1 349 0, 350 351 // Unknown 352 255, 0, 353 354 // Pad2 355 1, 0, 356 357 // Unknown 358 254, 1, 1, 359 360 // Pad3 361 1, 1, 1, 362 363 // Unknown 364 253, 4, 2, 3, 4, 5, 365 366 // Pad4 367 1, 2, 1, 2, 368 }, 369 expected: []IPv6ExtHdrOption{ 370 &IPv6UnknownExtHdrOption{Identifier: 255, Data: buffer.NewViewWithData([]byte{})}, 371 &IPv6UnknownExtHdrOption{Identifier: 254, Data: buffer.NewViewWithData([]byte{1})}, 372 &IPv6UnknownExtHdrOption{Identifier: 253, Data: buffer.NewViewWithData([]byte{2, 3, 4, 5})}, 373 }, 374 }, 375 } 376 377 checkIter := func(t *testing.T, it IPv6OptionsExtHdrOptionsIterator, expected []IPv6ExtHdrOption) { 378 for i, e := range expected { 379 opt, done, err := it.Next() 380 if err != nil { 381 t.Errorf("(i=%d) Next(): %s", i, err) 382 } 383 if done { 384 t.Errorf("(i=%d) unexpectedly done iterating", i) 385 } 386 if diff := cmp.Diff(e, opt, viewTransformer, bufferTransformer); diff != "" { 387 t.Errorf("(i=%d) got option mismatch (-want +got):\n%s", i, diff) 388 } 389 390 if t.Failed() { 391 t.FailNow() 392 } 393 } 394 395 opt, done, err := it.Next() 396 if err != nil { 397 t.Errorf("(last) Next(): %s", err) 398 } 399 if !done { 400 t.Errorf("(last) iterator unexpectedly not done") 401 } 402 if opt != nil { 403 t.Errorf("(last) got Next() = %T, want = nil", opt) 404 } 405 } 406 407 for _, test := range tests { 408 t.Run(test.name, func(t *testing.T) { 409 t.Run("Hop By Hop", func(t *testing.T) { 410 extHdr := IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData(test.bytes)}} 411 checkIter(t, extHdr.Iter(), test.expected) 412 }) 413 414 t.Run("Destination", func(t *testing.T) { 415 extHdr := IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData(test.bytes)}} 416 checkIter(t, extHdr.Iter(), test.expected) 417 }) 418 }) 419 } 420 } 421 422 func TestIPv6RoutingExtHdr(t *testing.T) { 423 tests := []struct { 424 name string 425 bytes []byte 426 segmentsLeft uint8 427 }{ 428 { 429 name: "Zeroes", 430 bytes: []byte{0, 0, 0, 0, 0, 0}, 431 segmentsLeft: 0, 432 }, 433 { 434 name: "Ones", 435 bytes: []byte{1, 1, 1, 1, 1, 1}, 436 segmentsLeft: 1, 437 }, 438 { 439 name: "Mixed", 440 bytes: []byte{1, 2, 3, 4, 5, 6}, 441 segmentsLeft: 2, 442 }, 443 } 444 445 for _, test := range tests { 446 t.Run(test.name, func(t *testing.T) { 447 extHdr := IPv6RoutingExtHdr{buffer.NewViewWithData(test.bytes)} 448 if got := extHdr.SegmentsLeft(); got != test.segmentsLeft { 449 t.Errorf("got SegmentsLeft() = %d, want = %d", got, test.segmentsLeft) 450 } 451 }) 452 } 453 } 454 455 func TestIPv6FragmentExtHdr(t *testing.T) { 456 tests := []struct { 457 name string 458 bytes [6]byte 459 fragmentOffset uint16 460 more bool 461 id uint32 462 }{ 463 { 464 name: "Zeroes", 465 bytes: [6]byte{0, 0, 0, 0, 0, 0}, 466 fragmentOffset: 0, 467 more: false, 468 id: 0, 469 }, 470 { 471 name: "Ones", 472 bytes: [6]byte{0, 9, 0, 0, 0, 1}, 473 fragmentOffset: 1, 474 more: true, 475 id: 1, 476 }, 477 { 478 name: "Mixed", 479 bytes: [6]byte{68, 9, 128, 4, 2, 1}, 480 fragmentOffset: 2177, 481 more: true, 482 id: 2147746305, 483 }, 484 } 485 486 for _, test := range tests { 487 t.Run(test.name, func(t *testing.T) { 488 extHdr := IPv6FragmentExtHdr(test.bytes) 489 if got := extHdr.FragmentOffset(); got != test.fragmentOffset { 490 t.Errorf("got FragmentOffset() = %d, want = %d", got, test.fragmentOffset) 491 } 492 if got := extHdr.More(); got != test.more { 493 t.Errorf("got More() = %t, want = %t", got, test.more) 494 } 495 if got := extHdr.ID(); got != test.id { 496 t.Errorf("got ID() = %d, want = %d", got, test.id) 497 } 498 }) 499 } 500 } 501 502 func makeBufferFromByteBuffers(bs ...[]byte) buffer.Buffer { 503 buf := buffer.Buffer{} 504 for _, b := range bs { 505 buf.Append(buffer.NewViewWithData(b)) 506 } 507 return buf 508 } 509 510 func TestIPv6ExtHdrIterErr(t *testing.T) { 511 tests := []struct { 512 name string 513 firstNextHdr IPv6ExtensionHeaderIdentifier 514 payload buffer.Buffer 515 err error 516 }{ 517 { 518 name: "Upper layer only without data", 519 firstNextHdr: 255, 520 }, 521 { 522 name: "Upper layer only with data", 523 firstNextHdr: 255, 524 payload: buffer.MakeWithData([]byte{1, 2, 3, 4}), 525 }, 526 { 527 name: "No next header", 528 firstNextHdr: IPv6NoNextHeaderIdentifier, 529 }, 530 { 531 name: "No next header with data", 532 firstNextHdr: IPv6NoNextHeaderIdentifier, 533 payload: buffer.MakeWithData([]byte{1, 2, 3, 4}), 534 }, 535 { 536 name: "Valid single hop by hop", 537 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 538 payload: buffer.MakeWithData([]byte{255, 0, 1, 4, 1, 2, 3, 4}), 539 }, 540 { 541 name: "Hop by hop too small", 542 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 543 payload: buffer.MakeWithData([]byte{255, 0, 1, 4, 1, 2, 3}), 544 err: io.ErrUnexpectedEOF, 545 }, 546 { 547 name: "Valid single fragment", 548 firstNextHdr: IPv6FragmentExtHdrIdentifier, 549 payload: buffer.MakeWithData([]byte{255, 0, 68, 9, 128, 4, 2, 1}), 550 }, 551 { 552 name: "Fragment too small", 553 firstNextHdr: IPv6FragmentExtHdrIdentifier, 554 payload: buffer.MakeWithData([]byte{255, 0, 68, 9, 128, 4, 2}), 555 err: io.ErrUnexpectedEOF, 556 }, 557 { 558 name: "Valid single destination", 559 firstNextHdr: IPv6DestinationOptionsExtHdrIdentifier, 560 payload: buffer.MakeWithData([]byte{255, 0, 1, 4, 1, 2, 3, 4}), 561 }, 562 { 563 name: "Destination too small", 564 firstNextHdr: IPv6DestinationOptionsExtHdrIdentifier, 565 payload: buffer.MakeWithData([]byte{255, 0, 1, 4, 1, 2, 3}), 566 err: io.ErrUnexpectedEOF, 567 }, 568 { 569 name: "Valid single routing", 570 firstNextHdr: IPv6RoutingExtHdrIdentifier, 571 payload: buffer.MakeWithData([]byte{255, 0, 1, 2, 3, 4, 5, 6}), 572 }, 573 { 574 name: "Valid single routing across views", 575 firstNextHdr: IPv6RoutingExtHdrIdentifier, 576 payload: makeBufferFromByteBuffers([]byte{255, 0, 1, 2}, []byte{3, 4, 5, 6}), 577 }, 578 { 579 name: "Routing too small with zero length field", 580 firstNextHdr: IPv6RoutingExtHdrIdentifier, 581 payload: buffer.MakeWithData([]byte{255, 0, 1, 2, 3, 4, 5}), 582 err: io.ErrUnexpectedEOF, 583 }, 584 { 585 name: "Valid routing with non-zero length field", 586 firstNextHdr: IPv6RoutingExtHdrIdentifier, 587 payload: buffer.MakeWithData([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8}), 588 }, 589 { 590 name: "Valid routing with non-zero length field across views", 591 firstNextHdr: IPv6RoutingExtHdrIdentifier, 592 payload: makeBufferFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7, 8}), 593 }, 594 { 595 name: "Routing too small with non-zero length field", 596 firstNextHdr: IPv6RoutingExtHdrIdentifier, 597 payload: buffer.MakeWithData([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7}), 598 err: io.ErrUnexpectedEOF, 599 }, 600 { 601 name: "Routing too small with non-zero length field across views", 602 firstNextHdr: IPv6RoutingExtHdrIdentifier, 603 payload: makeBufferFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7}), 604 err: io.ErrUnexpectedEOF, 605 }, 606 { 607 name: "Mixed", 608 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 609 payload: buffer.MakeWithData([]byte{ 610 // Hop By Hop Options extension header. 611 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, 612 613 // (Atomic) Fragment extension header. 614 // 615 // Reserved bits are 1 which should not affect anything. 616 uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, 617 618 // Routing extension header. 619 uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, 620 621 // Destination Options extension header. 622 255, 0, 255, 4, 1, 2, 3, 4, 623 624 // Upper layer data. 625 1, 2, 3, 4, 626 }), 627 }, 628 { 629 name: "Mixed without upper layer data", 630 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 631 payload: buffer.MakeWithData([]byte{ 632 // Hop By Hop Options extension header. 633 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, 634 635 // (Atomic) Fragment extension header. 636 // 637 // Reserved bits are 1 which should not affect anything. 638 uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, 639 640 // Routing extension header. 641 uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, 642 643 // Destination Options extension header. 644 255, 0, 255, 4, 1, 2, 3, 4, 645 }), 646 }, 647 { 648 name: "Mixed without upper layer data but last ext hdr too small", 649 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 650 payload: buffer.MakeWithData([]byte{ 651 // Hop By Hop Options extension header. 652 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, 653 654 // (Atomic) Fragment extension header. 655 // 656 // Reserved bits are 1 which should not affect anything. 657 uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, 658 659 // Routing extension header. 660 uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, 661 662 // Destination Options extension header. 663 255, 0, 255, 4, 1, 2, 3, 664 }), 665 err: io.ErrUnexpectedEOF, 666 }, 667 } 668 669 for _, test := range tests { 670 t.Run(test.name, func(t *testing.T) { 671 it := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload) 672 673 for i := 0; ; i++ { 674 _, done, err := it.Next() 675 if err != nil { 676 // If we encountered a non-nil error while iterating, make sure it is 677 // is the same error as test.err. 678 if !errors.Is(err, test.err) { 679 t.Fatalf("got %d-th Next() = %v, want = %v", i, err, test.err) 680 } 681 682 return 683 } 684 if done { 685 // If we are done (without an error), make sure that we did not expect 686 // an error. 687 if test.err != nil { 688 t.Fatalf("expected error when iterating; want = %s", test.err) 689 } 690 691 return 692 } 693 } 694 }) 695 } 696 } 697 698 func TestIPv6ExtHdrIter(t *testing.T) { 699 routingExtHdrWithUpperLayerData := []byte{255, 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4} 700 upperLayerData := []byte{1, 2, 3, 4} 701 tests := []struct { 702 name string 703 firstNextHdr IPv6ExtensionHeaderIdentifier 704 payload buffer.Buffer 705 expected []IPv6PayloadHeader 706 }{ 707 // With a non-atomic fragment that is not the first fragment, the payload 708 // after the fragment will not be parsed because the payload is expected to 709 // only hold upper layer data. 710 { 711 name: "hopbyhop - fragment (not first) - routing - upper", 712 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 713 payload: buffer.MakeWithData([]byte{ 714 // Hop By Hop extension header. 715 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, 716 717 // Fragment extension header. 718 // 719 // More = 1, Fragment Offset = 2117, ID = 2147746305 720 uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1, 721 722 // Routing extension header. 723 // 724 // Even though we have a routing ext header here, it should be 725 // be interpreted as raw bytes as only the first fragment is expected 726 // to hold headers. 727 255, 0, 1, 2, 3, 4, 5, 6, 728 729 // Upper layer data. 730 1, 2, 3, 4, 731 }), 732 expected: []IPv6PayloadHeader{ 733 IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData([]byte{1, 4, 1, 2, 3, 4})}}, 734 IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}), 735 IPv6RawPayloadHeader{ 736 Identifier: IPv6RoutingExtHdrIdentifier, 737 Buf: buffer.MakeWithData(routingExtHdrWithUpperLayerData), 738 }, 739 }, 740 }, 741 { 742 name: "hopbyhop - fragment (first) - routing - upper", 743 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 744 payload: buffer.MakeWithData([]byte{ 745 // Hop By Hop extension header. 746 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, 747 748 // Fragment extension header. 749 // 750 // More = 1, Fragment Offset = 0, ID = 2147746305 751 uint8(IPv6RoutingExtHdrIdentifier), 0, 0, 1, 128, 4, 2, 1, 752 753 // Routing extension header. 754 255, 0, 1, 2, 3, 4, 5, 6, 755 756 // Upper layer data. 757 1, 2, 3, 4, 758 }), 759 expected: []IPv6PayloadHeader{ 760 IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData([]byte{1, 4, 1, 2, 3, 4})}}, 761 IPv6FragmentExtHdr([6]byte{0, 1, 128, 4, 2, 1}), 762 IPv6RoutingExtHdr{buffer.NewViewWithData([]byte{1, 2, 3, 4, 5, 6})}, 763 IPv6RawPayloadHeader{ 764 Identifier: 255, 765 Buf: buffer.MakeWithData(upperLayerData), 766 }, 767 }, 768 }, 769 { 770 name: "fragment - routing - upper (across views)", 771 firstNextHdr: IPv6FragmentExtHdrIdentifier, 772 payload: makeBufferFromByteBuffers([]byte{ 773 // Fragment extension header. 774 uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1, 775 776 // Routing extension header. 777 255, 0, 1, 2}, []byte{3, 4, 5, 6, 778 779 // Upper layer data. 780 1, 2, 3, 4, 781 }), 782 expected: []IPv6PayloadHeader{ 783 IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}), 784 IPv6RawPayloadHeader{ 785 Identifier: IPv6RoutingExtHdrIdentifier, 786 Buf: buffer.MakeWithData(routingExtHdrWithUpperLayerData), 787 }, 788 }, 789 }, 790 791 // If we have an atomic fragment, the payload following the fragment 792 // extension header should be parsed normally. 793 { 794 name: "atomic fragment - routing - destination - upper", 795 firstNextHdr: IPv6FragmentExtHdrIdentifier, 796 payload: buffer.MakeWithData([]byte{ 797 // Fragment extension header. 798 // 799 // Reserved bits are 1 which should not affect anything. 800 uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1, 801 802 // Routing extension header. 803 uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, 804 805 // Destination Options extension header. 806 255, 0, 1, 4, 1, 2, 3, 4, 807 808 // Upper layer data. 809 1, 2, 3, 4, 810 }), 811 expected: []IPv6PayloadHeader{ 812 IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), 813 IPv6RoutingExtHdr{buffer.NewViewWithData([]byte{1, 2, 3, 4, 5, 6})}, 814 IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData([]byte{1, 4, 1, 2, 3, 4})}}, 815 IPv6RawPayloadHeader{ 816 Identifier: 255, 817 Buf: buffer.MakeWithData(upperLayerData), 818 }, 819 }, 820 }, 821 { 822 name: "atomic fragment - routing - upper (across views)", 823 firstNextHdr: IPv6FragmentExtHdrIdentifier, 824 payload: makeBufferFromByteBuffers([]byte{ 825 // Fragment extension header. 826 // 827 // Reserved bits are 1 which should not affect anything. 828 uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1, 829 830 // Routing extension header. 831 255, 0, 1, 2}, []byte{3, 4, 5, 6, 832 833 // Upper layer data. 834 1, 2}, []byte{3, 4}), 835 expected: []IPv6PayloadHeader{ 836 IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), 837 IPv6RoutingExtHdr{buffer.NewViewWithData([]byte{1, 2, 3, 4, 5, 6})}, 838 IPv6RawPayloadHeader{ 839 Identifier: 255, 840 Buf: makeBufferFromByteBuffers(upperLayerData[:2], upperLayerData[2:]), 841 }, 842 }, 843 }, 844 { 845 name: "atomic fragment - destination - no next header", 846 firstNextHdr: IPv6FragmentExtHdrIdentifier, 847 payload: buffer.MakeWithData([]byte{ 848 // Fragment extension header. 849 // 850 // Res (Reserved) bits are 1 which should not affect anything. 851 uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 0, 6, 128, 4, 2, 1, 852 853 // Destination Options extension header. 854 uint8(IPv6NoNextHeaderIdentifier), 0, 1, 4, 1, 2, 3, 4, 855 856 // Random data. 857 1, 2, 3, 4, 858 }), 859 expected: []IPv6PayloadHeader{ 860 IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), 861 IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData([]byte{1, 4, 1, 2, 3, 4})}}, 862 }, 863 }, 864 { 865 name: "routing - atomic fragment - no next header", 866 firstNextHdr: IPv6RoutingExtHdrIdentifier, 867 payload: buffer.MakeWithData([]byte{ 868 // Routing extension header. 869 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, 870 871 // Fragment extension header. 872 // 873 // Reserved bits are 1 which should not affect anything. 874 uint8(IPv6NoNextHeaderIdentifier), 0, 0, 6, 128, 4, 2, 1, 875 876 // Random data. 877 1, 2, 3, 4, 878 }), 879 expected: []IPv6PayloadHeader{ 880 IPv6RoutingExtHdr{buffer.NewViewWithData([]byte{1, 2, 3, 4, 5, 6})}, 881 IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), 882 }, 883 }, 884 { 885 name: "routing - atomic fragment - no next header (across views)", 886 firstNextHdr: IPv6RoutingExtHdrIdentifier, 887 payload: makeBufferFromByteBuffers([]byte{ 888 // Routing extension header. 889 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, 890 891 // Fragment extension header. 892 // 893 // Reserved bits are 1 which should not affect anything. 894 uint8(IPv6NoNextHeaderIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1, 895 896 // Random data. 897 1, 2, 3, 4, 898 }), 899 expected: []IPv6PayloadHeader{ 900 IPv6RoutingExtHdr{buffer.NewViewWithData([]byte{1, 2, 3, 4, 5, 6})}, 901 IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}), 902 }, 903 }, 904 { 905 name: "hopbyhop - routing - fragment - no next header", 906 firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier, 907 payload: buffer.MakeWithData([]byte{ 908 // Hop By Hop Options extension header. 909 uint8(IPv6RoutingExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4, 910 911 // Routing extension header. 912 uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6, 913 914 // Fragment extension header. 915 // 916 // Fragment Offset = 32; Res = 6. 917 uint8(IPv6NoNextHeaderIdentifier), 0, 1, 6, 128, 4, 2, 1, 918 919 // Random data. 920 1, 2, 3, 4, 921 }), 922 expected: []IPv6PayloadHeader{ 923 IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{buffer.NewViewWithData([]byte{1, 4, 1, 2, 3, 4})}}, 924 IPv6RoutingExtHdr{buffer.NewViewWithData([]byte{1, 2, 3, 4, 5, 6})}, 925 IPv6FragmentExtHdr([6]byte{1, 6, 128, 4, 2, 1}), 926 IPv6RawPayloadHeader{ 927 Identifier: IPv6NoNextHeaderIdentifier, 928 Buf: buffer.MakeWithData(upperLayerData), 929 }, 930 }, 931 }, 932 933 // Test the raw payload for common transport layer protocol numbers. 934 { 935 name: "TCP raw payload", 936 firstNextHdr: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber), 937 payload: buffer.MakeWithData(upperLayerData), 938 expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ 939 Identifier: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber), 940 Buf: buffer.MakeWithData(upperLayerData), 941 }}, 942 }, 943 { 944 name: "UDP raw payload", 945 firstNextHdr: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber), 946 payload: buffer.MakeWithData(upperLayerData), 947 expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ 948 Identifier: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber), 949 Buf: buffer.MakeWithData(upperLayerData), 950 }}, 951 }, 952 { 953 name: "ICMPv4 raw payload", 954 firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber), 955 payload: buffer.MakeWithData(upperLayerData), 956 expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ 957 Identifier: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber), 958 Buf: buffer.MakeWithData(upperLayerData), 959 }}, 960 }, 961 { 962 name: "ICMPv6 raw payload", 963 firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber), 964 payload: buffer.MakeWithData(upperLayerData), 965 expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ 966 Identifier: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber), 967 Buf: buffer.MakeWithData(upperLayerData), 968 }}, 969 }, 970 { 971 name: "Unknown next header raw payload", 972 firstNextHdr: 255, 973 payload: buffer.MakeWithData(upperLayerData), 974 expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ 975 Identifier: 255, 976 Buf: buffer.MakeWithData(upperLayerData), 977 }}, 978 }, 979 { 980 name: "Unknown next header raw payload (across views)", 981 firstNextHdr: 255, 982 payload: makeBufferFromByteBuffers(upperLayerData[:2], upperLayerData[2:]), 983 expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{ 984 Identifier: 255, 985 Buf: makeBufferFromByteBuffers(upperLayerData[:2], upperLayerData[2:]), 986 }}, 987 }, 988 } 989 990 for _, test := range tests { 991 t.Run(test.name, func(t *testing.T) { 992 it := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload) 993 994 for i, e := range test.expected { 995 extHdr, done, err := it.Next() 996 if err != nil { 997 t.Errorf("(i=%d) Next(): %s", i, err) 998 } 999 if done { 1000 t.Errorf("(i=%d) unexpectedly done iterating", i) 1001 } 1002 if diff := cmp.Diff(e, extHdr, viewTransformer, bufferTransformer); diff != "" { 1003 t.Errorf("(i=%d) got ext hdr mismatch (-want +got):\n%s", i, diff) 1004 } 1005 1006 if t.Failed() { 1007 t.FailNow() 1008 } 1009 } 1010 1011 extHdr, done, err := it.Next() 1012 if err != nil { 1013 t.Errorf("(last) Next(): %s", err) 1014 } 1015 if !done { 1016 t.Errorf("(last) iterator unexpectedly not done") 1017 } 1018 if extHdr != nil { 1019 t.Errorf("(last) got Next() = %T, want = nil", extHdr) 1020 } 1021 }) 1022 } 1023 } 1024 1025 var _ IPv6SerializableHopByHopOption = (*dummyHbHOptionSerializer)(nil) 1026 1027 // dummyHbHOptionSerializer provides a generic implementation of 1028 // IPv6SerializableHopByHopOption for use in tests. 1029 type dummyHbHOptionSerializer struct { 1030 id IPv6ExtHdrOptionIdentifier 1031 payload *buffer.View 1032 align int 1033 alignOffset int 1034 } 1035 1036 // identifier implements IPv6SerializableHopByHopOption. 1037 func (s *dummyHbHOptionSerializer) identifier() IPv6ExtHdrOptionIdentifier { 1038 return s.id 1039 } 1040 1041 // length implements IPv6SerializableHopByHopOption. 1042 func (s *dummyHbHOptionSerializer) length() uint8 { 1043 return uint8(s.payload.Size()) 1044 } 1045 1046 // alignment implements IPv6SerializableHopByHopOption. 1047 func (s *dummyHbHOptionSerializer) alignment() (int, int) { 1048 align := 1 1049 if s.align != 0 { 1050 align = s.align 1051 } 1052 return align, s.alignOffset 1053 } 1054 1055 // serializeInto implements IPv6SerializableHopByHopOption. 1056 func (s *dummyHbHOptionSerializer) serializeInto(b []byte) uint8 { 1057 return uint8(copy(b, s.payload.AsSlice())) 1058 } 1059 1060 func TestIPv6HopByHopSerializer(t *testing.T) { 1061 validateDummies := func(t *testing.T, serializable IPv6SerializableHopByHopOption, deserialized IPv6ExtHdrOption) { 1062 t.Helper() 1063 dummy, ok := serializable.(*dummyHbHOptionSerializer) 1064 if !ok { 1065 t.Fatalf("got serializable = %T, want = *dummyHbHOptionSerializer", serializable) 1066 } 1067 unknown, ok := deserialized.(*IPv6UnknownExtHdrOption) 1068 if !ok { 1069 t.Fatalf("got deserialized = %T, want = %T", deserialized, &IPv6UnknownExtHdrOption{}) 1070 } 1071 if dummy.id != unknown.Identifier { 1072 t.Errorf("got deserialized identifier = %d, want = %d", unknown.Identifier, dummy.id) 1073 } 1074 if diff := cmp.Diff(dummy.payload, unknown.Data, viewTransformer, bufferTransformer); diff != "" { 1075 t.Errorf("option payload deserialization mismatch (-want +got):\n%s", diff) 1076 } 1077 } 1078 tests := []struct { 1079 name string 1080 nextHeader uint8 1081 options []IPv6SerializableHopByHopOption 1082 expect []byte 1083 validate func(*testing.T, IPv6SerializableHopByHopOption, IPv6ExtHdrOption) 1084 }{ 1085 { 1086 name: "single option", 1087 nextHeader: 13, 1088 options: []IPv6SerializableHopByHopOption{ 1089 &dummyHbHOptionSerializer{ 1090 id: 15, 1091 payload: buffer.NewViewWithData([]byte{9, 8, 7, 6}), 1092 }, 1093 }, 1094 expect: []byte{13, 0, 15, 4, 9, 8, 7, 6}, 1095 validate: validateDummies, 1096 }, 1097 { 1098 name: "short option padN zero", 1099 nextHeader: 88, 1100 options: []IPv6SerializableHopByHopOption{ 1101 &dummyHbHOptionSerializer{ 1102 id: 22, 1103 payload: buffer.NewViewWithData([]byte{4, 5}), 1104 }, 1105 }, 1106 expect: []byte{88, 0, 22, 2, 4, 5, 1, 0}, 1107 validate: validateDummies, 1108 }, 1109 { 1110 name: "short option pad1", 1111 nextHeader: 11, 1112 options: []IPv6SerializableHopByHopOption{ 1113 &dummyHbHOptionSerializer{ 1114 id: 33, 1115 payload: buffer.NewViewWithData([]byte{1, 2, 3}), 1116 }, 1117 }, 1118 expect: []byte{11, 0, 33, 3, 1, 2, 3, 0}, 1119 validate: validateDummies, 1120 }, 1121 { 1122 name: "long option padN", 1123 nextHeader: 55, 1124 options: []IPv6SerializableHopByHopOption{ 1125 &dummyHbHOptionSerializer{ 1126 id: 77, 1127 payload: buffer.NewViewWithData([]byte{1, 2, 3, 4, 5, 6, 7, 8}), 1128 }, 1129 }, 1130 expect: []byte{55, 1, 77, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 0, 0}, 1131 validate: validateDummies, 1132 }, 1133 { 1134 name: "two options", 1135 nextHeader: 33, 1136 options: []IPv6SerializableHopByHopOption{ 1137 &dummyHbHOptionSerializer{ 1138 id: 11, 1139 payload: buffer.NewViewWithData([]byte{1, 2, 3}), 1140 }, 1141 &dummyHbHOptionSerializer{ 1142 id: 22, 1143 payload: buffer.NewViewWithData([]byte{4, 5, 6}), 1144 }, 1145 }, 1146 expect: []byte{33, 1, 11, 3, 1, 2, 3, 22, 3, 4, 5, 6, 1, 2, 0, 0}, 1147 validate: validateDummies, 1148 }, 1149 { 1150 name: "two options align 2n", 1151 nextHeader: 33, 1152 options: []IPv6SerializableHopByHopOption{ 1153 &dummyHbHOptionSerializer{ 1154 id: 11, 1155 payload: buffer.NewViewWithData([]byte{1, 2, 3}), 1156 }, 1157 &dummyHbHOptionSerializer{ 1158 id: 22, 1159 payload: buffer.NewViewWithData([]byte{4, 5, 6}), 1160 align: 2, 1161 }, 1162 }, 1163 expect: []byte{33, 1, 11, 3, 1, 2, 3, 0, 22, 3, 4, 5, 6, 1, 1, 0}, 1164 validate: validateDummies, 1165 }, 1166 { 1167 name: "two options align 8n+1", 1168 nextHeader: 33, 1169 options: []IPv6SerializableHopByHopOption{ 1170 &dummyHbHOptionSerializer{ 1171 id: 11, 1172 payload: buffer.NewViewWithData([]byte{1, 2}), 1173 }, 1174 &dummyHbHOptionSerializer{ 1175 id: 22, 1176 payload: buffer.NewViewWithData([]byte{4, 5, 6}), 1177 align: 8, 1178 alignOffset: 1, 1179 }, 1180 }, 1181 expect: []byte{33, 1, 11, 2, 1, 2, 1, 1, 0, 22, 3, 4, 5, 6, 1, 0}, 1182 validate: validateDummies, 1183 }, 1184 { 1185 name: "no options", 1186 nextHeader: 33, 1187 options: []IPv6SerializableHopByHopOption{}, 1188 expect: []byte{33, 0, 1, 4, 0, 0, 0, 0}, 1189 }, 1190 { 1191 name: "Router Alert", 1192 nextHeader: 33, 1193 options: []IPv6SerializableHopByHopOption{&IPv6RouterAlertOption{Value: IPv6RouterAlertMLD}}, 1194 expect: []byte{33, 0, 5, 2, 0, 0, 1, 0}, 1195 validate: func(t *testing.T, _ IPv6SerializableHopByHopOption, deserialized IPv6ExtHdrOption) { 1196 t.Helper() 1197 routerAlert, ok := deserialized.(*IPv6RouterAlertOption) 1198 if !ok { 1199 t.Fatalf("got deserialized = %T, want = *IPv6RouterAlertOption", deserialized) 1200 } 1201 if routerAlert.Value != IPv6RouterAlertMLD { 1202 t.Errorf("got routerAlert.Value = %d, want = %d", routerAlert.Value, IPv6RouterAlertMLD) 1203 } 1204 }, 1205 }, 1206 } 1207 for _, test := range tests { 1208 t.Run(test.name, func(t *testing.T) { 1209 s := IPv6SerializableHopByHopExtHdr(test.options) 1210 length := s.length() 1211 if length != len(test.expect) { 1212 t.Fatalf("got s.length() = %d, want = %d", length, len(test.expect)) 1213 } 1214 b := make([]byte, length) 1215 for i := range b { 1216 // Fill the buffer with ones to ensure all padding is correctly set. 1217 b[i] = 0xFF 1218 } 1219 if got := s.serializeInto(test.nextHeader, b); got != length { 1220 t.Fatalf("got s.serializeInto(..) = %d, want = %d", got, length) 1221 } 1222 if diff := cmp.Diff(test.expect, b); diff != "" { 1223 t.Fatalf("serialization mismatch (-want +got):\n%s", diff) 1224 } 1225 1226 // Deserialize the options and verify them. 1227 optLen := (b[ipv6HopByHopExtHdrLengthOffset] + ipv6HopByHopExtHdrUnaccountedLenWords) * ipv6ExtHdrLenBytesPerUnit 1228 iter := ipv6OptionsExtHdr{buffer.NewViewWithData(b[ipv6HopByHopExtHdrOptionsOffset:optLen])}.Iter() 1229 for _, testOpt := range test.options { 1230 opt, done, err := iter.Next() 1231 if err != nil { 1232 t.Fatalf("iter.Next(): %s", err) 1233 } 1234 if done { 1235 t.Fatalf("got iter.Next() = (%T, %t, _), want = (_, false, _)", opt, done) 1236 } 1237 test.validate(t, testOpt, opt) 1238 } 1239 opt, done, err := iter.Next() 1240 if err != nil { 1241 t.Fatalf("iter.Next(): %s", err) 1242 } 1243 if !done { 1244 t.Fatalf("got iter.Next() = (%T, %t, _), want = (_, true, _)", opt, done) 1245 } 1246 }) 1247 } 1248 } 1249 1250 var _ IPv6SerializableExtHdr = (*dummyIPv6ExtHdrSerializer)(nil) 1251 1252 // dummyIPv6ExtHdrSerializer provides a generic implementation of 1253 // IPv6SerializableExtHdr for use in tests. 1254 // 1255 // The dummy header always carries the nextHeader value in the first byte. 1256 type dummyIPv6ExtHdrSerializer struct { 1257 id IPv6ExtensionHeaderIdentifier 1258 headerContents []byte 1259 } 1260 1261 // identifier implements IPv6SerializableExtHdr. 1262 func (s *dummyIPv6ExtHdrSerializer) identifier() IPv6ExtensionHeaderIdentifier { 1263 return s.id 1264 } 1265 1266 // length implements IPv6SerializableExtHdr. 1267 func (s *dummyIPv6ExtHdrSerializer) length() int { 1268 return len(s.headerContents) + 1 1269 } 1270 1271 // serializeInto implements IPv6SerializableExtHdr. 1272 func (s *dummyIPv6ExtHdrSerializer) serializeInto(nextHeader uint8, b []byte) int { 1273 b[0] = nextHeader 1274 return copy(b[1:], s.headerContents) + 1 1275 } 1276 1277 func TestIPv6ExtHdrSerializer(t *testing.T) { 1278 tests := []struct { 1279 name string 1280 headers []IPv6SerializableExtHdr 1281 nextHeader tcpip.TransportProtocolNumber 1282 expectSerialized []byte 1283 expectNextHeader uint8 1284 }{ 1285 { 1286 name: "one header", 1287 headers: []IPv6SerializableExtHdr{ 1288 &dummyIPv6ExtHdrSerializer{ 1289 id: 15, 1290 headerContents: []byte{1, 2, 3, 4}, 1291 }, 1292 }, 1293 nextHeader: TCPProtocolNumber, 1294 expectSerialized: []byte{byte(TCPProtocolNumber), 1, 2, 3, 4}, 1295 expectNextHeader: 15, 1296 }, 1297 { 1298 name: "two headers", 1299 headers: []IPv6SerializableExtHdr{ 1300 &dummyIPv6ExtHdrSerializer{ 1301 id: 22, 1302 headerContents: []byte{1, 2, 3}, 1303 }, 1304 &dummyIPv6ExtHdrSerializer{ 1305 id: 23, 1306 headerContents: []byte{4, 5, 6}, 1307 }, 1308 }, 1309 nextHeader: ICMPv6ProtocolNumber, 1310 expectSerialized: []byte{ 1311 23, 1, 2, 3, 1312 byte(ICMPv6ProtocolNumber), 4, 5, 6, 1313 }, 1314 expectNextHeader: 22, 1315 }, 1316 { 1317 name: "no headers", 1318 headers: []IPv6SerializableExtHdr{}, 1319 nextHeader: UDPProtocolNumber, 1320 expectSerialized: []byte{}, 1321 expectNextHeader: byte(UDPProtocolNumber), 1322 }, 1323 } 1324 for _, test := range tests { 1325 t.Run(test.name, func(t *testing.T) { 1326 s := IPv6ExtHdrSerializer(test.headers) 1327 l := s.Length() 1328 if got, want := l, len(test.expectSerialized); got != want { 1329 t.Fatalf("got serialized length = %d, want = %d", got, want) 1330 } 1331 b := make([]byte, l) 1332 for i := range b { 1333 // Fill the buffer with garbage to make sure we're writing to all bytes. 1334 b[i] = 0xFF 1335 } 1336 nextHeader, serializedLen := s.Serialize(test.nextHeader, b) 1337 if serializedLen != len(test.expectSerialized) || nextHeader != test.expectNextHeader { 1338 t.Errorf( 1339 "got s.Serialize(..) = (%d, %d), want = (%d, %d)", 1340 nextHeader, 1341 serializedLen, 1342 test.expectNextHeader, 1343 len(test.expectSerialized), 1344 ) 1345 } 1346 if diff := cmp.Diff(test.expectSerialized, b); diff != "" { 1347 t.Errorf("serialization mismatch (-want +got):\n%s", diff) 1348 } 1349 }) 1350 } 1351 }