gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/network/internal/fragmentation/fragmentation_test.go (about) 1 // Copyright 2022 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 fragmentation 16 17 import ( 18 "errors" 19 "testing" 20 "time" 21 22 "github.com/google/go-cmp/cmp" 23 "gvisor.dev/gvisor/pkg/buffer" 24 "gvisor.dev/gvisor/pkg/tcpip/faketime" 25 "gvisor.dev/gvisor/pkg/tcpip/network/internal/testutil" 26 "gvisor.dev/gvisor/pkg/tcpip/stack" 27 ) 28 29 // reassembleTimeout is dummy timeout used for testing, where the clock never 30 // advances. 31 const reassembleTimeout = 1 32 33 // buf is a helper to build a Buffer from different strings. 34 func buf(size int, pieces ...string) buffer.Buffer { 35 buf := buffer.Buffer{} 36 c := buf.Clone() 37 defer c.Release() 38 for _, p := range pieces { 39 v := buffer.NewViewWithData([]byte(p)) 40 buf.Append(v) 41 } 42 43 return buf 44 } 45 46 func pkt(size int, pieces ...string) *stack.PacketBuffer { 47 return stack.NewPacketBuffer(stack.PacketBufferOptions{ 48 Payload: buf(size, pieces...), 49 }) 50 } 51 52 type processInput struct { 53 id FragmentID 54 first uint16 55 last uint16 56 more bool 57 proto uint8 58 pkt *stack.PacketBuffer 59 } 60 61 type processOutput struct { 62 buf buffer.Buffer 63 proto uint8 64 done bool 65 } 66 67 func TestFragmentationProcess(t *testing.T) { 68 var processTestCases = []struct { 69 comment string 70 in []processInput 71 out []processOutput 72 }{ 73 { 74 comment: "One ID", 75 in: []processInput{ 76 {id: FragmentID{ID: 0}, first: 0, last: 1, more: true, pkt: pkt(2, "01")}, 77 {id: FragmentID{ID: 0}, first: 2, last: 3, more: false, pkt: pkt(2, "23")}, 78 }, 79 out: []processOutput{ 80 {buf: buffer.Buffer{}, done: false}, 81 {buf: buf(4, "01", "23"), done: true}, 82 }, 83 }, 84 { 85 comment: "Next Header protocol mismatch", 86 in: []processInput{ 87 {id: FragmentID{ID: 0}, first: 0, last: 1, more: true, proto: 6, pkt: pkt(2, "01")}, 88 {id: FragmentID{ID: 0}, first: 2, last: 3, more: false, proto: 17, pkt: pkt(2, "23")}, 89 }, 90 out: []processOutput{ 91 {buf: buffer.Buffer{}, done: false}, 92 {buf: buf(4, "01", "23"), proto: 6, done: true}, 93 }, 94 }, 95 { 96 comment: "Two IDs", 97 in: []processInput{ 98 {id: FragmentID{ID: 0}, first: 0, last: 1, more: true, pkt: pkt(2, "01")}, 99 {id: FragmentID{ID: 1}, first: 0, last: 1, more: true, pkt: pkt(2, "ab")}, 100 {id: FragmentID{ID: 1}, first: 2, last: 3, more: false, pkt: pkt(2, "cd")}, 101 {id: FragmentID{ID: 0}, first: 2, last: 3, more: false, pkt: pkt(2, "23")}, 102 }, 103 out: []processOutput{ 104 {buf: buffer.Buffer{}, done: false}, 105 {buf: buffer.Buffer{}, done: false}, 106 {buf: buf(4, "ab", "cd"), done: true}, 107 {buf: buf(4, "01", "23"), done: true}, 108 }, 109 }, 110 } 111 for _, c := range processTestCases { 112 t.Run(c.comment, func(t *testing.T) { 113 f := NewFragmentation(minBlockSize, 2048, 512, reassembleTimeout, &faketime.NullClock{}, nil) 114 firstFragmentProto := c.in[0].proto 115 for i, in := range c.in { 116 in := in 117 defer in.pkt.DecRef() 118 defer c.out[i].buf.Release() 119 resPkt, proto, done, err := f.Process(in.id, in.first, in.last, in.more, in.proto, in.pkt) 120 if resPkt != nil { 121 defer resPkt.DecRef() 122 } 123 if err != nil { 124 t.Fatalf("f.Process(%+v, %d, %d, %t, %d, %#v) failed: %s", 125 in.id, in.first, in.last, in.more, in.proto, in.pkt, err) 126 } 127 if done != c.out[i].done { 128 t.Errorf("got Process(%+v, %d, %d, %t, %d, _) = (_, _, %t, _), want = (_, _, %t, _)", 129 in.id, in.first, in.last, in.more, in.proto, done, c.out[i].done) 130 } 131 if c.out[i].done { 132 if diff := cmp.Diff(c.out[i].buf.Flatten(), resPkt.Data().AsRange().ToSlice()); diff != "" { 133 t.Errorf("got Process(%+v, %d, %d, %t, %d, %#v) result mismatch (-want, +got):\n%s", 134 in.id, in.first, in.last, in.more, in.proto, in.pkt, diff) 135 } 136 if firstFragmentProto != proto { 137 t.Errorf("got Process(%+v, %d, %d, %t, %d, _) = (_, %d, _, _), want = (_, %d, _, _)", 138 in.id, in.first, in.last, in.more, in.proto, proto, firstFragmentProto) 139 } 140 if _, ok := f.reassemblers[in.id]; ok { 141 t.Errorf("Process(%d) did not remove buffer from reassemblers", i) 142 } 143 for n := f.rList.Front(); n != nil; n = n.Next() { 144 if n.id == in.id { 145 t.Errorf("Process(%d) did not remove buffer from rList", i) 146 } 147 } 148 } 149 } 150 }) 151 } 152 } 153 154 func TestReassemblingTimeout(t *testing.T) { 155 const ( 156 reassemblyTimeout = time.Millisecond 157 protocol = 0xff 158 ) 159 160 type fragment struct { 161 first uint16 162 last uint16 163 more bool 164 data string 165 } 166 167 type event struct { 168 // name is a nickname of this event. 169 name string 170 171 // clockAdvance is a duration to advance the clock. The clock advances 172 // before a fragment specified in the fragment field is processed. 173 clockAdvance time.Duration 174 175 // fragment is a fragment to process. This can be nil if there is no 176 // fragment to process. 177 fragment *fragment 178 179 // expectDone is true if the fragmentation instance should report the 180 // reassembly is done after the fragment is processd. 181 expectDone bool 182 183 // memSizeAfterEvent is the expected memory size of the fragmentation 184 // instance after the event. 185 memSizeAfterEvent int 186 } 187 188 memSizeOfFrags := func(frags ...*fragment) int { 189 var size int 190 for _, frag := range frags { 191 p := pkt(len(frag.data), frag.data) 192 size += p.MemSize() 193 p.DecRef() 194 } 195 return size 196 } 197 198 half1 := &fragment{first: 0, last: 0, more: true, data: "0"} 199 half2 := &fragment{first: 1, last: 1, more: false, data: "1"} 200 201 tests := []struct { 202 name string 203 events []event 204 }{ 205 { 206 name: "half1 and half2 are reassembled successfully", 207 events: []event{ 208 { 209 name: "half1", 210 fragment: half1, 211 expectDone: false, 212 memSizeAfterEvent: memSizeOfFrags(half1), 213 }, 214 { 215 name: "half2", 216 fragment: half2, 217 expectDone: true, 218 memSizeAfterEvent: 0, 219 }, 220 }, 221 }, 222 { 223 name: "half1 timeout, half2 timeout", 224 events: []event{ 225 { 226 name: "half1", 227 fragment: half1, 228 expectDone: false, 229 memSizeAfterEvent: memSizeOfFrags(half1), 230 }, 231 { 232 name: "half1 just before reassembly timeout", 233 clockAdvance: reassemblyTimeout - 1, 234 memSizeAfterEvent: memSizeOfFrags(half1), 235 }, 236 { 237 name: "half1 reassembly timeout", 238 clockAdvance: 1, 239 memSizeAfterEvent: 0, 240 }, 241 { 242 name: "half2", 243 fragment: half2, 244 expectDone: false, 245 memSizeAfterEvent: memSizeOfFrags(half2), 246 }, 247 { 248 name: "half2 just before reassembly timeout", 249 clockAdvance: reassemblyTimeout - 1, 250 memSizeAfterEvent: memSizeOfFrags(half2), 251 }, 252 { 253 name: "half2 reassembly timeout", 254 clockAdvance: 1, 255 memSizeAfterEvent: 0, 256 }, 257 }, 258 }, 259 } 260 for _, test := range tests { 261 t.Run(test.name, func(t *testing.T) { 262 clock := faketime.NewManualClock() 263 f := NewFragmentation(minBlockSize, HighFragThreshold, LowFragThreshold, reassemblyTimeout, clock, nil) 264 for _, event := range test.events { 265 clock.Advance(event.clockAdvance) 266 if frag := event.fragment; frag != nil { 267 p := pkt(len(frag.data), frag.data) 268 defer p.DecRef() 269 pkt, _, done, err := f.Process(FragmentID{}, frag.first, frag.last, frag.more, protocol, p) 270 if pkt != nil { 271 pkt.DecRef() 272 } 273 if err != nil { 274 t.Fatalf("%s: f.Process failed: %s", event.name, err) 275 } 276 if done != event.expectDone { 277 t.Fatalf("%s: got done = %t, want = %t", event.name, done, event.expectDone) 278 } 279 } 280 if got, want := f.memSize, event.memSizeAfterEvent; got != want { 281 t.Errorf("%s: got f.memSize = %d, want = %d", event.name, got, want) 282 } 283 } 284 }) 285 } 286 } 287 288 func TestMemoryLimits(t *testing.T) { 289 p := pkt(1, "0") 290 defer p.DecRef() 291 lowLimit := p.MemSize() 292 highLimit := 3 * lowLimit // Allow at most 3 such packets. 293 // Using a manual clock here and below because the fragmentation object 294 // cleans up its reassemblers with a job that's scheduled with the clock 295 // argument. If the clock does not schedule jobs, the reassemblers are not 296 // released and the fragmentation object leaks packets. 297 c := faketime.NewManualClock() 298 defer c.Advance(reassembleTimeout) 299 f := NewFragmentation(minBlockSize, highLimit, lowLimit, reassembleTimeout, c, nil) 300 // Send first fragment with id = 0. 301 p0 := pkt(1, "0") 302 defer p0.DecRef() 303 if _, _, _, err := f.Process(FragmentID{ID: 0}, 0, 0, true, 0xFF, p0); err != nil { 304 t.Fatal(err) 305 } 306 // Send first fragment with id = 1. 307 p1 := pkt(1, "1") 308 defer p1.DecRef() 309 if _, _, _, err := f.Process(FragmentID{ID: 1}, 0, 0, true, 0xFF, p1); err != nil { 310 t.Fatal(err) 311 } 312 // Send first fragment with id = 2. 313 p2 := pkt(1, "2") 314 defer p2.DecRef() 315 if _, _, _, err := f.Process(FragmentID{ID: 2}, 0, 0, true, 0xFF, p2); err != nil { 316 t.Fatal(err) 317 } 318 319 // Send first fragment with id = 3. This should caused id = 0 and id = 1 to be 320 // evicted. 321 p3 := pkt(1, "3") 322 defer p3.DecRef() 323 if _, _, _, err := f.Process(FragmentID{ID: 3}, 0, 0, true, 0xFF, p3); err != nil { 324 t.Fatal(err) 325 } 326 327 if _, ok := f.reassemblers[FragmentID{ID: 0}]; ok { 328 t.Errorf("Memory limits are not respected: id=0 has not been evicted.") 329 } 330 if _, ok := f.reassemblers[FragmentID{ID: 1}]; ok { 331 t.Errorf("Memory limits are not respected: id=1 has not been evicted.") 332 } 333 if _, ok := f.reassemblers[FragmentID{ID: 3}]; !ok { 334 t.Errorf("Implementation of memory limits is wrong: id=3 is not present.") 335 } 336 } 337 338 func TestMemoryLimitsIgnoresDuplicates(t *testing.T) { 339 p0 := pkt(1, "0") 340 defer p0.DecRef() 341 memSize := p0.MemSize() 342 c := faketime.NewManualClock() 343 defer c.Advance(reassembleTimeout) 344 f := NewFragmentation(minBlockSize, memSize, 0, reassembleTimeout, c, nil) 345 // Send first fragment with id = 0. 346 p1 := pkt(1, "0") 347 defer p1.DecRef() 348 if _, _, _, err := f.Process(FragmentID{}, 0, 0, true, 0xFF, p1); err != nil { 349 t.Fatal(err) 350 } 351 // Send the same packet again. 352 p1dup := pkt(1, "0") 353 defer p1dup.DecRef() 354 if _, _, _, err := f.Process(FragmentID{}, 0, 0, true, 0xFF, p1dup); err != nil { 355 t.Fatal(err) 356 } 357 358 if got, want := f.memSize, memSize; got != want { 359 t.Errorf("Wrong size, duplicates are not handled correctly: got=%d, want=%d.", got, want) 360 } 361 } 362 363 func TestErrors(t *testing.T) { 364 tests := []struct { 365 name string 366 blockSize uint16 367 first uint16 368 last uint16 369 more bool 370 data string 371 err error 372 }{ 373 { 374 name: "exact block size without more", 375 blockSize: 2, 376 first: 2, 377 last: 3, 378 more: false, 379 data: "01", 380 }, 381 { 382 name: "exact block size with more", 383 blockSize: 2, 384 first: 2, 385 last: 3, 386 more: true, 387 data: "01", 388 }, 389 { 390 name: "exact block size with more and extra data", 391 blockSize: 2, 392 first: 2, 393 last: 3, 394 more: true, 395 data: "012", 396 err: ErrInvalidArgs, 397 }, 398 { 399 name: "exact block size with more and too little data", 400 blockSize: 2, 401 first: 2, 402 last: 3, 403 more: true, 404 data: "0", 405 err: ErrInvalidArgs, 406 }, 407 { 408 name: "not exact block size with more", 409 blockSize: 2, 410 first: 2, 411 last: 2, 412 more: true, 413 data: "0", 414 err: ErrInvalidArgs, 415 }, 416 { 417 name: "not exact block size without more", 418 blockSize: 2, 419 first: 2, 420 last: 2, 421 more: false, 422 data: "0", 423 }, 424 { 425 name: "first not a multiple of block size", 426 blockSize: 2, 427 first: 3, 428 last: 4, 429 more: true, 430 data: "01", 431 err: ErrInvalidArgs, 432 }, 433 { 434 name: "first more than last", 435 blockSize: 2, 436 first: 4, 437 last: 3, 438 more: true, 439 data: "01", 440 err: ErrInvalidArgs, 441 }, 442 } 443 444 for _, test := range tests { 445 t.Run(test.name, func(t *testing.T) { 446 p0 := pkt(len(test.data), test.data) 447 defer p0.DecRef() 448 c := faketime.NewManualClock() 449 defer c.Advance(reassembleTimeout) 450 f := NewFragmentation(test.blockSize, HighFragThreshold, LowFragThreshold, reassembleTimeout, c, nil) 451 resPkt, _, done, err := f.Process(FragmentID{}, test.first, test.last, test.more, 0, p0) 452 453 if resPkt != nil { 454 resPkt.DecRef() 455 } 456 if !errors.Is(err, test.err) { 457 t.Errorf("got Process(_, %d, %d, %t, _, %q) = (_, _, _, %v), want = (_, _, _, %v)", test.first, test.last, test.more, test.data, err, test.err) 458 } 459 if done { 460 t.Errorf("got Process(_, %d, %d, %t, _, %q) = (_, _, true, _), want = (_, _, false, _)", test.first, test.last, test.more, test.data) 461 } 462 }) 463 } 464 } 465 466 type fragmentInfo struct { 467 remaining int 468 copied int 469 offset int 470 more bool 471 } 472 473 func TestPacketFragmenter(t *testing.T) { 474 const ( 475 reserve = 60 476 proto = 0 477 ) 478 479 tests := []struct { 480 name string 481 fragmentPayloadLen uint32 482 transportHeaderLen int 483 payloadSize int 484 wantFragments []fragmentInfo 485 }{ 486 { 487 name: "Packet exactly fits in MTU", 488 fragmentPayloadLen: 1280, 489 transportHeaderLen: 0, 490 payloadSize: 1280, 491 wantFragments: []fragmentInfo{ 492 {remaining: 0, copied: 1280, offset: 0, more: false}, 493 }, 494 }, 495 { 496 name: "Packet exactly does not fit in MTU", 497 fragmentPayloadLen: 1000, 498 transportHeaderLen: 0, 499 payloadSize: 1001, 500 wantFragments: []fragmentInfo{ 501 {remaining: 1, copied: 1000, offset: 0, more: true}, 502 {remaining: 0, copied: 1, offset: 1000, more: false}, 503 }, 504 }, 505 { 506 name: "Packet has a transport header", 507 fragmentPayloadLen: 560, 508 transportHeaderLen: 40, 509 payloadSize: 560, 510 wantFragments: []fragmentInfo{ 511 {remaining: 1, copied: 560, offset: 0, more: true}, 512 {remaining: 0, copied: 40, offset: 560, more: false}, 513 }, 514 }, 515 { 516 name: "Packet has a huge transport header", 517 fragmentPayloadLen: 500, 518 transportHeaderLen: 1300, 519 payloadSize: 500, 520 wantFragments: []fragmentInfo{ 521 {remaining: 3, copied: 500, offset: 0, more: true}, 522 {remaining: 2, copied: 500, offset: 500, more: true}, 523 {remaining: 1, copied: 500, offset: 1000, more: true}, 524 {remaining: 0, copied: 300, offset: 1500, more: false}, 525 }, 526 }, 527 } 528 529 for _, test := range tests { 530 t.Run(test.name, func(t *testing.T) { 531 pkt := testutil.MakeRandPkt(test.transportHeaderLen, reserve, []int{test.payloadSize}, proto) 532 defer pkt.DecRef() 533 payloadView := stack.PayloadSince(pkt.TransportHeader()) 534 defer payloadView.Release() 535 originalPayload := payloadView.AsSlice() 536 var reassembledPayload buffer.Buffer 537 defer reassembledPayload.Release() 538 pf := MakePacketFragmenter(pkt, test.fragmentPayloadLen, reserve) 539 for i := 0; ; i++ { 540 fragPkt, offset, copied, more := pf.BuildNextFragment() 541 defer fragPkt.DecRef() 542 wantFragment := test.wantFragments[i] 543 if got := pf.RemainingFragmentCount(); got != wantFragment.remaining { 544 t.Errorf("(fragment #%d) got pf.RemainingFragmentCount() = %d, want = %d", i, got, wantFragment.remaining) 545 } 546 if copied != wantFragment.copied { 547 t.Errorf("(fragment #%d) got copied = %d, want = %d", i, copied, wantFragment.copied) 548 } 549 if offset != wantFragment.offset { 550 t.Errorf("(fragment #%d) got offset = %d, want = %d", i, offset, wantFragment.offset) 551 } 552 if more != wantFragment.more { 553 t.Errorf("(fragment #%d) got more = %t, want = %t", i, more, wantFragment.more) 554 } 555 if got := uint32(fragPkt.Size()); got > test.fragmentPayloadLen { 556 t.Errorf("(fragment #%d) got fragPkt.Size() = %d, want <= %d", i, got, test.fragmentPayloadLen) 557 } 558 if got := fragPkt.AvailableHeaderBytes(); got != reserve { 559 t.Errorf("(fragment #%d) got fragPkt.AvailableHeaderBytes() = %d, want = %d", i, got, reserve) 560 } 561 if got := len(fragPkt.TransportHeader().Slice()); got != 0 { 562 t.Errorf("(fragment #%d) got fragPkt.TransportHeader().View().Size() = %d, want = 0", i, got) 563 } 564 fragBuf := fragPkt.Data().ToBuffer() 565 reassembledPayload.Merge(&fragBuf) 566 if !more { 567 if i != len(test.wantFragments)-1 { 568 t.Errorf("got fragment count = %d, want = %d", i, len(test.wantFragments)-1) 569 } 570 break 571 } 572 } 573 if diff := cmp.Diff(reassembledPayload.Flatten(), originalPayload); diff != "" { 574 t.Errorf("reassembledPayload mismatch (-want +got):\n%s", diff) 575 } 576 }) 577 } 578 } 579 580 type testTimeoutHandler struct { 581 pkt *stack.PacketBuffer 582 } 583 584 func (h *testTimeoutHandler) OnReassemblyTimeout(pkt *stack.PacketBuffer) { 585 h.pkt = pkt 586 } 587 588 func TestTimeoutHandler(t *testing.T) { 589 const ( 590 proto = 99 591 ) 592 593 pk1 := pkt(1, "1") 594 defer pk1.DecRef() 595 pk2 := pkt(1, "2") 596 defer pk2.DecRef() 597 598 type processParam struct { 599 first uint16 600 last uint16 601 more bool 602 pkt *stack.PacketBuffer 603 } 604 605 tests := []struct { 606 name string 607 params []processParam 608 wantError bool 609 wantPkt *stack.PacketBuffer 610 }{ 611 { 612 name: "onTimeout runs", 613 params: []processParam{ 614 { 615 first: 0, 616 last: 0, 617 more: true, 618 pkt: pk1, 619 }, 620 }, 621 wantError: false, 622 wantPkt: pk1, 623 }, 624 { 625 name: "no first fragment", 626 params: []processParam{ 627 { 628 first: 1, 629 last: 1, 630 more: true, 631 pkt: pk1, 632 }, 633 }, 634 wantError: false, 635 wantPkt: nil, 636 }, 637 { 638 name: "second pkt is ignored", 639 params: []processParam{ 640 { 641 first: 0, 642 last: 0, 643 more: true, 644 pkt: pk1, 645 }, 646 { 647 first: 0, 648 last: 0, 649 more: true, 650 pkt: pk2, 651 }, 652 }, 653 wantError: false, 654 wantPkt: pk1, 655 }, 656 { 657 name: "invalid args - first is greater than last", 658 params: []processParam{ 659 { 660 first: 1, 661 last: 0, 662 more: true, 663 pkt: pk1, 664 }, 665 }, 666 wantError: true, 667 wantPkt: nil, 668 }, 669 } 670 671 id := FragmentID{ID: 0} 672 673 for _, test := range tests { 674 t.Run(test.name, func(t *testing.T) { 675 handler := &testTimeoutHandler{pkt: nil} 676 677 f := NewFragmentation(minBlockSize, HighFragThreshold, LowFragThreshold, reassembleTimeout, &faketime.NullClock{}, handler) 678 679 for _, p := range test.params { 680 if _, _, _, err := f.Process(id, p.first, p.last, p.more, proto, p.pkt); err != nil && !test.wantError { 681 t.Errorf("f.Process error = %s", err) 682 } 683 } 684 if !test.wantError { 685 r, ok := f.reassemblers[id] 686 if !ok { 687 t.Fatal("Reassembler not found") 688 } 689 f.release(r, true) 690 } 691 switch { 692 case handler.pkt != nil && test.wantPkt == nil: 693 t.Errorf("got handler.pkt = not nil (pkt.Data = %x), want = nil", handler.pkt.Data().AsRange().ToSlice()) 694 case handler.pkt == nil && test.wantPkt != nil: 695 t.Errorf("got handler.pkt = nil, want = not nil (pkt.Data = %x)", test.wantPkt.Data().AsRange().ToSlice()) 696 case handler.pkt != nil && test.wantPkt != nil: 697 if diff := cmp.Diff(test.wantPkt.Data().AsRange().ToSlice(), handler.pkt.Data().AsRange().ToSlice()); diff != "" { 698 t.Errorf("pkt.Data mismatch (-want, +got):\n%s", diff) 699 } 700 } 701 }) 702 } 703 } 704 705 func TestFragmentSurvivesReleaseJob(t *testing.T) { 706 handler := &testTimeoutHandler{pkt: nil} 707 c := faketime.NewManualClock() 708 f := NewFragmentation(minBlockSize, HighFragThreshold, LowFragThreshold, reassembleTimeout, c, handler) 709 pkt := pkt(2, "01") 710 // Values to Process don't matter except for pkt. 711 resPkt, _, _, _ := f.Process(FragmentID{ID: 0}, 0, 1, false, 0, pkt) 712 pkt.DecRef() 713 // This clears out the references held by the reassembler. 714 c.Advance(reassembleTimeout) 715 // If Process doesn't give the returned packet its own reference, this will 716 // fail. 717 resPkt.DecRef() 718 }