Skip to content
Snippets Groups Projects
fsm_open_sent.go 5.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Herms's avatar
    Oliver Herms committed
    package server
    
    import (
    	"bytes"
    	"fmt"
    	"math"
    	"time"
    
    	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
    )
    
    type openSentState struct {
    
    	fsm         *FSM
    	peerASNRcvd uint32
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func newOpenSentState(fsm *FSM) *openSentState {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return &openSentState{
    		fsm: fsm,
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (s openSentState) run() (state, string) {
    	go s.fsm.msgReceiver()
    
    Oliver Herms's avatar
    Oliver Herms committed
    	for {
    		select {
    		case e := <-s.fsm.eventCh:
    			switch e {
    			case ManualStop:
    				return s.manualStop()
    			case AutomaticStop:
    				return s.automaticStop()
    
    Oliver Herms's avatar
    Oliver Herms committed
    			case Cease:
    				return s.cease()
    
    Oliver Herms's avatar
    Oliver Herms committed
    			default:
    				continue
    			}
    		case <-s.fsm.holdTimer.C:
    			return s.holdTimerExpired()
    		case recvMsg := <-s.fsm.msgRecvCh:
    			return s.msgReceived(recvMsg)
    		}
    	}
    }
    
    func (s *openSentState) manualStop() (state, string) {
    	s.fsm.sendNotification(packet.Cease, 0)
    	s.fsm.resetConnectRetryTimer()
    	s.fsm.con.Close()
    	s.fsm.resetConnectRetryCounter()
    	return newIdleState(s.fsm), "Manual stop event"
    }
    
    func (s *openSentState) automaticStop() (state, string) {
    	s.fsm.sendNotification(packet.Cease, 0)
    	s.fsm.resetConnectRetryTimer()
    	s.fsm.con.Close()
    	s.fsm.connectRetryCounter++
    	return newIdleState(s.fsm), "Automatic stop event"
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (s *openSentState) cease() (state, string) {
    	s.fsm.sendNotification(packet.Cease, 0)
    	s.fsm.con.Close()
    	return newCeaseState(), "Cease"
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (s *openSentState) holdTimerExpired() (state, string) {
    	s.fsm.sendNotification(packet.HoldTimeExpired, 0)
    	stopTimer(s.fsm.connectRetryTimer)
    	s.fsm.con.Close()
    	s.fsm.connectRetryCounter++
    	return newIdleState(s.fsm), "Holdtimer expired"
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (s *openSentState) msgReceived(data []byte) (state, string) {
    	msg, err := packet.Decode(bytes.NewBuffer(data))
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if err != nil {
    		switch bgperr := err.(type) {
    		case packet.BGPError:
    			s.fsm.sendNotification(bgperr.ErrorCode, bgperr.ErrorSubCode)
    		}
    		stopTimer(s.fsm.connectRetryTimer)
    		s.fsm.con.Close()
    		s.fsm.connectRetryCounter++
    		return newIdleState(s.fsm), fmt.Sprintf("Failed to decode BGP message: %v", err)
    	}
    	switch msg.Header.Type {
    	case packet.NotificationMsg:
    		return s.notification(msg)
    	case packet.OpenMsg:
    		return s.openMsgReceived(msg)
    	default:
    		return s.unexpectedMessage()
    	}
    }
    
    func (s *openSentState) unexpectedMessage() (state, string) {
    	s.fsm.sendNotification(packet.FiniteStateMachineError, 0)
    	stopTimer(s.fsm.connectRetryTimer)
    	s.fsm.con.Close()
    	s.fsm.connectRetryCounter++
    	return newIdleState(s.fsm), "FSM Error"
    }
    
    func (s *openSentState) openMsgReceived(msg *packet.BGPMessage) (state, string) {
    	openMsg := msg.Body.(*packet.BGPOpen)
    
    	s.peerASNRcvd = uint32(openMsg.ASN)
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	s.fsm.neighborID = openMsg.BGPIdentifier
    	stopTimer(s.fsm.connectRetryTimer)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if s.fsm.peer.collisionHandling(s.fsm) {
    		return s.cease()
    	}
    
    Oliver Herms's avatar
    Oliver Herms committed
    	err := s.fsm.sendKeepalive()
    	if err != nil {
    		return s.tcpFailure()
    	}
    
    
    	return s.handleOpenMessage(openMsg)
    }
    
    func (s *openSentState) handleOpenMessage(openMsg *packet.BGPOpen) (state, string) {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	s.fsm.holdTime = time.Duration(math.Min(float64(s.fsm.peer.holdTime), float64(time.Duration(openMsg.HoldTime)*time.Second)))
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if s.fsm.holdTime != 0 {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		if !s.fsm.holdTimer.Reset(s.fsm.holdTime) {
    			<-s.fsm.holdTimer.C
    		}
    
    Oliver Herms's avatar
    Oliver Herms committed
    		s.fsm.keepaliveTime = s.fsm.holdTime / 3
    
    Oliver Herms's avatar
    Oliver Herms committed
    		s.fsm.keepaliveTimer = time.NewTimer(s.fsm.keepaliveTime)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    
    
    	s.peerASNRcvd = uint32(openMsg.ASN)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	s.processOpenOptions(openMsg.OptParams)
    
    
    	if s.peerASNRcvd != s.fsm.peer.peerASN {
    		return newCeaseState(), fmt.Sprintf("Expected session from %d, got open message with ASN %d", s.fsm.peer.peerASN, s.peerASNRcvd)
    	}
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return newOpenConfirmState(s.fsm), "Received OPEN message"
    }
    
    func (s *openSentState) tcpFailure() (state, string) {
    	s.fsm.con.Close()
    	s.fsm.resetConnectRetryTimer()
    	return newActiveState(s.fsm), "TCP connection failure"
    }
    
    func (s *openSentState) processOpenOptions(optParams []packet.OptParam) {
    	for _, optParam := range optParams {
    		if optParam.Type != packet.CapabilitiesParamType {
    			continue
    		}
    
    		s.processCapabilities(optParam.Value.(packet.Capabilities))
    	}
    }
    
    func (s *openSentState) processCapabilities(caps packet.Capabilities) {
    	for _, cap := range caps {
    		s.processCapability(cap)
    	}
    }
    
    func (s *openSentState) processCapability(cap packet.Capability) {
    	switch cap.Code {
    	case packet.AddPathCapabilityCode:
    		s.processAddPathCapability(cap.Value.(packet.AddPathCapability))
    
    	case packet.ASN4CapabilityCode:
    		s.processASN4Capability(cap.Value.(packet.ASN4Capability))
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    }
    
    func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapability) {
    	if addPathCap.AFI != 1 {
    		return
    	}
    	if addPathCap.SAFI != 1 {
    		return
    	}
    
    	switch addPathCap.SendReceive {
    	case packet.AddPathReceive:
    		if !s.fsm.peer.addPathSend.BestOnly {
    			s.fsm.capAddPathSend = true
    		}
    	case packet.AddPathSend:
    		if s.fsm.peer.addPathRecv {
    			s.fsm.capAddPathRecv = true
    		}
    	case packet.AddPathSendReceive:
    		if !s.fsm.peer.addPathSend.BestOnly {
    			s.fsm.capAddPathSend = true
    		}
    		if s.fsm.peer.addPathRecv {
    			s.fsm.capAddPathRecv = true
    		}
    	}
    }
    
    
    func (s *openSentState) processASN4Capability(cap packet.ASN4Capability) {
    	if s.peerASNRcvd == packet.ASTransASN {
    		s.peerASNRcvd = cap.ASN4
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (s *openSentState) notification(msg *packet.BGPMessage) (state, string) {
    	stopTimer(s.fsm.connectRetryTimer)
    	s.fsm.con.Close()
    	nMsg := msg.Body.(*packet.BGPNotification)
    	if nMsg.ErrorCode != packet.UnsupportedVersionNumber {
    		s.fsm.connectRetryCounter++
    	}
    
    	return newIdleState(s.fsm), "Received NOTIFICATION"
    }