Mostly working PMTU discovery when going over UDP links

This commit is contained in:
Arceliar 2018-02-25 20:24:36 -06:00
parent 0fae932512
commit 63b55cda62
3 changed files with 40 additions and 10 deletions

View File

@ -91,6 +91,7 @@ const boxPubKeyLen = 32
const boxPrivKeyLen = 32 const boxPrivKeyLen = 32
const boxSharedKeyLen = 32 const boxSharedKeyLen = 32
const boxNonceLen = 24 const boxNonceLen = 24
const boxOverhead = box.Overhead
type boxPubKey [boxPubKeyLen]byte type boxPubKey [boxPubKeyLen]byte
type boxPrivKey [boxPrivKeyLen]byte type boxPrivKey [boxPrivKeyLen]byte

View File

@ -28,7 +28,8 @@ type sessionInfo struct {
send chan []byte send chan []byte
recv chan *wire_trafficPacket recv chan *wire_trafficPacket
nonceMask uint64 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 { type sessionPing struct {
@ -152,15 +153,7 @@ func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
sinfo.myNonce = *newBoxNonce() sinfo.myNonce = *newBoxNonce()
sinfo.theirMTU = 1280 sinfo.theirMTU = 1280
sinfo.myMTU = uint16(ss.core.tun.mtu) sinfo.myMTU = uint16(ss.core.tun.mtu)
if sinfo.myMTU > 2048 { sinfo.mtuTime = time.Now()
// 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
}
higher := false higher := false
for idx := range ss.core.boxPub { for idx := range ss.core.boxPub {
if ss.core.boxPub[idx] > sinfo.theirPermPub[idx] { 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) { func (sinfo *sessionInfo) doRecv(p *wire_trafficPacket) {
defer util_putBytes(p.payload) defer util_putBytes(p.payload)
payloadSize := uint16(len(p.payload))
if !sinfo.nonceIsOK(&p.nonce) { if !sinfo.nonceIsOK(&p.nonce) {
return return
} }
bs, isOK := boxOpen(&sinfo.sharedSesKey, p.payload, &p.nonce) bs, isOK := boxOpen(&sinfo.sharedSesKey, p.payload, &p.nonce)
if !isOK { 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) util_putBytes(bs)
return 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.updateNonce(&p.nonce)
sinfo.time = time.Now() sinfo.time = time.Now()
sinfo.core.router.recvPacket(bs, &sinfo.theirAddr, &sinfo.theirSubnet) sinfo.core.router.recvPacket(bs, &sinfo.theirAddr, &sinfo.theirSubnet)

View File

@ -182,6 +182,13 @@ func (iface *udpInterface) handleKeys(msg []byte, addr connAddr) {
//defer util_putBytes(bs) //defer util_putBytes(bs)
chunks, chunk, count, payload := udp_decode(bs) chunks, chunk, count, payload := udp_decode(bs)
if count != conn.countIn { 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 inChunks = 0
inBuf = inBuf[:0] inBuf = inBuf[:0]
conn.countIn = count conn.countIn = count
@ -194,6 +201,7 @@ func (iface *udpInterface) handleKeys(msg []byte, addr connAddr) {
} }
msg := append(util_getBytes(), inBuf...) msg := append(util_getBytes(), inBuf...)
conn.peer.handlePacket(msg, conn.linkIn) conn.peer.handlePacket(msg, conn.linkIn)
inBuf = inBuf[:0]
} }
} }
conn.peer.out = func(msg []byte) { conn.peer.out = func(msg []byte) {