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

import (
	"bytes"
	"fmt"
	"time"
Oliver Herms's avatar
Oliver Herms committed

	bnet "github.com/bio-routing/bio-rd/net"
Oliver Herms's avatar
Oliver Herms committed
	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
	"github.com/bio-routing/bio-rd/route"
	"github.com/bio-routing/bio-rd/routingtable"
Julian Kornberger's avatar
Julian Kornberger committed
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
Oliver Herms's avatar
Oliver Herms committed
)

type establishedState 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 newEstablishedState(fsm *FSM) *establishedState {
Oliver Herms's avatar
Oliver Herms committed
	return &establishedState{
		fsm: fsm,
	}
}

Oliver Herms's avatar
Oliver Herms committed
func (s establishedState) run() (state, string) {
Oliver Herms's avatar
Oliver Herms committed
	if !s.fsm.ribsInitialized {
		err := s.init()
		if err != nil {
			return newCeaseState(), fmt.Sprintf("Init failed: %v", err)
		}
Oliver Herms's avatar
Oliver Herms committed
	}

	opt := s.fsm.decodeOptions()
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 AutomaticStop:
Oliver Herms's avatar
Oliver Herms committed
				return s.automaticStop()
Oliver Herms's avatar
Oliver Herms committed
			case Cease:
				return s.cease()
			default:
				continue
Oliver Herms's avatar
Oliver Herms committed
			}
		case <-s.fsm.keepaliveTimer.C:
			return s.keepaliveTimerExpired()
		case <-time.After(time.Second):
			return s.checkHoldtimer()
Oliver Herms's avatar
Oliver Herms committed
		case recvMsg := <-s.fsm.msgRecvCh:
			return s.msgReceived(recvMsg, opt)
Oliver Herms's avatar
Oliver Herms committed
		}
	}
}

func (s *establishedState) checkHoldtimer() (state, string) {
	if time.Since(s.fsm.lastUpdateOrKeepalive) > s.fsm.holdTime {
		return s.holdTimerExpired()
	}

	return newEstablishedState(s.fsm), s.fsm.reason
}

func (s *establishedState) init() error {
	host, _, err := net.SplitHostPort(s.fsm.con.LocalAddr().String())
	if err != nil {
Julian Kornberger's avatar
Julian Kornberger committed
		return errors.Wrap(err, "Unable to get local address")
Julian Kornberger's avatar
Julian Kornberger committed
		return errors.Wrap(err, "Unable to parse address")
Oliver Herms's avatar
Oliver Herms committed

	n := &routingtable.Neighbor{
		Type:                 route.BGPPathType,
		Address:              s.fsm.peer.addr,
		IBGP:                 s.fsm.peer.localASN == s.fsm.peer.peerASN,
		LocalASN:             s.fsm.peer.localASN,
		RouteServerClient:    s.fsm.peer.routeServerClient,
		LocalAddress:         localAddr,
		RouteReflectorClient: s.fsm.peer.routeReflectorClient,
		ClusterID:            s.fsm.peer.clusterID,
Oliver Herms's avatar
Oliver Herms committed
	}

	if s.fsm.ipv4Unicast != nil {
		s.fsm.ipv4Unicast.init(n)
	}

	if s.fsm.ipv6Unicast != nil {
		s.fsm.ipv6Unicast.init(n)
	}

Oliver Herms's avatar
Oliver Herms committed
	s.fsm.ribsInitialized = true
	return nil
Oliver Herms's avatar
Oliver Herms committed
}

func (s *establishedState) uninit() {
	if s.fsm.ipv4Unicast != nil {
		s.fsm.ipv4Unicast.dispose()
	}

	if s.fsm.ipv6Unicast != nil {
		s.fsm.ipv6Unicast.dispose()
	}
Oliver Herms's avatar
Oliver Herms committed
}

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

func (s *establishedState) automaticStop() (state, string) {
	s.fsm.sendNotification(packet.Cease, 0)
	s.uninit()
	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 *establishedState) cease() (state, string) {
	s.fsm.sendNotification(packet.Cease, 0)
	s.uninit()
	s.fsm.con.Close()
	return newCeaseState(), "Cease"
}

Oliver Herms's avatar
Oliver Herms committed
func (s *establishedState) holdTimerExpired() (state, string) {
	s.fsm.sendNotification(packet.HoldTimeExpired, 0)
	s.uninit()
	stopTimer(s.fsm.connectRetryTimer)
	s.fsm.con.Close()
	s.fsm.connectRetryCounter++
	return newIdleState(s.fsm), "Holdtimer expired"
}

func (s *establishedState) keepaliveTimerExpired() (state, string) {
	err := s.fsm.sendKeepalive()
	if err != nil {
takt's avatar
takt committed
		s.uninit()
Oliver Herms's avatar
Oliver Herms committed
		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 newEstablishedState(s.fsm), s.fsm.reason
}

func (s *establishedState) msgReceived(data []byte, opt *packet.DecodeOptions) (state, string) {
	msg, err := packet.Decode(bytes.NewBuffer(data), opt)
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)
		if s.fsm.con != nil {
			s.fsm.con.Close()
		}
Oliver Herms's avatar
Oliver Herms committed
		s.fsm.connectRetryCounter++
		return newIdleState(s.fsm), fmt.Sprintf("Failed to decode BGP message: %v", err)
Oliver Herms's avatar
Oliver Herms committed
	}
Oliver Herms's avatar
Oliver Herms committed
	switch msg.Header.Type {
	case packet.NotificationMsg:
Daniel Czerwonk's avatar
Daniel Czerwonk committed
		fmt.Println(data)
Oliver Herms's avatar
Oliver Herms committed
		return s.notification()
	case packet.UpdateMsg:
		return s.update(msg.Body.(*packet.BGPUpdate))
Oliver Herms's avatar
Oliver Herms committed
	case packet.KeepaliveMsg:
		return s.keepaliveReceived()
Oliver Herms's avatar
Oliver Herms committed
	default:
		return s.unexpectedMessage()
	}
}

