diff --git a/connection.go b/connection.go index f569c2da..0eb1bd63 100644 --- a/connection.go +++ b/connection.go @@ -99,26 +99,12 @@ func (conn *LocalConnection) CheckFatal(err error) error { func (conn *LocalConnection) setEffectivePMTU(pmtu int) { conn.Lock() defer conn.Unlock() - conn.effectivePMTU = pmtu - conn.pmtuVerified = false - conn.log("Effective PMTU set to", pmtu) -} - -func (conn *LocalConnection) effectivePMTUVerification(pmtu int) { - conn.Lock() - defer conn.Unlock() - if pmtu == conn.effectivePMTU && !conn.pmtuVerified { - conn.pmtuVerified = true - conn.log("Effective PMTU verified at", pmtu) + if (conn.effectivePMTU != pmtu) { + conn.effectivePMTU = pmtu + conn.log("Effective PMTU set to", pmtu) } } -func (conn *LocalConnection) isEffectivePMTUVerfied() bool { - conn.RLock() - defer conn.RUnlock() - return conn.pmtuVerified -} - // Called by the connection's actor process, by the connection's // heartbeat process and by the connection's TCP received // process. StackFrag is read in conn.Forward (called by router udp diff --git a/forwarder.go b/forwarder.go index aecaa787..74c168b8 100644 --- a/forwarder.go +++ b/forwarder.go @@ -49,7 +49,7 @@ func (conn *LocalConnection) ensureForwarders() error { conn.stopForward = stopForward conn.stopForwardDF = stopForwardDF conn.verifyPMTU = verifyPMTU - conn.effectivePMTU = forwarder.effectivePMTU + conn.effectivePMTU = forwarder.unverifiedPMTU conn.Unlock() go forwarder.Run() @@ -183,16 +183,17 @@ func fragment(eth layers.Ethernet, ip layers.IPv4, pmtu int, frame *ForwardedFra // Forwarder -func NewForwarder(conn *LocalConnection, ch <-chan *ForwardedFrame, stop <-chan interface{}, pmtuVerified <-chan int, enc Encryptor, udpSender UDPSender, pmtu int) *Forwarder { - forwarder := &Forwarder{ - conn: conn, - ch: ch, - stop: stop, - pmtuVerified: pmtuVerified, - enc: enc, - udpSender: udpSender} - forwarder.setMaxPayloadAndEffectivePMTU(pmtu) - return forwarder +func NewForwarder(conn *LocalConnection, ch <-chan *ForwardedFrame, stop <-chan interface{}, verifyPMTU <-chan int, enc Encryptor, udpSender UDPSender, pmtu int) *Forwarder { + fwd := &Forwarder{ + conn: conn, + ch: ch, + stop: stop, + verifyPMTU: verifyPMTU, + enc: enc, + udpSender: udpSender} + fwd.unverifiedPMTU = pmtu - fwd.effectiveOverhead() + fwd.maxPayload = pmtu - UDPOverhead + return fwd } func (fwd *Forwarder) Run() { @@ -205,24 +206,36 @@ func (fwd *Forwarder) Run() { case <-fwd.stop: fwd.drain() return - case <-fwd.pmtuVerifyTick: + case <-fwd.verifyPMTUTick: // We only do this case here when we know the buffers are // all empty so that we don't risk appending verify-frames // to other data. - fwd.pmtuVerifyTick = nil - if !fwd.conn.isEffectivePMTUVerfied() { - if fwd.pmtuVerifyCount == 0 { - fwd.pmtuVerifyCount = PMTUVerifyAttempts - fwd.effectivePMTU -= 8 - fwd.maxPayload -= 8 - fwd.conn.setEffectivePMTU(fwd.effectivePMTU) - } else { - fwd.pmtuVerifyCount-- - } - fwd.verifyEffectivePMTU() + fwd.verifyPMTUTick = nil + if fwd.pmtuVerified { + continue + } + if fwd.pmtuVerifyCount > 0 { + fwd.pmtuVerifyCount-- + fwd.attemptVerifyEffectivePMTU() + } else { + // we've exceeded the verification attempts of the + // unverifiedPMTU + fwd.lowestBadPMTU = fwd.unverifiedPMTU + fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2) + } + case epmtu := <-fwd.verifyPMTU: + if fwd.pmtuVerified || epmtu != fwd.unverifiedPMTU { + continue + } + if epmtu+1 < fwd.lowestBadPMTU { + fwd.highestGoodPMTU = fwd.unverifiedPMTU // = epmtu + fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2) + } else { + fwd.pmtuVerified = true + fwd.maxPayload = epmtu + fwd.effectiveOverhead() - UDPOverhead + fwd.conn.setEffectivePMTU(epmtu) + fwd.conn.log("Effective PMTU verified at", epmtu) } - case pmtu := <-fwd.pmtuVerified: - fwd.conn.effectivePMTUVerification(pmtu) case frame = <-fwd.ch: if !fwd.appendFrame(frame) { fwd.logDrop(frame) @@ -250,20 +263,25 @@ func (fwd *Forwarder) Run() { } } -func (fwd *Forwarder) setMaxPayloadAndEffectivePMTU(pmtu int) { - fwd.maxPayload = pmtu - UDPOverhead - fwd.effectivePMTU = fwd.maxPayload - fwd.enc.PacketOverhead() - fwd.enc.FrameOverhead() - EthernetOverhead +func (fwd *Forwarder) effectiveOverhead() int { + return UDPOverhead + fwd.enc.PacketOverhead() + fwd.enc.FrameOverhead() + EthernetOverhead } -func (fwd *Forwarder) verifyEffectivePMTU() { +func (fwd *Forwarder) verifyEffectivePMTU(newUnverifiedPMTU int) { + fwd.unverifiedPMTU = newUnverifiedPMTU + fwd.pmtuVerifyCount = PMTUVerifyAttempts + fwd.attemptVerifyEffectivePMTU() +} + +func (fwd *Forwarder) attemptVerifyEffectivePMTU() { pmtuVerifyFrame := &ForwardedFrame{ srcPeer: fwd.conn.local, dstPeer: fwd.conn.remote, - frame: make([]byte, fwd.effectivePMTU+EthernetOverhead)} + frame: make([]byte, fwd.unverifiedPMTU+EthernetOverhead)} fwd.enc.AppendFrame(pmtuVerifyFrame) fwd.flush() - if fwd.pmtuVerifyTick == nil { - fwd.pmtuVerifyTick = time.After(PMTUVerifyTimeout << (PMTUVerifyAttempts - fwd.pmtuVerifyCount)) + if fwd.verifyPMTUTick == nil { + fwd.verifyPMTUTick = time.After(PMTUVerifyTimeout << (PMTUVerifyAttempts - fwd.pmtuVerifyCount)) } } @@ -280,10 +298,16 @@ func (fwd *Forwarder) flush() { err := fwd.udpSender.Send(fwd.enc.Bytes()) if err != nil { if mtbe, ok := err.(MsgTooBigError); ok { - fwd.setMaxPayloadAndEffectivePMTU(mtbe.PMTU) - fwd.conn.setEffectivePMTU(fwd.effectivePMTU) - fwd.pmtuVerifyCount = PMTUVerifyAttempts - fwd.verifyEffectivePMTU() + newUnverifiedPMTU := mtbe.PMTU - fwd.effectiveOverhead() + if newUnverifiedPMTU >= fwd.unverifiedPMTU { + return + } + fwd.pmtuVerified = false + fwd.maxPayload = mtbe.PMTU - UDPOverhead + fwd.highestGoodPMTU = 8 + fwd.lowestBadPMTU = newUnverifiedPMTU + 1 + fwd.conn.setEffectivePMTU(newUnverifiedPMTU) + fwd.verifyEffectivePMTU(newUnverifiedPMTU) } else if PosixError(err) == syscall.ENOBUFS { // TODO handle this better } else { @@ -306,7 +330,7 @@ func (fwd *Forwarder) drain() { } func (fwd *Forwarder) logDrop(frame *ForwardedFrame) { - fwd.conn.log("dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.effectivePMTU) + fwd.conn.log("Dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.maxPayload+UDPOverhead-fwd.effectiveOverhead()) } // UDP Senders diff --git a/types.go b/types.go index 7826fb5f..020fba18 100644 --- a/types.go +++ b/types.go @@ -66,7 +66,6 @@ type LocalConnection struct { established bool stackFrag bool effectivePMTU int - pmtuVerified bool SessionKey *[32]byte heartbeatStop chan<- interface{} forwardChan chan<- *ForwardedFrame @@ -90,13 +89,16 @@ type Forwarder struct { conn *LocalConnection ch <-chan *ForwardedFrame stop <-chan interface{} - pmtuVerifyTick <-chan time.Time - pmtuVerified <-chan int + verifyPMTUTick <-chan time.Time + verifyPMTU <-chan int pmtuVerifyCount uint enc Encryptor udpSender UDPSender maxPayload int - effectivePMTU int + pmtuVerified bool + highestGoodPMTU int + unverifiedPMTU int + lowestBadPMTU int } type UDPPacket struct {