Merge pull request #425 from neilalexander/sessionmtu

Re-add ICMPv6 packet too big handling
This commit is contained in:
Neil Alexander 2019-05-29 20:19:41 +01:00 committed by GitHub
commit 396c879d0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 8 deletions

View File

@ -7,6 +7,8 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
) )
type tunConn struct { type tunConn struct {
@ -97,8 +99,22 @@ func (s *tunConn) writer() error {
} }
// TODO write timeout and close // TODO write timeout and close
if _, err := s.conn.Write(b); err != nil { if _, err := s.conn.Write(b); err != nil {
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) s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err)
} }
}
util.PutBytes(b) util.PutBytes(b)
s.stillAlive() s.stillAlive()
} }

View File

@ -11,21 +11,33 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
) )
// Error implements the net.Error interface // ConnError implements the net.Error interface
type ConnError struct { type ConnError struct {
error error
timeout bool timeout bool
temporary bool temporary bool
maxsize int
} }
// Timeout returns true if the error relates to a timeout condition on the
// connection.
func (e *ConnError) Timeout() bool { func (e *ConnError) Timeout() bool {
return e.timeout 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 { func (e *ConnError) Temporary() bool {
return e.temporary 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 { type Conn struct {
core *Core core *Core
nodeID *crypto.NodeID nodeID *crypto.NodeID
@ -166,7 +178,7 @@ func (c *Conn) Read(b []byte) (int, error) {
select { select {
case <-c.searchwait: case <-c.searchwait:
case <-timer.C: 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 // Retrieve our session info again
c.mutex.RLock() 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 // Wait for some traffic to come through from the session
select { select {
case <-timer.C: 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: case p, ok := <-sinfo.recv:
// If the session is closed then do nothing // If the session is closed then do nothing
if !ok { if !ok {
@ -222,7 +234,7 @@ func (c *Conn) Read(b []byte) (int, error) {
select { // Send to worker select { // Send to worker
case sinfo.worker <- workerFunc: case sinfo.worker <- workerFunc:
case <-timer.C: 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) <-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 // 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 var packet []byte
done := make(chan struct{}) done := make(chan struct{})
written := len(b)
workerFunc := func() { workerFunc := func() {
defer close(done) 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 // Encrypt the packet
payload, nonce := crypto.BoxSeal(&sinfo.sharedSesKey, b, &sinfo.myNonce) payload, nonce := crypto.BoxSeal(&sinfo.sharedSesKey, b, &sinfo.myNonce)
defer util.PutBytes(payload) defer util.PutBytes(payload)
@ -282,14 +300,16 @@ func (c *Conn) Write(b []byte) (bytesWritten int, err error) {
select { // Send to worker select { // Send to worker
case sinfo.worker <- workerFunc: case sinfo.worker <- workerFunc:
case <-timer.C: 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) // Wait for the worker to finish, otherwise there are memory errors ([Get||Put]Bytes stuff)
<-done <-done
// Give the packet to the router // Give the packet to the router
if written > 0 {
sinfo.core.router.out(packet) sinfo.core.router.out(packet)
}
// Finally return the number of bytes we wrote // Finally return the number of bytes we wrote
return len(b), nil return written, err
} }
func (c *Conn) Close() error { func (c *Conn) Close() error {