From 407694232ab53489cd6a4c9b3149dc10fa2ca66b Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Tue, 22 Oct 2019 18:43:09 +0100 Subject: [PATCH] Measure roundtrip times on link --- tunnel/default.go | 38 +++++++++++++++++++++++++++++++++++++- tunnel/link.go | 26 ++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 73052581..5ba6b868 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -966,7 +966,7 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { c.mode = options.Mode // set the dial timeout c.timeout = options.Timeout - + // get the current time now := time.Now() after := func() time.Duration { @@ -980,6 +980,8 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { } var links []string + // did we measure the rtt + var measured bool // non multicast so we need to find the link if id := options.Link; id != "" { @@ -1021,6 +1023,9 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // shit fuck if !c.discovered { + // piggy back roundtrip + nowRTT := time.Now() + // create a new discovery message for this channel msg := c.newMessage("discover") msg.mode = Broadcast @@ -1075,12 +1080,31 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { return nil, err } + // set roundtrip + d := time.Since(nowRTT) + + // set the link time + t.RLock() + link, ok := t.links[msg.link] + t.RUnlock() + + if ok { + // set the rountrip time + link.setRTT(d) + } + + // set measured to true + measured = true + // set discovered to true c.discovered = true } // a unicast session so we call "open" and wait for an "accept" + // reset now in case we use it + now = time.Now() + // try to open the session err := c.Open() if err != nil { @@ -1089,6 +1113,18 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { return nil, err } + // if we haven't measured the roundtrip do it now + if !measured && c.mode == Unicast { + // set the link time + t.RLock() + link, ok := t.links[c.link] + t.RUnlock() + if ok { + // set the rountrip time + link.setRTT(time.Since(now)) + } + } + return c, nil } diff --git a/tunnel/link.go b/tunnel/link.go index 22362198..91636075 100644 --- a/tunnel/link.go +++ b/tunnel/link.go @@ -32,6 +32,9 @@ type link struct { // channels keeps a mapping of channels and last seen channels map[string]time.Time + // the weighed moving average roundtrip + rtt int64 + // keep an error count on the link errCount int } @@ -48,6 +51,21 @@ func newLink(s transport.Socket) *link { return l } +func (l *link) setRTT(d time.Duration) { + l.Lock() + defer l.Unlock() + + if l.rtt < 0 { + l.rtt = d.Nanoseconds() + return + } + + // https://fishi.devtail.io/weblog/2015/04/12/measuring-bandwidth-and-round-trip-time-tcp-connection-inside-application-layer/ + rtt := 0.8*float64(l.rtt) + 0.2*float64(d.Nanoseconds()) + // set new rtt + l.rtt = int64(rtt) +} + // watches the channel expiry func (l *link) expiry() { t := time.NewTicker(time.Minute) @@ -95,9 +113,13 @@ func (l *link) Rate() float64 { return float64(10e8) } -// Length returns the roundtrip time as nanoseconds (lower is better) +// Length returns the roundtrip time as nanoseconds (lower is better). +// Returns 0 where no measurement has been taken. func (l *link) Length() int64 { - return time.Second.Nanoseconds() + l.RLock() + defer l.RUnlock() + + return l.rtt } func (l *link) Id() string {