packet.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package packet
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "iter"
  6. "net/netip"
  7. "slices"
  8. "syscall"
  9. "unsafe"
  10. "golang.org/x/sys/unix"
  11. )
  12. const Size = 0xffff
  13. type UDPPacket struct {
  14. Payload []byte
  15. Control []byte
  16. Name []byte
  17. SegSize int
  18. ReadyToSend bool
  19. wasSegmented bool
  20. isV4 bool
  21. }
  22. func New(isV4 bool) *UDPPacket {
  23. return &UDPPacket{
  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 *UDPPacket) 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 *UDPPacket) encodeSockaddr(dst []byte, addr netip.AddrPort) (uint32, error) {
  41. //todo no chance this works on windows?
  42. if p.isV4 {
  43. if !addr.Addr().Is4() {
  44. return 0, fmt.Errorf("Listener is IPv4, but writing to IPv6 remote")
  45. }
  46. var sa unix.RawSockaddrInet4
  47. sa.Family = unix.AF_INET
  48. sa.Addr = addr.Addr().As4()
  49. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&sa.Port))[:], addr.Port())
  50. size := unix.SizeofSockaddrInet4
  51. copy(dst[:size], (*(*[unix.SizeofSockaddrInet4]byte)(unsafe.Pointer(&sa)))[:])
  52. return uint32(size), nil
  53. }
  54. var sa unix.RawSockaddrInet6
  55. sa.Family = unix.AF_INET6
  56. sa.Addr = addr.Addr().As16()
  57. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&sa.Port))[:], addr.Port())
  58. size := unix.SizeofSockaddrInet6
  59. copy(dst[:size], (*(*[unix.SizeofSockaddrInet6]byte)(unsafe.Pointer(&sa)))[:])
  60. return uint32(size), nil
  61. }
  62. func (p *UDPPacket) SetAddrPort(addr netip.AddrPort) error {
  63. nl, err := p.encodeSockaddr(p.Name, addr)
  64. if err != nil {
  65. return err
  66. }
  67. p.Name = p.Name[:nl]
  68. return nil
  69. }
  70. func (p *UDPPacket) updateCtrl(ctrlLen int) {
  71. p.SegSize = len(p.Payload)
  72. p.wasSegmented = false
  73. if ctrlLen == 0 {
  74. return
  75. }
  76. if len(p.Control) == 0 {
  77. return
  78. }
  79. header, data, _ /*remain*/, err := unix.ParseOneSocketControlMessage(p.Control)
  80. if err != nil {
  81. return // oh well
  82. }
  83. if header.Level == unix.SOL_UDP && header.Type == unix.UDP_GRO && len(data) >= 2 {
  84. p.wasSegmented = true
  85. p.SegSize = int(binary.LittleEndian.Uint16(data[:2]))
  86. return
  87. }
  88. }
  89. // Update sets a UDPPacket into "just received, not processed" state
  90. func (p *UDPPacket) Update(ctrlLen int) {
  91. p.updateCtrl(ctrlLen)
  92. }
  93. func (p *UDPPacket) SetSegSizeForTX() {
  94. p.SegSize = len(p.Payload)
  95. hdr := (*unix.Cmsghdr)(unsafe.Pointer(&p.Control[0]))
  96. hdr.Level = unix.SOL_UDP
  97. hdr.Type = unix.UDP_SEGMENT
  98. hdr.SetLen(syscall.CmsgLen(2))
  99. binary.NativeEndian.PutUint16(p.Control[unix.CmsgLen(0):unix.CmsgLen(0)+2], uint16(p.SegSize))
  100. }
  101. func (p *UDPPacket) CompatibleForSegmentationWith(otherP *UDPPacket, currentTotalSize int) bool {
  102. //same dest
  103. if !slices.Equal(p.Name, otherP.Name) {
  104. return false
  105. }
  106. //don't get too big
  107. if len(p.Payload)+currentTotalSize >= 0xffff {
  108. return false
  109. }
  110. //same body len
  111. //todo allow single different size at end
  112. if len(p.Payload) != len(otherP.Payload) {
  113. return false //todo technically you can cram one extra in
  114. }
  115. return true
  116. }
  117. func (p *UDPPacket) Segments() iter.Seq[[]byte] {
  118. return func(yield func([]byte) bool) {
  119. //cursor := 0
  120. for offset := 0; offset < len(p.Payload); offset += p.SegSize {
  121. end := offset + p.SegSize
  122. if end > len(p.Payload) {
  123. end = len(p.Payload)
  124. }
  125. if !yield(p.Payload[offset:end]) {
  126. return
  127. }
  128. }
  129. }
  130. }