Skip to content
Snippets Groups Projects
fsm_established.go 6.72 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Herms's avatar
    Oliver Herms committed
    package server
    
    import (
    	"bytes"
    	"fmt"
    
    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"
    	"github.com/bio-routing/bio-rd/routingtable/adjRIBIn"
    	"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
    )
    
    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
    	}
    
    	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.holdTimer.C:
    			return s.holdTimerExpired()
    		case <-s.fsm.keepaliveTimer.C:
    			return s.keepaliveTimerExpired()
    		case recvMsg := <-s.fsm.msgRecvCh:
    			return s.msgReceived(recvMsg)
    		}
    	}
    }
    
    
    func (s *establishedState) init() error {
    	s.fsm.adjRIBIn = adjRIBIn.New(s.fsm.peer.importFilter)
    	s.fsm.adjRIBIn.Register(s.fsm.rib)
    
    	host, _, err := net.SplitHostPort(s.fsm.con.LocalAddr().String())
    	if err != nil {
    		return fmt.Errorf("Unable to get local address: %v", err)
    	}
    	hostIP := net.ParseIP(host)
    	if hostIP == nil {
    		return fmt.Errorf("Unable to parse address: %v", err)
    	}
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	n := &routingtable.Neighbor{
    
    		Type:              route.BGPPathType,
    		Address:           bnet.IPv4ToUint32(s.fsm.peer.addr),
    		IBGP:              s.fsm.peer.localASN == s.fsm.peer.peerASN,
    		LocalASN:          s.fsm.peer.localASN,
    		RouteServerClient: s.fsm.peer.routeServerClient,
    		LocalAddress:      bnet.IPv4ToUint32(hostIP),
    		CapAddPathRX:      s.fsm.capAddPathSend,
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    
    
    	s.fsm.adjRIBOut = adjRIBOut.New(n, s.fsm.peer.exportFilter)
    
    	clientOptions := routingtable.ClientOptions{
    		BestOnly: true,
    	}
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if s.fsm.capAddPathSend {
    		s.fsm.updateSender = newUpdateSenderAddPath(s.fsm)
    		clientOptions = s.fsm.peer.addPathSend
    	} else {
    		s.fsm.updateSender = newUpdateSender(s.fsm)
    	}
    
    	s.fsm.adjRIBOut.Register(s.fsm.updateSender)
    
    	s.fsm.rib.RegisterWithOptions(s.fsm.adjRIBOut, clientOptions)
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	s.fsm.ribsInitialized = true
    
    	return nil
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    func (s *establishedState) uninit() {
    
    	s.fsm.adjRIBIn.Unregister(s.fsm.rib)
    	s.fsm.rib.Unregister(s.fsm.adjRIBOut)
    	s.fsm.adjRIBOut.Unregister(s.fsm.updateSender)
    
    
    	s.fsm.adjRIBIn = nil
    	s.fsm.adjRIBOut = nil
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	s.fsm.ribsInitialized = false
    }
    
    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 {
    		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
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (s *establishedState) msgReceived(data []byte) (state, string) {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fmt.Printf("Processing MSG\n")
    
    Oliver Herms's avatar
    Oliver Herms committed
    	msg, err := packet.Decode(bytes.NewBuffer(data))
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if err != nil {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		fmt.Printf("Decode failure: %v\n", err)
    
    Oliver Herms's avatar
    Oliver Herms committed
    		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), "Failed to decode BGP message"
    	}
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fmt.Printf("Msg type: %d\n", msg.Header.Type)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	switch msg.Header.Type {
    	case packet.NotificationMsg:
    		return s.notification()
    	case packet.UpdateMsg:
    		return s.update(msg)
    
    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(msg *packet.BGPMessage) (state, string) {
    	if s.fsm.holdTime != 0 {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		s.fsm.holdTimer.Reset(s.fsm.holdTime)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    
    	u := msg.Body.(*packet.BGPUpdate)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fmt.Printf("Processing withdraws\n")
    
    Oliver Herms's avatar
    Oliver Herms committed
    	s.withdraws(u)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fmt.Printf("Processing advertisements\n")
    
    Oliver Herms's avatar
    Oliver Herms committed
    	s.updates(u)
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fmt.Printf("update done\n")
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return newEstablishedState(s.fsm), s.fsm.reason
    }
    
    func (s *establishedState) withdraws(u *packet.BGPUpdate) {
    	for r := u.WithdrawnRoutes; r != nil; r = r.Next {
    
    		pfx := bnet.NewPfx(r.IP, r.Pfxlen)
    
    Oliver Herms's avatar
    Oliver Herms committed
    		s.fsm.adjRIBIn.RemovePath(pfx, nil)
    	}
    }
    
    func (s *establishedState) updates(u *packet.BGPUpdate) {
    	for r := u.NLRI; r != nil; r = r.Next {
    
    		pfx := bnet.NewPfx(r.IP, r.Pfxlen)
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    		path := &route.Path{
    			Type: route.BGPPathType,
    			BGPPath: &route.BGPPath{
    
    				Source: bnet.IPv4ToUint32(s.fsm.peer.addr),
    
    Oliver Herms's avatar
    Oliver Herms committed
    			},
    		}
    
    		for pa := u.PathAttributes; pa != nil; pa = pa.Next {
    			switch pa.TypeCode {
    			case packet.OriginAttr:
    				path.BGPPath.Origin = pa.Value.(uint8)
    			case packet.LocalPrefAttr:
    				path.BGPPath.LocalPref = pa.Value.(uint32)
    			case packet.MEDAttr:
    				path.BGPPath.MED = pa.Value.(uint32)
    			case packet.NextHopAttr:
    				path.BGPPath.NextHop = pa.Value.(uint32)
    			case packet.ASPathAttr:
    				path.BGPPath.ASPath = pa.ASPathString()
    				path.BGPPath.ASPathLen = pa.ASPathLen()
    
    Oliver Herms's avatar
    Oliver Herms committed
    			case packet.CommunitiesAttr:
    				path.BGPPath.Communities = pa.CommunityString()
    			case packet.LargeCommunitiesAttr:
    				path.BGPPath.LargeCommunities = pa.LargeCommunityString()
    
    Oliver Herms's avatar
    Oliver Herms committed
    			}
    		}
    		s.fsm.adjRIBIn.AddPath(pfx, path)
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (s *establishedState) keepaliveReceived() (state, string) {
    	if s.fsm.holdTime != 0 {
    		s.fsm.holdTimer.Reset(s.fsm.holdTime)
    	}
    	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"
    }