Skip to content
Snippets Groups Projects
fsm_open_confirm.go 3.23 KiB
Newer Older
Oliver Herms's avatar
Oliver Herms committed
package server

import (
	"bytes"
	"fmt"

	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
)

type openConfirmState struct {
Oliver Herms's avatar
Oliver Herms committed
	fsm *FSM
Oliver Herms's avatar
Oliver Herms committed
}

Oliver Herms's avatar
Oliver Herms committed
func newOpenConfirmState(fsm *FSM) *openConfirmState {
Oliver Herms's avatar
Oliver Herms committed
	return &openConfirmState{
		fsm: fsm,
	}
}

Oliver Herms's avatar
Oliver Herms committed
func (s openConfirmState) run() (state, string) {
Oliver Herms's avatar
Oliver Herms committed
	for {
		select {
		case e := <-s.fsm.eventCh:
Oliver Herms's avatar
Oliver Herms committed
			switch e {
			case ManualStop:
Oliver Herms's avatar
Oliver Herms committed
				return s.manualStop()
Oliver Herms's avatar
Oliver Herms committed
			case Cease:
				return s.cease()
			default:
				continue
Oliver Herms's avatar
Oliver Herms committed
			}
		case <-s.fsm.holdTimer.C:
			return s.holdTimerExpired()
		case <-s.fsm.keepaliveTimer.C:
			return s.keepaliveTimerExpired()
		case recvMsg := <-s.fsm.msgRecvCh:
			return s.msgReceived(recvMsg)
		}
	}
}

func (s *openConfirmState) manualStop() (state, string) {
	s.fsm.sendNotification(packet.Cease, 0)
	stopTimer(s.fsm.connectRetryTimer)
	s.fsm.con.Close()
	s.fsm.resetConnectRetryCounter()
	return newIdleState(s.fsm), "Manual stop event"
}

func (s *openConfirmState) automaticStop() (state, string) {
	s.fsm.sendNotification(packet.Cease, 0)
	stopTimer(s.fsm.connectRetryTimer)
	s.fsm.con.Close()
	s.fsm.connectRetryCounter++
	return newIdleState(s.fsm), "Automatic stop event"
}

Oliver Herms's avatar
Oliver Herms committed
func (s *openConfirmState) 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 *openConfirmState) 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"
}

func (s *openConfirmState) keepaliveTimerExpired() (state, string) {
	err := s.fsm.sendKeepalive()
	if err != nil {
		stopTimer(s.fsm.connectRetryTimer)
		s.fsm.con.Close()
		s.fsm.connectRetryCounter++
		return newIdleState(s.fsm), fmt.Sprintf("Failed to send keepalive: %v", err)
	}
Oliver Herms's avatar
Oliver Herms committed
	s.fsm.keepaliveTimer.Reset(s.fsm.keepaliveTime)
Oliver Herms's avatar
Oliver Herms committed
	return newOpenConfirmState(s.fsm), s.fsm.reason
}

Oliver Herms's avatar
Oliver Herms committed
func (s *openConfirmState) 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.KeepaliveMsg:
		return s.keepaliveReceived()
	default:
		return s.unexpectedMessage()
	}
}

func (s *openConfirmState) 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"
}

func (s *openConfirmState) keepaliveReceived() (state, string) {
Oliver Herms's avatar
Oliver Herms committed
	s.fsm.holdTimer.Reset(s.fsm.holdTime)
Oliver Herms's avatar
Oliver Herms committed
	return newEstablishedState(s.fsm), "Received KEEPALIVE"
}

func (s *openConfirmState) 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"
}