Implement correct keepalive handling.
The session logic now takes the lower of the two hold times, and
derives a keepalive from that, rather than assume we always send
the lowest hold time.
@@ -29,10 +29,13 @@ type Session struct {
peerASN uint32
holdTime time.Duration
+ newHoldTime chan bool
+
mu sync.Mutex
cond *sync.Cond
closed bool
conn net.Conn
+ actualHoldTime time.Duration
advertised map[string]*Advertisement
changed map[string]*net.IPNet
pending *list.List
@@ -124,9 +127,13 @@ func (s *Session) connect() error {
conn.Close()
return fmt.Errorf("read OPEN from %q: %s", s.addr, err)
}
- hold := s.holdTime
- if requestedHold < hold {
- hold = requestedHold
+ s.actualHoldTime = s.holdTime
+ if requestedHold < s.actualHoldTime {
+ s.actualHoldTime = requestedHold
+ }
+ select {
+ case s.newHoldTime <- true:
+ default:
}
// Consume BGP messages until the connection closes.
@@ -143,8 +150,28 @@ func (s *Session) connect() error {
}
func (s *Session) sendKeepalives() {
+ var (
+ t *time.Ticker
+ ch <-chan time.Time
+ )
+
for {
- time.Sleep(s.holdTime / 3)
+ select {
+ case <-s.newHoldTime:
+ s.mu.Lock()
+ ht := s.actualHoldTime
+ s.mu.Unlock()
+ if t != nil {
+ t.Stop()
+ t = nil
+ ch = nil
+ }
+ if ht != 0 {
+ t = time.NewTicker(ht / 3)
+ ch = t.C
+ }
+
+ case <-ch:
if err := s.sendKeepalive(); err != nil {
if err == errClosed {
// Session has been closed by package caller, we're
@@ -155,6 +182,7 @@ func (s *Session) sendKeepalives() {
}
}
}
+}
func (s *Session) sendKeepalive() error {
s.mu.Lock()
@@ -309,9 +337,9 @@ func New(addr string, asn uint32, routerID net.IP, peerASN uint32, holdTime time
routerID: routerID.To4(),
peerASN: peerASN,
holdTime: holdTime,
+ newHoldTime: make(chan bool, 1),
advertised: map[string]*Advertisement{},
changed: map[string]*net.IPNet{},
- newKeepalive: make(chan (<-chan time.Time)),
}
if ret.routerID == nil {
return nil, fmt.Errorf("invalid routerID %q, must be IPv4", routerID)
