From 0096d1ae3ec4fdd058dacec9b7c3e0ade2d88b2a Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 29 May 2019 20:16:17 +0100 Subject: [PATCH] Re-add ICMPv6 packet too big handling --- src/tuntap/conn.go | 18 +++++++++++++++++- src/yggdrasil/conn.go | 34 +++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go index 66c1011..f303e0c 100644 --- a/src/tuntap/conn.go +++ b/src/tuntap/conn.go @@ -7,6 +7,8 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv6" ) type tunConn struct { @@ -97,7 +99,21 @@ func (s *tunConn) writer() error { } // TODO write timeout and close if _, err := s.conn.Write(b); err != nil { - s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err) + e, eok := err.(yggdrasil.ConnError) + if !eok { + s.tun.log.Errorln(s.conn.String(), "TUN/TAP generic write error:", err) + } else if ispackettoobig, maxsize := e.PacketTooBig(); ispackettoobig { + // TODO: This currently isn't aware of IPv4 for CKR + ptb := &icmp.PacketTooBig{ + MTU: int(maxsize), + Data: b[:900], + } + if packet, err := CreateICMPv6(b[8:24], b[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil { + s.tun.send <- packet + } + } else { + s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err) + } } util.PutBytes(b) s.stillAlive() diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 1290ad0..9175aa5 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -11,21 +11,33 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/util" ) -// Error implements the net.Error interface +// ConnError implements the net.Error interface type ConnError struct { error timeout bool temporary bool + maxsize int } +// Timeout returns true if the error relates to a timeout condition on the +// connection. func (e *ConnError) Timeout() bool { return e.timeout } +// Temporary return true if the error is temporary or false if it is a permanent +// error condition. func (e *ConnError) Temporary() bool { return e.temporary } +// PacketTooBig returns in response to sending a packet that is too large, and +// if so, the maximum supported packet size that should be used for the +// connection. +func (e *ConnError) PacketTooBig() (bool, int) { + return e.maxsize > 0, e.maxsize +} + type Conn struct { core *Core nodeID *crypto.NodeID @@ -166,7 +178,7 @@ func (c *Conn) Read(b []byte) (int, error) { select { case <-c.searchwait: case <-timer.C: - return 0, ConnError{errors.New("Timeout"), true, false} + return 0, ConnError{errors.New("timeout"), true, false, 0} } // Retrieve our session info again c.mutex.RLock() @@ -182,7 +194,7 @@ func (c *Conn) Read(b []byte) (int, error) { // Wait for some traffic to come through from the session select { case <-timer.C: - return 0, ConnError{errors.New("Timeout"), true, false} + return 0, ConnError{errors.New("timeout"), true, false, 0} case p, ok := <-sinfo.recv: // If the session is closed then do nothing if !ok { @@ -222,7 +234,7 @@ func (c *Conn) Read(b []byte) (int, error) { select { // Send to worker case sinfo.worker <- workerFunc: case <-timer.C: - return 0, ConnError{errors.New("Timeout"), true, false} + return 0, ConnError{errors.New("timeout"), true, false, 0} } <-done // Wait for the worker to finish, failing this can cause memory errors (util.[Get||Put]Bytes stuff) // Something went wrong in the session worker so abort @@ -260,8 +272,14 @@ func (c *Conn) Write(b []byte) (bytesWritten int, err error) { } var packet []byte done := make(chan struct{}) + written := len(b) workerFunc := func() { defer close(done) + // Does the packet exceed the permitted size for the session? + if uint16(len(b)) > sinfo.getMTU() { + written, err = 0, ConnError{errors.New("packet too big"), true, false, int(sinfo.getMTU())} + return + } // Encrypt the packet payload, nonce := crypto.BoxSeal(&sinfo.sharedSesKey, b, &sinfo.myNonce) defer util.PutBytes(payload) @@ -282,14 +300,16 @@ func (c *Conn) Write(b []byte) (bytesWritten int, err error) { select { // Send to worker case sinfo.worker <- workerFunc: case <-timer.C: - return 0, ConnError{errors.New("Timeout"), true, false} + return 0, ConnError{errors.New("timeout"), true, false, 0} } // Wait for the worker to finish, otherwise there are memory errors ([Get||Put]Bytes stuff) <-done // Give the packet to the router - sinfo.core.router.out(packet) + if written > 0 { + sinfo.core.router.out(packet) + } // Finally return the number of bytes we wrote - return len(b), nil + return written, err } func (c *Conn) Close() error {