github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/tcpip/stack/packet_buffer.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package stack 15 16 import ( 17 "fmt" 18 "io" 19 20 "github.com/metacubex/gvisor/pkg/buffer" 21 "github.com/metacubex/gvisor/pkg/sync" 22 "github.com/metacubex/gvisor/pkg/tcpip" 23 "github.com/metacubex/gvisor/pkg/tcpip/header" 24 ) 25 26 type headerType int 27 28 const ( 29 virtioNetHeader headerType = iota 30 linkHeader 31 networkHeader 32 transportHeader 33 numHeaderType 34 ) 35 36 var pkPool = sync.Pool{ 37 New: func() any { 38 return &PacketBuffer{} 39 }, 40 } 41 42 // PacketBufferOptions specifies options for PacketBuffer creation. 43 type PacketBufferOptions struct { 44 // ReserveHeaderBytes is the number of bytes to reserve for headers. Total 45 // number of bytes pushed onto the headers must not exceed this value. 46 ReserveHeaderBytes int 47 48 // Payload is the initial unparsed data for the new packet. If set, it will 49 // be owned by the new packet. 50 Payload buffer.Buffer 51 52 // IsForwardedPacket identifies that the PacketBuffer being created is for a 53 // forwarded packet. 54 IsForwardedPacket bool 55 56 // OnRelease is a function to be run when the packet buffer is no longer 57 // referenced (released back to the pool). 58 OnRelease func() 59 } 60 61 // A PacketBuffer contains all the data of a network packet. 62 // 63 // As a PacketBuffer traverses up the stack, it may be necessary to pass it to 64 // multiple endpoints. 65 // 66 // The whole packet is expected to be a series of bytes in the following order: 67 // LinkHeader, NetworkHeader, TransportHeader, and Data. Any of them can be 68 // empty. Use of PacketBuffer in any other order is unsupported. 69 // 70 // PacketBuffer must be created with NewPacketBuffer, which sets the initial 71 // reference count to 1. Owners should call `DecRef()` when they are finished 72 // with the buffer to return it to the pool. 73 // 74 // Internal structure: A PacketBuffer holds a pointer to buffer.Buffer, which 75 // exposes a logically-contiguous byte storage. The underlying storage structure 76 // is abstracted out, and should not be a concern here for most of the time. 77 // 78 // |- reserved ->| 79 // |--->| consumed (incoming) 80 // 0 V V 81 // +--------+----+----+--------------------+ 82 // | | | | current data ... | (buf) 83 // +--------+----+----+--------------------+ 84 // ^ | 85 // |<---| pushed (outgoing) 86 // 87 // When a PacketBuffer is created, a `reserved` header region can be specified, 88 // which stack pushes headers in this region for an outgoing packet. There could 89 // be no such region for an incoming packet, and `reserved` is 0. The value of 90 // `reserved` never changes in the entire lifetime of the packet. 91 // 92 // Outgoing Packet: When a header is pushed, `pushed` gets incremented by the 93 // pushed length, and the current value is stored for each header. PacketBuffer 94 // subtracts this value from `reserved` to compute the starting offset of each 95 // header in `buf`. 96 // 97 // Incoming Packet: When a header is consumed (a.k.a. parsed), the current 98 // `consumed` value is stored for each header, and it gets incremented by the 99 // consumed length. PacketBuffer adds this value to `reserved` to compute the 100 // starting offset of each header in `buf`. 101 // 102 // +stateify savable 103 type PacketBuffer struct { 104 _ sync.NoCopy 105 106 packetBufferRefs 107 108 // buf is the underlying buffer for the packet. See struct level docs for 109 // details. 110 buf buffer.Buffer 111 reserved int 112 pushed int 113 consumed int 114 115 // headers stores metadata about each header. 116 headers [numHeaderType]headerInfo 117 118 // NetworkProtocolNumber is only valid when NetworkHeader().View().IsEmpty() 119 // returns false. 120 // TODO(gvisor.dev/issue/3574): Remove the separately passed protocol 121 // numbers in registration APIs that take a PacketBuffer. 122 NetworkProtocolNumber tcpip.NetworkProtocolNumber 123 124 // TransportProtocol is only valid if it is non zero. 125 // TODO(gvisor.dev/issue/3810): This and the network protocol number should 126 // be moved into the headerinfo. This should resolve the validity issue. 127 TransportProtocolNumber tcpip.TransportProtocolNumber 128 129 // Hash is the transport layer hash of this packet. A value of zero 130 // indicates no valid hash has been set. 131 Hash uint32 132 133 // Owner is implemented by task to get the uid and gid. 134 // Only set for locally generated packets. 135 Owner tcpip.PacketOwner 136 137 // The following fields are only set by the qdisc layer when the packet 138 // is added to a queue. 139 EgressRoute RouteInfo 140 GSOOptions GSO 141 142 // snatDone indicates if the packet's source has been manipulated as per 143 // iptables NAT table. 144 snatDone bool 145 146 // dnatDone indicates if the packet's destination has been manipulated as per 147 // iptables NAT table. 148 dnatDone bool 149 150 // PktType indicates the SockAddrLink.PacketType of the packet as defined in 151 // https://www.man7.org/linux/man-pages/man7/packet.7.html. 152 PktType tcpip.PacketType 153 154 // NICID is the ID of the last interface the network packet was handled at. 155 NICID tcpip.NICID 156 157 // RXChecksumValidated indicates that checksum verification may be 158 // safely skipped. 159 RXChecksumValidated bool 160 161 // NetworkPacketInfo holds an incoming packet's network-layer information. 162 NetworkPacketInfo NetworkPacketInfo 163 164 tuple *tuple 165 166 // onRelease is a function to be run when the packet buffer is no longer 167 // referenced (released back to the pool). 168 onRelease func() `state:"nosave"` 169 } 170 171 // NewPacketBuffer creates a new PacketBuffer with opts. 172 func NewPacketBuffer(opts PacketBufferOptions) *PacketBuffer { 173 pk := pkPool.Get().(*PacketBuffer) 174 pk.reset() 175 if opts.ReserveHeaderBytes != 0 { 176 v := buffer.NewViewSize(opts.ReserveHeaderBytes) 177 pk.buf.Append(v) 178 pk.reserved = opts.ReserveHeaderBytes 179 } 180 if opts.Payload.Size() > 0 { 181 pk.buf.Merge(&opts.Payload) 182 } 183 pk.NetworkPacketInfo.IsForwardedPacket = opts.IsForwardedPacket 184 pk.onRelease = opts.OnRelease 185 pk.InitRefs() 186 return pk 187 } 188 189 // IncRef increments the PacketBuffer's refcount. 190 func (pk *PacketBuffer) IncRef() *PacketBuffer { 191 pk.packetBufferRefs.IncRef() 192 return pk 193 } 194 195 // DecRef decrements the PacketBuffer's refcount. If the refcount is 196 // decremented to zero, the PacketBuffer is returned to the PacketBuffer 197 // pool. 198 func (pk *PacketBuffer) DecRef() { 199 pk.packetBufferRefs.DecRef(func() { 200 if pk.onRelease != nil { 201 pk.onRelease() 202 } 203 204 pk.buf.Release() 205 pkPool.Put(pk) 206 }) 207 } 208 209 func (pk *PacketBuffer) reset() { 210 *pk = PacketBuffer{} 211 } 212 213 // ReservedHeaderBytes returns the number of bytes initially reserved for 214 // headers. 215 func (pk *PacketBuffer) ReservedHeaderBytes() int { 216 return pk.reserved 217 } 218 219 // AvailableHeaderBytes returns the number of bytes currently available for 220 // headers. This is relevant to PacketHeader.Push method only. 221 func (pk *PacketBuffer) AvailableHeaderBytes() int { 222 return pk.reserved - pk.pushed 223 } 224 225 // VirtioNetHeader returns the handle to virtio-layer header. 226 func (pk *PacketBuffer) VirtioNetHeader() PacketHeader { 227 return PacketHeader{ 228 pk: pk, 229 typ: virtioNetHeader, 230 } 231 } 232 233 // LinkHeader returns the handle to link-layer header. 234 func (pk *PacketBuffer) LinkHeader() PacketHeader { 235 return PacketHeader{ 236 pk: pk, 237 typ: linkHeader, 238 } 239 } 240 241 // NetworkHeader returns the handle to network-layer header. 242 func (pk *PacketBuffer) NetworkHeader() PacketHeader { 243 return PacketHeader{ 244 pk: pk, 245 typ: networkHeader, 246 } 247 } 248 249 // TransportHeader returns the handle to transport-layer header. 250 func (pk *PacketBuffer) TransportHeader() PacketHeader { 251 return PacketHeader{ 252 pk: pk, 253 typ: transportHeader, 254 } 255 } 256 257 // HeaderSize returns the total size of all headers in bytes. 258 func (pk *PacketBuffer) HeaderSize() int { 259 return pk.pushed + pk.consumed 260 } 261 262 // Size returns the size of packet in bytes. 263 func (pk *PacketBuffer) Size() int { 264 return int(pk.buf.Size()) - pk.headerOffset() 265 } 266 267 // MemSize returns the estimation size of the pk in memory, including backing 268 // buffer data. 269 func (pk *PacketBuffer) MemSize() int { 270 return int(pk.buf.Size()) + PacketBufferStructSize 271 } 272 273 // Data returns the handle to data portion of pk. 274 func (pk *PacketBuffer) Data() PacketData { 275 return PacketData{pk: pk} 276 } 277 278 // AsSlices returns the underlying storage of the whole packet. 279 // 280 // Note that AsSlices can allocate a lot. In hot paths it may be preferable to 281 // iterate over a PacketBuffer's data via AsViewList. 282 func (pk *PacketBuffer) AsSlices() [][]byte { 283 vl := pk.buf.AsViewList() 284 views := make([][]byte, 0, vl.Len()) 285 offset := pk.headerOffset() 286 pk.buf.SubApply(offset, int(pk.buf.Size())-offset, func(v *buffer.View) { 287 views = append(views, v.AsSlice()) 288 }) 289 return views 290 } 291 292 // AsViewList returns the list of Views backing the PacketBuffer along with the 293 // header offset into them. Users may not save or modify the ViewList returned. 294 func (pk *PacketBuffer) AsViewList() (buffer.ViewList, int) { 295 return pk.buf.AsViewList(), pk.headerOffset() 296 } 297 298 // ToBuffer returns a caller-owned copy of the underlying storage of the whole 299 // packet. 300 func (pk *PacketBuffer) ToBuffer() buffer.Buffer { 301 b := pk.buf.Clone() 302 b.TrimFront(int64(pk.headerOffset())) 303 return b 304 } 305 306 // ToView returns a caller-owned copy of the underlying storage of the whole 307 // packet as a view. 308 func (pk *PacketBuffer) ToView() *buffer.View { 309 p := buffer.NewView(int(pk.buf.Size())) 310 offset := pk.headerOffset() 311 pk.buf.SubApply(offset, int(pk.buf.Size())-offset, func(v *buffer.View) { 312 p.Write(v.AsSlice()) 313 }) 314 return p 315 } 316 317 func (pk *PacketBuffer) headerOffset() int { 318 return pk.reserved - pk.pushed 319 } 320 321 func (pk *PacketBuffer) headerOffsetOf(typ headerType) int { 322 return pk.reserved + pk.headers[typ].offset 323 } 324 325 func (pk *PacketBuffer) dataOffset() int { 326 return pk.reserved + pk.consumed 327 } 328 329 func (pk *PacketBuffer) push(typ headerType, size int) []byte { 330 h := &pk.headers[typ] 331 if h.length > 0 { 332 panic(fmt.Sprintf("push(%s, %d) called after previous push", typ, size)) 333 } 334 if pk.pushed+size > pk.reserved { 335 panic(fmt.Sprintf("push(%s, %d) overflows; pushed=%d reserved=%d", typ, size, pk.pushed, pk.reserved)) 336 } 337 pk.pushed += size 338 h.offset = -pk.pushed 339 h.length = size 340 view := pk.headerView(typ) 341 return view.AsSlice() 342 } 343 344 func (pk *PacketBuffer) consume(typ headerType, size int) (v []byte, consumed bool) { 345 h := &pk.headers[typ] 346 if h.length > 0 { 347 panic(fmt.Sprintf("consume must not be called twice: type %s", typ)) 348 } 349 if pk.reserved+pk.consumed+size > int(pk.buf.Size()) { 350 return nil, false 351 } 352 h.offset = pk.consumed 353 h.length = size 354 pk.consumed += size 355 view := pk.headerView(typ) 356 return view.AsSlice(), true 357 } 358 359 func (pk *PacketBuffer) headerView(typ headerType) buffer.View { 360 h := &pk.headers[typ] 361 if h.length == 0 { 362 return buffer.View{} 363 } 364 v, ok := pk.buf.PullUp(pk.headerOffsetOf(typ), h.length) 365 if !ok { 366 panic("PullUp failed") 367 } 368 return v 369 } 370 371 // Clone makes a semi-deep copy of pk. The underlying packet payload is 372 // shared. Hence, no modifications is done to underlying packet payload. 373 func (pk *PacketBuffer) Clone() *PacketBuffer { 374 newPk := pkPool.Get().(*PacketBuffer) 375 newPk.reset() 376 newPk.buf = pk.buf.Clone() 377 newPk.reserved = pk.reserved 378 newPk.pushed = pk.pushed 379 newPk.consumed = pk.consumed 380 newPk.headers = pk.headers 381 newPk.Hash = pk.Hash 382 newPk.Owner = pk.Owner 383 newPk.GSOOptions = pk.GSOOptions 384 newPk.NetworkProtocolNumber = pk.NetworkProtocolNumber 385 newPk.dnatDone = pk.dnatDone 386 newPk.snatDone = pk.snatDone 387 newPk.TransportProtocolNumber = pk.TransportProtocolNumber 388 newPk.PktType = pk.PktType 389 newPk.NICID = pk.NICID 390 newPk.RXChecksumValidated = pk.RXChecksumValidated 391 newPk.NetworkPacketInfo = pk.NetworkPacketInfo 392 newPk.tuple = pk.tuple 393 newPk.InitRefs() 394 return newPk 395 } 396 397 // ReserveHeaderBytes prepends reserved space for headers at the front 398 // of the underlying buf. Can only be called once per packet. 399 func (pk *PacketBuffer) ReserveHeaderBytes(reserved int) { 400 if pk.reserved != 0 { 401 panic(fmt.Sprintf("ReserveHeaderBytes(...) called on packet with reserved=%d, want reserved=0", pk.reserved)) 402 } 403 pk.reserved = reserved 404 pk.buf.Prepend(buffer.NewViewSize(reserved)) 405 } 406 407 // Network returns the network header as a header.Network. 408 // 409 // Network should only be called when NetworkHeader has been set. 410 func (pk *PacketBuffer) Network() header.Network { 411 switch netProto := pk.NetworkProtocolNumber; netProto { 412 case header.IPv4ProtocolNumber: 413 return header.IPv4(pk.NetworkHeader().Slice()) 414 case header.IPv6ProtocolNumber: 415 return header.IPv6(pk.NetworkHeader().Slice()) 416 default: 417 panic(fmt.Sprintf("unknown network protocol number %d", netProto)) 418 } 419 } 420 421 // CloneToInbound makes a semi-deep copy of the packet buffer (similar to 422 // Clone) to be used as an inbound packet. 423 // 424 // See PacketBuffer.Data for details about how a packet buffer holds an inbound 425 // packet. 426 func (pk *PacketBuffer) CloneToInbound() *PacketBuffer { 427 newPk := pkPool.Get().(*PacketBuffer) 428 newPk.reset() 429 newPk.buf = pk.buf.Clone() 430 newPk.InitRefs() 431 // Treat unfilled header portion as reserved. 432 newPk.reserved = pk.AvailableHeaderBytes() 433 newPk.tuple = pk.tuple 434 return newPk 435 } 436 437 // DeepCopyForForwarding creates a deep copy of the packet buffer for 438 // forwarding. 439 // 440 // The returned packet buffer will have the network and transport headers 441 // set if the original packet buffer did. 442 func (pk *PacketBuffer) DeepCopyForForwarding(reservedHeaderBytes int) *PacketBuffer { 443 payload := BufferSince(pk.NetworkHeader()) 444 defer payload.Release() 445 newPk := NewPacketBuffer(PacketBufferOptions{ 446 ReserveHeaderBytes: reservedHeaderBytes, 447 Payload: payload.DeepClone(), 448 IsForwardedPacket: true, 449 }) 450 451 { 452 consumeBytes := len(pk.NetworkHeader().Slice()) 453 if _, consumed := newPk.NetworkHeader().Consume(consumeBytes); !consumed { 454 panic(fmt.Sprintf("expected to consume network header %d bytes from new packet", consumeBytes)) 455 } 456 newPk.NetworkProtocolNumber = pk.NetworkProtocolNumber 457 } 458 459 { 460 consumeBytes := len(pk.TransportHeader().Slice()) 461 if _, consumed := newPk.TransportHeader().Consume(consumeBytes); !consumed { 462 panic(fmt.Sprintf("expected to consume transport header %d bytes from new packet", consumeBytes)) 463 } 464 newPk.TransportProtocolNumber = pk.TransportProtocolNumber 465 } 466 467 newPk.tuple = pk.tuple 468 469 return newPk 470 } 471 472 // IsNil returns whether the pointer is logically nil. 473 func (pk *PacketBuffer) IsNil() bool { 474 return pk == nil 475 } 476 477 // headerInfo stores metadata about a header in a packet. 478 // 479 // +stateify savable 480 type headerInfo struct { 481 // offset is the offset of the header in pk.buf relative to 482 // pk.buf[pk.reserved]. See the PacketBuffer struct for details. 483 offset int 484 485 // length is the length of this header. 486 length int 487 } 488 489 // PacketHeader is a handle object to a header in the underlying packet. 490 type PacketHeader struct { 491 pk *PacketBuffer 492 typ headerType 493 } 494 495 // View returns an caller-owned copy of the underlying storage of h as a 496 // *buffer.View. 497 func (h PacketHeader) View() *buffer.View { 498 view := h.pk.headerView(h.typ) 499 if view.Size() == 0 { 500 return nil 501 } 502 return view.Clone() 503 } 504 505 // Slice returns the underlying storage of h as a []byte. The returned slice 506 // should not be modified if the underlying packet could be shared, cloned, or 507 // borrowed. 508 func (h PacketHeader) Slice() []byte { 509 view := h.pk.headerView(h.typ) 510 return view.AsSlice() 511 } 512 513 // Push pushes size bytes in the front of its residing packet, and returns the 514 // backing storage. Callers may only call one of Push or Consume once on each 515 // header in the lifetime of the underlying packet. 516 func (h PacketHeader) Push(size int) []byte { 517 return h.pk.push(h.typ, size) 518 } 519 520 // Consume moves the first size bytes of the unparsed data portion in the packet 521 // to h, and returns the backing storage. In the case of data is shorter than 522 // size, consumed will be false, and the state of h will not be affected. 523 // Callers may only call one of Push or Consume once on each header in the 524 // lifetime of the underlying packet. 525 func (h PacketHeader) Consume(size int) (v []byte, consumed bool) { 526 return h.pk.consume(h.typ, size) 527 } 528 529 // PacketData represents the data portion of a PacketBuffer. 530 // 531 // +stateify savable 532 type PacketData struct { 533 pk *PacketBuffer 534 } 535 536 // PullUp returns a contiguous slice of size bytes from the beginning of d. 537 // Callers should not keep the view for later use. Callers can write to the 538 // returned slice if they have singular ownership over the underlying 539 // Buffer. 540 func (d PacketData) PullUp(size int) (b []byte, ok bool) { 541 view, ok := d.pk.buf.PullUp(d.pk.dataOffset(), size) 542 return view.AsSlice(), ok 543 } 544 545 // Consume is the same as PullUp except that is additionally consumes the 546 // returned bytes. Subsequent PullUp or Consume will not return these bytes. 547 func (d PacketData) Consume(size int) ([]byte, bool) { 548 v, ok := d.PullUp(size) 549 if ok { 550 d.pk.consumed += size 551 } 552 return v, ok 553 } 554 555 // ReadTo reads bytes from d to dst. It also removes these bytes from d 556 // unless peek is true. 557 func (d PacketData) ReadTo(dst io.Writer, peek bool) (int, error) { 558 var ( 559 err error 560 done int 561 ) 562 offset := d.pk.dataOffset() 563 d.pk.buf.SubApply(offset, int(d.pk.buf.Size())-offset, func(v *buffer.View) { 564 if err != nil { 565 return 566 } 567 var n int 568 n, err = dst.Write(v.AsSlice()) 569 done += n 570 if err != nil { 571 return 572 } 573 if n != v.Size() { 574 panic(fmt.Sprintf("io.Writer.Write succeeded with incomplete write: %d != %d", n, v.Size())) 575 } 576 }) 577 if !peek { 578 d.pk.buf.TrimFront(int64(done)) 579 } 580 return done, err 581 } 582 583 // CapLength reduces d to at most length bytes. 584 func (d PacketData) CapLength(length int) { 585 if length < 0 { 586 panic("length < 0") 587 } 588 d.pk.buf.Truncate(int64(length + d.pk.dataOffset())) 589 } 590 591 // ToBuffer returns the underlying storage of d in a buffer.Buffer. 592 func (d PacketData) ToBuffer() buffer.Buffer { 593 buf := d.pk.buf.Clone() 594 offset := d.pk.dataOffset() 595 buf.TrimFront(int64(offset)) 596 return buf 597 } 598 599 // AppendView appends v into d, taking the ownership of v. 600 func (d PacketData) AppendView(v *buffer.View) { 601 d.pk.buf.Append(v) 602 } 603 604 // MergeBuffer merges b into d and clears b. 605 func (d PacketData) MergeBuffer(b *buffer.Buffer) { 606 d.pk.buf.Merge(b) 607 } 608 609 // MergeFragment appends the data portion of frag to dst. It modifies 610 // frag and frag should not be used again. 611 func MergeFragment(dst, frag *PacketBuffer) { 612 frag.buf.TrimFront(int64(frag.dataOffset())) 613 dst.buf.Merge(&frag.buf) 614 } 615 616 // ReadFrom moves at most count bytes from the beginning of src to the end 617 // of d and returns the number of bytes moved. 618 func (d PacketData) ReadFrom(src *buffer.Buffer, count int) int { 619 toRead := int64(count) 620 if toRead > src.Size() { 621 toRead = src.Size() 622 } 623 clone := src.Clone() 624 clone.Truncate(toRead) 625 d.pk.buf.Merge(&clone) 626 src.TrimFront(toRead) 627 return int(toRead) 628 } 629 630 // ReadFromPacketData moves count bytes from the beginning of oth to the end of 631 // d. 632 func (d PacketData) ReadFromPacketData(oth PacketData, count int) { 633 buf := oth.ToBuffer() 634 buf.Truncate(int64(count)) 635 d.MergeBuffer(&buf) 636 oth.TrimFront(count) 637 buf.Release() 638 } 639 640 // Merge clears headers in oth and merges its data with d. 641 func (d PacketData) Merge(oth PacketData) { 642 oth.pk.buf.TrimFront(int64(oth.pk.dataOffset())) 643 d.pk.buf.Merge(&oth.pk.buf) 644 } 645 646 // TrimFront removes up to count bytes from the front of d's payload. 647 func (d PacketData) TrimFront(count int) { 648 if count > d.Size() { 649 count = d.Size() 650 } 651 buf := d.pk.Data().ToBuffer() 652 buf.TrimFront(int64(count)) 653 d.pk.buf.Truncate(int64(d.pk.dataOffset())) 654 d.pk.buf.Merge(&buf) 655 } 656 657 // Size returns the number of bytes in the data payload of the packet. 658 func (d PacketData) Size() int { 659 return int(d.pk.buf.Size()) - d.pk.dataOffset() 660 } 661 662 // AsRange returns a Range representing the current data payload of the packet. 663 func (d PacketData) AsRange() Range { 664 return Range{ 665 pk: d.pk, 666 offset: d.pk.dataOffset(), 667 length: d.Size(), 668 } 669 } 670 671 // Checksum returns a checksum over the data payload of the packet. 672 func (d PacketData) Checksum() uint16 { 673 return d.pk.buf.Checksum(d.pk.dataOffset()) 674 } 675 676 // ChecksumAtOffset returns a checksum over the data payload of the packet 677 // starting from offset. 678 func (d PacketData) ChecksumAtOffset(offset int) uint16 { 679 return d.pk.buf.Checksum(offset) 680 } 681 682 // Range represents a contiguous subportion of a PacketBuffer. 683 type Range struct { 684 pk *PacketBuffer 685 offset int 686 length int 687 } 688 689 // Size returns the number of bytes in r. 690 func (r Range) Size() int { 691 return r.length 692 } 693 694 // SubRange returns a new Range starting at off bytes of r. It returns an empty 695 // range if off is out-of-bounds. 696 func (r Range) SubRange(off int) Range { 697 if off > r.length { 698 return Range{pk: r.pk} 699 } 700 return Range{ 701 pk: r.pk, 702 offset: r.offset + off, 703 length: r.length - off, 704 } 705 } 706 707 // Capped returns a new Range with the same starting point of r and length 708 // capped at max. 709 func (r Range) Capped(max int) Range { 710 if r.length <= max { 711 return r 712 } 713 return Range{ 714 pk: r.pk, 715 offset: r.offset, 716 length: max, 717 } 718 } 719 720 // ToSlice returns a caller-owned copy of data in r. 721 func (r Range) ToSlice() []byte { 722 if r.length == 0 { 723 return nil 724 } 725 all := make([]byte, 0, r.length) 726 r.iterate(func(v *buffer.View) { 727 all = append(all, v.AsSlice()...) 728 }) 729 return all 730 } 731 732 // ToView returns a caller-owned copy of data in r. 733 func (r Range) ToView() *buffer.View { 734 if r.length == 0 { 735 return nil 736 } 737 newV := buffer.NewView(r.length) 738 r.iterate(func(v *buffer.View) { 739 newV.Write(v.AsSlice()) 740 }) 741 return newV 742 } 743 744 // iterate calls fn for each piece in r. fn is always called with a non-empty 745 // slice. 746 func (r Range) iterate(fn func(*buffer.View)) { 747 r.pk.buf.SubApply(r.offset, r.length, fn) 748 } 749 750 // PayloadSince returns a caller-owned view containing the payload starting from 751 // and including a particular header. 752 func PayloadSince(h PacketHeader) *buffer.View { 753 offset := h.pk.headerOffset() 754 for i := headerType(0); i < h.typ; i++ { 755 offset += h.pk.headers[i].length 756 } 757 return Range{ 758 pk: h.pk, 759 offset: offset, 760 length: int(h.pk.buf.Size()) - offset, 761 }.ToView() 762 } 763 764 // BufferSince returns a caller-owned view containing the packet payload 765 // starting from and including a particular header. 766 func BufferSince(h PacketHeader) buffer.Buffer { 767 offset := h.pk.headerOffset() 768 for i := headerType(0); i < h.typ; i++ { 769 offset += h.pk.headers[i].length 770 } 771 clone := h.pk.buf.Clone() 772 clone.TrimFront(int64(offset)) 773 return clone 774 }