getting there...

This commit is contained in:
Adam Ierymenko 2019-09-21 18:22:25 -07:00
parent 5e35346f17
commit 2eef9d22e6
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
11 changed files with 278 additions and 45 deletions

View file

@ -661,3 +661,52 @@ extern "C" void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu)
{
reinterpret_cast<EthernetTap *>(tap)->setMtu(mtu);
}
extern "C" int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric)
{
InetAddress target,via;
switch(targetAf) {
case AF_INET:
target.set(targetIp,4,(unsigned int)targetNetmaskBits);
break;
case AF_INET6:
target.set(targetIp,16,(unsigned int)targetNetmaskBits);
break;
}
switch(viaAf) {
case AF_INET:
via.set(viaIp,4,0);
break;
case AF_INET6:
via.set(viaIp,16,0);
break;
}
return reinterpret_cast<EthernetTap *>(tap)->addRoute(target,via,metric);
}
extern "C" int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric)
{
InetAddress target,via;
switch(targetAf) {
case AF_INET:
target.set(targetIp,4,(unsigned int)targetNetmaskBits);
break;
case AF_INET6:
target.set(targetIp,16,(unsigned int)targetNetmaskBits);
break;
}
switch(viaAf) {
case AF_INET:
via.set(viaIp,4,0);
break;
case AF_INET6:
via.set(viaIp,16,0);
break;
}
return reinterpret_cast<EthernetTap *>(tap)->removeRoute(target,via,metric);
}
extern "C" int ZT_GoTap_syncRoutes(ZT_GoTap *tap)
{
return reinterpret_cast<EthernetTap *>(tap)->syncRoutes();
}

View file

@ -91,6 +91,12 @@ void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName);
void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu);
int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric);
int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric);
int ZT_GoTap_syncRoutes(ZT_GoTap *tap);
/****************************************************************************/
#ifdef __cplusplus

View file

@ -15,10 +15,22 @@ package zerotier
import (
"encoding/base32"
"net"
"time"
"unsafe"
)
var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
// TimeMs returns the time in milliseconds since epoch.
func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) }
// ipNetToKey creates a key that can be used in a map[] from a net.IPNet
func ipNetToKey(ipn *net.IPNet) (k [3]uint64) {
if len(ipn.IP) > 0 {
copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], ipn.IP)
}
ones, bits := ipn.Mask.Size()
k[2] = (uint64(ones) << 32) | uint64(bits)
return
}

View file

@ -116,3 +116,36 @@ func (n *Network) Tap() Tap {
defer n.tapLock.RUnlock()
return n.tap
}
func (n *Network) handleNetworkConfigUpdate(nc *NetworkConfig) {
n.tapLock.RLock()
n.configLock.Lock()
defer n.configLock.Unlock()
defer n.tapLock.RUnlock()
if n.tap == nil { // sanity check
return
}
// 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)
for _, ip := range n.config.AssignedAddresses {
haveAssignedIPs[ipNetToKey(&ip)] = &ip
}
wantAssignedIPs := make(map[[3]uint64]bool)
for _, ip := range nc.AssignedAddresses {
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)
}
}
}

View file

