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  }