From 63b55cda62f244872d128bb00fa395c49ce6f728 Mon Sep 17 00:00:00 2001 From: Arceliar Date: Sun, 25 Feb 2018 20:24:36 -0600 Subject: [PATCH] Mostly working PMTU discovery when going over UDP links --- src/yggdrasil/crypto.go | 1 + src/yggdrasil/session.go | 41 ++++++++++++++++++++++++++++++---------- src/yggdrasil/udp.go | 8 ++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/yggdrasil/crypto.go b/src/yggdrasil/crypto.go index 9e311aa7..405c60d7 100644 --- a/src/yggdrasil/crypto.go +++ b/src/yggdrasil/crypto.go @@ -91,6 +91,7 @@ const boxPubKeyLen = 32 const boxPrivKeyLen = 32 const boxSharedKeyLen = 32 const boxNonceLen = 24 +const boxOverhead = box.Overhead type boxPubKey [boxPubKeyLen]byte type boxPrivKey [boxPrivKeyLen]byte diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 5aeeacb6..13606f85 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -28,7 +28,8 @@ type sessionInfo struct { send chan []byte recv chan *wire_trafficPacket nonceMask uint64 - tstamp int64 // tstamp from their last session ping, replay attack mitigation + tstamp int64 // tstamp from their last session ping, replay attack mitigation + mtuTime time.Time // time myMTU was last changed } type sessionPing struct { @@ -152,15 +153,7 @@ func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo { sinfo.myNonce = *newBoxNonce() sinfo.theirMTU = 1280 sinfo.myMTU = uint16(ss.core.tun.mtu) - if sinfo.myMTU > 2048 { - // FIXME this is a temporary workaround to an issue with UDP peers - // UDP links need to fragment packets (within ygg) to get them over the wire - // For some reason, TCP streams over UDP peers can get stuck in a bad state - // When this happens, TCP throttles back, and each TCP retransmission loses fragments - // On my wifi network, it seems to happen around the 22nd-23rd fragment of a large packet - // By setting the path MTU to something small, this should (hopefully) mitigate the issue - sinfo.myMTU = 2048 - } + sinfo.mtuTime = time.Now() higher := false for idx := range ss.core.boxPub { if ss.core.boxPub[idx] > sinfo.theirPermPub[idx] { @@ -387,14 +380,42 @@ func (sinfo *sessionInfo) doSend(bs []byte) { func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) { defer util_putBytes(p.payload) + payloadSize := uint16(len(p.payload)) if !sinfo.nonceIsOK(&p.nonce) { return } bs, isOK := boxOpen(&sinfo.sharedSesKey, p.payload, &p.nonce) if !isOK { + // We're going to guess that the session MTU is too large + // Set myMTU to the largest value we think we can receive + fixSessionMTU := func() { + // This clamps down to 1280 almost immediately over ipv4 + // Over link-local ipv6, it seems to approach link MTU + // So maybe it's doing the right thing?... + //sinfo.core.log.Println("DEBUG got bad packet:", payloadSize) + newMTU := payloadSize - boxOverhead + if newMTU < 1280 { + newMTU = 1280 + } + if newMTU < sinfo.myMTU { + sinfo.myMTU = newMTU + //sinfo.core.log.Println("DEBUG set MTU to:", sinfo.myMTU) + sinfo.core.sessions.sendPingPong(sinfo, false) + sinfo.mtuTime = time.Now() + } + } + go func() { sinfo.core.router.admin <- fixSessionMTU }() util_putBytes(bs) return } + fixSessionMTU := func() { + if time.Since(sinfo.mtuTime) > time.Minute { + sinfo.myMTU = uint16(sinfo.core.tun.mtu) + sinfo.mtuTime = time.Now() + //sinfo.core.log.Println("DEBUG: Reset MTU to:", sinfo.myMTU) + } + } + go func() { sinfo.core.router.admin <- fixSessionMTU }() sinfo.updateNonce(&p.nonce) sinfo.time = time.Now() sinfo.core.router.recvPacket(bs, &sinfo.theirAddr, &sinfo.theirSubnet) diff --git a/src/yggdrasil/udp.go b/src/yggdrasil/udp.go index 0a04e7f0..a2cf0af2 100644 --- a/src/yggdrasil/udp.go +++ b/src/yggdrasil/udp.go @@ -182,6 +182,13 @@ func (iface *udpInterface) handleKeys(msg []byte, addr connAddr) { //defer util_putBytes(bs) chunks, chunk, count, payload := udp_decode(bs) if count != conn.countIn { + if len(inBuf) > 0 { + // Something went wrong + // Forward whatever we have + // Maybe the destination can do something about it + msg := append(util_getBytes(), inBuf...) + conn.peer.handlePacket(msg, conn.linkIn) + } inChunks = 0 inBuf = inBuf[:0] conn.countIn = count @@ -194,6 +201,7 @@ func (iface *udpInterface) handleKeys(msg []byte, addr connAddr) { } msg := append(util_getBytes(), inBuf...) conn.peer.handlePacket(msg, conn.linkIn) + inBuf = inBuf[:0] } } conn.peer.out = func(msg []byte) {