func (s *establishedState) notification() (state, string) {
	stopTimer(s.fsm.connectRetryTimer)
	s.uninit()
	s.fsm.con.Close()
	s.fsm.connectRetryCounter++
	return newIdleState(s.fsm), "Received NOTIFICATION"
}

func (s *establishedState) update(u *packet.BGPUpdate) (state, string) {
Oliver Herms's avatar
Oliver Herms committed
	if s.fsm.holdTime != 0 {
		s.fsm.updateLastUpdateOrKeepalive()
Oliver Herms's avatar
Oliver Herms committed
	}

	if s.fsm.ipv4Unicast != nil {
		s.fsm.ipv4Unicast.processUpdate(u)
	}

	if s.fsm.ipv6Unicast != nil {
		s.fsm.ipv6Unicast.processUpdate(u)
	}

	afi, safi := s.updateAddressFamily(u)
Oliver Herms's avatar
Oliver Herms committed

	if safi != packet.UnicastSAFI {
		// only unicast support, so other SAFIs are ignored
		return newEstablishedState(s.fsm), s.fsm.reason
Oliver Herms's avatar
Oliver Herms committed
	}
	switch afi {
	case packet.IPv4AFI:
		if s.fsm.ipv4Unicast == nil {
			log.Warnf("Received update for family IPv4 unicast, but this family is not configured.")
	case packet.IPv6AFI:
		if s.fsm.ipv6Unicast == nil {
			log.Warnf("Received update for family IPv6 unicast, but this family is not configured.")
	return newEstablishedState(s.fsm), s.fsm.reason
func (s *establishedState) updateAddressFamily(u *packet.BGPUpdate) (afi uint16, safi uint8) {
	if u.WithdrawnRoutes != nil || u.NLRI != nil {
		return packet.IPv4AFI, packet.UnicastSAFI
	for cur := u.PathAttributes; cur != nil; cur = cur.Next {
		if cur.TypeCode == packet.MultiProtocolReachNLRICode {
			a := cur.Value.(packet.MultiProtocolReachNLRI)
			return a.AFI, a.SAFI
		if cur.TypeCode == packet.MultiProtocolUnreachNLRICode {
			a := cur.Value.(packet.MultiProtocolUnreachNLRI)
			return a.AFI, a.SAFI
		}
Oliver Herms's avatar
Oliver Herms committed
	}
Oliver Herms's avatar
Oliver Herms committed
}

Oliver Herms's avatar
Oliver Herms committed
func (s *establishedState) keepaliveReceived() (state, string) {
	if s.fsm.holdTime != 0 {
		s.fsm.updateLastUpdateOrKeepalive()
Oliver Herms's avatar
Oliver Herms committed
	}
	return newEstablishedState(s.fsm), s.fsm.reason
}

Oliver Herms's avatar
Oliver Herms committed
func (s *establishedState) unexpectedMessage() (state, string) {
	s.fsm.sendNotification(packet.FiniteStateMachineError, 0)
	s.uninit()
	stopTimer(s.fsm.connectRetryTimer)
	s.fsm.con.Close()
	s.fsm.connectRetryCounter++
	return newIdleState(s.fsm), "FSM Error"
}