Skip to content
Snippets Groups Projects
fsm2.go 5.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Herms's avatar
    Oliver Herms committed
    package server
    
    import (
    	"fmt"
    	"net"
    
    Oliver Herms's avatar
    Oliver Herms committed
    	"sync"
    
    Oliver Herms's avatar
    Oliver Herms committed
    	"time"
    
    	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
    	"github.com/bio-routing/bio-rd/routingtable"
    
    Oliver Herms's avatar
    Oliver Herms committed
    	log "github.com/sirupsen/logrus"
    )
    
    const (
    	// Administrative events
    	ManualStart                               = 1
    	ManualStop                                = 2
    	AutomaticStart                            = 3
    	ManualStartWithPassiveTcpEstablishment    = 4
    	AutomaticStartWithPassiveTcpEstablishment = 5
    	AutomaticStop                             = 8
    	Cease                                     = 100
    
    Oliver Herms's avatar
    Oliver Herms committed
    )
    
    type state interface {
    	run() (state, string)
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    // FSM implements the BGP finite state machine (RFC4271)
    type FSM struct {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	peer        *Peer
    	eventCh     chan int
    	con         net.Conn
    	conCh       chan net.Conn
    	initiateCon chan struct{}
    	conErrCh    chan error
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	delayOpen      bool
    	delayOpenTime  time.Duration
    	delayOpenTimer *time.Timer
    
    	connectRetryTime    time.Duration
    	connectRetryTimer   *time.Timer
    	connectRetryCounter int
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	holdTime  time.Duration
    	holdTimer *time.Timer
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	keepaliveTime  time.Duration
    	keepaliveTimer *time.Timer
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	msgRecvCh     chan []byte
    	msgRecvFailCh chan error
    
    Oliver Herms's avatar
    Oliver Herms committed
    	stopMsgRecvCh chan struct{}
    
    	capAddPathSend bool
    	capAddPathRecv bool
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	local net.IP
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	ribsInitialized bool
    
    Oliver Herms's avatar
    Oliver Herms committed
    	adjRIBIn        routingtable.RouteTableClient
    
    Oliver Herms's avatar
    Oliver Herms committed
    	adjRIBOut       routingtable.RouteTableClient
    
    Oliver Herms's avatar
    Oliver Herms committed
    	rib             routingtable.RouteTableClient
    
    Oliver Herms's avatar
    Oliver Herms committed
    	updateSender    routingtable.RouteTableClient
    
    	neighborID uint32
    	state      state
    
    Oliver Herms's avatar
    Oliver Herms committed
    	stateMu    sync.RWMutex
    
    Oliver Herms's avatar
    Oliver Herms committed
    	reason     string
    	active     bool
    }
    
    // NewPassiveFSM2 initiates a new passive FSM
    
    Oliver Herms's avatar
    Oliver Herms committed
    func NewPassiveFSM2(peer *Peer, con *net.TCPConn) *FSM {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fsm := newFSM2(peer)
    	fsm.con = con
    	fsm.state = newIdleState(fsm)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return fsm
    }
    
    // NewActiveFSM2 initiates a new passive FSM
    
    Oliver Herms's avatar
    Oliver Herms committed
    func NewActiveFSM2(peer *Peer) *FSM {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fsm := newFSM2(peer)
    	fsm.active = true
    	fsm.state = newIdleState(fsm)
    	return fsm
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func newFSM2(peer *Peer) *FSM {
    	return &FSM{
    
    Oliver Herms's avatar
    Oliver Herms committed
    		connectRetryTime: time.Minute,
    		peer:             peer,
    		eventCh:          make(chan int),
    		conCh:            make(chan net.Conn),
    		conErrCh:         make(chan error),
    		initiateCon:      make(chan struct{}),
    		msgRecvCh:        make(chan []byte),
    		msgRecvFailCh:    make(chan error),
    		stopMsgRecvCh:    make(chan struct{}),
    		rib:              peer.rib,
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) start() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	go fsm.run()
    	go fsm.tcpConnector()
    	return
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) activate() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fsm.eventCh <- AutomaticStart
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) run() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	next, reason := fsm.state.run()
    	for {
    		newState := stateName(next)
    		oldState := stateName(fsm.state)
    
    		if oldState != newState {
    			log.WithFields(log.Fields{
    				"peer":       fsm.peer.addr.String(),
    				"last_state": oldState,
    				"new_state":  newState,
    				"reason":     reason,
    			}).Info("FSM: Neighbor state change")
    		}
    
    		if newState == "cease" {
    			return
    		}
    
    		fsm.stateMu.Lock()
    		fsm.state = next
    		fsm.stateMu.Unlock()
    
    		next, reason = fsm.state.run()
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func stateName(s state) string {
    	switch s.(type) {
    	case *idleState:
    		return "idle"
    	case *connectState:
    		return "connect"
    	case *activeState:
    		return "active"
    	case *openSentState:
    		return "openSent"
    	case *openConfirmState:
    		return "openConfirm"
    	case *establishedState:
    		return "established"
    	case *ceaseState:
    		return "cease"
    	default:
    		panic(fmt.Sprintf("Unknown state: %v", s))
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) cease() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fsm.eventCh <- Cease
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) tcpConnector() error {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	for {
    		select {
    		case <-fsm.initiateCon:
    			c, err := net.DialTCP("tcp", &net.TCPAddr{IP: fsm.local}, &net.TCPAddr{IP: fsm.peer.addr, Port: BGPPORT})
    			if err != nil {
    				select {
    				case fsm.conErrCh <- err:
    					continue
    				case <-time.NewTimer(time.Second * 30).C:
    					continue
    				}
    			}
    
    			select {
    			case fsm.conCh <- c:
    				continue
    			case <-time.NewTimer(time.Second * 30).C:
    				c.Close()
    				continue
    			}
    		}
    	}
    }
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) tcpConnect() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fsm.initiateCon <- struct{}{}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) msgReceiver() error {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	for {
    		msg, err := recvMsg(fsm.con)
    		if err != nil {
    			fsm.msgRecvFailCh <- err
    			return nil
    		}
    		fsm.msgRecvCh <- msg
    	}
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) startConnectRetryTimer() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fsm.connectRetryTimer = time.NewTimer(fsm.connectRetryTime)
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) resetConnectRetryTimer() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if !fsm.connectRetryTimer.Reset(fsm.connectRetryTime) {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		<-fsm.connectRetryTimer.C
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) resetConnectRetryCounter() {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	fsm.connectRetryCounter = 0
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) sendOpen() error {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	msg := packet.SerializeOpenMsg(&packet.BGPOpen{
    		Version:       BGPVersion,
    
    Oliver Herms's avatar
    Oliver Herms committed
    		AS:            uint16(fsm.peer.localASN),
    		HoldTime:      uint16(fsm.peer.holdTime / time.Second),
    		BGPIdentifier: fsm.peer.server.routerID,
    
    Oliver Herms's avatar
    Oliver Herms committed
    		OptParams:     fsm.peer.optOpenParams,
    	})
    
    	_, err := fsm.con.Write(msg)
    	if err != nil {
    		return fmt.Errorf("Unable to send OPEN message: %v", err)
    	}
    
    	return nil
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) sendNotification(errorCode uint8, errorSubCode uint8) error {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	msg := packet.SerializeNotificationMsg(&packet.BGPNotification{})
    
    	_, err := fsm.con.Write(msg)
    	if err != nil {
    		return fmt.Errorf("Unable to send NOTIFICATION message: %v", err)
    	}
    
    	return nil
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (fsm *FSM) sendKeepalive() error {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	msg := packet.SerializeKeepaliveMsg()
    
    	_, err := fsm.con.Write(msg)
    	if err != nil {
    		return fmt.Errorf("Unable to send KEEPALIVE message: %v", err)
    	}
    
    	return nil
    }
    
    func stopTimer(t *time.Timer) {
    	if !t.Stop() {
    		select {
    		case <-t.C:
    		default:
    		}
    	}
    }