ZeroTierOne/go/pkg/zerotier/network.go
2019-09-22 17:41:15 -07:00

336 lines
9.2 KiB
Go

/*
* Copyright (c)2019 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2023-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
package zerotier
import (
"encoding/binary"
"encoding/json"
"fmt"
"net"
"sort"
"strconv"
"sync"
)
// NetworkID is a network's 64-bit unique ID
type NetworkID uint64
// NewNetworkIDFromString parses a network ID in string form
func NewNetworkIDFromString(s string) (NetworkID, error) {
if len(s) != 16 {
return NetworkID(0), ErrInvalidNetworkID
}
n, err := strconv.ParseUint(s, 16, 64)
return NetworkID(n), err
}
// NewNetworkIDFromBytes reads an 8-byte / 64-bit network ID.
func NewNetworkIDFromBytes(b []byte) (NetworkID, error) {
if len(b) < 8 {
return NetworkID(0), ErrInvalidNetworkID
}
return NetworkID(binary.BigEndian.Uint64(b)), nil
}
// String returns this network ID's 16-digit hex identifier
func (n NetworkID) String() string {
return fmt.Sprintf("%.16x", uint64(n))
}
// Bytes returns this network ID as an 8-byte / 64-bit big-endian value.
func (n NetworkID) Bytes() []byte {
var b [8]byte
binary.BigEndian.PutUint64(b[:], uint64(n))
return b[:]
}
// MarshalJSON marshals this NetworkID as a string
func (n NetworkID) MarshalJSON() ([]byte, error) {
return []byte("\"" + n.String() + "\""), nil
}
// UnmarshalJSON unmarshals this NetworkID from a string
func (n *NetworkID) UnmarshalJSON(j []byte) error {
var s string
err := json.Unmarshal(j, &s)
if err != nil {
return err
}
tmp, err := NewNetworkIDFromString(s)
*n = tmp
return err
}
// NetworkConfig represents the network's current configuration as distributed by its network controller.
type NetworkConfig struct {
// ID is this network's 64-bit globally unique identifier
ID NetworkID
// MAC is the Ethernet MAC address of this device on this network
MAC MAC
// Name is a short human-readable name set by the controller
Name string
// Status is a status code indicating this network's authorization status
Status int
// Type is this network's type
Type int
// MTU is the Ethernet MTU for this network
MTU int
// Bridge is true if this network is allowed to bridge in other devices with different Ethernet addresses
Bridge bool
// BroadcastEnabled is true if the broadcast (ff:ff:ff:ff:ff:ff) address works (excluding IPv4 ARP which is handled via a special path)
BroadcastEnabled bool
// NetconfRevision is the revision number reported by the controller
NetconfRevision uint64
// AssignedAddresses are static IPs assigned by the network controller to this device
AssignedAddresses []net.IPNet
// Routes are static routes assigned by the network controller to this device
Routes []Route
}
// NetworkLocalSettings is settings for this network that can be changed locally
type NetworkLocalSettings struct {
// AllowManagedIPs determines whether managed IP assignment is allowed
AllowManagedIPs bool
// AllowGlobalIPs determines if managed IPs that overlap with public Internet addresses are allowed
AllowGlobalIPs bool
// AllowManagedRoutes determines whether managed routes can be set
AllowManagedRoutes bool
// AllowGlobalRoutes determines if managed routes can overlap with public Internet addresses
AllowGlobalRoutes bool
// AllowDefaultRouteOverride determines if the default (0.0.0.0 or ::0) route on the system can be overridden ("full tunnel" mode)
AllowDefaultRouteOverride bool
}
// Network is a currently joined network
type Network struct {
node *Node
id NetworkID
tap Tap
config NetworkConfig
settings NetworkLocalSettings // locked by configLock
multicastSubscriptions map[[2]uint64]*MulticastGroup
configLock sync.RWMutex
multicastSubscriptionsLock sync.RWMutex
}
// newNetwork creates a new network with default settings
func newNetwork(node *Node, id NetworkID, t Tap) (*Network, error) {
n := &Network{
node: node,
id: id,
tap: t,
config: NetworkConfig{
ID: id,
Status: NetworkStatusRequestConfiguration,
},
settings: NetworkLocalSettings{
AllowManagedIPs: true,
AllowGlobalIPs: false,
AllowManagedRoutes: true,
AllowGlobalRoutes: false,
AllowDefaultRouteOverride: false,
},
multicastSubscriptions: make(map[[2]uint64]*MulticastGroup),
}
t.AddMulticastGroupChangeHandler(func(added bool, mg *MulticastGroup) {
if added {
n.MulticastSubscribe(mg)
} else {
n.MulticastUnsubscribe(mg)
}
})
return n, nil
}
// ID gets this network's unique ID
func (n *Network) ID() NetworkID { return n.id }
// Config returns a copy of this network's current configuration
func (n *Network) Config() NetworkConfig {
n.configLock.RLock()
defer n.configLock.RUnlock()
return n.config
}
// Tap gets this network's tap device
func (n *Network) Tap() Tap { return n.tap }
// SetLocalSettings modifies this network's local settings
func (n *Network) SetLocalSettings(ls *NetworkLocalSettings) { n.updateConfig(nil, ls) }
// MulticastSubscribe subscribes to a multicast group
func (n *Network) MulticastSubscribe(mg *MulticastGroup) {
k := mg.key()
n.multicastSubscriptionsLock.Lock()
if _, have := n.multicastSubscriptions[k]; have {
n.multicastSubscriptionsLock.Unlock()
return
}
n.multicastSubscriptions[k] = mg
n.multicastSubscriptionsLock.Unlock()
n.node.multicastSubscribe(uint64(n.id), mg)
}
// MulticastUnsubscribe removes a subscription to a multicast group
func (n *Network) MulticastUnsubscribe(mg *MulticastGroup) {
n.multicastSubscriptionsLock.Lock()
delete(n.multicastSubscriptions, mg.key())
n.multicastSubscriptionsLock.Unlock()
n.node.multicastUnsubscribe(uint64(n.id), mg)
}
// MulticastSubscriptions returns an array of all multicast subscriptions for this network
func (n *Network) MulticastSubscriptions() []*MulticastGroup {
n.multicastSubscriptionsLock.RLock()
mgs := make([]*MulticastGroup, 0, len(n.multicastSubscriptions))
for _, mg := range n.multicastSubscriptions {
mgs = append(mgs, mg)
}
n.multicastSubscriptionsLock.RUnlock()
sort.Slice(mgs, func(a, b int) bool { return mgs[a].Less(mgs[b]) })
return mgs
}
func (n *Network) networkConfigRevision() uint64 {
n.configLock.RLock()
defer n.configLock.RUnlock()
return n.config.NetconfRevision
}
func networkManagedIPAllowed(ip net.IP, ls *NetworkLocalSettings) bool {
if !ls.AllowManagedIPs {
return false
}
switch ipClassify(ip) {
case ipClassificationNone, ipClassificationLoopback, ipClassificationLinkLocal, ipClassificationMulticast:
return false
case ipClassificationGlobal:
return ls.AllowGlobalIPs
}
return true
}
func networkManagedRouteAllowed(r *Route, ls *NetworkLocalSettings) bool {
if !ls.AllowManagedRoutes {
return false
}
bits, _ := r.Target.Mask.Size()
if len(r.Target.IP) > 0 && allZero(r.Target.IP) && bits == 0 {
return ls.AllowDefaultRouteOverride
}
switch ipClassify(r.Target.IP) {
case ipClassificationNone, ipClassificationLoopback, ipClassificationLinkLocal, ipClassificationMulticast:
return false
case ipClassificationGlobal:
return ls.AllowGlobalRoutes
}
return true
}
func (n *Network) updateConfig(nc *NetworkConfig, ls *NetworkLocalSettings) {
n.configLock.Lock()
defer n.configLock.Unlock()
if n.tap == nil { // sanity check, should never happen
return
}
if nc == nil {
nc = &n.config
}
if ls == nil {
ls = &n.settings
}
// Add IPs to tap that are newly assigned in this config update,
// and remove any IPs from the tap that were assigned that are no
// longer wanted. IPs assigned to the tap externally (e.g. by an
// "ifconfig" command) are left alone.
haveAssignedIPs := make(map[[3]uint64]*net.IPNet)
wantAssignedIPs := make(map[[3]uint64]bool)
if n.settings.AllowManagedIPs {
for _, ip := range n.config.AssignedAddresses {
if networkManagedIPAllowed(ip.IP, &n.settings) { // was it allowed?
haveAssignedIPs[ipNetToKey(&ip)] = &ip
}
}
}
if ls.AllowManagedIPs {
for _, ip := range nc.AssignedAddresses {
if networkManagedIPAllowed(ip.IP, ls) { // should it be allowed now?
k := ipNetToKey(&ip)
wantAssignedIPs[k] = true
if _, have := haveAssignedIPs[k]; !have {
n.tap.AddIP(&ip)
}
}
}
}
for k, ip := range haveAssignedIPs {
if _, want := wantAssignedIPs[k]; !want {
n.tap.RemoveIP(ip)
}
}
// Do the same for managed routes
haveManagedRoutes := make(map[[6]uint64]*Route)
wantManagedRoutes := make(map[[6]uint64]bool)
if n.settings.AllowManagedRoutes {
for _, r := range n.config.Routes {
if networkManagedRouteAllowed(&r, &n.settings) { // was it allowed?
haveManagedRoutes[r.key()] = &r
}
}
}
if ls.AllowManagedRoutes {
for _, r := range nc.Routes {
if networkManagedRouteAllowed(&r, ls) { // should it be allowed now?
k := r.key()
wantManagedRoutes[k] = true
if _, have := haveManagedRoutes[k]; !have {
n.tap.AddRoute(&r)
}
}
}
}
for k, r := range haveManagedRoutes {
if _, want := wantManagedRoutes[k]; !want {
n.tap.RemoveRoute(r)
}
}
if nc != &n.config {
n.config = *nc
}
if ls != &n.settings {
n.settings = *ls
}
}