2
0

standalone.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netcheck
  4. import (
  5. "context"
  6. "errors"
  7. "net/netip"
  8. "tailscale.com/net/netaddr"
  9. "tailscale.com/net/netns"
  10. "tailscale.com/net/stun"
  11. "tailscale.com/types/logger"
  12. "tailscale.com/types/nettype"
  13. )
  14. // Standalone creates the necessary UDP sockets on the given bindAddr and starts
  15. // an IO loop so that the Client can perform active probes with no further need
  16. // for external driving of IO (no need to set/implement SendPacket, or call
  17. // ReceiveSTUNPacket). It must be called prior to starting any reports and is
  18. // shut down by cancellation of the provided context. If both IPv4 and IPv6 fail
  19. // to bind, errors will be returned, if one or both protocols can bind no error
  20. // is returned.
  21. func (c *Client) Standalone(ctx context.Context, bindAddr string) error {
  22. if c.NetMon == nil {
  23. panic("netcheck.Client.NetMon must be set")
  24. }
  25. if bindAddr == "" {
  26. bindAddr = ":0"
  27. }
  28. var errs []error
  29. u4, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, c.NetMon)).ListenPacket(ctx, "udp4", bindAddr)
  30. if err != nil {
  31. c.logf("udp4: %v", err)
  32. errs = append(errs, err)
  33. } else {
  34. go readPackets(ctx, c.logf, u4, c.ReceiveSTUNPacket)
  35. }
  36. u6, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, c.NetMon)).ListenPacket(ctx, "udp6", bindAddr)
  37. if err != nil {
  38. c.logf("udp6: %v", err)
  39. errs = append(errs, err)
  40. } else {
  41. go readPackets(ctx, c.logf, u6, c.ReceiveSTUNPacket)
  42. }
  43. c.SendPacket = func(pkt []byte, dst netip.AddrPort) (int, error) {
  44. pc := u4
  45. if dst.Addr().Is6() {
  46. pc = u6
  47. }
  48. if pc == nil {
  49. return 0, errors.New("no UDP socket")
  50. }
  51. return pc.WriteToUDPAddrPort(pkt, dst)
  52. }
  53. // If both v4 and v6 failed, report an error, otherwise let one succeed.
  54. if len(errs) == 2 {
  55. return errors.Join(errs...)
  56. }
  57. return nil
  58. }
  59. // readPackets reads STUN packets from pc until there's an error or ctx is done.
  60. // In either case, it closes pc.
  61. func readPackets(ctx context.Context, logf logger.Logf, pc nettype.PacketConn, recv func([]byte, netip.AddrPort)) {
  62. done := make(chan struct{})
  63. defer close(done)
  64. go func() {
  65. select {
  66. case <-ctx.Done():
  67. case <-done:
  68. }
  69. pc.Close()
  70. }()
  71. var buf [64 << 10]byte
  72. for {
  73. n, addr, err := pc.ReadFromUDPAddrPort(buf[:])
  74. if err != nil {
  75. if ctx.Err() != nil {
  76. return
  77. }
  78. logf("ReadFrom: %v", err)
  79. return
  80. }
  81. pkt := buf[:n]
  82. if !stun.Is(pkt) {
  83. continue
  84. }
  85. if ap := netaddr.Unmap(addr); ap.IsValid() {
  86. recv(pkt, ap)
  87. }
  88. }
  89. }