From eb22ed44ac24664e7f964a7c1fb27f2d7303d7ba Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 28 Mar 2019 09:50:13 +0000 Subject: [PATCH] Add new reject channel to router so we can send back rejected packets to adapter (e.g. for ICMPv6 Packet Too Big), implement ICMPv6 PTB in TUN/TAP instead of router --- src/tuntap/tun.go | 188 ++++++++++++++++++++++----------------- src/yggdrasil/adapter.go | 6 +- src/yggdrasil/router.go | 37 +++++--- 3 files changed, 135 insertions(+), 96 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 780043f..f85d8bb 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -11,6 +11,8 @@ import ( "time" "github.com/gologme/log" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv6" "github.com/songgao/packets/ethernet" "github.com/yggdrasil-network/water" @@ -64,10 +66,10 @@ func (tun *TunAdapter) IsTAP() bool { } // Initialises the TUN/TAP adapter. -func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte) { +func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte, reject <-chan yggdrasil.RejectedPacket) { tun.config = config tun.log = log - tun.Adapter.Init(config, log, send, recv) + tun.Adapter.Init(config, log, send, recv, reject) tun.icmpv6.Init(tun) go func() { for { @@ -146,81 +148,118 @@ func (tun *TunAdapter) Start(a address.Address, s address.Subnet) error { // host operating system. func (tun *TunAdapter) Write() error { for { - data := <-tun.Recv - if tun.iface == nil { - continue - } - if tun.iface.IsTAP() { - var destAddr 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 + 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) + } } - copy(destAddr[: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(destAddr[:4], data[16:]) - } else { - return errors.New("Invalid address family") + fallthrough + default: + continue } - sendndp := func(destAddr address.Address) { - neigh, known := tun.icmpv6.peermacs[destAddr] - known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30) - if !known { - request, err := tun.icmpv6.CreateNDPL2(destAddr) - if err != nil { - panic(err) + case data := <-tun.Recv: + if tun.iface == nil { + continue + } + if tun.iface.IsTAP() { + var destAddr 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 } - if _, err := tun.iface.Write(request); err != nil { - panic(err) + copy(destAddr[: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 } - tun.icmpv6.peermacs[destAddr] = neighbor{ - lastsolicitation: time.Now(), + copy(destAddr[:4], data[16:]) + } else { + return errors.New("Invalid address family") + } + sendndp := func(destAddr address.Address) { + neigh, known := tun.icmpv6.peermacs[destAddr] + known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30) + if !known { + request, err := tun.icmpv6.CreateNDPL2(destAddr) + if err != nil { + panic(err) + } + if _, err := tun.iface.Write(request); err != nil { + panic(err) + } + tun.icmpv6.peermacs[destAddr] = neighbor{ + lastsolicitation: time.Now(), + } } } - } - var peermac macAddress - var peerknown bool - if data[0]&0xf0 == 0x40 { - destAddr = tun.addr - } else if data[0]&0xf0 == 0x60 { - if !bytes.Equal(tun.addr[:16], destAddr[:16]) && !bytes.Equal(tun.subnet[:8], destAddr[:8]) { + var peermac macAddress + var peerknown bool + if data[0]&0xf0 == 0x40 { destAddr = tun.addr + } else if data[0]&0xf0 == 0x60 { + if !bytes.Equal(tun.addr[:16], destAddr[:16]) && !bytes.Equal(tun.subnet[:8], destAddr[:8]) { + destAddr = tun.addr + } + } + if neighbor, ok := tun.icmpv6.peermacs[destAddr]; 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(destAddr) + } 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) + } + } } - } - if neighbor, ok := tun.icmpv6.peermacs[destAddr]; 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(destAddr) } 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 { + if _, err := tun.iface.Write(data); err != nil { tun.mutex.RLock() open := tun.isOpen tun.mutex.RUnlock() @@ -231,19 +270,8 @@ func (tun *TunAdapter) Write() error { } } } - } 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) } - util.PutBytes(data) } } diff --git a/src/yggdrasil/adapter.go b/src/yggdrasil/adapter.go index 4103d1e..6be3ef0 100644 --- a/src/yggdrasil/adapter.go +++ b/src/yggdrasil/adapter.go @@ -13,12 +13,13 @@ type Adapter struct { Core *Core Send chan<- []byte Recv <-chan []byte + Reject <-chan RejectedPacket Reconfigure chan chan error } // Defines the minimum required functions for an adapter type type adapterImplementation interface { - Init(*config.NodeState, *log.Logger, chan<- []byte, <-chan []byte) + Init(*config.NodeState, *log.Logger, chan<- []byte, <-chan []byte, <-chan RejectedPacket) Name() string MTU() int IsTAP() bool @@ -29,9 +30,10 @@ type adapterImplementation interface { } // Initialises the adapter. -func (adapter *Adapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte) { +func (adapter *Adapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte, reject <-chan RejectedPacket) { log.Traceln("Adapter setup - given channels:", send, recv) adapter.Send = send adapter.Recv = recv + adapter.Reject = reject adapter.Reconfigure = make(chan chan error, 1) } diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 04f5ee5..aa2cd54 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -44,6 +44,7 @@ type router struct { tun adapterImplementation // TUN/TAP adapter recv chan<- []byte // place where the tun pulls received packets from send <-chan []byte // place where the tun puts outgoing packets + reject chan<- RejectedPacket // place where we send error packets back to tun reset chan struct{} // signal that coords changed (re-init sessions/dht) admin chan func() // pass a lambda for the admin socket to query stuff cryptokey cryptokey @@ -56,6 +57,19 @@ type router_recvPacket struct { sinfo *sessionInfo } +type RejectedPacketReason int + +const ( + // The router rejected the packet because it is too big for the session + PacketTooBig = 1 + iota +) + +type RejectedPacket struct { + Reason RejectedPacketReason + Packet []byte + Detail interface{} +} + // Initializes the router struct, which includes setting up channels to/from the tun/tap. func (r *router) init(core *Core) { r.core = core @@ -103,8 +117,10 @@ func (r *router) init(core *Core) { r.toRecv = make(chan router_recvPacket, 32) recv := make(chan []byte, 32) send := make(chan []byte, 32) + reject := make(chan RejectedPacket, 32) r.recv = recv r.send = send + r.reject = reject r.reset = make(chan struct{}, 1) r.admin = make(chan func(), 32) r.nodeinfo.init(r.core) @@ -112,7 +128,7 @@ func (r *router) init(core *Core) { r.nodeinfo.setNodeInfo(r.core.config.Current.NodeInfo, r.core.config.Current.NodeInfoPrivacy) r.core.config.Mutex.RUnlock() r.cryptokey.init(r.core) - r.tun.Init(&r.core.config, r.core.log, send, recv) + r.tun.Init(&r.core.config, r.core.log, send, recv, reject) } // Starts the mainLoop goroutine. @@ -303,25 +319,18 @@ func (r *router) sendPacket(bs []byte) { // Generate an ICMPv6 Packet Too Big for packets larger than session MTU if len(bs) > int(sinfo.getMTU()) { // Get the size of the oversized payload, up to a max of 900 bytes - /*window := 900 + window := 900 if int(sinfo.getMTU()) < window { window = int(sinfo.getMTU()) } - // Create the Packet Too Big response - ptb := &icmp.PacketTooBig{ - MTU: int(sinfo.getMTU()), - Data: bs[:window], + // Send the error back to the adapter + r.reject <- RejectedPacket{ + Reason: PacketTooBig, + Packet: bs[:window], + Detail: int(sinfo.getMTU()), } - // Create the ICMPv6 response from it - icmpv6Buf, err := CreateICMPv6( - bs[8:24], bs[24:40], - ipv6.ICMPTypePacketTooBig, 0, ptb) - if err == nil { - r.recv <- icmpv6Buf - }*/ - // Don't continue - drop the packet return }