Procházet zdrojové kódy

darwin: Assign host address to the utun and route vpn explicitly (#1931)

Tailscale with an exit node treats every interface address prefix as an
attached LAN and either claims it or excludes it, and configd then rewrites
the matching route away from nebula's utun. Interfaces holding a single
address are skipped, so assign /32 and /128 and add the network route
ourselves for both families.
John Maguire před 1 týdnem
rodič
revize
42280f04ce
1 změnil soubory, kde provedl 22 přidání a 7 odebrání
  1. 22 7
      overlay/tun_darwin.go

+ 22 - 7
overlay/tun_darwin.go

@@ -271,6 +271,7 @@ func (t *tun) Activate() error {
 	return t.addRoutes(false)
 }
 
+// activate4 assigns our vpn address as a /32 and routes the vpn network to the tun explicitly.
 func (t *tun) activate4(network netip.Prefix) error {
 	s, err := unix.Socket(
 		unix.AF_INET,
@@ -297,22 +298,32 @@ func (t *tun) activate4(network netip.Prefix) error {
 		MaskAddr: unix.RawSockaddrInet4{
 			Len:    unix.SizeofSockaddrInet4,
 			Family: unix.AF_INET,
-			Addr:   prefixToMask(network).As4(),
+			Addr:   [4]byte{255, 255, 255, 255}, //always make the address a /32
 		},
 	}
 
+	// On Darwin, VPNs don't edit the routing table directly.
+	// A network extension hands its routes to the system, and configd installs them.
+	// configd replaces any route already holding the same prefix, including ours.
+	// As an example, Tailscale with an exit node treats any interface prefix wider than one address as a LAN.
+	// With LAN access off, it routes each LAN into Tailscale's utun.
+	// With LAN access on, it excludes each LAN from its tunnel.
+	// configd routes an excluded prefix like its own routes would, ignoring ours, so it goes out the default route.
+	// Either way our route is replaced, and traffic for the vpn network no longer reaches nebula.
+	// A single address isn't a LAN to Tailscale, so our route is left alone.
+	// We route the vpn network to the tun explicitly instead.
+
 	if err := ioctl(uintptr(s), unix.SIOCAIFADDR, uintptr(unsafe.Pointer(&ifr))); err != nil {
 		return fmt.Errorf("failed to set tun v4 address: %s", err)
 	}
 
-	err = addRoute(network, t.linkAddr.Load())
-	if err != nil {
-		return err
+	if err := addRoute(network, t.linkAddr.Load()); err != nil {
+		return fmt.Errorf("failed to add route for %v: %w", network.Masked(), err)
 	}
-
 	return nil
 }
 
+// activate6 is activate4 for IPv6
 func (t *tun) activate6(network netip.Prefix) error {
 	s, err := unix.Socket(
 		unix.AF_INET6,
@@ -334,20 +345,24 @@ func (t *tun) activate6(network netip.Prefix) error {
 		PrefixMask: unix.RawSockaddrInet6{
 			Len:    unix.SizeofSockaddrInet6,
 			Family: unix.AF_INET6,
-			Addr:   prefixToMask(network).As16(),
+			// Always set the netmask to be a /128. See activate4 for why we do this.
+			Addr: [16]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
 		},
 		Lifetime: addrLifetime{
 			// never expires
 			Vltime: 0xffffffff,
 			Pltime: 0xffffffff,
 		},
-		Flags: _IN6_IFF_NODAD,
+		Flags: _IN6_IFF_NODAD, // No DAD, a utun has no neighbors.
 	}
 
 	if err := ioctl(uintptr(s), _SIOCAIFADDR_IN6, uintptr(unsafe.Pointer(&ifr))); err != nil {
 		return fmt.Errorf("failed to set tun address: %s", err)
 	}
 
+	if err := addRoute(network, t.linkAddr.Load()); err != nil {
+		return fmt.Errorf("failed to add route for %v: %w", network.Masked(), err)
+	}
 	return nil
 }