mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-25 17:43:38 +01:00
149 lines
3.7 KiB
Go
149 lines
3.7 KiB
Go
package server
|
|
|
|
import (
|
|
"math/rand"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/c-robinson/iplib"
|
|
"github.com/rs/xid"
|
|
|
|
nbdns "github.com/netbirdio/netbird/dns"
|
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
|
"github.com/netbirdio/netbird/management/server/status"
|
|
"github.com/netbirdio/netbird/route"
|
|
)
|
|
|
|
const (
|
|
// SubnetSize is a size of the subnet of the global network, e.g. 100.77.0.0/16
|
|
SubnetSize = 16
|
|
// NetSize is a global network size 100.64.0.0/10
|
|
NetSize = 10
|
|
|
|
// AllowedIPsFormat generates Wireguard AllowedIPs format (e.g. 100.64.30.1/32)
|
|
AllowedIPsFormat = "%s/32"
|
|
)
|
|
|
|
type NetworkMap struct {
|
|
Peers []*nbpeer.Peer
|
|
Network *Network
|
|
Routes []*route.Route
|
|
DNSConfig nbdns.Config
|
|
OfflinePeers []*nbpeer.Peer
|
|
FirewallRules []*FirewallRule
|
|
}
|
|
|
|
type Network struct {
|
|
Identifier string `json:"id"`
|
|
Net net.IPNet `gorm:"serializer:json"`
|
|
Dns string
|
|
// Serial is an ID that increments by 1 when any change to the network happened (e.g. new peer has been added).
|
|
// Used to synchronize state to the client apps.
|
|
Serial uint64
|
|
|
|
mu sync.Mutex `json:"-" gorm:"-"`
|
|
}
|
|
|
|
// 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}
|
|
}
|
|
|
|
// IncSerial increments Serial by 1 reflecting that the network state has been changed
|
|
func (n *Network) IncSerial() {
|
|
n.mu.Lock()
|
|
defer n.mu.Unlock()
|
|
n.Serial++
|
|
}
|
|
|
|
// CurrentSerial returns the Network.Serial of the network (latest state id)
|
|
func (n *Network) CurrentSerial() uint64 {
|
|
n.mu.Lock()
|
|
defer n.mu.Unlock()
|
|
return n.Serial
|
|
}
|
|
|
|
func (n *Network) Copy() *Network {
|
|
return &Network{
|
|
Identifier: n.Identifier,
|
|
Net: n.Net,
|
|
Dns: n.Dns,
|
|
Serial: n.Serial,
|
|
}
|
|
}
|
|
|
|
// AllocatePeerIP pics an available IP from an net.IPNet.
|
|
// 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
|
|
}
|
|
|
|
// generateIPs generates a list of all possible IPs of the given network excluding IPs specified in the exclusion list
|
|
func generateIPs(ipNet *net.IPNet, exclusions map[string]struct{}) ([]net.IP, int) {
|
|
|
|
var ips []net.IP
|
|
for ip := ipNet.IP.Mask(ipNet.Mask); ipNet.Contains(ip); incIP(ip) {
|
|
if _, ok := exclusions[ip.String()]; !ok && ip[3] != 0 {
|
|
ips = append(ips, copyIP(ip))
|
|
}
|
|
}
|
|
|
|
// remove network address, broadcast and Fake DNS resolver address
|
|
lenIPs := len(ips)
|
|
switch {
|
|
case lenIPs < 2:
|
|
return ips, lenIPs
|
|
case lenIPs < 3:
|
|
return ips[1 : len(ips)-1], lenIPs - 2
|
|
default:
|
|
return ips[1 : len(ips)-2], lenIPs - 3
|
|
}
|
|
}
|
|
|
|
func copyIP(ip net.IP) net.IP {
|
|
dup := make(net.IP, len(ip))
|
|
copy(dup, ip)
|
|
return dup
|
|
}
|
|
|
|
func incIP(ip net.IP) {
|
|
for j := len(ip) - 1; j >= 0; j-- {
|
|
ip[j]++
|
|
if ip[j] > 0 {
|
|
break
|
|
}
|
|
}
|
|
}
|