@ -20,6 +20,7 @@ package zerotier
import "C"
import (
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
@ -28,7 +29,6 @@ import (
"path"
"sync"
"sync/atomic"
"time"
"unsafe"
acl "github.com/hectane/go-acl"
@ -139,10 +139,8 @@ func (n *Node) Join(nwid uint64, tap Tap) (*Network, error) {
nw := &Network{
id: NetworkID(nwid),
config: NetworkConfig{
ID: NetworkID(nwid),
Status: NetworkStatusRequestConfiguration,
LastUpdated: time.Now(),
Enabled: true,
ID: NetworkID(nwid),
Status: NetworkStatusRequestConfiguration,
},
tap: &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1},
}
@ -347,20 +345,74 @@ func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSR
}()
}
func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet {
var a net.IPNet
switch ss.ss_family {
case afInet:
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
var ip4 [4]byte
copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
a.IP = net.IP(ip4[:])
a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:])), 32)
return &a
case afInet6:
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
var ip6 [16]byte
copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
a.IP = net.IP(ip6[:])
a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:])), 128)
return &a
}
return nil
}
//export goVirtualNetworkConfigFunc
func goVirtualNetworkConfigFunc(gn, tapP unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) {
nodesByUserPtrLock.RLock()
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
if node == nil {
return 255
return
}
node.networksLock.RLock()
network := node.networks[uint64(nwid)]
node.networksLock.RUnlock()
if network != nil {
ncc := (*C.ZT_VirtualNetworkConfig)(conf)
var nc NetworkConfig
nc.ID = uint64(ncc.nwid)
nc.MAC = MAC(ncc.mac)
nc.Name = C.GoString(ncc.name)
nc.Status = int(ncc.status)
nc.Type = int(ncc._type)
nc.MTU = int(ncc.mtu)
nc.Bridge = (ncc.bridge != 0)
nc.BroadcastEnabled = (ncc.broadcastEnabled != 0)
nc.NetconfRevision = uint64(ncc.netconfRevision)
for i := 0; i < int(ncc.assignedAddressCount); i++ {
a := sockaddrStorageToIPNet(&ncc.assignedAddresses[i])
if a != nil {
nc.AssignedAddresses = append(nc.AssignedAddresses, *a)
}
}
for i := 0; i < int(ncc.routeCount); i++ {
tgt := sockaddrStorageToIPNet(&ncc.routes[i].target)
viaN := sockaddrStorageToIPNet(&ncc.routes[i].via)
var via net.IP
if viaN != nil {
via = viaN.IP
}
if tgt != nil {
nc.Routes = append(nc.Routes, Route{
Target: *tgt,
Via: via,
Flags: uint16(ncc.routes[i].flags),
Metric: uint16(ncc.routes[i].metric),
})
}
}
network.handleNetworkConfigUpdate(&nc)
}
//return C.int(node.handleNetworkConfigUpdate(uint64(nwid), int(op), (*C.ZT_VirtualNetworkConfig)(conf)))
}
//export goZtEvent
@ -461,7 +513,7 @@ func (t *nativeTap) Enabled() bool {
}
// AddIP adds an IP address (with netmask) to this tap
func (t *nativeTap) AddIP(ip net.IPNet) error {
func (t *nativeTap) AddIP(ip *net.IPNet) error {
bits, _ := ip.Mask.Size()
if len(ip.IP) == 16 {
if bits > 128 || bits < 0 {
@ -478,7 +530,7 @@ func (t *nativeTap) AddIP(ip net.IPNet) error {
}
// RemoveIP removes this IP address (with netmask) from this tap
func (t *nativeTap) RemoveIP(ip net.IPNet) error {
func (t *nativeTap) RemoveIP(ip *net.IPNet) error {
bits, _ := ip.Mask.Size()
if len(ip.IP) == 16 {
if bits > 128 || bits < 0 {
@ -553,3 +605,61 @@ func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *Multicast
t.multicastGroupHandlers = append(t.multicastGroupHandlers, handler)
t.multicastGroupHandlersLock.Unlock()
}
// AddRoute adds or updates a managed route on this tap's interface
func (t *nativeTap) AddRoute(r *Route) error {
rc := 0
if r != nil {
if len(r.Target.IP) == 4 {
mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 {
rc = int(C.ZT_GoTap_addRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
} else {
rc = int(C.ZT_GoTap_addRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
}
} else if len(r.Target.IP) == 16 {
mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 {
rc = int(C.ZT_GoTap_addRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet6, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
} else {
rc = int(C.ZT_GoTap_addRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
}
}
}
if rc != 0 {
return fmt.Errorf("tap device error adding route: %d", rc)
}
return nil
}
// RemoveRoute removes a managed route on this tap's interface
func (t *nativeTap) RemoveRoute(r *Route) error {
rc := 0
if r != nil {
if len(r.Target.IP) == 4 {
mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 {
rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
} else {
rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
}
} else if len(r.Target.IP) == 16 {
mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 {
rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet6, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
} else {
rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
}
}
}
if rc != 0 {
return fmt.Errorf("tap device error removing route: %d", rc)
}
return nil
}
// SyncRoutes synchronizes managed routes
func (t *nativeTap) SyncRoutes() error {
C.ZT_GoTap_syncRoutes(t.tap)
return nil
}

View file

@ -23,6 +23,9 @@ type Route struct {
// Via is how to reach this target (null/empty if the target IP range is local to this virtual LAN)
Via net.IP
// Route flags (currently unused, always 0)
Flags uint16
// Metric is an interface metric that can affect route priority (behavior can be OS-specific)
Metric int
Metric uint16
}

View file

@ -21,9 +21,12 @@ type Tap interface {
Error() (int, string)
SetEnabled(enabled bool)
Enabled() bool
AddIP(ip net.IPNet) error
RemoveIP(ip net.IPNet) error
AddIP(ip *net.IPNet) error
RemoveIP(ip *net.IPNet) error
IPs() ([]net.IPNet, error)
DeviceName() string
AddMulticastGroupChangeHandler(func(bool, *MulticastGroup))
AddRoute(r *Route) error
RemoveRoute(r *Route) error
SyncRoutes() error
}