diff --git a/management/server/network.go b/management/server/network.go index ffe098c964c..647bab59801 100644 --- a/management/server/network.go +++ b/management/server/network.go @@ -5,6 +5,9 @@ import ( "net" "sync" "time" + "os" + "log" + "fmt" "github.com/c-robinson/iplib" "github.com/rs/xid" @@ -48,19 +51,35 @@ type Network struct { // NewNetwork creates a new Network initializing it with a Serial=0 // It takes a random /16 subnet from 100.64.0.0/10 (64 different subnets) func NewNetwork() *Network { - - n := iplib.NewNet4(net.ParseIP("100.64.0.0"), NetSize) - sub, _ := n.Subnet(SubnetSize) - - s := rand.NewSource(time.Now().Unix()) - r := rand.New(s) - intn := r.Intn(len(sub)) - - return &Network{ - Identifier: xid.New().String(), - Net: sub[intn].IPNet, - Dns: "", - Serial: 0} + // Check if the NETBIRD_SUBNET environment variable is set + customSubnet := os.Getenv("NETBIRD_SUBNET") + if customSubnet != "" { + if _, subnet, err := net.ParseCIDR(customSubnet); err == nil { + return &Network{ + Identifier: xid.New().String(), + Net: *subnet, + Dns: "", + Serial: 0, + } + } + // Handle error if parsing custom subnet fails + log.Println("Error parsing custom subnet from NETBIRD_SUBNET variable:", err) + } + + // If NETBIRD_SUBNET is not set or parsing fails, choose a random subnet + n := iplib.NewNet4(net.ParseIP("100.64.0.0"), NetSize) + sub, _ := n.Subnet(SubnetSize) + + s := rand.NewSource(time.Now().Unix()) + r := rand.New(s) + intn := r.Intn(len(sub)) + + return &Network{ + Identifier: xid.New().String(), + Net: sub[intn].IPNet, + Dns: "", + Serial: 0, + } } // IncSerial increments Serial by 1 reflecting that the network state has been changed @@ -90,24 +109,34 @@ func (n *Network) Copy() *Network { // This method considers already taken IPs and reuses IPs if there are gaps in takenIps // E.g. if ipNet=100.30.0.0/16 and takenIps=[100.30.0.1, 100.30.0.4] then the result would be 100.30.0.2 or 100.30.0.3 func AllocatePeerIP(ipNet net.IPNet, takenIps []net.IP) (net.IP, error) { - takenIPMap := make(map[string]struct{}) - takenIPMap[ipNet.IP.String()] = struct{}{} - for _, ip := range takenIps { - takenIPMap[ip.String()] = struct{}{} - } - - ips, _ := generateIPs(&ipNet, takenIPMap) - - if len(ips) == 0 { - return nil, status.Errorf(status.PreconditionFailed, "failed allocating new IP for the ipNet %s - network is out of IPs", ipNet.String()) - } - - // pick a random IP - s := rand.NewSource(time.Now().Unix()) - r := rand.New(s) - intn := r.Intn(len(ips)) - - return ips[intn], nil + // Check if NETBIRD_ALLOCATE_SEQUENTIAL_IPS is set to 1 + allocateSequential := os.Getenv("NETBIRD_ALLOCATE_SEQUENTIAL_IPS") == "1" + + takenIPMap := make(map[string]struct{}) + takenIPMap[ipNet.IP.String()] = struct{}{} + for _, ip := range takenIps { + takenIPMap[ip.String()] = struct{}{} + } + + ips, _ := generateIPs(&ipNet, takenIPMap) + + if len(ips) == 0 { + return nil, fmt.Errorf("failed allocating new IP for the ipNet %s - network is out of IPs", ipNet.String()) + } + + // Pick an IP based on the allocation method + var selectedIP net.IP + if allocateSequential { + selectedIP = ips[0] // Allocate the smallest available IP + } else { + // Pick a random IP + s := rand.NewSource(time.Now().Unix()) + r := rand.New(s) + intn := r.Intn(len(ips)) + selectedIP = ips[intn] + } + + return selectedIP, nil } // generateIPs generates a list of all possible IPs of the given network excluding IPs specified in the exclusion list