connsock_darwin_test.go 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. //go:build !e2e_testing
  2. package udp
  3. import (
  4. "context"
  5. "fmt"
  6. "log/slog"
  7. "net"
  8. "net/netip"
  9. "strings"
  10. "sync"
  11. "sync/atomic"
  12. "syscall"
  13. "testing"
  14. "time"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. "golang.org/x/sys/unix"
  18. )
  19. // newConnSockListener opens a listener with the connected socket knobs pinned to their defaults, so NEBULA_*
  20. // variables in the environment can't change what a test sees, applies cfg, and starts ListenOut with r.
  21. func newConnSockListener(t *testing.T, s Settings, h slog.Handler, r EncReader, cfg ...func(*connSockConfig)) *StdConn {
  22. t.Helper()
  23. for k, v := range map[string]string{
  24. "NEBULA_CONNSOCKS": "8",
  25. "NEBULA_CONNSOCK_RUN": "64",
  26. "NEBULA_CONNSOCK_WINDOW": "1s",
  27. "NEBULA_CONNSOCK_IDLE": "30s",
  28. "NEBULA_CONNSOCK_OPEN_EVERY": "1s",
  29. } {
  30. t.Setenv(k, v)
  31. }
  32. if h == nil {
  33. h = slog.DiscardHandler
  34. }
  35. if r == nil {
  36. r = func(netip.AddrPort, []byte) {}
  37. }
  38. c, err := NewListener(slog.New(h), s)
  39. require.NoError(t, err)
  40. t.Cleanup(func() { _ = c.Close() })
  41. u := c.(*StdConn)
  42. for _, f := range cfg {
  43. f(&u.socks.cfg)
  44. }
  45. go func() { _ = u.ListenOut(r, func() {}) }()
  46. require.Eventually(t, func() bool { return u.reader.Load() != nil }, 5*time.Second, time.Millisecond)
  47. return u
  48. }
  49. func listen(s string) Settings { return Settings{Listen: netip.MustParseAddrPort(s)} }
  50. // run builds n datagrams to dst.
  51. func run(dst netip.AddrPort, n int, tag string) ([][]byte, []netip.AddrPort) {
  52. bufs := make([][]byte, n)
  53. addrs := make([]netip.AddrPort, n)
  54. for i := range bufs {
  55. bufs[i] = fmt.Appendf(nil, "%s-%d", tag, i)
  56. addrs[i] = dst
  57. }
  58. return bufs, addrs
  59. }
  60. func newPeer(t *testing.T, network, addr string) (*net.UDPConn, netip.AddrPort) {
  61. t.Helper()
  62. pc, err := net.ListenPacket(network, addr)
  63. require.NoError(t, err)
  64. t.Cleanup(func() { _ = pc.Close() })
  65. uc := pc.(*net.UDPConn)
  66. return uc, uc.LocalAddr().(*net.UDPAddr).AddrPort()
  67. }
  68. // newEstablishedPeer opens a peer as newPeer does and tells u it is an established tunnel's remote.
  69. func newEstablishedPeer(t *testing.T, u *StdConn, network, addr string) (*net.UDPConn, netip.AddrPort) {
  70. t.Helper()
  71. c, dst := newPeer(t, network, addr)
  72. u.SetEstablishedPeer(dst, true)
  73. return c, dst
  74. }
  75. // readFrom reads n datagrams at peer and returns the source of each.
  76. func readFrom(t *testing.T, peer *net.UDPConn, n int) []netip.AddrPort {
  77. t.Helper()
  78. buf := make([]byte, MTU)
  79. var from []netip.AddrPort
  80. for range n {
  81. require.NoError(t, peer.SetReadDeadline(time.Now().Add(5*time.Second)))
  82. _, f, err := peer.ReadFromUDPAddrPort(buf)
  83. require.NoError(t, err)
  84. from = append(from, netip.AddrPortFrom(f.Addr().Unmap(), f.Port()))
  85. }
  86. return from
  87. }
  88. func reusePort(t *testing.T, c syscall.Conn) int {
  89. t.Helper()
  90. rc, err := c.SyscallConn()
  91. require.NoError(t, err)
  92. var v int
  93. var serr error
  94. require.NoError(t, rc.Control(func(fd uintptr) {
  95. v, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT)
  96. }))
  97. require.NoError(t, serr)
  98. return v
  99. }
  100. // rogueBinds tries each of binds, "network address plain|reuseport", on port and returns the ones that bound.
  101. func rogueBinds(t *testing.T, port uint16, binds []string) []string {
  102. t.Helper()
  103. var bound []string
  104. for _, b := range binds {
  105. f := strings.Fields(b)
  106. network, addr, reuse := f[0], f[1], f[2]
  107. lc := net.ListenConfig{Control: connSockControl(reuse == "reuseport", 0)}
  108. pc, err := lc.ListenPacket(context.Background(), network, net.JoinHostPort(addr, fmt.Sprint(port)))
  109. if err == nil {
  110. _ = pc.Close()
  111. bound = append(bound, b)
  112. }
  113. }
  114. return bound
  115. }
  116. // The listener never sets SO_REUSEPORT, and a connected socket binds with it only to a specific address, so every
  117. // bind a lone listener refuses is refused after NewListener returns and while connected sockets are open. Same-uid
  118. // SO_REUSEPORT binds to a specific address are left out: xnu allows them beside a lone wildcard listener too,
  119. // which is how a connected socket binds, and refuses them to any other uid.
  120. func TestConnSocksNoSharedBind(t *testing.T) {
  121. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  122. la, err := u.LocalAddr()
  123. require.NoError(t, err)
  124. binds := []string{
  125. "udp4 0.0.0.0 plain", "udp4 0.0.0.0 reuseport", "udp4 127.0.0.1 plain",
  126. "udp6 :: plain", "udp6 :: reuseport", "udp6 ::1 plain",
  127. }
  128. assert.Zero(t, reusePort(t, u.UDPConn), "listener")
  129. assert.Empty(t, rogueBinds(t, la.Port(), binds), "bound beside the listener")
  130. peer4, dst4 := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  131. peer6, dst6 := newEstablishedPeer(t, u, "udp6", "[::1]:0")
  132. for _, p := range []struct {
  133. c *net.UDPConn
  134. dst netip.AddrPort
  135. }{{peer4, dst4}, {peer6, dst6}} {
  136. bufs, addrs := run(p.dst, u.socks.cfg.run, "p")
  137. _, err := u.WriteBatch(bufs, addrs)
  138. require.NoError(t, err)
  139. readFrom(t, p.c, len(bufs))
  140. s := u.connSockFor(p.dst)
  141. require.NotNil(t, s, "no connected socket to %v", p.dst)
  142. assert.NotZero(t, reusePort(t, s.conn), "connected socket to %v", p.dst)
  143. }
  144. assert.Empty(t, rogueBinds(t, la.Port(), binds), "bound beside the connected sockets")
  145. }
  146. // A connected socket binds the source address the listener's route to its peer picks, for either family on a
  147. // dual-stack listener, so the peer sees one address and port whichever socket sent.
  148. func TestConnSocksSourceMatchesListener(t *testing.T) {
  149. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  150. la, err := u.LocalAddr()
  151. require.NoError(t, err)
  152. for _, p := range []struct{ network, addr string }{{"udp4", "127.0.0.1:0"}, {"udp6", "[::1]:0"}} {
  153. peer, dst := newEstablishedPeer(t, u, p.network, p.addr)
  154. require.NoError(t, u.WriteTo([]byte("probe"), dst))
  155. require.Nil(t, u.connSockFor(dst))
  156. viaListener := readFrom(t, peer, 1)[0]
  157. assert.Equal(t, la.Port(), viaListener.Port())
  158. bufs, addrs := run(dst, u.socks.cfg.run, p.network)
  159. _, err := u.WriteBatch(bufs, addrs)
  160. require.NoError(t, err)
  161. s := u.connSockFor(dst)
  162. require.NotNil(t, s, "no connected socket to %v", dst)
  163. for _, from := range readFrom(t, peer, len(bufs)) {
  164. assert.Equal(t, viaListener, from, "source seen by %v", dst)
  165. }
  166. local := s.conn.LocalAddr().(*net.UDPAddr).AddrPort()
  167. assert.Equal(t, viaListener, netip.AddrPortFrom(local.Addr().Unmap(), local.Port()))
  168. }
  169. }
  170. // A run long enough opens a connected socket per peer, its replies reach the listener's reader, and a peer that
  171. // goes away sends its traffic back through the listener with its connected socket closed, while the next peer gets
  172. // a connected socket of its own.
  173. func TestConnSocks(t *testing.T) {
  174. var mu sync.Mutex
  175. got := map[netip.AddrPort]int{}
  176. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, func(addr netip.AddrPort, _ []byte) {
  177. mu.Lock()
  178. got[addr]++
  179. mu.Unlock()
  180. })
  181. la, err := u.LocalAddr()
  182. require.NoError(t, err)
  183. to := netip.AddrPortFrom(netip.MustParseAddr("127.0.0.1"), la.Port())
  184. var peers [3]*net.UDPConn
  185. var dsts [3]netip.AddrPort
  186. for i := range peers {
  187. peers[i], dsts[i] = newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  188. }
  189. for i, dst := range dsts[:2] {
  190. bufs, addrs := run(dst, u.socks.cfg.run, fmt.Sprint("p", i))
  191. n, err := u.WriteBatch(bufs, addrs)
  192. require.NoError(t, err)
  193. assert.Equal(t, len(bufs), n)
  194. require.NotNil(t, u.connSockFor(dst), "no connected socket to peer %d", i)
  195. for _, from := range readFrom(t, peers[i], len(bufs)) {
  196. assert.Equal(t, to, from)
  197. }
  198. for range 3 {
  199. _, err := peers[i].WriteToUDPAddrPort([]byte("reply"), to)
  200. require.NoError(t, err)
  201. }
  202. }
  203. require.Eventually(t, func() bool {
  204. mu.Lock()
  205. defer mu.Unlock()
  206. return got[dsts[0]] == 3 && got[dsts[1]] == 3
  207. }, 5*time.Second, time.Millisecond)
  208. // The peer's ICMP port unreachable surfaces on the connected socket's next call. The deadline is generous
  209. // because xnu rate-limits and delivers the unreachable on its own schedule.
  210. gone := dsts[1]
  211. goneSock := u.connSockFor(gone)
  212. require.NoError(t, peers[1].Close())
  213. bufs, addrs := run(gone, u.socks.cfg.run, "gone")
  214. require.Eventually(t, func() bool {
  215. _, _ = u.WriteBatch(bufs, addrs)
  216. return u.connSockFor(gone) == nil
  217. }, 10*time.Second, 10*time.Millisecond)
  218. assert.NotNil(t, u.connSockFor(dsts[0]))
  219. u.socks.closers.Wait()
  220. _, err = goneSock.conn.Write([]byte("x"))
  221. assert.ErrorIs(t, err, net.ErrClosed, "closed connected socket left open")
  222. bufs, addrs = run(dsts[2], u.socks.cfg.run, "p2")
  223. _, err = u.WriteBatch(bufs, addrs)
  224. require.NoError(t, err)
  225. require.NotNil(t, u.connSockFor(dsts[2]))
  226. for _, from := range readFrom(t, peers[2], len(bufs)) {
  227. assert.Equal(t, to, from)
  228. }
  229. require.NoError(t, u.Rebind())
  230. for _, dst := range dsts {
  231. assert.Nil(t, u.connSockFor(dst), "connected socket left after Rebind")
  232. }
  233. }
  234. // Listen routines on one port share one set of connected sockets: the cap and the open bucket are theirs together,
  235. // a peer's connected socket carries every routine's sends to it, and Rebind on the first routine, which is all
  236. // Control.RebindUDPServer calls, clears every routine's listener and closes the connected sockets.
  237. func TestConnSocksSharedAcrossRoutines(t *testing.T) {
  238. s := listen("0.0.0.0:0")
  239. s.Multi = true
  240. u0 := newConnSockListener(t, s, nil, nil, func(c *connSockConfig) { c.max = 1 })
  241. la, err := u0.LocalAddr()
  242. require.NoError(t, err)
  243. s.Listen = netip.AddrPortFrom(s.Listen.Addr(), la.Port())
  244. u1 := newConnSockListener(t, s, nil, nil)
  245. require.Same(t, u0.socks, u1.socks)
  246. lo, err := net.InterfaceByName("lo0")
  247. require.NoError(t, err)
  248. peerA, a := newPeer(t, "udp4", "127.0.0.1:0")
  249. peerB, b := newPeer(t, "udp4", "127.0.0.1:0")
  250. for _, dst := range []netip.AddrPort{a, b} {
  251. // Nebula tells every writer.
  252. u0.SetEstablishedPeer(dst, true)
  253. u1.SetEstablishedPeer(dst, true)
  254. }
  255. bufs, addrs := run(a, u1.socks.cfg.run, "a")
  256. _, err = u1.WriteBatch(bufs, addrs)
  257. require.NoError(t, err)
  258. readFrom(t, peerA, len(bufs))
  259. s1 := u1.connSockFor(a)
  260. require.NotNil(t, s1)
  261. var sends atomic.Int64
  262. s1.sys = func(fd int, b []byte) error {
  263. sends.Add(1)
  264. return writeFd(fd, b)
  265. }
  266. require.NoError(t, u0.WriteTo([]byte("via u0"), a))
  267. readFrom(t, peerA, 1)
  268. assert.EqualValues(t, 1, sends.Load(), "routine 0 didn't send on routine 1's connected socket")
  269. bufs, addrs = run(b, 2*u0.socks.cfg.run, "b")
  270. _, err = u0.WriteBatch(bufs, addrs)
  271. require.NoError(t, err)
  272. readFrom(t, peerB, len(bufs))
  273. assert.Nil(t, u0.connSockFor(b), "a second connected socket past a cap of 1")
  274. level, opt := unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF
  275. if u0.isV4 {
  276. level, opt = unix.IPPROTO_IP, unix.IP_BOUND_IF
  277. }
  278. boundIf := func(u *StdConn) int {
  279. v, err := unix.GetsockoptInt(int(u.sysFd), level, opt)
  280. require.NoError(t, err)
  281. return v
  282. }
  283. for _, u := range []*StdConn{u0, u1} {
  284. require.NoError(t, unix.SetsockoptInt(int(u.sysFd), level, opt, lo.Index))
  285. require.Equal(t, lo.Index, boundIf(u))
  286. }
  287. require.NoError(t, u0.Rebind())
  288. assert.Zero(t, boundIf(u0))
  289. assert.Zero(t, boundIf(u1), "Rebind left routine 1 scoped")
  290. assert.Nil(t, u1.connSockFor(a), "Rebind left routine 1's connected socket open")
  291. u0.socks.closers.Wait()
  292. _, err = s1.conn.Write([]byte("x"))
  293. assert.ErrorIs(t, err, net.ErrClosed)
  294. }
  295. // A connected socket on a specific listen.host would need SO_REUSEPORT on the listener, so connected sockets stay
  296. // off there unless listen routines already set it.
  297. func TestConnSocksSpecificHost(t *testing.T) {
  298. for _, multi := range []bool{false, true} {
  299. t.Run(fmt.Sprint("multi=", multi), func(t *testing.T) {
  300. s := listen("127.0.0.1:0")
  301. s.Multi = multi
  302. u := newConnSockListener(t, s, nil, nil)
  303. la, err := u.LocalAddr()
  304. require.NoError(t, err)
  305. if !multi {
  306. assert.Zero(t, reusePort(t, u.UDPConn), "listener")
  307. }
  308. peer, dst := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  309. bufs, addrs := run(dst, u.socks.cfg.run, "p")
  310. _, err = u.WriteBatch(bufs, addrs)
  311. require.NoError(t, err)
  312. for _, from := range readFrom(t, peer, len(bufs)) {
  313. assert.Equal(t, la, from)
  314. }
  315. if multi {
  316. assert.NotNil(t, u.connSockFor(dst))
  317. } else {
  318. assert.Nil(t, u.connSockFor(dst))
  319. }
  320. })
  321. }
  322. }
  323. // A v4 listener can't send to a v6 destination; however often it's asked, it says so and opens no connected
  324. // socket.
  325. func TestConnSocksV6DestinationOnV4Listener(t *testing.T) {
  326. s := listen("127.0.0.1:0")
  327. s.Multi = true
  328. u := newConnSockListener(t, s, nil, nil)
  329. require.True(t, u.isV4)
  330. dst := netip.MustParseAddrPort("[::1]:9")
  331. u.SetEstablishedPeer(dst, true)
  332. bufs, _ := run(dst, 2*u.socks.cfg.run, "v6")
  333. for _, b := range bufs {
  334. assert.ErrorIs(t, u.WriteTo(b, dst), ErrInvalidIPv6RemoteForSocket)
  335. }
  336. assert.Nil(t, u.connSockFor(dst))
  337. }
  338. // darwin's tun hands over one packet per read, so a busy peer's traffic reaches the socket as single WriteTo
  339. // calls. cfg.run of them inside one window earn a connected socket; the same count spread over more than a window
  340. // doesn't.
  341. func TestConnSocksOpenFromWriteTo(t *testing.T) {
  342. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  343. var now atomic.Int64
  344. now.Store(int64(time.Hour))
  345. u.socks.clock = now.Load
  346. _, dst := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  347. pkt := []byte("pkt")
  348. for range u.socks.cfg.run - 1 {
  349. require.NoError(t, u.WriteTo(pkt, dst))
  350. }
  351. now.Add(int64(u.socks.cfg.window) + 1)
  352. require.NoError(t, u.WriteTo(pkt, dst))
  353. assert.Nil(t, u.connSockFor(dst), "a connected socket for sends spread over two windows")
  354. for range u.socks.cfg.run - 2 {
  355. require.NoError(t, u.WriteTo(pkt, dst))
  356. }
  357. assert.Nil(t, u.connSockFor(dst))
  358. require.NoError(t, u.WriteTo(pkt, dst))
  359. assert.NotNil(t, u.connSockFor(dst), "no connected socket after %d sends in one window", u.socks.cfg.run)
  360. }
  361. // Only the remote of an established tunnel gets a connected socket, however busy another destination is, and one
  362. // that stops being established loses its connected socket and has to be busy again to get a new one.
  363. func TestConnSocksOnlyForEstablishedPeers(t *testing.T) {
  364. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  365. peer, dst := newPeer(t, "udp4", "127.0.0.1:0")
  366. run1 := u.socks.cfg.run
  367. send := func(n int) {
  368. t.Helper()
  369. bufs, addrs := run(dst, n, "p")
  370. _, err := u.WriteBatch(bufs, addrs)
  371. require.NoError(t, err)
  372. readFrom(t, peer, n)
  373. }
  374. send(2 * run1)
  375. assert.Nil(t, u.connSockFor(dst), "a connected socket for a busy destination with no tunnel")
  376. u.SetEstablishedPeer(dst, true)
  377. send(run1)
  378. s := u.connSockFor(dst)
  379. require.NotNil(t, s, "no connected socket for a busy established peer")
  380. u.SetEstablishedPeer(dst, false)
  381. assert.Nil(t, u.connSockFor(dst))
  382. u.socks.closers.Wait()
  383. _, err := s.conn.Write([]byte("x"))
  384. assert.ErrorIs(t, err, net.ErrClosed, "connected socket left open after its tunnel went away")
  385. send(2 * run1)
  386. assert.Nil(t, u.connSockFor(dst), "a connected socket after the tunnel went away")
  387. u.SetEstablishedPeer(dst, true)
  388. send(run1 - 1)
  389. assert.Nil(t, u.connSockFor(dst), "a re-established peer kept its old count")
  390. send(1)
  391. assert.NotNil(t, u.connSockFor(dst))
  392. }
  393. // A tunnel going away while its busy remote is opening a connected socket never leaves one open, whether or not
  394. // the remote is established again before the open finishes. The dial hook holds the open between reserving its
  395. // destination and dialing until the tunnel has changed, so every round races.
  396. func TestConnSocksEstablishedRace(t *testing.T) {
  397. h := &countingHandler{}
  398. u := newConnSockListener(t, listen("0.0.0.0:0"), h, nil, func(c *connSockConfig) {
  399. c.run = 1
  400. c.openEvery = time.Nanosecond
  401. })
  402. cs := u.socks
  403. peer, dst := newPeer(t, "udp4", "127.0.0.1:0")
  404. for i, reestablish := range []bool{false, true} {
  405. u.SetEstablishedPeer(dst, true)
  406. dialing, release := make(chan struct{}), make(chan struct{})
  407. cs.dialHook = func(netip.AddrPort) {
  408. close(dialing)
  409. <-release
  410. }
  411. done := make(chan error)
  412. go func() { done <- u.WriteTo([]byte("p"), dst) }()
  413. <-dialing
  414. u.SetEstablishedPeer(dst, false)
  415. if reestablish {
  416. u.SetEstablishedPeer(dst, true)
  417. }
  418. close(release)
  419. require.NoError(t, <-done)
  420. cs.dialHook = nil
  421. readFrom(t, peer, 1)
  422. assert.Nil(t, u.connSockFor(dst), "reestablish=%v", reestablish)
  423. assert.EqualValues(t, i+1, h.discarded.Load(), "reestablish=%v", reestablish)
  424. cs.mu.Lock()
  425. assert.Empty(t, cs.pending)
  426. cs.mu.Unlock()
  427. assert.Zero(t, cs.reserved.Load())
  428. u.SetEstablishedPeer(dst, false)
  429. }
  430. assert.Zero(t, h.opened.Load())
  431. }
  432. // Neither an open in flight nor a close holds anything SetEstablishedPeer waits on, since Nebula calls it under
  433. // the hostmap's write lock; and a peer whose connected socket is still closing can't open another until the close
  434. // completes. The hooks hold the open in its dials and the close before its Close.
  435. func TestConnSocksSetEstablishedPeerMakesNoSyscalls(t *testing.T) {
  436. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil, func(c *connSockConfig) { c.openEvery = time.Nanosecond })
  437. cs := u.socks
  438. peer, dst := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  439. _, other := newPeer(t, "udp4", "127.0.0.1:0")
  440. dialing, releaseDial := make(chan struct{}), make(chan struct{})
  441. cs.dialHook = func(netip.AddrPort) {
  442. close(dialing)
  443. <-releaseDial
  444. }
  445. opened := make(chan *connSock)
  446. go func() {
  447. bufs, addrs := run(dst, cs.cfg.run, "p")
  448. _, _ = u.WriteBatch(bufs, addrs)
  449. opened <- u.connSockFor(dst)
  450. }()
  451. <-dialing
  452. u.SetEstablishedPeer(other, true)
  453. u.SetEstablishedPeer(other, false)
  454. close(releaseDial)
  455. s := <-opened
  456. require.NotNil(t, s)
  457. readFrom(t, peer, cs.cfg.run)
  458. cs.dialHook = nil
  459. closing, releaseClose := make(chan struct{}), make(chan struct{})
  460. cs.closeHook = func(netip.AddrPort) {
  461. close(closing)
  462. <-releaseClose
  463. }
  464. u.SetEstablishedPeer(dst, false)
  465. <-closing
  466. assert.Nil(t, u.connSockFor(dst))
  467. u.SetEstablishedPeer(dst, true)
  468. bufs, addrs := run(dst, 2*cs.cfg.run, "q")
  469. _, err := u.WriteBatch(bufs, addrs)
  470. require.NoError(t, err)
  471. readFrom(t, peer, len(bufs))
  472. assert.Nil(t, u.connSockFor(dst), "a connected socket opened while the last one to its peer was closing")
  473. close(releaseClose)
  474. cs.closers.Wait()
  475. cs.closeHook = nil
  476. _, err = s.conn.Write([]byte("x"))
  477. assert.ErrorIs(t, err, net.ErrClosed)
  478. bufs, addrs = run(dst, cs.cfg.run, "r")
  479. _, err = u.WriteBatch(bufs, addrs)
  480. require.NoError(t, err)
  481. readFrom(t, peer, len(bufs))
  482. assert.NotNil(t, u.connSockFor(dst), "no connected socket once the last one closed")
  483. }
  484. // A listener whose interface scope can't be read gives a busy peer no connected socket, rather than one without
  485. // the scope; the peer stays on the listener for the backoff. A pipe stands in for the listener's descriptor, so
  486. // getsockopt fails with ENOTSOCK.
  487. func TestConnSocksBoundIfFailsClosed(t *testing.T) {
  488. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  489. _, dst := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  490. var pipe [2]int
  491. require.NoError(t, unix.Pipe(pipe[:]))
  492. t.Cleanup(func() {
  493. _ = unix.Close(pipe[0])
  494. _ = unix.Close(pipe[1])
  495. })
  496. listener := u.sysFd
  497. u.sysFd = uintptr(pipe[0])
  498. s := u.maybeOpenConnSock(u.socks.peer(dst), dst, u.socks.cfg.run)
  499. u.sysFd = listener
  500. assert.Nil(t, s)
  501. assert.Nil(t, u.connSockFor(dst))
  502. assert.Greater(t, u.socks.peer(dst).retryAt, u.socks.clock(), "no backoff after a failed open")
  503. }
  504. // readTags reads n datagrams at peer and returns their payloads.
  505. func readTags(t *testing.T, peer *net.UDPConn, n int) []string {
  506. t.Helper()
  507. buf := make([]byte, MTU)
  508. var tags []string
  509. for range n {
  510. require.NoError(t, peer.SetReadDeadline(time.Now().Add(5*time.Second)))
  511. m, _, err := peer.ReadFromUDPAddrPort(buf)
  512. require.NoError(t, err)
  513. tags = append(tags, string(buf[:m]))
  514. }
  515. return tags
  516. }
  517. // One batch can hold runs to a peer with a connected socket, to an established peer without one, and to a
  518. // destination that is no tunnel's remote, interleaved; each run goes out its own way, in order, and all count.
  519. func TestConnSocksWriteBatchMixedDestinations(t *testing.T) {
  520. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  521. s, peerA := openConnSock(t, u)
  522. peerB, b := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  523. peerC, c := newPeer(t, "udp4", "127.0.0.1:0")
  524. var onSock atomic.Int64
  525. s.sys = func(fd int, b []byte) error {
  526. onSock.Add(1)
  527. return writeFd(fd, b)
  528. }
  529. var bufs [][]byte
  530. var addrs []netip.AddrPort
  531. for _, r := range []struct {
  532. dst netip.AddrPort
  533. tag string
  534. n int
  535. }{{s.dst, "a", 3}, {b, "b", 2}, {c, "c", 2}, {s.dst, "A", 2}, {c, "C", 1}} {
  536. for i := range r.n {
  537. bufs = append(bufs, fmt.Appendf(nil, "%s%d", r.tag, i))
  538. addrs = append(addrs, r.dst)
  539. }
  540. }
  541. n, err := u.WriteBatch(bufs, addrs)
  542. require.NoError(t, err)
  543. assert.Equal(t, len(bufs), n)
  544. assert.EqualValues(t, 5, onSock.Load(), "datagrams on a's connected socket")
  545. assert.Equal(t, []string{"a0", "a1", "a2", "A0", "A1"}, readTags(t, peerA, 5))
  546. assert.Equal(t, []string{"b0", "b1"}, readTags(t, peerB, 2))
  547. assert.Equal(t, []string{"c0", "c1", "C0"}, readTags(t, peerC, 3))
  548. assert.Nil(t, u.connSockFor(b))
  549. }
  550. // A connected socket error that doesn't mean the path is gone costs only its datagram: WriteBatch skips it and
  551. // sends the rest of the run on the connected socket, WriteTo returns it, and the connected socket stays open.
  552. func TestConnSocksWriteError(t *testing.T) {
  553. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  554. s, peer := openConnSock(t, u)
  555. s.sys = func(fd int, b []byte) error {
  556. if string(b) == "bad" {
  557. return syscall.EMSGSIZE
  558. }
  559. return writeFd(fd, b)
  560. }
  561. bufs := [][]byte{[]byte("ok0"), []byte("bad"), []byte("ok2")}
  562. n, err := u.WriteBatch(bufs, []netip.AddrPort{s.dst, s.dst, s.dst})
  563. require.NoError(t, err)
  564. assert.Equal(t, 2, n)
  565. assert.Equal(t, []string{"ok0", "ok2"}, readTags(t, peer, 2))
  566. assert.ErrorIs(t, u.WriteTo([]byte("bad"), s.dst), syscall.EMSGSIZE)
  567. assert.Same(t, s, u.connSockFor(s.dst))
  568. }
  569. // A connected socket that turns out closed or dead partway through a run leaves the rest of the run to the
  570. // listener, in order and all counted; a dead one is closed and its peer backs off to the listener.
  571. func TestConnSocksWriteBatchFallsBackMidRun(t *testing.T) {
  572. for _, fail := range []error{net.ErrClosed, syscall.ECONNREFUSED} {
  573. t.Run(fail.Error(), func(t *testing.T) {
  574. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  575. s, peer := openConnSock(t, u)
  576. var calls atomic.Int64
  577. s.sys = func(fd int, b []byte) error {
  578. if calls.Add(1) > 2 {
  579. return fail
  580. }
  581. return writeFd(fd, b)
  582. }
  583. bufs, addrs := run(s.dst, 5, "r")
  584. n, err := u.WriteBatch(bufs, addrs)
  585. require.NoError(t, err)
  586. assert.Equal(t, 5, n)
  587. assert.Equal(t, []string{"r-0", "r-1", "r-2", "r-3", "r-4"}, readTags(t, peer, 5))
  588. assert.EqualValues(t, 3, calls.Load(), "the connected socket was tried again after it failed")
  589. if fail == syscall.ECONNREFUSED {
  590. assert.Nil(t, u.connSockFor(s.dst))
  591. u.socks.mu.Lock()
  592. assert.Greater(t, u.socks.peer(s.dst).retryAt, u.socks.clock(), "no backoff after a dead connected socket")
  593. u.socks.mu.Unlock()
  594. }
  595. })
  596. }
  597. }
  598. // openConnSock earns a connected socket to a fresh loopback peer and returns it and the peer.
  599. func openConnSock(t *testing.T, u *StdConn) (*connSock, *net.UDPConn) {
  600. t.Helper()
  601. peer, dst := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  602. bufs, addrs := run(dst, u.socks.cfg.run, "p")
  603. _, err := u.WriteBatch(bufs, addrs)
  604. require.NoError(t, err)
  605. readFrom(t, peer, len(bufs))
  606. s := u.connSockFor(dst)
  607. require.NotNil(t, s)
  608. return s, peer
  609. }
  610. // A flow-controlled interface makes a connected socket's sends wait, up to cfg.enobufsMax without one going
  611. // through; after that each datagram is dropped without waiting until a send succeeds again, and each stall logs
  612. // its drops once. s.sys stands in for an interface that answers every write with ENOBUFS until enobufs is
  613. // cleared, and each sleep advances the clock.
  614. func TestConnSockWriteENOBUFS(t *testing.T) {
  615. h := &countingHandler{}
  616. u := newConnSockListener(t, listen("0.0.0.0:0"), h, nil)
  617. s, _ := openConnSock(t, u)
  618. var now atomic.Int64
  619. now.Store(int64(time.Hour))
  620. sleeps := 0
  621. u.socks.clock = now.Load
  622. u.socks.sleep = func(d time.Duration) {
  623. sleeps++
  624. now.Add(int64(d))
  625. }
  626. var enobufs atomic.Bool
  627. s.sys = func(fd int, b []byte) error {
  628. if enobufs.Load() {
  629. return syscall.ENOBUFS
  630. }
  631. return writeFd(fd, b)
  632. }
  633. perStall := int(u.socks.cfg.enobufsMax / enobufsWait)
  634. bufs, _ := run(s.dst, 3, "q")
  635. for stall := 1; stall <= 2; stall++ {
  636. enobufs.Store(true)
  637. sent, errno, closed := u.connSockWrite(s, bufs)
  638. assert.Equal(t, 3, sent)
  639. assert.Zero(t, errno)
  640. assert.False(t, closed)
  641. assert.Equal(t, stall*perStall, sleeps)
  642. sent, _, _ = u.connSockWrite(s, bufs)
  643. assert.Equal(t, 3, sent)
  644. assert.Equal(t, stall*perStall, sleeps, "a stalled connected socket waited again")
  645. assert.EqualValues(t, stall, h.dropping.Load(), "drop log lines after stall %d", stall)
  646. enobufs.Store(false)
  647. sent, _, _ = u.connSockWrite(s, bufs[:1])
  648. assert.Equal(t, 1, sent)
  649. assert.Zero(t, s.stallAt.Load())
  650. }
  651. }
  652. // Concurrent writers share one ENOBUFS budget measured from the first ENOBUFS since a send went through: two
  653. // writers stalled together wait as long as one would, and a send going through while a writer sleeps gives that
  654. // writer a fresh budget. The sleep hook parks each writer until the test has moved the clock.
  655. func TestConnSockWriteENOBUFSConcurrentWriters(t *testing.T) {
  656. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  657. s, _ := openConnSock(t, u)
  658. var now atomic.Int64
  659. u.socks.clock = now.Load
  660. asleep, wake := make(chan struct{}), make(chan struct{})
  661. u.socks.sleep = func(time.Duration) {
  662. asleep <- struct{}{}
  663. <-wake
  664. }
  665. // Datagrams starting with 'e' get ENOBUFS while enobufs is set; the rest go out.
  666. var enobufs atomic.Bool
  667. var wrote atomic.Int64
  668. s.sys = func(fd int, b []byte) error {
  669. if b[0] == 'e' && enobufs.Load() {
  670. return syscall.ENOBUFS
  671. }
  672. wrote.Add(1)
  673. return writeFd(fd, b)
  674. }
  675. write := func(tag string, done chan<- int) {
  676. sent, _, _ := u.connSockWrite(s, [][]byte{[]byte(tag)})
  677. done <- sent
  678. }
  679. budget := int64(u.socks.cfg.enobufsMax)
  680. t.Run("a send going through ends the stall", func(t *testing.T) {
  681. enobufs.Store(true)
  682. now.Store(int64(time.Hour))
  683. done := make(chan int)
  684. go write("e1", done)
  685. <-asleep
  686. now.Add(budget / 2)
  687. sent, _, _ := u.connSockWrite(s, [][]byte{[]byte("ok")})
  688. require.Equal(t, 1, sent)
  689. now.Add(budget)
  690. wake <- struct{}{}
  691. <-asleep // e1 hit ENOBUFS again 1.5x the budget after its first, and waited instead of dropping.
  692. enobufs.Store(false)
  693. before := wrote.Load()
  694. wake <- struct{}{}
  695. assert.Equal(t, 1, <-done)
  696. assert.Equal(t, before+1, wrote.Load(), "e1 was dropped")
  697. })
  698. t.Run("two stalled writers spend one budget", func(t *testing.T) {
  699. enobufs.Store(true)
  700. now.Store(int64(2 * time.Hour))
  701. done := make(chan int)
  702. go write("e2", done)
  703. go write("e3", done)
  704. <-asleep
  705. <-asleep
  706. now.Add(budget * 6 / 10)
  707. wake <- struct{}{}
  708. wake <- struct{}{}
  709. <-asleep // Both are 0.6x the budget into one stall, not 1.2x, so both wait again.
  710. <-asleep
  711. now.Add(budget / 2)
  712. before := wrote.Load()
  713. wake <- struct{}{}
  714. wake <- struct{}{}
  715. assert.Equal(t, 1, <-done)
  716. assert.Equal(t, 1, <-done)
  717. assert.Equal(t, before, wrote.Load(), "a datagram went out while ENOBUFS lasted")
  718. })
  719. }
  720. // A full send buffer that never drains parks a connected socket send only for cfg.enobufsMax, the stall budget
  721. // ENOBUFS gets: golang/go#73919 has darwin UDP writes parked on EAGAIN waiting forever. Past it the datagrams are
  722. // dropped and counted as sent, concurrent writers spend that one budget, and once a send goes through the
  723. // connected socket works as before. s.sys stands in for a send buffer that stays full while eagain is set.
  724. func TestConnSockWriteEAGAINBounded(t *testing.T) {
  725. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  726. s, peer := openConnSock(t, u)
  727. var eagain atomic.Bool
  728. eagain.Store(true)
  729. s.sys = func(fd int, b []byte) error {
  730. if eagain.Load() {
  731. return syscall.EAGAIN
  732. }
  733. return writeFd(fd, b)
  734. }
  735. budget := u.socks.cfg.enobufsMax
  736. start := time.Now()
  737. sent, errno, closed := u.connSockWrite(s, [][]byte{[]byte("x")})
  738. elapsed := time.Since(start)
  739. assert.Equal(t, 1, sent)
  740. assert.Zero(t, errno)
  741. assert.False(t, closed)
  742. assert.GreaterOrEqual(t, elapsed, budget, "the send didn't wait for the buffer to drain")
  743. assert.Less(t, elapsed, time.Second, "the send waited past its deadline")
  744. // The budget is spent, so these drop without waiting, however many writers there are.
  745. var wg sync.WaitGroup
  746. start = time.Now()
  747. for range 4 {
  748. wg.Go(func() {
  749. bufs, _ := run(s.dst, 8, "y")
  750. sent, _, _ := u.connSockWrite(s, bufs)
  751. assert.Equal(t, 8, sent)
  752. })
  753. }
  754. wg.Wait()
  755. assert.Less(t, time.Since(start), time.Second)
  756. // A send going through starts a fresh budget, which two writers stalled together spend once; the upper bound
  757. // is far looser than that, so a descheduled runner doesn't fail it.
  758. eagain.Store(false)
  759. sent, _, _ = u.connSockWrite(s, [][]byte{[]byte("ok")})
  760. require.Equal(t, 1, sent)
  761. readTags(t, peer, 1)
  762. eagain.Store(true)
  763. start = time.Now()
  764. for range 2 {
  765. wg.Go(func() {
  766. sent, _, _ := u.connSockWrite(s, [][]byte{[]byte("w")})
  767. assert.Equal(t, 1, sent)
  768. })
  769. }
  770. wg.Wait()
  771. elapsed = time.Since(start)
  772. assert.GreaterOrEqual(t, elapsed, budget)
  773. assert.Less(t, elapsed, time.Second)
  774. eagain.Store(false)
  775. bufs, addrs := run(s.dst, 3, "z")
  776. n, err := u.WriteBatch(bufs, addrs)
  777. require.NoError(t, err)
  778. assert.Equal(t, 3, n)
  779. assert.Equal(t, []string{"z-0", "z-1", "z-2"}, readTags(t, peer, 3))
  780. assert.Zero(t, s.stallAt.Load())
  781. assert.Same(t, s, u.connSockFor(s.dst))
  782. }
  783. // A send waiting out ENOBUFS holds nothing a close or Close needs, and returns closed once it wakes to a closed
  784. // connected socket. The sleep hook parks the writer until both closes have finished; after the close, s.sys
  785. // writes for real, so the closed descriptor answers.
  786. func TestConnSockWriteDoesNotBlockClose(t *testing.T) {
  787. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil)
  788. s, _ := openConnSock(t, u)
  789. asleep, wake := make(chan struct{}), make(chan struct{})
  790. u.socks.sleep = func(time.Duration) {
  791. asleep <- struct{}{}
  792. <-wake
  793. }
  794. var gone atomic.Bool
  795. s.sys = func(fd int, b []byte) error {
  796. if gone.Load() {
  797. return writeFd(fd, b)
  798. }
  799. return syscall.ENOBUFS
  800. }
  801. done := make(chan bool)
  802. go func() {
  803. bufs, _ := run(s.dst, 1, "q")
  804. _, _, closed := u.connSockWrite(s, bufs)
  805. done <- closed
  806. }()
  807. <-asleep
  808. u.closeConnSock(s, "test", false)
  809. u.socks.closers.Wait()
  810. require.NoError(t, u.Close())
  811. gone.Store(true)
  812. close(wake)
  813. assert.True(t, <-done, "the writer didn't see its connected socket close")
  814. }
  815. // countingHandler counts connected socket open, close, discard and ENOBUFS drop log lines.
  816. type countingHandler struct{ opened, closed, discarded, dropping atomic.Int64 }
  817. func (h *countingHandler) Enabled(context.Context, slog.Level) bool { return true }
  818. func (h *countingHandler) Handle(_ context.Context, r slog.Record) error {
  819. switch r.Message {
  820. case "connected socket: opened":
  821. h.opened.Add(1)
  822. case "connected socket: closed":
  823. h.closed.Add(1)
  824. case "connected socket: discarded, its tunnel or route changed while it opened":
  825. h.discarded.Add(1)
  826. case "connected socket: sends stalled, dropping datagrams until one goes through":
  827. h.dropping.Add(1)
  828. }
  829. return nil
  830. }
  831. func (h *countingHandler) WithAttrs([]slog.Attr) slog.Handler { return h }
  832. func (h *countingHandler) WithGroup(string) slog.Handler { return h }
  833. // Anyone can make Nebula reply to them, as recv_error does to every datagram with an unknown index; the reader
  834. // below stands in for that reply. However many replies a sender draws, it never earns a connected socket without
  835. // an established tunnel.
  836. func TestConnSocksNoneForUnestablishedSenders(t *testing.T) {
  837. h := &countingHandler{}
  838. reply := []byte("reply")
  839. var c *StdConn
  840. var ready atomic.Bool
  841. c = newConnSockListener(t, listen("0.0.0.0:0"), h, func(from netip.AddrPort, _ []byte) {
  842. if ready.Load() {
  843. _ = c.WriteTo(reply, from)
  844. }
  845. })
  846. ready.Store(true)
  847. la, err := c.LocalAddr()
  848. require.NoError(t, err)
  849. to := netip.AddrPortFrom(netip.MustParseAddr("127.0.0.1"), la.Port())
  850. pkt := []byte("junk")
  851. buf := make([]byte, 64)
  852. for range 10 {
  853. pc, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
  854. require.NoError(t, err)
  855. for range 2 * c.socks.cfg.run {
  856. _, err := pc.WriteToUDPAddrPort(pkt, to)
  857. require.NoError(t, err)
  858. require.NoError(t, pc.SetReadDeadline(time.Now().Add(5*time.Second)))
  859. _, _, err = pc.ReadFromUDPAddrPort(buf)
  860. require.NoError(t, err)
  861. }
  862. _ = pc.Close()
  863. }
  864. assert.Zero(t, h.opened.Load())
  865. assert.Nil(t, c.socks.table.Load())
  866. }
  867. // Established peers that keep going away churn connected sockets at most as fast as the open bucket allows, and
  868. // what connSocks keeps about each goes when its tunnel does. Each round a fresh peer is established, gets busy and
  869. // goes away, and the clock moves 10ms.
  870. func TestConnSocksChurnBounded(t *testing.T) {
  871. h := &countingHandler{}
  872. u := newConnSockListener(t, listen("0.0.0.0:0"), h, nil)
  873. var now atomic.Int64
  874. now.Store(int64(time.Hour))
  875. u.socks.clock = now.Load
  876. const rounds, step = 300, 10 * time.Millisecond
  877. for range rounds {
  878. peer, dst := newEstablishedPeer(t, u, "udp4", "127.0.0.1:0")
  879. bufs, addrs := run(dst, u.socks.cfg.run, "p")
  880. n, err := u.WriteBatch(bufs, addrs)
  881. require.NoError(t, err)
  882. require.Equal(t, len(bufs), n)
  883. readFrom(t, peer, len(bufs))
  884. u.SetEstablishedPeer(dst, false)
  885. u.socks.closers.Wait()
  886. require.NoError(t, peer.Close())
  887. now.Add(int64(step))
  888. }
  889. elapsed := time.Duration(rounds) * step
  890. budget := int64(u.socks.cfg.max) + int64(elapsed/u.socks.cfg.openEvery) + 1
  891. t.Logf("%d rounds over %v: %d opens, %d closes", rounds, elapsed, h.opened.Load(), h.closed.Load())
  892. require.GreaterOrEqual(t, h.opened.Load(), int64(u.socks.cfg.max), "the rounds never churned")
  893. require.LessOrEqual(t, h.opened.Load(), budget)
  894. assert.Equal(t, h.opened.Load(), h.closed.Load())
  895. assert.Nil(t, u.socks.table.Load(), "peers left after their tunnels went away")
  896. assert.Zero(t, u.socks.reserved.Load())
  897. }
  898. // No send allocates, down any path: the listener with connected sockets off, to a destination that is no
  899. // tunnel's remote, to an established peer that isn't busy, or on a connected socket, one datagram or a run.
  900. func TestConnSockSendsDontAllocate(t *testing.T) {
  901. if raceEnabled {
  902. t.Skip("-race makes sync.Pool drop writers at random, so connected sends allocate")
  903. }
  904. pkt := make([]byte, 1400)
  905. for _, c := range []struct {
  906. name string
  907. cfg func(*connSockConfig)
  908. setup func(u *StdConn, dst netip.AddrPort)
  909. }{
  910. {name: "off", cfg: func(c *connSockConfig) { c.max = 0 }},
  911. {name: "ineligible", setup: func(u *StdConn, dst netip.AddrPort) {
  912. u.SetEstablishedPeer(netip.AddrPortFrom(dst.Addr(), dst.Port()+1), true)
  913. }},
  914. {name: "eligible", cfg: func(c *connSockConfig) { c.run = 1 << 30 },
  915. setup: func(u *StdConn, dst netip.AddrPort) { u.SetEstablishedPeer(dst, true) }},
  916. {name: "connected", setup: func(u *StdConn, dst netip.AddrPort) {
  917. u.SetEstablishedPeer(dst, true)
  918. bufs, addrs := run(dst, u.socks.cfg.run, "p")
  919. _, err := u.WriteBatch(bufs, addrs)
  920. require.NoError(t, err)
  921. require.NotNil(t, u.connSockFor(dst))
  922. }},
  923. } {
  924. t.Run(c.name, func(t *testing.T) {
  925. var cfg []func(*connSockConfig)
  926. if c.cfg != nil {
  927. cfg = append(cfg, c.cfg)
  928. }
  929. u := newConnSockListener(t, listen("0.0.0.0:0"), nil, nil, cfg...)
  930. _, dst := newPeer(t, "udp4", "127.0.0.1:0")
  931. if c.setup != nil {
  932. c.setup(u, dst)
  933. }
  934. bufs, addrs := run(dst, 8, "b")
  935. assert.Zero(t, testing.AllocsPerRun(100, func() { _ = u.WriteTo(pkt, dst) }), "WriteTo")
  936. assert.Zero(t, testing.AllocsPerRun(100, func() { _, _ = u.WriteBatch(bufs, addrs) }), "WriteBatch")
  937. })
  938. }
  939. }
  940. // Once burst tokens are spent the bucket earns one back per interval.
  941. func TestGCRA(t *testing.T) {
  942. var g gcra
  943. now := int64(time.Hour)
  944. for i := range 3 {
  945. require.True(t, g.take(now, 3, time.Second), "token %d of the burst", i)
  946. }
  947. require.False(t, g.take(now, 3, time.Second))
  948. require.False(t, g.take(now+int64(999*time.Millisecond), 3, time.Second))
  949. require.True(t, g.take(now+int64(time.Second), 3, time.Second))
  950. require.False(t, g.take(now+int64(time.Second), 3, time.Second))
  951. require.True(t, g.take(now+int64(time.Hour), 3, time.Second))
  952. }