From e8272926a422ce4deaae20f8fdf4ff8b4154740c Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 6 Jul 2019 15:08:17 +0100 Subject: [PATCH 1/3] Fix TAP mode --- src/tuntap/ckr.go | 3 ++ src/tuntap/icmpv6.go | 94 +++++++++++++++++++++++++++++++++----------- src/tuntap/iface.go | 25 ++++++------ src/tuntap/tun.go | 19 ++------- 4 files changed, 88 insertions(+), 53 deletions(-) diff --git a/src/tuntap/ckr.go b/src/tuntap/ckr.go index c9233e6..0032039 100644 --- a/src/tuntap/ckr.go +++ b/src/tuntap/ckr.go @@ -48,8 +48,11 @@ func (c *cryptokey) init(tun *TunAdapter) { } }() + c.tun.log.Debugln("Configuring CKR...") if err := c.configure(); err != nil { c.tun.log.Errorln("CKR configuration failed:", err) + } else { + c.tun.log.Debugln("CKR configured") } } diff --git a/src/tuntap/icmpv6.go b/src/tuntap/icmpv6.go index 8159e0f..ea1a785 100644 --- a/src/tuntap/icmpv6.go +++ b/src/tuntap/icmpv6.go @@ -13,6 +13,7 @@ import ( "encoding/binary" "errors" "net" + "sync" "time" "golang.org/x/net/icmp" @@ -21,19 +22,18 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" ) -type macAddress [6]byte - const len_ETHER = 14 type ICMPv6 struct { - tun *TunAdapter - mylladdr net.IP - mymac macAddress - peermacs map[address.Address]neighbor + tun *TunAdapter + mylladdr net.IP + mymac net.HardwareAddr + peermacs map[address.Address]neighbor + peermacsmutex sync.RWMutex } type neighbor struct { - mac macAddress + mac net.HardwareAddr learned bool lastadvertisement time.Time lastsolicitation time.Time @@ -61,10 +61,12 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) { // addresses. func (i *ICMPv6) Init(t *TunAdapter) { i.tun = t + i.peermacsmutex.Lock() i.peermacs = make(map[address.Address]neighbor) + i.peermacsmutex.Unlock() // Our MAC address and link-local address - i.mymac = macAddress{ + i.mymac = net.HardwareAddr{ 0x02, 0x00, 0x00, 0x00, 0x00, 0x02} i.mylladdr = net.IP{ 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -181,16 +183,30 @@ func (i *ICMPv6) UnmarshalPacket(datain []byte, datamac *[]byte) ([]byte, error) if datamac != nil { var addr address.Address var target address.Address - var mac macAddress + mac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} copy(addr[:], ipv6Header.Src[:]) copy(target[:], datain[48:64]) copy(mac[:], (*datamac)[:]) - // fmt.Printf("Learning peer MAC %x for %x\n", mac, target) + i.peermacsmutex.Lock() neighbor := i.peermacs[target] neighbor.mac = mac neighbor.learned = true neighbor.lastadvertisement = time.Now() i.peermacs[target] = neighbor + i.peermacsmutex.Unlock() + i.tun.log.Debugln("Learned peer MAC", mac.String(), "for", net.IP(target[:]).String()) + /* + i.tun.log.Debugln("Peer MAC table:") + i.peermacsmutex.RLock() + for t, n := range i.peermacs { + if n.learned { + i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "has MAC", n.mac.String()) + } else { + i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "is not learned yet") + } + } + i.peermacsmutex.RUnlock() + */ } return nil, errors.New("No response needed") } @@ -201,7 +217,7 @@ func (i *ICMPv6) UnmarshalPacket(datain []byte, datamac *[]byte) ([]byte, error) // Creates an ICMPv6 packet based on the given icmp.MessageBody and other // parameters, complete with ethernet and IP headers, which can be written // directly to a TAP adapter. -func (i *ICMPv6) CreateICMPv6L2(dstmac macAddress, dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { +func (i *ICMPv6) CreateICMPv6L2(dstmac net.HardwareAddr, dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { // Pass through to CreateICMPv6 ipv6packet, err := CreateICMPv6(dst, src, mtype, mcode, mbody) if err != nil { @@ -264,13 +280,46 @@ func CreateICMPv6(dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody return responsePacket, nil } -func (i *ICMPv6) CreateNDPL2(dst address.Address) ([]byte, error) { +func (i *ICMPv6) Solicit(addr address.Address) { + retries := 5 + for retries > 0 { + retries-- + i.peermacsmutex.RLock() + if n, ok := i.peermacs[addr]; ok && n.learned { + i.tun.log.Debugln("MAC learned for", net.IP(addr[:]).String()) + i.peermacsmutex.RUnlock() + return + } + i.peermacsmutex.RUnlock() + i.tun.log.Debugln("Sending neighbor solicitation for", net.IP(addr[:]).String()) + i.peermacsmutex.Lock() + if n, ok := i.peermacs[addr]; !ok { + i.peermacs[addr] = neighbor{ + lastsolicitation: time.Now(), + } + } else { + n.lastsolicitation = time.Now() + } + i.peermacsmutex.Unlock() + request, err := i.createNDPL2(addr) + if err != nil { + panic(err) + } + if _, err := i.tun.iface.Write(request); err != nil { + panic(err) + } + i.tun.log.Debugln("Sent neighbor solicitation for", net.IP(addr[:]).String()) + time.Sleep(time.Second) + } +} + +func (i *ICMPv6) createNDPL2(dst address.Address) ([]byte, error) { // Create the ND payload var payload [28]byte - copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00}) - copy(payload[4:20], dst[:]) - copy(payload[20:22], []byte{0x01, 0x01}) - copy(payload[22:28], i.mymac[:6]) + copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00}) // Flags + copy(payload[4:20], dst[:]) // Destination + copy(payload[20:22], []byte{0x01, 0x01}) // Type & length + copy(payload[22:28], i.mymac[:6]) // Link layer address // Create the ICMPv6 solicited-node address var dstaddr address.Address @@ -281,7 +330,7 @@ func (i *ICMPv6) CreateNDPL2(dst address.Address) ([]byte, error) { copy(dstaddr[13:], dst[13:16]) // Create the multicast MAC - var dstmac macAddress + dstmac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} copy(dstmac[:2], []byte{0x33, 0x33}) copy(dstmac[2:6], dstaddr[12:16]) @@ -293,9 +342,6 @@ func (i *ICMPv6) CreateNDPL2(dst address.Address) ([]byte, error) { if err != nil { return nil, err } - neighbor := i.peermacs[dstaddr] - neighbor.lastsolicitation = time.Now() - i.peermacs[dstaddr] = neighbor return requestPacket, nil } @@ -319,10 +365,10 @@ func (i *ICMPv6) HandleNDP(in []byte) ([]byte, error) { // Create our NDP message body response body := make([]byte, 28) - binary.BigEndian.PutUint32(body[:4], uint32(0x20000000)) - copy(body[4:20], in[8:24]) // Target address - body[20] = uint8(2) - body[21] = uint8(1) + binary.BigEndian.PutUint32(body[:4], uint32(0x40000000)) // Flags + copy(body[4:20], in[8:24]) // Target address + body[20] = uint8(2) // Type: Target link-layer address + body[21] = uint8(1) // Length: 1x address (8 bytes) copy(body[22:28], i.mymac[:6]) // Send it back diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 60c814c..be3988a 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -3,6 +3,7 @@ package tuntap import ( "bytes" "errors" + "net" "time" "github.com/songgao/packets/ethernet" @@ -43,19 +44,10 @@ func (tun *TunAdapter) writer() error { neigh, known := tun.icmpv6.peermacs[dstAddr] known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30) if !known { - request, err := tun.icmpv6.CreateNDPL2(dstAddr) - if err != nil { - panic(err) - } - if _, err := tun.iface.Write(request); err != nil { - panic(err) - } - tun.icmpv6.peermacs[dstAddr] = neighbor{ - lastsolicitation: time.Now(), - } + tun.icmpv6.Solicit(dstAddr) } } - var peermac macAddress + peermac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} var peerknown bool if b[0]&0xf0 == 0x40 { dstAddr = tun.addr @@ -65,14 +57,19 @@ func (tun *TunAdapter) writer() error { } } if neighbor, ok := tun.icmpv6.peermacs[dstAddr]; ok && neighbor.learned { + // If we've learned the MAC of a 300::/7 address, for example, or a CKR + // address, use the MAC address of that peermac = neighbor.mac peerknown = true } else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned { + // Otherwise send directly to the MAC address of the host if that's + // known instead peermac = neighbor.mac peerknown = true - sendndp(dstAddr) } else { + // Nothing has been discovered, try to discover the destination sendndp(tun.addr) + } if peerknown { var proto ethernet.Ethertype @@ -92,6 +89,8 @@ func (tun *TunAdapter) writer() error { copy(frame[tun_ETHER_HEADER_LENGTH:], b[:n]) n += tun_ETHER_HEADER_LENGTH w, err = tun.iface.Write(frame[:n]) + } else { + tun.log.Errorln("TUN/TAP iface write error: no peer MAC known for", net.IP(dstAddr[:]).String(), "- dropping packet") } } else { w, err = tun.iface.Write(b[:n]) @@ -184,7 +183,7 @@ func (tun *TunAdapter) reader() error { // Unknown address length or protocol, so drop the packet and ignore it continue } - if !tun.ckr.isValidSource(srcAddr, addrlen) { + if tun.ckr.isEnabled() && !tun.ckr.isValidSource(srcAddr, addrlen) { // The packet had a source address that doesn't belong to us or our // configured crypto-key routing source subnets continue diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index b7b4cfa..15530d2 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -14,7 +14,6 @@ import ( "fmt" "net" "sync" - "time" "github.com/gologme/log" "github.com/yggdrasil-network/water" @@ -152,21 +151,6 @@ func (tun *TunAdapter) Start() error { tun.send = make(chan []byte, 32) // TODO: is this a sensible value? tun.reconfigure = make(chan chan error) tun.mutex.Unlock() - if iftapmode { - go func() { - for { - if _, ok := tun.icmpv6.peermacs[tun.addr]; ok { - break - } - request, err := tun.icmpv6.CreateNDPL2(tun.addr) - if err != nil { - panic(err) - } - tun.send <- request - time.Sleep(time.Second) - } - }() - } go func() { for { e := <-tun.reconfigure @@ -177,6 +161,9 @@ func (tun *TunAdapter) Start() error { go tun.reader() go tun.writer() tun.icmpv6.Init(tun) + if iftapmode { + go tun.icmpv6.Solicit(tun.addr) + } tun.ckr.init(tun) return nil } From a10c141896d1fa292e0c74f7e6fd4df3a0eb7e93 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 6 Jul 2019 15:15:43 +0100 Subject: [PATCH 2/3] Fix data race on peermacs --- src/tuntap/icmpv6.go | 8 ++++++++ src/tuntap/iface.go | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tuntap/icmpv6.go b/src/tuntap/icmpv6.go index ea1a785..fe80dfb 100644 --- a/src/tuntap/icmpv6.go +++ b/src/tuntap/icmpv6.go @@ -313,6 +313,14 @@ func (i *ICMPv6) Solicit(addr address.Address) { } } +func (i *ICMPv6) getNeighbor(addr address.Address) (neighbor, bool) { + i.peermacsmutex.RLock() + defer i.peermacsmutex.RUnlock() + + n, ok := i.peermacs[addr] + return n, ok +} + func (i *ICMPv6) createNDPL2(dst address.Address) ([]byte, error) { // Create the ND payload var payload [28]byte diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index be3988a..9ffde85 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -41,7 +41,7 @@ func (tun *TunAdapter) writer() error { return errors.New("Invalid address family") } sendndp := func(dstAddr address.Address) { - neigh, known := tun.icmpv6.peermacs[dstAddr] + neigh, known := tun.icmpv6.getNeighbor(dstAddr) known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30) if !known { tun.icmpv6.Solicit(dstAddr) @@ -56,12 +56,12 @@ func (tun *TunAdapter) writer() error { dstAddr = tun.addr } } - if neighbor, ok := tun.icmpv6.peermacs[dstAddr]; ok && neighbor.learned { + if neighbor, ok := tun.icmpv6.getNeighbor(dstAddr); ok && neighbor.learned { // If we've learned the MAC of a 300::/7 address, for example, or a CKR // address, use the MAC address of that peermac = neighbor.mac peerknown = true - } else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned { + } else if neighbor, ok := tun.icmpv6.getNeighbor(tun.addr); ok && neighbor.learned { // Otherwise send directly to the MAC address of the host if that's // known instead peermac = neighbor.mac From 30c03369cdd34c9064df3050cc0e68e6bf6f3892 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sat, 6 Jul 2019 20:08:32 +0100 Subject: [PATCH 3/3] Try to fix CKR setup deadlock, fix some Windows output formatting --- src/tuntap/ckr.go | 13 ++++++------- src/tuntap/tun.go | 11 +++++------ src/tuntap/tun_windows.go | 16 ++++++++-------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/tuntap/ckr.go b/src/tuntap/ckr.go index 0032039..52c1159 100644 --- a/src/tuntap/ckr.go +++ b/src/tuntap/ckr.go @@ -59,11 +59,10 @@ func (c *cryptokey) init(tun *TunAdapter) { // Configure the CKR routes - this must only ever be called from the router // goroutine, e.g. through router.doAdmin func (c *cryptokey) configure() error { - c.tun.config.Mutex.RLock() - defer c.tun.config.Mutex.RUnlock() + current, _ := c.tun.config.Get() // Set enabled/disabled state - c.setEnabled(c.tun.config.Current.TunnelRouting.Enable) + c.setEnabled(current.TunnelRouting.Enable) // Clear out existing routes c.mutexroutes.Lock() @@ -72,14 +71,14 @@ func (c *cryptokey) configure() error { c.mutexroutes.Unlock() // Add IPv6 routes - for ipv6, pubkey := range c.tun.config.Current.TunnelRouting.IPv6Destinations { + for ipv6, pubkey := range current.TunnelRouting.IPv6Destinations { if err := c.addRoute(ipv6, pubkey); err != nil { return err } } // Add IPv4 routes - for ipv4, pubkey := range c.tun.config.Current.TunnelRouting.IPv4Destinations { + for ipv4, pubkey := range current.TunnelRouting.IPv4Destinations { if err := c.addRoute(ipv4, pubkey); err != nil { return err } @@ -93,7 +92,7 @@ func (c *cryptokey) configure() error { // Add IPv6 sources c.ipv6sources = make([]net.IPNet, 0) - for _, source := range c.tun.config.Current.TunnelRouting.IPv6Sources { + for _, source := range current.TunnelRouting.IPv6Sources { if err := c.addSourceSubnet(source); err != nil { return err } @@ -101,7 +100,7 @@ func (c *cryptokey) configure() error { // Add IPv4 sources c.ipv4sources = make([]net.IPNet, 0) - for _, source := range c.tun.config.Current.TunnelRouting.IPv4Sources { + for _, source := range current.TunnelRouting.IPv4Sources { if err := c.addSourceSubnet(source); err != nil { return err } diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 15530d2..cc12497 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -119,13 +119,12 @@ func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, listener // Start the setup process for the TUN/TAP adapter. If successful, starts the // read/write goroutines to handle packets on that interface. func (tun *TunAdapter) Start() error { - tun.config.Mutex.RLock() - defer tun.config.Mutex.RUnlock() + current, _ := tun.config.Get() if tun.config == nil || tun.listener == nil || tun.dialer == nil { return errors.New("No configuration available to TUN/TAP") } var boxPub crypto.BoxPubKey - boxPubHex, err := hex.DecodeString(tun.config.Current.EncryptionPublicKey) + boxPubHex, err := hex.DecodeString(current.EncryptionPublicKey) if err != nil { return err } @@ -133,9 +132,9 @@ func (tun *TunAdapter) Start() error { nodeID := crypto.GetNodeID(&boxPub) tun.addr = *address.AddrForNodeID(nodeID) tun.subnet = *address.SubnetForNodeID(nodeID) - tun.mtu = tun.config.Current.IfMTU - ifname := tun.config.Current.IfName - iftapmode := tun.config.Current.IfTAPMode + tun.mtu = current.IfMTU + ifname := current.IfName + iftapmode := current.IfTAPMode addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1) if ifname != "none" { if err := tun.setup(ifname, iftapmode, addr, tun.mtu); err != nil { diff --git a/src/tuntap/tun_windows.go b/src/tuntap/tun_windows.go index 8a66ac6..002c354 100644 --- a/src/tuntap/tun_windows.go +++ b/src/tuntap/tun_windows.go @@ -31,18 +31,18 @@ func (tun *TunAdapter) setup(ifname string, iftapmode bool, addr string, mtu int } // Disable/enable the interface to resets its configuration (invalidating iface) cmd := exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=DISABLED") - tun.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Errorln("Windows netsh failed:", err) tun.log.Traceln(string(output)) return err } cmd = exec.Command("netsh", "interface", "set", "interface", iface.Name(), "admin=ENABLED") - tun.log.Printf("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) output, err = cmd.CombinedOutput() if err != nil { - tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Errorln("Windows netsh failed:", err) tun.log.Traceln(string(output)) return err } @@ -71,10 +71,10 @@ func (tun *TunAdapter) setupMTU(mtu int) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("mtu=%d", mtu), "store=active") - tun.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Errorln("Windows netsh failed:", err) tun.log.Traceln(string(output)) return err } @@ -88,10 +88,10 @@ func (tun *TunAdapter) setupAddress(addr string) error { fmt.Sprintf("interface=%s", tun.iface.Name()), fmt.Sprintf("addr=%s", addr), "store=active") - tun.log.Debugln("netsh command: %v", strings.Join(cmd.Args, " ")) + tun.log.Debugln("netsh command:", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - tun.log.Errorf("Windows netsh failed: %v.", err) + tun.log.Errorln("Windows netsh failed:", err) tun.log.Traceln(string(output)) return err }