tio_gso_linux.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. package tio
  2. import (
  3. "fmt"
  4. "io"
  5. "log/slog"
  6. "os"
  7. "sync"
  8. "sync/atomic"
  9. "syscall"
  10. "unsafe"
  11. "golang.org/x/sys/unix"
  12. "github.com/slackhq/nebula/overlay/tio/virtio"
  13. )
  14. // tunRxBufSize is the per-Read worst-case footprint inside rxBuf: one
  15. // kernel-supplied packet body, which is at most ~64 KiB (tunReadBufSize).
  16. // Segmentation happens at encrypt time on a per-routine MTU-sized scratch
  17. // (see SegmentSuperpacket), so rxBuf only holds raw kernel-supplied bytes.
  18. // We round up to give comfortable margin for the drain headroom check
  19. // below.
  20. const tunRxBufSize = 64 * 1024
  21. // tunRxBufCap is the total size we allocate for the per-reader rx
  22. // buffer. With reads landing directly in rxBuf, each drain iteration
  23. // consumes up to tunRxBufSize of headroom for the kernel-supplied bytes.
  24. // Sized to two such iterations so the initial blocking read plus one
  25. // drain read both fit without partial-drop.
  26. const tunRxBufCap = tunRxBufSize * 2
  27. // tunDrainCap caps how many packets a single Read will accumulate via
  28. // the post-wake drain loop. Sized to soak up a burst of small ACKs while
  29. // bounding how much work a single caller holds before handing off.
  30. const tunDrainCap = 64
  31. // gsoMaxIovs caps the iovec budget WriteGSO assembles per call: 3 fixed
  32. // entries (virtio_net_hdr, IP hdr, transport hdr) plus up to gsoMaxIovs-3
  33. // payload fragments. Sized comfortably above the typical kernel GSO
  34. // segment cap (Linux UDP_GRO is 64) so realistic coalesced bursts never
  35. // touch the limit. iovecs are tiny (16 bytes), so the entire scratch is
  36. // 4 KiB — fine to keep resident on every queue. WriteGSO returns an error
  37. // rather than reallocating when a caller exceeds this budget.
  38. const gsoMaxIovs = 256
  39. // validVnetHdr is the 10-byte virtio_net_hdr we prepend to every non-GSO TUN
  40. // write. Only flag set is VIRTIO_NET_HDR_F_DATA_VALID, which marks the skb
  41. // CHECKSUM_UNNECESSARY so the receiving network stack skips L4 checksum
  42. // verification. All packets that reach the plain Write paths already carry
  43. // a valid L4 checksum (either supplied by a remote peer whose ciphertext we
  44. // AEAD-authenticated, produced by segmentTCPYield/segmentUDPYield during
  45. // superpacket segmentation, or built locally by CreateRejectPacket), so
  46. // trusting them is safe.
  47. var validVnetHdr = [virtio.Size]byte{unix.VIRTIO_NET_HDR_F_DATA_VALID}
  48. // Offload wraps a TUN file descriptor with poll-based reads. The FD provided will be changed to non-blocking.
  49. // A shared eventfd allows Close to wake all readers blocked in poll.
  50. type Offload struct {
  51. fd int
  52. shutdownFd int
  53. readPoll [2]unix.PollFd
  54. writePoll [2]unix.PollFd
  55. // writeLock serializes blockOnWrite's read+clear of writePoll[*].Revents.
  56. // Any goroutine that calls Write may end up parked in poll(2); without
  57. // the lock concurrent waiters could race the Revents reset and lose
  58. // events.
  59. writeLock sync.Mutex
  60. closed atomic.Bool
  61. rxBuf []byte // backing store for kernel-handed packets read this drain
  62. rxOff int // cursor into rxBuf for the current Read drain
  63. pending []Packet // packets returned from the most recent Read
  64. // readVnetScratch holds the 10-byte virtio_net_hdr split off the front of
  65. // every TUN read via readv(2). Decoupling the header from the packet body
  66. // lets us read the body directly into rxBuf at the current rxOff with
  67. // no userspace copy on the GSO_NONE fast path.
  68. readVnetScratch [virtio.Size]byte
  69. // readIovs is the readv(2) iovec scratch wired once at construction —
  70. // iovec[0] points at readVnetScratch; iovec[1].Base/Len is updated per
  71. // read to address the current rxBuf slot.
  72. readIovs [2]unix.Iovec
  73. // usoEnabled records whether the kernel agreed to TUN_F_USO* on this FD,
  74. // so writers can decide whether emitting GSO_UDP_L4 superpackets is safe.
  75. usoEnabled bool
  76. // gsoHdrBuf is a per-queue 10-byte scratch for the virtio_net_hdr emitted
  77. // by WriteGSO. Kept separate from the read-only package-level validVnetHdr
  78. // so non-GSO Writes can ship that constant directly while WriteGSO
  79. // rewrites this scratch on every call.
  80. gsoHdrBuf [virtio.Size]byte
  81. // gsoIovs is the writev iovec scratch for WriteGSO. Pre-sized to
  82. // gsoMaxIovs at construction; never grown. WriteGSO returns an error
  83. // (and drops the call) if a caller hands it more fragments than fit.
  84. gsoIovs []unix.Iovec
  85. }
  86. func newOffload(fd int, shutdownFd int, usoEnabled bool) (*Offload, error) {
  87. if err := unix.SetNonblock(fd, true); err != nil {
  88. return nil, fmt.Errorf("failed to set tun fd non-blocking: %w", err)
  89. }
  90. out := &Offload{
  91. fd: fd,
  92. shutdownFd: shutdownFd,
  93. usoEnabled: usoEnabled,
  94. closed: atomic.Bool{},
  95. readPoll: [2]unix.PollFd{
  96. {Fd: int32(fd), Events: unix.POLLIN},
  97. {Fd: int32(shutdownFd), Events: unix.POLLIN},
  98. },
  99. writePoll: [2]unix.PollFd{
  100. {Fd: int32(fd), Events: unix.POLLOUT},
  101. {Fd: int32(shutdownFd), Events: unix.POLLIN},
  102. },
  103. writeLock: sync.Mutex{},
  104. rxBuf: make([]byte, tunRxBufCap),
  105. gsoIovs: make([]unix.Iovec, 2, gsoMaxIovs),
  106. }
  107. out.gsoIovs[0].Base = &out.gsoHdrBuf[0]
  108. out.gsoIovs[0].SetLen(virtio.Size)
  109. // readIovs[0] is wired once to the virtio_net_hdr scratch; per-read we
  110. // only repoint readIovs[1] at the next rxBuf slot (see readPacket).
  111. out.readIovs[0].Base = &out.readVnetScratch[0]
  112. out.readIovs[0].SetLen(virtio.Size)
  113. return out, nil
  114. }
  115. func (r *Offload) blockOnRead() error {
  116. const problemFlags = unix.POLLHUP | unix.POLLNVAL | unix.POLLERR
  117. var err error
  118. for {
  119. _, err = unix.Poll(r.readPoll[:], -1)
  120. if err != unix.EINTR {
  121. break
  122. }
  123. }
  124. //always reset these!
  125. tunEvents := r.readPoll[0].Revents
  126. shutdownEvents := r.readPoll[1].Revents
  127. r.readPoll[0].Revents = 0
  128. r.readPoll[1].Revents = 0
  129. //do the err check before trusting the potentially bogus bits we just got
  130. if err != nil {
  131. return err
  132. }
  133. if shutdownEvents&(unix.POLLIN|problemFlags) != 0 {
  134. return os.ErrClosed
  135. } else if tunEvents&problemFlags != 0 {
  136. return os.ErrClosed
  137. }
  138. return nil
  139. }
  140. func (r *Offload) blockOnWrite() error {
  141. const problemFlags = unix.POLLHUP | unix.POLLNVAL | unix.POLLERR
  142. var err error
  143. for {
  144. _, err = unix.Poll(r.writePoll[:], -1)
  145. if err != unix.EINTR {
  146. break
  147. }
  148. }
  149. //always reset these!
  150. r.writeLock.Lock()
  151. tunEvents := r.writePoll[0].Revents
  152. shutdownEvents := r.writePoll[1].Revents
  153. r.writePoll[0].Revents = 0
  154. r.writePoll[1].Revents = 0
  155. r.writeLock.Unlock()
  156. //do the err check before trusting the potentially bogus bits we just got
  157. if err != nil {
  158. return err
  159. }
  160. if shutdownEvents&(unix.POLLIN|problemFlags) != 0 {
  161. return os.ErrClosed
  162. } else if tunEvents&problemFlags != 0 {
  163. return os.ErrClosed
  164. }
  165. return nil
  166. }
  167. // readPacket issues a single readv(2) splitting the virtio_net_hdr off
  168. // into readVnetScratch and reading the packet body directly into rxBuf at
  169. // the current rxOff. Returns the body length (zero virtio header bytes,
  170. // just the IP packet/superpacket). block controls whether EAGAIN is
  171. // retried via poll: the initial read of a drain blocks; subsequent drain
  172. // reads do not.
  173. //
  174. // The body iovec capacity is always tunReadBufSize; callers (the Read
  175. // drain loop) gate entry on tunRxBufCap-rxOff >= tunRxBufSize, sized to
  176. // hold one worst-case kernel-supplied packet body. Without that gate the
  177. // body iovec could be smaller than the next inbound packet and the
  178. // kernel would truncate.
  179. func (r *Offload) readPacket(block bool) (int, error) {
  180. for {
  181. r.readIovs[1].Base = &r.rxBuf[r.rxOff]
  182. r.readIovs[1].SetLen(tunReadBufSize)
  183. n, _, errno := syscall.Syscall(unix.SYS_READV, uintptr(r.fd), uintptr(unsafe.Pointer(&r.readIovs[0])), uintptr(len(r.readIovs)))
  184. if errno == 0 {
  185. if int(n) < virtio.Size {
  186. return 0, io.ErrShortWrite
  187. }
  188. return int(n) - virtio.Size, nil
  189. }
  190. if errno == unix.EAGAIN {
  191. if !block {
  192. return 0, errno
  193. }
  194. if err := r.blockOnRead(); err != nil {
  195. return 0, err
  196. }
  197. continue
  198. }
  199. if errno == unix.EINTR {
  200. continue
  201. }
  202. if errno == unix.EBADF {
  203. return 0, os.ErrClosed
  204. }
  205. return 0, errno
  206. }
  207. }
  208. // Read returns one or more packets from the tun. Each Packet either
  209. // carries a single ready-to-use IP datagram (GSO zero) or a TSO/USO
  210. // superpacket plus the GSOInfo a caller needs to segment it (see
  211. // SegmentSuperpacket). The first read blocks via poll; once the fd is
  212. // known readable we drain additional packets non-blocking until the
  213. // kernel queue is empty (EAGAIN), we've collected tunDrainCap packets,
  214. // or we're out of rxBuf headroom. This amortizes the poll wake over
  215. // bursts of small packets (e.g. TCP ACKs). Packet.Bytes slices point
  216. // into the Offload's internal buffer and are only valid until the next
  217. // Read or Close on this Queue.
  218. func (r *Offload) Read() ([]Packet, error) {
  219. r.pending = r.pending[:0]
  220. r.rxOff = 0
  221. // Initial (blocking) read. Retry on decode errors so a single bad
  222. // packet does not stall the reader.
  223. for {
  224. n, err := r.readPacket(true)
  225. if err != nil {
  226. return nil, err
  227. }
  228. if err := r.decodeRead(n); err != nil {
  229. // Drop and read again — a bad packet should not kill the reader.
  230. continue
  231. }
  232. break
  233. }
  234. // Drain: non-blocking reads until the kernel queue is empty, the drain
  235. // cap is reached, or rxBuf no longer has room for another worst-case
  236. // kernel-supplied packet (tunRxBufSize).
  237. for len(r.pending) < tunDrainCap && tunRxBufCap-r.rxOff >= tunRxBufSize {
  238. n, err := r.readPacket(false)
  239. if err != nil {
  240. // EAGAIN / EINTR / anything else: stop draining. We already
  241. // have a valid batch from the first read.
  242. break
  243. }
  244. if n <= 0 {
  245. break
  246. }
  247. if err := r.decodeRead(n); err != nil {
  248. // Drop this packet and stop the drain; we'd rather hand off
  249. // what we have than keep spinning here.
  250. break
  251. }
  252. }
  253. return r.pending, nil
  254. }
  255. // decodeRead processes the packet sitting in rxBuf at rxOff (length
  256. // pktLen). The bytes stay in rxBuf — for GSO_NONE we slice them as a
  257. // regular IP datagram (running finishChecksum if NEEDS_CSUM is set);
  258. // for TSO/USO superpackets we attach the corrected GSO metadata so the
  259. // caller can segment lazily at encrypt time. rxOff advances past the
  260. // kernel-supplied body and nothing else, since segmentation no longer
  261. // writes back into rxBuf.
  262. func (r *Offload) decodeRead(pktLen int) error {
  263. if pktLen <= 0 {
  264. return fmt.Errorf("short tun read: %d", pktLen)
  265. }
  266. var hdr virtio.Hdr
  267. hdr.Decode(r.readVnetScratch[:])
  268. body := r.rxBuf[r.rxOff : r.rxOff+pktLen]
  269. if hdr.GSOType == unix.VIRTIO_NET_HDR_GSO_NONE {
  270. if hdr.Flags&unix.VIRTIO_NET_HDR_F_NEEDS_CSUM != 0 {
  271. if err := virtio.FinishChecksum(body, hdr); err != nil {
  272. return err
  273. }
  274. }
  275. r.pending = append(r.pending, Packet{Bytes: body})
  276. r.rxOff += pktLen
  277. return nil
  278. }
  279. // GSO superpacket: validate, fix the kernel-supplied HdrLen on the
  280. // FORWARD path (CorrectHdrLen), pick the L4 protocol, and attach
  281. // the metadata. The bytes stay in rxBuf untouched, segmentation
  282. // happens in SegmentSuperpacket at encrypt time.
  283. if err := virtio.CheckValid(body, hdr); err != nil {
  284. return err
  285. }
  286. if err := virtio.CorrectHdrLen(body, &hdr); err != nil {
  287. return err
  288. }
  289. proto, err := protoFromGSOType(hdr.GSOType)
  290. if err != nil {
  291. return err
  292. }
  293. r.pending = append(r.pending, Packet{
  294. Bytes: body,
  295. GSO: GSOInfo{
  296. Size: hdr.GSOSize,
  297. HdrLen: hdr.HdrLen,
  298. CsumStart: hdr.CsumStart,
  299. Proto: proto,
  300. },
  301. })
  302. r.rxOff += pktLen
  303. return nil
  304. }
  305. func (r *Offload) Write(buf []byte) (int, error) {
  306. iovs := [2]unix.Iovec{
  307. {Base: &validVnetHdr[0]},
  308. {Base: &buf[0]},
  309. }
  310. iovs[0].SetLen(virtio.Size)
  311. iovs[1].SetLen(len(buf))
  312. return r.writeWithScratch(buf, &iovs)
  313. }
  314. func (r *Offload) writeWithScratch(buf []byte, iovs *[2]unix.Iovec) (int, error) {
  315. if len(buf) == 0 {
  316. return 0, nil
  317. }
  318. iovs[1].Base = &buf[0]
  319. iovs[1].SetLen(len(buf))
  320. return r.rawWrite(unsafe.Slice(&iovs[0], len(iovs)))
  321. }
  322. func (r *Offload) rawWrite(iovs []unix.Iovec) (int, error) {
  323. for {
  324. n, _, errno := syscall.Syscall(unix.SYS_WRITEV, uintptr(r.fd), uintptr(unsafe.Pointer(&iovs[0])), uintptr(len(iovs)))
  325. if errno == 0 {
  326. if int(n) < virtio.Size {
  327. return 0, io.ErrShortWrite
  328. }
  329. return int(n) - virtio.Size, nil
  330. }
  331. if errno == unix.EAGAIN {
  332. if err := r.blockOnWrite(); err != nil {
  333. return 0, err
  334. }
  335. continue
  336. }
  337. if errno == unix.EINTR {
  338. continue
  339. }
  340. if errno == unix.EBADF {
  341. return 0, os.ErrClosed
  342. }
  343. return 0, errno
  344. }
  345. }
  346. // Capabilities reports the offload features negotiated for this Queue. TSO
  347. // is always true for Offload (we only construct it on IFF_VNET_HDR FDs);
  348. // USO is true only when the kernel agreed to TUN_F_USO4|6 at open time
  349. // (Linux ≥ 6.2).
  350. func (r *Offload) Capabilities() Capabilities {
  351. return Capabilities{TSO: true, USO: r.usoEnabled}
  352. }
  353. func (r *Offload) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, proto GSOProto) error {
  354. if len(hdr) == 0 || len(pays) == 0 || len(transportHdr) == 0 {
  355. return nil
  356. }
  357. // L4 checksum offset inside transportHdr: TCP=16 (the `check` field after
  358. // seq/ack/dataoff/flags/window), UDP=6 (after sport/dport/length).
  359. var csumOff uint16
  360. switch proto {
  361. case GSOProtoUDP:
  362. csumOff = 6
  363. default:
  364. csumOff = 16
  365. }
  366. vhdr := virtio.Hdr{
  367. Flags: unix.VIRTIO_NET_HDR_F_NEEDS_CSUM,
  368. HdrLen: uint16(len(hdr) + len(transportHdr)),
  369. GSOSize: uint16(len(pays[0])),
  370. CsumStart: uint16(len(hdr)),
  371. CsumOffset: csumOff,
  372. }
  373. if len(pays) > 1 {
  374. ipVer := hdr[0] >> 4
  375. switch {
  376. case proto == GSOProtoUDP && (ipVer == 4 || ipVer == 6):
  377. vhdr.GSOType = unix.VIRTIO_NET_HDR_GSO_UDP_L4
  378. case ipVer == 6:
  379. vhdr.GSOType = unix.VIRTIO_NET_HDR_GSO_TCPV6
  380. case ipVer == 4:
  381. vhdr.GSOType = unix.VIRTIO_NET_HDR_GSO_TCPV4
  382. default:
  383. vhdr.GSOType = unix.VIRTIO_NET_HDR_GSO_NONE
  384. vhdr.GSOSize = 0
  385. }
  386. } else {
  387. vhdr.GSOType = unix.VIRTIO_NET_HDR_GSO_NONE
  388. vhdr.GSOSize = 0
  389. }
  390. vhdr.Encode(r.gsoHdrBuf[:])
  391. // Build the iovec array: [virtio_hdr, hdr, transportHdr, pays...]. r.gsoIovs[0] is
  392. // wired to gsoHdrBuf at construction and never changes.
  393. need := 3 + len(pays)
  394. if need > cap(r.gsoIovs) {
  395. slog.Default().Warn("tio: WriteGSO iovec budget exceeded; dropping superpacket",
  396. "need", need, "cap", cap(r.gsoIovs), "segments", len(pays))
  397. return fmt.Errorf("tio: WriteGSO needs %d iovecs but cap is %d", need, cap(r.gsoIovs))
  398. }
  399. r.gsoIovs = r.gsoIovs[:need]
  400. r.gsoIovs[1].Base = &hdr[0]
  401. r.gsoIovs[1].SetLen(len(hdr))
  402. r.gsoIovs[2].Base = &transportHdr[0]
  403. r.gsoIovs[2].SetLen(len(transportHdr))
  404. for i, p := range pays {
  405. r.gsoIovs[3+i].Base = &p[0]
  406. r.gsoIovs[3+i].SetLen(len(p))
  407. }
  408. _, err := r.rawWrite(r.gsoIovs)
  409. return err
  410. }
  411. func (r *Offload) Close() error {
  412. if r.closed.Swap(true) {
  413. return nil
  414. }
  415. //shutdownFd is owned by the container, so we should not close it
  416. var err error
  417. if r.fd >= 0 {
  418. err = unix.Close(r.fd)
  419. r.fd = -1
  420. }
  421. return err
  422. }