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