diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go new file mode 100644 index 00000000..e59e560e --- /dev/null +++ b/src/tuntap/conn.go @@ -0,0 +1,75 @@ +package tuntap + +import ( + "errors" + + "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" +) + +type tunConn struct { + tun *TunAdapter + conn *yggdrasil.Conn + send chan []byte + stop chan interface{} +} + +func (s *tunConn) close() { + close(s.stop) +} + +func (s *tunConn) reader() error { + select { + case _, ok := <-s.stop: + if !ok { + return errors.New("session was already closed") + } + default: + } + var n int + var err error + read := make(chan bool) + b := make([]byte, 65535) + for { + go func() { + if n, err = s.conn.Read(b); err != nil { + s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn read error:", err) + return + } + read <- true + }() + select { + case <-read: + if n > 0 { + s.tun.send <- b[:n] + } + case <-s.stop: + s.tun.log.Debugln("Stopping conn reader for", s) + return nil + } + } +} + +func (s *tunConn) writer() error { + select { + case _, ok := <-s.stop: + if !ok { + return errors.New("session was already closed") + } + default: + } + for { + select { + case <-s.stop: + s.tun.log.Debugln("Stopping conn writer for", s) + return nil + case b, ok := <-s.send: + if !ok { + return errors.New("send closed") + } + if _, err := s.conn.Write(b); err != nil { + s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err) + continue + } + } + } +} diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go new file mode 100644 index 00000000..d837633b --- /dev/null +++ b/src/tuntap/iface.go @@ -0,0 +1,255 @@ +package tuntap + +import ( + "bytes" + "errors" + "time" + + "github.com/songgao/packets/ethernet" + "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/yggdrasil-network/yggdrasil-go/src/util" +) + +func (tun *TunAdapter) writer() error { + var w int + var err error + for { + b := <-tun.send + n := len(b) + if n == 0 { + continue + } + if tun.iface.IsTAP() { + var dstAddr address.Address + if b[0]&0xf0 == 0x60 { + if len(b) < 40 { + //panic("Tried to send a packet shorter than an IPv6 header...") + util.PutBytes(b) + continue + } + copy(dstAddr[:16], b[24:]) + } else if b[0]&0xf0 == 0x40 { + if len(b) < 20 { + //panic("Tried to send a packet shorter than an IPv4 header...") + util.PutBytes(b) + continue + } + copy(dstAddr[:4], b[16:]) + } else { + return errors.New("Invalid address family") + } + sendndp := func(dstAddr address.Address) { + 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(), + } + } + } + var peermac macAddress + var peerknown bool + if b[0]&0xf0 == 0x40 { + dstAddr = tun.addr + } else if b[0]&0xf0 == 0x60 { + if !bytes.Equal(tun.addr[:16], dstAddr[:16]) && !bytes.Equal(tun.subnet[:8], dstAddr[:8]) { + dstAddr = tun.addr + } + } + if neighbor, ok := tun.icmpv6.peermacs[dstAddr]; ok && neighbor.learned { + peermac = neighbor.mac + peerknown = true + } else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned { + peermac = neighbor.mac + peerknown = true + sendndp(dstAddr) + } else { + sendndp(tun.addr) + } + if peerknown { + var proto ethernet.Ethertype + switch { + case b[0]&0xf0 == 0x60: + proto = ethernet.IPv6 + case b[0]&0xf0 == 0x40: + proto = ethernet.IPv4 + } + var frame ethernet.Frame + frame.Prepare( + peermac[:6], // Destination MAC address + tun.icmpv6.mymac[:6], // Source MAC address + ethernet.NotTagged, // VLAN tagging + proto, // Ethertype + len(b)) // Payload length + copy(frame[tun_ETHER_HEADER_LENGTH:], b[:n]) + n += tun_ETHER_HEADER_LENGTH + w, err = tun.iface.Write(frame[:n]) + } + } else { + w, err = tun.iface.Write(b[:n]) + } + if err != nil { + tun.log.Errorln("TUN/TAP iface write error:", err) + continue + } + if w != n { + tun.log.Errorln("TUN/TAP iface write mismatch:", w, "bytes written vs", n, "bytes given") + continue + } + } +} + +func (tun *TunAdapter) reader() error { + bs := make([]byte, 65535) + for { + // Wait for a packet to be delivered to us through the TUN/TAP adapter + n, err := tun.iface.Read(bs) + if err != nil { + panic(err) + } + if n == 0 { + continue + } + // If it's a TAP adapter, update the buffer slice so that we no longer + // include the ethernet headers + offset := 0 + if tun.iface.IsTAP() { + // Set our offset to beyond the ethernet headers + offset = tun_ETHER_HEADER_LENGTH + // If we detect an ICMP packet then hand it to the ICMPv6 module + if bs[offset+6] == 58 { + // Found an ICMPv6 packet + b := make([]byte, n) + copy(b, bs) + go tun.icmpv6.ParsePacket(b) + } + // Then offset the buffer so that we can now just treat it as an IP + // packet from now on + bs = bs[offset:] + } + // From the IP header, work out what our source and destination addresses + // and node IDs are. We will need these in order to work out where to send + // the packet + var srcAddr address.Address + var dstAddr address.Address + var dstNodeID *crypto.NodeID + var dstNodeIDMask *crypto.NodeID + var dstSnet address.Subnet + var addrlen int + // Check the IP protocol - if it doesn't match then we drop the packet and + // do nothing with it + if bs[0]&0xf0 == 0x60 { + // Check if we have a fully-sized IPv6 header + if len(bs) < 40 { + continue + } + // Check the packet size + if n != 256*int(bs[4])+int(bs[5])+offset+tun_IPv6_HEADER_LENGTH { + continue + } + // IPv6 address + addrlen = 16 + copy(srcAddr[:addrlen], bs[8:]) + copy(dstAddr[:addrlen], bs[24:]) + copy(dstSnet[:addrlen/2], bs[24:]) + } else if bs[0]&0xf0 == 0x40 { + // Check if we have a fully-sized IPv4 header + if len(bs) < 20 { + continue + } + // Check the packet size + if n != 256*int(bs[2])+int(bs[3])+offset { + continue + } + // IPv4 address + addrlen = 4 + copy(srcAddr[:addrlen], bs[12:]) + copy(dstAddr[:addrlen], bs[16:]) + } else { + // Unknown address length or protocol, so drop the packet and ignore it + continue + } + if !dstAddr.IsValid() && !dstSnet.IsValid() { + // For now don't deal with any non-Yggdrasil ranges + continue + } + // Do we have an active connection for this node address? + tun.mutex.RLock() + session, isIn := tun.addrToConn[dstAddr] + if !isIn || session == nil { + session, isIn = tun.subnetToConn[dstSnet] + if !isIn || session == nil { + // Neither an address nor a subnet mapping matched, therefore populate + // the node ID and mask to commence a search + dstNodeID, dstNodeIDMask = dstAddr.GetNodeIDandMask() + } + } + tun.mutex.RUnlock() + // If we don't have a connection then we should open one + if !isIn || session == nil { + // Check we haven't been given empty node ID, really this shouldn't ever + // happen but just to be sure... + if dstNodeID == nil || dstNodeIDMask == nil { + panic("Given empty dstNodeID and dstNodeIDMask - this shouldn't happen") + } + // Dial to the remote node + if conn, err := tun.dialer.DialByNodeIDandMask(dstNodeID, dstNodeIDMask); err == nil { + // We've been given a connection so prepare the session wrapper + if s, err := tun.wrap(conn); err != nil { + // Something went wrong when storing the connection, typically that + // something already exists for this address or subnet + tun.log.Debugln("TUN/TAP iface wrap:", err) + } else { + // Update our reference to the connection + session, isIn = s, true + } + } else { + // We weren't able to dial for some reason so there's no point in + // continuing this iteration - skip to the next one + continue + } + } + // If we have a connection now, try writing to it + if isIn && session != nil { + select { + case session.send <- bs[:n]: + default: + } + } + + /*if !r.cryptokey.isValidSource(srcAddr, addrlen) { + // The packet had a src address that doesn't belong to us or our + // configured crypto-key routing src subnets + return + } + if !dstAddr.IsValid() && !dstSnet.IsValid() { + // The addresses didn't match valid Yggdrasil node addresses so let's see + // whether it matches a crypto-key routing range instead + if key, err := r.cryptokey.getPublicKeyForAddress(dstAddr, addrlen); err == nil { + // A public key was found, get the node ID for the search + dstPubKey = &key + dstNodeID = crypto.GetNodeID(dstPubKey) + // Do a quick check to ensure that the node ID refers to a vaild Yggdrasil + // address or subnet - this might be superfluous + addr := *address.AddrForNodeID(dstNodeID) + copy(dstAddr[:], addr[:]) + copy(dstSnet[:], addr[:]) + if !dstAddr.IsValid() && !dstSnet.IsValid() { + return + } + } else { + // No public key was found in the CKR table so we've exhausted our options + return + } + }*/ + + } +} diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index c19764c3..cfc8a668 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -6,10 +6,9 @@ package tuntap // TODO: Set MTU of session properly // TODO: Reject packets that exceed session MTU with ICMPv6 for PMTU Discovery // TODO: Connection timeouts (call Conn.Close() when we want to time out) -// TODO: Don't block in ifaceReader on writes that are pending searches +// TODO: Don't block in reader on writes that are pending searches import ( - "bytes" "encoding/hex" "errors" "fmt" @@ -18,14 +17,12 @@ import ( "time" "github.com/gologme/log" - "github.com/songgao/packets/ethernet" "github.com/yggdrasil-network/water" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" - "github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" ) @@ -47,9 +44,10 @@ type TunAdapter struct { icmpv6 ICMPv6 mtu int iface *water.Interface - mutex sync.RWMutex // Protects the below - addrToConn map[address.Address]*yggdrasil.Conn // Managed by connReader - subnetToConn map[address.Subnet]*yggdrasil.Conn // Managed by connReader + send chan []byte + mutex sync.RWMutex // Protects the below + addrToConn map[address.Address]*tunConn + subnetToConn map[address.Subnet]*tunConn isOpen bool } @@ -112,8 +110,8 @@ func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, listener tun.log = log tun.listener = listener tun.dialer = dialer - tun.addrToConn = make(map[address.Address]*yggdrasil.Conn) - tun.subnetToConn = make(map[address.Subnet]*yggdrasil.Conn) + tun.addrToConn = make(map[address.Address]*tunConn) + tun.subnetToConn = make(map[address.Subnet]*tunConn) } // Start the setup process for the TUN/TAP adapter. If successful, starts the @@ -148,6 +146,7 @@ func (tun *TunAdapter) Start() error { } tun.mutex.Lock() tun.isOpen = true + tun.send = make(chan []byte, 32) // TODO: is this a sensible value? tun.mutex.Unlock() if iftapmode { go func() { @@ -159,9 +158,7 @@ func (tun *TunAdapter) Start() error { if err != nil { panic(err) } - if _, err := tun.iface.Write(request); err != nil { - panic(err) - } + tun.send <- request time.Sleep(time.Second) } }() @@ -173,7 +170,8 @@ func (tun *TunAdapter) Start() error { } }() go tun.handler() - go tun.ifaceReader() + go tun.reader() + go tun.writer() tun.icmpv6.Init(tun) return nil } @@ -186,473 +184,47 @@ func (tun *TunAdapter) handler() error { tun.log.Errorln("TUN/TAP connection accept error:", err) return err } - go tun.connReader(conn) + if _, err := tun.wrap(conn); err != nil { + // Something went wrong when storing the connection, typically that + // something already exists for this address or subnet + tun.log.Debugln("TUN/TAP handler wrap:", err) + } } } -func (tun *TunAdapter) connReader(conn *yggdrasil.Conn) error { +func (tun *TunAdapter) wrap(conn *yggdrasil.Conn) (c *tunConn, err error) { + // Prepare a session wrapper for the given connection + s := tunConn{ + tun: tun, + conn: conn, + send: make(chan []byte, 32), // TODO: is this a sensible value? + stop: make(chan interface{}), + } + // Get the remote address and subnet of the other side remoteNodeID := conn.RemoteAddr() remoteAddr := address.AddrForNodeID(&remoteNodeID) remoteSubnet := address.SubnetForNodeID(&remoteNodeID) - err := func() error { - tun.mutex.RLock() - defer tun.mutex.RUnlock() - if _, isIn := tun.addrToConn[*remoteAddr]; isIn { - return errors.New("duplicate connection for address " + net.IP(remoteAddr[:]).String()) - } - if _, isIn := tun.subnetToConn[*remoteSubnet]; isIn { - return errors.New("duplicate connection for subnet " + net.IP(remoteSubnet[:]).String()) - } - return nil - }() - if err != nil { - //return err - panic(err) - } - // Store the connection mapped to address and subnet + // Work out if this is already a destination we already know about tun.mutex.Lock() - tun.addrToConn[*remoteAddr] = conn - tun.subnetToConn[*remoteSubnet] = conn - tun.mutex.Unlock() - // Make sure to clean those up later when the connection is closed - defer func() { - tun.mutex.Lock() - delete(tun.addrToConn, *remoteAddr) - delete(tun.subnetToConn, *remoteSubnet) - tun.mutex.Unlock() - }() - b := make([]byte, 65535) - for { - n, err := conn.Read(b) - if err != nil { - tun.log.Errorln(conn.String(), "TUN/TAP conn read error:", err) - continue - } - if n == 0 { - continue - } - var w int - if tun.iface.IsTAP() { - var dstAddr address.Address - if b[0]&0xf0 == 0x60 { - if len(b) < 40 { - //panic("Tried to send a packet shorter than an IPv6 header...") - util.PutBytes(b) - continue - } - copy(dstAddr[:16], b[24:]) - } else if b[0]&0xf0 == 0x40 { - if len(b) < 20 { - //panic("Tried to send a packet shorter than an IPv4 header...") - util.PutBytes(b) - continue - } - copy(dstAddr[:4], b[16:]) - } else { - return errors.New("Invalid address family") - } - sendndp := func(dstAddr address.Address) { - 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(), - } - } - } - var peermac macAddress - var peerknown bool - if b[0]&0xf0 == 0x40 { - dstAddr = tun.addr - } else if b[0]&0xf0 == 0x60 { - if !bytes.Equal(tun.addr[:16], dstAddr[:16]) && !bytes.Equal(tun.subnet[:8], dstAddr[:8]) { - dstAddr = tun.addr - } - } - if neighbor, ok := tun.icmpv6.peermacs[dstAddr]; ok && neighbor.learned { - peermac = neighbor.mac - peerknown = true - } else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned { - peermac = neighbor.mac - peerknown = true - sendndp(dstAddr) - } else { - sendndp(tun.addr) - } - if peerknown { - var proto ethernet.Ethertype - switch { - case b[0]&0xf0 == 0x60: - proto = ethernet.IPv6 - case b[0]&0xf0 == 0x40: - proto = ethernet.IPv4 - } - var frame ethernet.Frame - frame.Prepare( - peermac[:6], // Destination MAC address - tun.icmpv6.mymac[:6], // Source MAC address - ethernet.NotTagged, // VLAN tagging - proto, // Ethertype - len(b)) // Payload length - copy(frame[tun_ETHER_HEADER_LENGTH:], b[:n]) - n += tun_ETHER_HEADER_LENGTH - w, err = tun.iface.Write(frame[:n]) - } - } else { - w, err = tun.iface.Write(b[:n]) - } - if err != nil { - tun.log.Errorln(conn.String(), "TUN/TAP iface write error:", err) - continue - } - if w != n { - tun.log.Errorln(conn.String(), "TUN/TAP iface write mismatch:", w, "bytes written vs", n, "bytes given") - continue - } + defer tun.mutex.Unlock() + atc, aok := tun.addrToConn[*remoteAddr] + stc, sok := tun.subnetToConn[*remoteSubnet] + // If we know about a connection for this destination already then assume it + // is no longer valid and close it + if aok { + atc.close() + err = errors.New("replaced connection for address") + } else if sok { + stc.close() + err = errors.New("replaced connection for subnet") } + // Save the session wrapper so that we can look it up quickly next time + // we receive a packet through the interface for this address + tun.addrToConn[*remoteAddr] = &s + tun.subnetToConn[*remoteSubnet] = &s + // Start the connection goroutines + go s.reader() + go s.writer() + // Return + return c, err } - -func (tun *TunAdapter) ifaceReader() error { - bs := make([]byte, 65535) - for { - // Wait for a packet to be delivered to us through the TUN/TAP adapter - n, err := tun.iface.Read(bs) - if err != nil { - continue - } - // If it's a TAP adapter, update the buffer slice so that we no longer - // include the ethernet headers - offset := 0 - if tun.iface.IsTAP() { - // Set our offset to beyond the ethernet headers - offset = tun_ETHER_HEADER_LENGTH - // If we detect an ICMP packet then hand it to the ICMPv6 module - if bs[offset+6] == 58 { - // Found an ICMPv6 packet - b := make([]byte, n) - copy(b, bs) - go tun.icmpv6.ParsePacket(b) - } - // Then offset the buffer so that we can now just treat it as an IP - // packet from now on - bs = bs[offset:] - } - // From the IP header, work out what our source and destination addresses - // and node IDs are. We will need these in order to work out where to send - // the packet - var srcAddr address.Address - var dstAddr address.Address - var dstNodeID *crypto.NodeID - var dstNodeIDMask *crypto.NodeID - var dstSnet address.Subnet - var addrlen int - // Check the IP protocol - if it doesn't match then we drop the packet and - // do nothing with it - if bs[0]&0xf0 == 0x60 { - // Check if we have a fully-sized IPv6 header - if len(bs) < 40 { - continue - } - // Check the packet size - if n != 256*int(bs[4])+int(bs[5])+offset+tun_IPv6_HEADER_LENGTH { - continue - } - // IPv6 address - addrlen = 16 - copy(srcAddr[:addrlen], bs[8:]) - copy(dstAddr[:addrlen], bs[24:]) - copy(dstSnet[:addrlen/2], bs[24:]) - } else if bs[0]&0xf0 == 0x40 { - // Check if we have a fully-sized IPv4 header - if len(bs) < 20 { - continue - } - // Check the packet size - if n != 256*int(bs[2])+int(bs[3])+offset { - continue - } - // IPv4 address - addrlen = 4 - copy(srcAddr[:addrlen], bs[12:]) - copy(dstAddr[:addrlen], bs[16:]) - } else { - // Unknown address length or protocol, so drop the packet and ignore it - continue - } - if !dstAddr.IsValid() && !dstSnet.IsValid() { - // For now don't deal with any non-Yggdrasil ranges - continue - } - // Do we have an active connection for this node address? - tun.mutex.RLock() - conn, isIn := tun.addrToConn[dstAddr] - if !isIn || conn == nil { - conn, isIn = tun.subnetToConn[dstSnet] - if !isIn || conn == nil { - // Neither an address nor a subnet mapping matched, therefore populate - // the node ID and mask to commence a search - dstNodeID, dstNodeIDMask = dstAddr.GetNodeIDandMask() - } - } - tun.mutex.RUnlock() - // If we don't have a connection then we should open one - if !isIn || conn == nil { - // Check we haven't been given empty node ID, really this shouldn't ever - // happen but just to be sure... - if dstNodeID == nil || dstNodeIDMask == nil { - panic("Given empty dstNodeID and dstNodeIDMask - this shouldn't happen") - } - // Dial to the remote node - if c, err := tun.dialer.DialByNodeIDandMask(dstNodeID, dstNodeIDMask); err == nil { - // We've been given a connection so start the connection reader goroutine - go tun.connReader(c) - // Then update our reference to the connection - conn, isIn = c, true - } else { - // We weren't able to dial for some reason so there's no point in - // continuing this iteration - skip to the next one - continue - } - } - // If we have a connection now, try writing to it - if isIn && conn != nil { - // If we have an open connection, either because we already had one or - // because we opened one above, try writing the packet to it - w, err := conn.Write(bs[:n]) - if err != nil { - tun.log.Errorln(conn.String(), "TUN/TAP conn write error:", err) - continue - } - if w != n { - tun.log.Errorln(conn.String(), "TUN/TAP conn write mismatch:", w, "bytes written vs", n, "bytes given") - continue - } - } - - /*if !r.cryptokey.isValidSource(srcAddr, addrlen) { - // The packet had a src address that doesn't belong to us or our - // configured crypto-key routing src subnets - return - } - if !dstAddr.IsValid() && !dstSnet.IsValid() { - // The addresses didn't match valid Yggdrasil node addresses so let's see - // whether it matches a crypto-key routing range instead - if key, err := r.cryptokey.getPublicKeyForAddress(dstAddr, addrlen); err == nil { - // A public key was found, get the node ID for the search - dstPubKey = &key - dstNodeID = crypto.GetNodeID(dstPubKey) - // Do a quick check to ensure that the node ID refers to a vaild Yggdrasil - // address or subnet - this might be superfluous - addr := *address.AddrForNodeID(dstNodeID) - copy(dstAddr[:], addr[:]) - copy(dstSnet[:], addr[:]) - if !dstAddr.IsValid() && !dstSnet.IsValid() { - return - } - } else { - // No public key was found in the CKR table so we've exhausted our options - return - } - }*/ - - } -} - -// Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP -// mode then additional ethernet encapsulation is added for the benefit of the -// host operating system. -/* -func (tun *TunAdapter) write() error { - for { - select { - case reject := <-tun.Reject: - switch reject.Reason { - case yggdrasil.PacketTooBig: - if mtu, ok := reject.Detail.(int); ok { - // Create the Packet Too Big response - ptb := &icmp.PacketTooBig{ - MTU: int(mtu), - Data: reject.Packet, - } - - // Create the ICMPv6 response from it - icmpv6Buf, err := CreateICMPv6( - reject.Packet[8:24], reject.Packet[24:40], - ipv6.ICMPTypePacketTooBig, 0, ptb) - - // Send the ICMPv6 response back to the TUN/TAP adapter - if err == nil { - tun.iface.Write(icmpv6Buf) - } - } - fallthrough - default: - continue - } - case data := <-tun.Recv: - if tun.iface == nil { - continue - } - if tun.iface.IsTAP() { - var dstAddr address.Address - if data[0]&0xf0 == 0x60 { - if len(data) < 40 { - //panic("Tried to send a packet shorter than an IPv6 header...") - util.PutBytes(data) - continue - } - copy(dstAddr[:16], data[24:]) - } else if data[0]&0xf0 == 0x40 { - if len(data) < 20 { - //panic("Tried to send a packet shorter than an IPv4 header...") - util.PutBytes(data) - continue - } - copy(dstAddr[:4], data[16:]) - } else { - return errors.New("Invalid address family") - } - sendndp := func(dstAddr address.Address) { - 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(), - } - } - } - var peermac macAddress - var peerknown bool - if data[0]&0xf0 == 0x40 { - dstAddr = tun.addr - } else if data[0]&0xf0 == 0x60 { - if !bytes.Equal(tun.addr[:16], dstAddr[:16]) && !bytes.Equal(tun.subnet[:8], dstAddr[:8]) { - dstAddr = tun.addr - } - } - if neighbor, ok := tun.icmpv6.peermacs[dstAddr]; ok && neighbor.learned { - peermac = neighbor.mac - peerknown = true - } else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned { - peermac = neighbor.mac - peerknown = true - sendndp(dstAddr) - } else { - sendndp(tun.addr) - } - if peerknown { - var proto ethernet.Ethertype - switch { - case data[0]&0xf0 == 0x60: - proto = ethernet.IPv6 - case data[0]&0xf0 == 0x40: - proto = ethernet.IPv4 - } - var frame ethernet.Frame - frame.Prepare( - peermac[:6], // Destination MAC address - tun.icmpv6.mymac[:6], // Source MAC address - ethernet.NotTagged, // VLAN tagging - proto, // Ethertype - len(data)) // Payload length - copy(frame[tun_ETHER_HEADER_LENGTH:], data[:]) - if _, err := tun.iface.Write(frame); err != nil { - tun.mutex.RLock() - open := tun.isOpen - tun.mutex.RUnlock() - if !open { - return nil - } else { - panic(err) - } - } - } - } else { - if _, err := tun.iface.Write(data); err != nil { - tun.mutex.RLock() - open := tun.isOpen - tun.mutex.RUnlock() - if !open { - return nil - } else { - panic(err) - } - } - } - util.PutBytes(data) - } - } -} - -// Reads any packets that are waiting on the TUN/TAP adapter. If the adapter -// is running in TAP mode then the ethernet headers will automatically be -// processed and stripped if necessary. If an ICMPv6 packet is found, then -// the relevant helper functions in icmpv6.go are called. -func (tun *TunAdapter) read() error { - mtu := tun.mtu - if tun.iface.IsTAP() { - mtu += tun_ETHER_HEADER_LENGTH - } - buf := make([]byte, mtu) - for { - n, err := tun.iface.Read(buf) - if err != nil { - tun.mutex.RLock() - open := tun.isOpen - tun.mutex.RUnlock() - if !open { - return nil - } else { - return err - } - } - o := 0 - if tun.iface.IsTAP() { - o = tun_ETHER_HEADER_LENGTH - } - switch { - case buf[o]&0xf0 == 0x60 && n == 256*int(buf[o+4])+int(buf[o+5])+tun_IPv6_HEADER_LENGTH+o: - case buf[o]&0xf0 == 0x40 && n == 256*int(buf[o+2])+int(buf[o+3])+o: - default: - continue - } - if buf[o+6] == 58 { - if tun.iface.IsTAP() { - // Found an ICMPv6 packet - b := make([]byte, n) - copy(b, buf) - go tun.icmpv6.ParsePacket(b) - } - } - packet := append(util.GetBytes(), buf[o:n]...) - tun.Send <- packet - } -} - -// Closes the TUN/TAP adapter. This is only usually called when the Yggdrasil -// process stops. Typically this operation will happen quickly, but on macOS -// it can block until a read operation is completed. -func (tun *TunAdapter) Close() error { - tun.mutex.Lock() - tun.isOpen = false - tun.mutex.Unlock() - if tun.iface == nil { - return nil - } - return tun.iface.Close() -} -*/