tun_routes_darwin.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. //go:build !ios && !e2e_testing
  2. package overlay
  3. import (
  4. "errors"
  5. "fmt"
  6. "net"
  7. "net/netip"
  8. "os"
  9. "strconv"
  10. "sync/atomic"
  11. "time"
  12. "unsafe"
  13. "github.com/slackhq/nebula/config"
  14. "github.com/slackhq/nebula/util"
  15. netroute "golang.org/x/net/route"
  16. "golang.org/x/sys/unix"
  17. )
  18. // Darwin has no route metrics, the routing table holds one unscoped route per prefix. When another interface already
  19. // routes an unsafe_route's prefix, like the lan we started on, theirs stays and ours is added once theirs is removed.
  20. // Our own route removed by someone else, with nothing in its place, was removed on purpose and stays gone until a reload
  21. // or until another interface takes the prefix, after which ours follows theirs like any other.
  22. const (
  23. // routeRetryDelay is how long the system routing table must be quiet before we judge who holds a prefix. configd
  24. // replaces a route by deleting and adding, looking in between would find the prefix free
  25. routeRetryDelay = time.Second
  26. // routeSweepInterval bounds how long a freed unsafe_route can go unnoticed. The routing socket drops messages that
  27. // don't fit its receive buffer without telling the reader, and detaching an interface deletes its routes without
  28. // a message for each
  29. routeSweepInterval = time.Minute
  30. // routeSocketRcvBuf is well past the 8KiB default, which a burst of neighbor routes expiring can fill
  31. routeSocketRcvBuf = 1 << 20
  32. )
  33. type routeStateKind int
  34. const (
  35. routeUnknown routeStateKind = iota
  36. // routeInstalled is our route holding the prefix
  37. routeInstalled
  38. // routeYielded is another interface holding the prefix, ours is added once it lets go
  39. routeYielded
  40. // routeRemoved is our route deleted by someone else with nothing in its place, it is left alone until a reload or
  41. // another interface takes the prefix
  42. routeRemoved
  43. // routeFailed is adding our route failing for some other reason, it is retried
  44. routeFailed
  45. )
  46. type routeState struct {
  47. kind routeStateKind
  48. // holder is the index of the interface holding the prefix when kind is routeYielded
  49. holder int
  50. }
  51. // routeMover is the process that last added, changed or deleted a route for an unsafe_route's prefix, for logs. Its name
  52. // is looked up as the message arrives, route(8) has exited by the time the table settles
  53. type routeMover struct {
  54. pid int32
  55. name string
  56. }
  57. // logArgs appends m to a log line's args, when we know who it was
  58. func (m routeMover) logArgs(args ...any) []any {
  59. if m.pid == 0 {
  60. return args
  61. }
  62. args = append(args, "pid", m.pid)
  63. if m.name != "" {
  64. args = append(args, "process", m.name)
  65. }
  66. return args
  67. }
  68. // processName is the name of process pid, truncated to 16 bytes, or empty when it has already exited
  69. func processName(pid int32) string {
  70. kp, err := unix.SysctlKinfoProc("kern.proc.pid", int(pid))
  71. if err != nil || kp.Proc.P_pid != pid {
  72. return ""
  73. }
  74. return unix.ByteSliceToString(kp.Proc.P_comm[:])
  75. }
  76. // installRoutes returns the routes we put in the system routing table, one per prefix
  77. func installRoutes(routes []Route) []Route {
  78. seen := make(map[netip.Prefix]struct{}, len(routes))
  79. var out []Route
  80. for _, r := range routes {
  81. if len(r.Via) == 0 || !r.Install {
  82. // We don't allow route MTUs so only install routes with a via
  83. continue
  84. }
  85. p := r.Cidr.Masked()
  86. if _, ok := seen[p]; ok {
  87. continue
  88. }
  89. seen[p] = struct{}{}
  90. out = append(out, r)
  91. }
  92. return out
  93. }
  94. // addRoutes puts every unsafe_route in the system routing table, including ones someone deleted, unless another
  95. // interface holds its prefix. It runs on start and on every reload.
  96. func (t *tun) addRoutes(logErrors bool, rl *config.Reload) error {
  97. // The route tree is still ours, the system routing table is not
  98. if t.hostOwned {
  99. return nil
  100. }
  101. t.routesMu.Lock()
  102. defer t.routesMu.Unlock()
  103. // Activate hasn't run yet, it will install the routes
  104. linkAddr := t.linkAddr.Load()
  105. if linkAddr == nil || t.closed {
  106. return nil
  107. }
  108. var msgs []netroute.Message
  109. var msgsErr error
  110. fetched := false
  111. states := make(map[netip.Prefix]routeState)
  112. for _, r := range installRoutes(*t.Routes.Load()) {
  113. p := r.Cidr.Masked()
  114. prev := t.routeStates[p]
  115. err := addRoute(p, linkAddr)
  116. if err == nil {
  117. t.l.Info("Added route", "route", r)
  118. states[p] = routeState{kind: routeInstalled}
  119. continue
  120. }
  121. if !errors.Is(err, unix.EEXIST) {
  122. retErr := util.NewContextualError("Failed to add route", map[string]any{"route": r}, err)
  123. if !logErrors {
  124. return retErr
  125. }
  126. retErr.Log(t.l)
  127. rl.Failed()
  128. states[p] = routeState{kind: routeFailed}
  129. continue
  130. }
  131. // Every route we added already exists on a reload, only warn about someone else's
  132. if !fetched {
  133. msgs, msgsErr = fetchRoutes()
  134. fetched = true
  135. }
  136. if msgsErr != nil {
  137. t.l.Warn("unable to add unsafe_route, a route already exists but its interface could not be determined", "route", r.Cidr, "error", msgsErr)
  138. states[p] = prev
  139. continue
  140. }
  141. switch holder := routeInterfaceIn(msgs, p); holder {
  142. case linkAddr.Index:
  143. states[p] = routeState{kind: routeInstalled}
  144. case 0:
  145. // Removed since the add, the watcher will see to it
  146. states[p] = prev
  147. default:
  148. if prev.kind != routeYielded || prev.holder != holder {
  149. t.l.Warn("unable to add unsafe_route, the destination is already routed through another interface, it will be added once that route is removed", "route", r.Cidr, "interface", interfaceName(holder))
  150. }
  151. states[p] = routeState{kind: routeYielded, holder: holder}
  152. }
  153. }
  154. t.routeStates = states
  155. return nil
  156. }
  157. func (t *tun) removeRoutes(routes []Route, rl *config.Reload) error {
  158. if t.hostOwned {
  159. return nil
  160. }
  161. t.routesMu.Lock()
  162. defer t.routesMu.Unlock()
  163. linkAddr := t.linkAddr.Load()
  164. if linkAddr == nil || t.closed {
  165. return nil
  166. }
  167. // The kernel deletes a route by its prefix alone, whichever interface holds it. Only delete the ones that are ours
  168. msgs, err := fetchRoutes()
  169. if err != nil {
  170. var left []netip.Prefix
  171. for _, r := range installRoutes(routes) {
  172. left = append(left, r.Cidr)
  173. }
  174. return fmt.Errorf("failed to read the system routing table, leaving routes %v installed: %w", left, err)
  175. }
  176. for _, r := range installRoutes(routes) {
  177. p := r.Cidr.Masked()
  178. // Whatever happens next, the prefix is no longer one we know about
  179. delete(t.routeStates, p)
  180. if routeInterfaceIn(msgs, p) != linkAddr.Index {
  181. continue
  182. }
  183. err = delRoute(p, linkAddr)
  184. if errors.Is(err, unix.ESRCH) {
  185. // Already gone, which is what we wanted
  186. t.l.Info("Route was already removed", "route", r)
  187. } else if err != nil {
  188. t.l.Error("Failed to remove route", "error", err, "route", r)
  189. rl.Failed()
  190. } else {
  191. t.l.Info("Removed route", "route", r)
  192. }
  193. }
  194. return nil
  195. }
  196. // reconcileRoutes brings the unsafe_routes in line with who holds their prefix now. It is quiet unless something
  197. // changed since it last looked, it runs every routeSweepEvery.
  198. func (t *tun) reconcileRoutes() {
  199. t.routesMu.Lock()
  200. defer t.routesMu.Unlock()
  201. linkAddr := t.linkAddr.Load()
  202. if linkAddr == nil || t.closed {
  203. return
  204. }
  205. t.routeMoversMu.Lock()
  206. movers := t.routeMovers
  207. t.routeMovers = nil
  208. t.routeMoversMu.Unlock()
  209. msgs, err := fetchRoutes()
  210. if err != nil {
  211. t.l.Error("Failed to read the system routing table", "error", err)
  212. return
  213. }
  214. states := make(map[netip.Prefix]routeState)
  215. for _, r := range installRoutes(*t.Routes.Load()) {
  216. p := r.Cidr.Masked()
  217. prev := t.routeStates[p]
  218. by := movers[p]
  219. holder := routeInterfaceIn(msgs, p)
  220. switch {
  221. case holder == linkAddr.Index:
  222. states[p] = routeState{kind: routeInstalled}
  223. case holder != 0:
  224. // Another interface taking a prefix we left alone puts ours back in line behind theirs
  225. if prev.kind != routeYielded || prev.holder != holder {
  226. t.l.Info("unsafe_route is routed through another interface, it will be added back when that route is removed", by.logArgs("route", r.Cidr, "interface", interfaceName(holder))...)
  227. }
  228. states[p] = routeState{kind: routeYielded, holder: holder}
  229. case prev.kind == routeInstalled:
  230. t.l.Warn("unsafe_route was removed outside nebula, leaving it until a reload or another interface takes its destination", by.logArgs("route", r.Cidr)...)
  231. states[p] = routeState{kind: routeRemoved}
  232. case prev.kind == routeRemoved:
  233. states[p] = prev
  234. default:
  235. err := addRoute(p, linkAddr)
  236. switch {
  237. case err == nil:
  238. if prev.kind == routeYielded {
  239. t.l.Info("unsafe_route's destination is free again, added it", by.logArgs("route", r)...)
  240. } else {
  241. t.l.Info("Added route", "route", r)
  242. }
  243. states[p] = routeState{kind: routeInstalled}
  244. case errors.Is(err, unix.EEXIST):
  245. // Taken since the fetch, the next look will see by whom
  246. states[p] = prev
  247. default:
  248. if prev.kind != routeFailed {
  249. util.NewContextualError("Failed to add route", map[string]any{"route": r}, err).Log(t.l)
  250. }
  251. states[p] = routeState{kind: routeFailed}
  252. }
  253. }
  254. }
  255. t.routeStates = states
  256. }
  257. // startRouteWatch starts reconciling the unsafe_routes, every routeSweepEvery and, when watch isn't nil, once the
  258. // routing table settles after anything that may have moved a prefix
  259. func (t *tun) startRouteWatch(watch *os.File) {
  260. t.routesMu.Lock()
  261. defer t.routesMu.Unlock()
  262. if t.closed {
  263. if watch != nil {
  264. _ = watch.Close()
  265. }
  266. return
  267. }
  268. t.routeSweep = time.AfterFunc(t.routeSweepEvery, t.sweepRoutes)
  269. if watch != nil {
  270. t.routeWatch = watch
  271. go t.watchRoutes(watch)
  272. }
  273. }
  274. func (t *tun) sweepRoutes() {
  275. // A table still changing gets reconciled once it settles
  276. if time.Since(time.Unix(0, t.lastRouteEvent.Load())) >= routeRetryDelay {
  277. t.reconcileRoutes()
  278. }
  279. t.routesMu.Lock()
  280. defer t.routesMu.Unlock()
  281. if !t.closed {
  282. t.routeSweep.Reset(t.routeSweepEvery)
  283. }
  284. }
  285. func openRouteSocket() (*os.File, error) {
  286. fd, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
  287. if err != nil {
  288. return nil, fmt.Errorf("unable to create AF_ROUTE socket: %w", err)
  289. }
  290. // Fewer drops, the sweep catches the rest
  291. _ = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF, routeSocketRcvBuf)
  292. // Non-blocking puts it on the poller, so Close interrupts a Read
  293. if err = unix.SetNonblock(fd, true); err != nil {
  294. _ = unix.Close(fd)
  295. return nil, fmt.Errorf("failed to set the AF_ROUTE socket to non-blocking mode: %w", err)
  296. }
  297. return os.NewFile(uintptr(fd), "route"), nil
  298. }
  299. // watchRoutes reconciles the unsafe_routes once the routing table has been quiet for routeRetryDelay after a message
  300. // that may have moved a prefix. A table that keeps changing is reconciled every routeSweepEvery regardless.
  301. func (t *tun) watchRoutes(f *os.File) {
  302. var reconciled atomic.Int64
  303. settle := time.AfterFunc(time.Hour, func() {
  304. reconciled.Store(time.Now().UnixNano())
  305. t.reconcileRoutes()
  306. })
  307. settle.Stop()
  308. defer settle.Stop()
  309. var burst time.Time
  310. self := int32(os.Getpid())
  311. b := make([]byte, os.Getpagesize())
  312. for {
  313. n, err := f.Read(b)
  314. if err != nil {
  315. if !errors.Is(err, os.ErrClosed) {
  316. t.l.Error("Stopped watching the system routing table, unsafe_routes held by another interface are only retried every minute", "error", err)
  317. }
  318. return
  319. }
  320. moved, p, pid := routesMayHaveMoved(b[:n], *t.Routes.Load())
  321. if !moved {
  322. continue
  323. }
  324. // Our own are already in routeStates
  325. if p.IsValid() && pid != self {
  326. t.routeMoversMu.Lock()
  327. if t.routeMovers == nil {
  328. t.routeMovers = make(map[netip.Prefix]routeMover)
  329. }
  330. t.routeMovers[p] = routeMover{pid: pid, name: processName(pid)}
  331. t.routeMoversMu.Unlock()
  332. }
  333. now := time.Now()
  334. t.lastRouteEvent.Store(now.UnixNano())
  335. if burst.IsZero() || reconciled.Load() >= burst.UnixNano() {
  336. burst = now
  337. }
  338. settle.Reset(max(0, min(routeRetryDelay, time.Until(burst.Add(t.routeSweepEvery)))))
  339. }
  340. }
  341. // routesMayHaveMoved reports whether b, one routing socket message, may have freed a prefix one of routes installs or
  342. // handed it to another interface. That's a route for it added, changed or removed, or an interface that lost an address
  343. // or changed state, since detaching an interface deletes its routes without a message of their own. The header is read
  344. // directly, x/net/route skips the RTM_IFINFO the socket sends, which has no interface address, and misreads the
  345. // unpadded sockaddrs in messages the kernel makes itself. A request naming one of the prefixes also returns that prefix
  346. // and the pid of the process that made it.
  347. func routesMayHaveMoved(b []byte, routes []Route) (moved bool, p netip.Prefix, pid int32) {
  348. if len(b) < 4 {
  349. return false, p, 0
  350. }
  351. switch int(b[3]) {
  352. case unix.RTM_IFINFO, unix.RTM_DELADDR:
  353. return true, p, 0
  354. case unix.RTM_ADD, unix.RTM_CHANGE, unix.RTM_DELETE:
  355. default:
  356. return false, p, 0
  357. }
  358. if len(b) < unix.SizeofRtMsghdr {
  359. return false, p, 0
  360. }
  361. hdr := (*unix.RtMsghdr)(unsafe.Pointer(&b[0]))
  362. // A failed request is reported too, and a scoped route never held the prefix
  363. if hdr.Errno != 0 || hdr.Flags&unix.RTF_IFSCOPE != 0 {
  364. return false, p, 0
  365. }
  366. if hdr.Pid == 0 {
  367. // The kernel's own, which x/net/route can't parse. Neighbors coming and going are host routes, anything else
  368. // could be holding a prefix
  369. return hdr.Flags&(unix.RTF_HOST|unix.RTF_LLINFO) == 0, p, 0
  370. }
  371. // Replies to a request carry the requester's padded sockaddrs
  372. msgs, err := netroute.ParseRIB(unix.NET_RT_DUMP, b)
  373. if err != nil || len(msgs) != 1 {
  374. return false, p, 0
  375. }
  376. m, ok := msgs[0].(*netroute.RouteMessage)
  377. if !ok || len(m.Addrs) <= unix.RTAX_NETMASK {
  378. return false, p, 0
  379. }
  380. p, ok = routePrefix(m.Addrs[unix.RTAX_DST], m.Addrs[unix.RTAX_NETMASK])
  381. if !ok {
  382. return false, netip.Prefix{}, 0
  383. }
  384. p = p.Masked()
  385. for _, r := range installRoutes(routes) {
  386. if r.Cidr.Masked() == p {
  387. return true, p, hdr.Pid
  388. }
  389. }
  390. return false, netip.Prefix{}, 0
  391. }
  392. // interfaceName is the name of the interface with index i, for logs
  393. func interfaceName(i int) string {
  394. if ifc, err := net.InterfaceByIndex(i); err == nil {
  395. return ifc.Name
  396. }
  397. return strconv.Itoa(i)
  398. }