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