packet.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package packet
  2. import (
  3. "encoding/binary"
  4. "iter"
  5. "net/netip"
  6. "slices"
  7. "syscall"
  8. "unsafe"
  9. "golang.org/x/sys/unix"
  10. )
  11. const Size = 0xffff
  12. type Packet struct {
  13. Payload []byte
  14. Control []byte
  15. Name []byte
  16. SegSize int
  17. //todo should this hold out as well?
  18. OutLen int
  19. wasSegmented bool
  20. isV4 bool
  21. }
  22. func New(isV4 bool) *Packet {
  23. return &Packet{
  24. Payload: make([]byte, Size),
  25. Control: make([]byte, unix.CmsgSpace(2)),
  26. Name: make([]byte, unix.SizeofSockaddrInet6),
  27. isV4: isV4,
  28. }
  29. }
  30. func (p *Packet) AddrPort() netip.AddrPort {
  31. var ip netip.Addr
  32. // Its ok to skip the ok check here, the slicing is the only error that can occur and it will panic
  33. if p.isV4 {
  34. ip, _ = netip.AddrFromSlice(p.Name[4:8])
  35. } else {
  36. ip, _ = netip.AddrFromSlice(p.Name[8:24])
  37. }
  38. return netip.AddrPortFrom(ip.Unmap(), binary.BigEndian.Uint16(p.Name[2:4]))
  39. }
  40. func (p *Packet) updateCtrl(ctrlLen int) {
  41. p.SegSize = len(p.Payload)
  42. p.wasSegmented = false
  43. if ctrlLen == 0 {
  44. return
  45. }
  46. if len(p.Control) == 0 {
  47. return
  48. }
  49. cmsgs, err := unix.ParseSocketControlMessage(p.Control)
  50. if err != nil {
  51. return // oh well
  52. }
  53. for _, c := range cmsgs {
  54. if c.Header.Level == unix.SOL_UDP && c.Header.Type == unix.UDP_GRO && len(c.Data) >= 2 {
  55. p.wasSegmented = true
  56. p.SegSize = int(binary.LittleEndian.Uint16(c.Data[:2]))
  57. return
  58. }
  59. }
  60. }
  61. // Update sets a Packet into "just received, not processed" state
  62. func (p *Packet) Update(ctrlLen int) {
  63. p.OutLen = -1
  64. p.updateCtrl(ctrlLen)
  65. }
  66. func (p *Packet) SetSegSizeForTX() {
  67. p.SegSize = len(p.Payload)
  68. hdr := (*unix.Cmsghdr)(unsafe.Pointer(&p.Control[0]))
  69. hdr.Level = unix.SOL_UDP
  70. hdr.Type = unix.UDP_SEGMENT
  71. hdr.SetLen(syscall.CmsgLen(2))
  72. binary.NativeEndian.PutUint16(p.Control[unix.CmsgLen(0):unix.CmsgLen(0)+2], uint16(p.SegSize))
  73. }
  74. func (p *Packet) CompatibleForSegmentationWith(otherP *Packet, currentTotalSize int) bool {
  75. //same dest
  76. if !slices.Equal(p.Name, otherP.Name) {
  77. return false
  78. }
  79. //don't get too big
  80. if len(p.Payload)+currentTotalSize >= 0xffff {
  81. return false
  82. }
  83. //same body len
  84. //todo allow single different size at end
  85. if len(p.Payload) != len(otherP.Payload) {
  86. return false //todo technically you can cram one extra in
  87. }
  88. return true
  89. }
  90. func (p *Packet) Segments() iter.Seq[[]byte] {
  91. return func(yield func([]byte) bool) {
  92. //cursor := 0
  93. for offset := 0; offset < len(p.Payload); offset += p.SegSize {
  94. end := offset + p.SegSize
  95. if end > len(p.Payload) {
  96. end = len(p.Payload)
  97. }
  98. if !yield(p.Payload[offset:end]) {
  99. return
  100. }
  101. }
  102. }
  103. }