route.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. package overlay
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "math"
  6. "net"
  7. "net/netip"
  8. "runtime"
  9. "strconv"
  10. "github.com/gaissmai/bart"
  11. "github.com/slackhq/nebula/config"
  12. "github.com/slackhq/nebula/routing"
  13. )
  14. type Route struct {
  15. MTU int
  16. Metric int
  17. Cidr netip.Prefix
  18. Via routing.Gateways
  19. Install bool
  20. }
  21. // Equal determines if a route that could be installed in the system route table is equal to another
  22. // Via is ignored since that is only consumed within nebula itself
  23. func (r Route) Equal(t Route) bool {
  24. if r.Cidr != t.Cidr {
  25. return false
  26. }
  27. if r.Metric != t.Metric {
  28. return false
  29. }
  30. if r.MTU != t.MTU {
  31. return false
  32. }
  33. if r.Install != t.Install {
  34. return false
  35. }
  36. return true
  37. }
  38. func (r Route) String() string {
  39. s := r.Cidr.String()
  40. if r.Metric != 0 {
  41. s += fmt.Sprintf(" metric: %v", r.Metric)
  42. }
  43. return s
  44. }
  45. func makeRouteTree(l *slog.Logger, routes []Route, allowMTU bool) (*bart.Table[routing.Gateways], error) {
  46. routeTree := new(bart.Table[routing.Gateways])
  47. for _, r := range routes {
  48. if !allowMTU && r.MTU > 0 {
  49. l.Warn("route MTU is not supported on this platform",
  50. "goos", runtime.GOOS,
  51. "route", r,
  52. )
  53. }
  54. gateways := r.Via
  55. if len(gateways) > 0 {
  56. routing.CalculateBucketsForGateways(gateways)
  57. routeTree.Insert(r.Cidr, gateways)
  58. }
  59. }
  60. return routeTree, nil
  61. }
  62. func parseRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
  63. var err error
  64. r := c.Get("tun.routes")
  65. if r == nil {
  66. return []Route{}, nil
  67. }
  68. rawRoutes, ok := r.([]any)
  69. if !ok {
  70. return nil, fmt.Errorf("tun.routes is not an array")
  71. }
  72. if len(rawRoutes) < 1 {
  73. return []Route{}, nil
  74. }
  75. routes := make([]Route, len(rawRoutes))
  76. for i, r := range rawRoutes {
  77. m, ok := r.(map[string]any)
  78. if !ok {
  79. return nil, fmt.Errorf("entry %v in tun.routes is invalid", i+1)
  80. }
  81. rMtu, ok := m["mtu"]
  82. if !ok {
  83. return nil, fmt.Errorf("entry %v.mtu in tun.routes is not present", i+1)
  84. }
  85. mtu, ok := rMtu.(int)
  86. if !ok {
  87. mtu, err = strconv.Atoi(rMtu.(string))
  88. if err != nil {
  89. return nil, fmt.Errorf("entry %v.mtu in tun.routes is not an integer: %v", i+1, err)
  90. }
  91. }
  92. if mtu < 500 {
  93. return nil, fmt.Errorf("entry %v.mtu in tun.routes is below 500: %v", i+1, mtu)
  94. }
  95. rRoute, ok := m["route"]
  96. if !ok {
  97. return nil, fmt.Errorf("entry %v.route in tun.routes is not present", i+1)
  98. }
  99. r := Route{
  100. Install: true,
  101. MTU: mtu,
  102. }
  103. r.Cidr, err = netip.ParsePrefix(fmt.Sprintf("%v", rRoute))
  104. if err != nil {
  105. return nil, fmt.Errorf("entry %v.route in tun.routes failed to parse: %v", i+1, err)
  106. }
  107. found := false
  108. for _, network := range networks {
  109. if network.Contains(r.Cidr.Addr()) && r.Cidr.Bits() >= network.Bits() {
  110. found = true
  111. break
  112. }
  113. }
  114. if !found {
  115. return nil, fmt.Errorf(
  116. "entry %v.route in tun.routes is not contained within the configured vpn networks; route: %v, networks: %v",
  117. i+1,
  118. r.Cidr.String(),
  119. networks,
  120. )
  121. }
  122. routes[i] = r
  123. }
  124. return routes, nil
  125. }
  126. func parseUnsafeRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
  127. var err error
  128. r := c.Get("tun.unsafe_routes")
  129. if r == nil {
  130. return []Route{}, nil
  131. }
  132. rawRoutes, ok := r.([]any)
  133. if !ok {
  134. return nil, fmt.Errorf("tun.unsafe_routes is not an array")
  135. }
  136. if len(rawRoutes) < 1 {
  137. return []Route{}, nil
  138. }
  139. routes := make([]Route, len(rawRoutes))
  140. for i, r := range rawRoutes {
  141. m, ok := r.(map[string]any)
  142. if !ok {
  143. return nil, fmt.Errorf("entry %v in tun.unsafe_routes is invalid", i+1)
  144. }
  145. var mtu int
  146. if rMtu, ok := m["mtu"]; ok {
  147. mtu, ok = rMtu.(int)
  148. if !ok {
  149. mtu, err = strconv.Atoi(rMtu.(string))
  150. if err != nil {
  151. return nil, fmt.Errorf("entry %v.mtu in tun.unsafe_routes is not an integer: %v", i+1, err)
  152. }
  153. }
  154. if mtu != 0 && mtu < 500 {
  155. return nil, fmt.Errorf("entry %v.mtu in tun.unsafe_routes is below 500: %v", i+1, mtu)
  156. }
  157. }
  158. rMetric, ok := m["metric"]
  159. if !ok {
  160. rMetric = 0
  161. }
  162. metric, ok := rMetric.(int)
  163. if !ok {
  164. _, err = strconv.ParseInt(rMetric.(string), 10, 32)
  165. if err != nil {
  166. return nil, fmt.Errorf("entry %v.metric in tun.unsafe_routes is not an integer: %v", i+1, err)
  167. }
  168. }
  169. if metric < 0 || metric > math.MaxInt32 {
  170. return nil, fmt.Errorf("entry %v.metric in tun.unsafe_routes is not in range (0-%d) : %v", i+1, math.MaxInt32, metric)
  171. }
  172. rVia, ok := m["via"]
  173. if !ok {
  174. return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not present", i+1)
  175. }
  176. var gateways routing.Gateways
  177. switch via := rVia.(type) {
  178. case string:
  179. viaIp, err := netip.ParseAddr(via)
  180. if err != nil {
  181. return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes failed to parse address: %v", i+1, err)
  182. }
  183. gateways = routing.Gateways{routing.NewGateway(viaIp, 1)}
  184. case []any:
  185. gateways = make(routing.Gateways, len(via))
  186. for ig, v := range via {
  187. gatewayMap, ok := v.(map[string]any)
  188. if !ok {
  189. return nil, fmt.Errorf("entry %v in tun.unsafe_routes[%v].via is invalid", i+1, ig+1)
  190. }
  191. rGateway, ok := gatewayMap["gateway"]
  192. if !ok {
  193. return nil, fmt.Errorf("entry .gateway in tun.unsafe_routes[%v].via[%v] is not present", i+1, ig+1)
  194. }
  195. parsedGateway, ok := rGateway.(string)
  196. if !ok {
  197. return nil, fmt.Errorf("entry .gateway in tun.unsafe_routes[%v].via[%v] is not a string", i+1, ig+1)
  198. }
  199. gatewayIp, err := netip.ParseAddr(parsedGateway)
  200. if err != nil {
  201. return nil, fmt.Errorf("entry .gateway in tun.unsafe_routes[%v].via[%v] failed to parse address: %v", i+1, ig+1, err)
  202. }
  203. rGatewayWeight, ok := gatewayMap["weight"]
  204. if !ok {
  205. rGatewayWeight = 1
  206. }
  207. gatewayWeight, ok := rGatewayWeight.(int)
  208. if !ok {
  209. _, err = strconv.ParseInt(rGatewayWeight.(string), 10, 32)
  210. if err != nil {
  211. return nil, fmt.Errorf("entry .weight in tun.unsafe_routes[%v].via[%v] is not an integer", i+1, ig+1)
  212. }
  213. }
  214. if gatewayWeight < 1 || gatewayWeight > math.MaxInt32 {
  215. return nil, fmt.Errorf("entry .weight in tun.unsafe_routes[%v].via[%v] is not in range (1-%d) : %v", i+1, ig+1, math.MaxInt32, gatewayWeight)
  216. }
  217. gateways[ig] = routing.NewGateway(gatewayIp, gatewayWeight)
  218. }
  219. default:
  220. return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not a string or list of gateways: found %T", i+1, rVia)
  221. }
  222. rRoute, ok := m["route"]
  223. if !ok {
  224. return nil, fmt.Errorf("entry %v.route in tun.unsafe_routes is not present", i+1)
  225. }
  226. install := true
  227. rInstall, ok := m["install"]
  228. if ok {
  229. install, err = strconv.ParseBool(fmt.Sprintf("%v", rInstall))
  230. if err != nil {
  231. return nil, fmt.Errorf("entry %v.install in tun.unsafe_routes is not a boolean: %v", i+1, err)
  232. }
  233. }
  234. r := Route{
  235. Via: gateways,
  236. MTU: mtu,
  237. Metric: metric,
  238. Install: install,
  239. }
  240. r.Cidr, err = netip.ParsePrefix(fmt.Sprintf("%v", rRoute))
  241. if err != nil {
  242. return nil, fmt.Errorf("entry %v.route in tun.unsafe_routes failed to parse: %v", i+1, err)
  243. }
  244. for _, network := range networks {
  245. if network.Contains(r.Cidr.Addr()) {
  246. return nil, fmt.Errorf(
  247. "entry %v.route in tun.unsafe_routes is contained within the configured vpn networks; route: %v, network: %v",
  248. i+1,
  249. r.Cidr.String(),
  250. network.String(),
  251. )
  252. }
  253. }
  254. routes[i] = r
  255. }
  256. return routes, nil
  257. }
  258. func ipWithin(o *net.IPNet, i *net.IPNet) bool {
  259. // Make sure o contains the lowest form of i
  260. if !o.Contains(i.IP.Mask(i.Mask)) {
  261. return false
  262. }
  263. // Find the max ip in i
  264. ip4 := i.IP.To4()
  265. if ip4 == nil {
  266. return false
  267. }
  268. last := make(net.IP, len(ip4))
  269. copy(last, ip4)
  270. for x := range ip4 {
  271. last[x] |= ^i.Mask[x]
  272. }
  273. // Make sure o contains the max
  274. if !o.Contains(last) {
  275. return false
  276. }
  277. return true
  278. }