diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/fsm.go | 166 | ||||
-rw-r--r-- | server/fsm_test.go | 8 |
2 files changed, 105 insertions, 69 deletions
diff --git a/server/fsm.go b/server/fsm.go index 1d83fa89..3e275ca7 100644 --- a/server/fsm.go +++ b/server/fsm.go @@ -29,6 +29,51 @@ import ( "time" ) +type FsmStateReason int + +const ( + _ FsmStateReason = iota + FSM_DYING + FSM_ADMIN_DOWN + FSM_READ_FAILED + FSM_WRITE_FAILED + FSM_NOTIFICATION_SENT + FSM_NOTIFICATION_RECV + FSM_HOLD_TIMER_EXPIRED + FSM_IDLE_HOLD_TIMER_EXPIRED + FSM_RESTART_TIMER_EXPIRED + FSM_GRACEFUL_RESTART + FSM_INVALID_MSG +) + +func (r FsmStateReason) String() string { + switch r { + case FSM_DYING: + return "dying" + case FSM_ADMIN_DOWN: + return "admin-down" + case FSM_READ_FAILED: + return "read-failed" + case FSM_WRITE_FAILED: + return "write-failed" + case FSM_NOTIFICATION_SENT: + return "notification-sent" + case FSM_NOTIFICATION_RECV: + return "notification-recved" + case FSM_HOLD_TIMER_EXPIRED: + return "hold-timer-expired" + case FSM_IDLE_HOLD_TIMER_EXPIRED: + return "idle-hold-timer-expired" + case FSM_RESTART_TIMER_EXPIRED: + return "restart-timer-expired" + case FSM_GRACEFUL_RESTART: + return "graceful-restart" + case FSM_INVALID_MSG: + return "invalid-msg" + } + return "unknown" +} + type FsmMsgType int const ( @@ -75,6 +120,7 @@ type FSM struct { gConf *config.Global pConf *config.Neighbor state bgp.FSMState + reason FsmStateReason conn net.Conn connCh chan net.Conn idleHoldTime float64 @@ -167,10 +213,11 @@ func NewFSM(gConf *config.Global, pConf *config.Neighbor, policy *table.RoutingP func (fsm *FSM) StateChange(nextState bgp.FSMState) { log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": fsm.pConf.Config.NeighborAddress, - "old": fsm.state.String(), - "new": nextState.String(), + "Topic": "Peer", + "Key": fsm.pConf.Config.NeighborAddress, + "old": fsm.state.String(), + "new": nextState.String(), + "reason": fsm.reason.String(), }).Debug("state changed") fsm.state = nextState switch nextState { @@ -304,18 +351,17 @@ type FSMHandler struct { fsm *FSM conn net.Conn msgCh chan *FsmMsg - errorCh chan bool + errorCh chan FsmStateReason incoming chan *FsmMsg stateCh chan *FsmMsg outgoing chan *bgp.BGPMessage holdTimerResetCh chan bool - reason string } func NewFSMHandler(fsm *FSM, incoming, stateCh chan *FsmMsg, outgoing chan *bgp.BGPMessage) *FSMHandler { h := &FSMHandler{ fsm: fsm, - errorCh: make(chan bool, 2), + errorCh: make(chan FsmStateReason, 2), incoming: incoming, stateCh: stateCh, outgoing: outgoing, @@ -325,14 +371,14 @@ func NewFSMHandler(fsm *FSM, incoming, stateCh chan *FsmMsg, outgoing chan *bgp. return h } -func (h *FSMHandler) idle() bgp.FSMState { +func (h *FSMHandler) idle() (bgp.FSMState, FsmStateReason) { fsm := h.fsm idleHoldTimer := time.NewTimer(time.Second * time.Duration(fsm.idleHoldTime)) for { select { case <-h.t.Dying(): - return 0 + return -1, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break @@ -351,7 +397,7 @@ func (h *FSMHandler) idle() bgp.FSMState { "Duration": fsm.idleHoldTime, }).Debug("IdleHoldTimer expired") fsm.idleHoldTime = HOLDTIME_IDLE - return bgp.BGP_FSM_ACTIVE + return bgp.BGP_FSM_ACTIVE, FSM_IDLE_HOLD_TIMER_EXPIRED } else { log.Debug("IdleHoldTimer expired, but stay at idle because the admin state is DOWN") @@ -374,12 +420,12 @@ func (h *FSMHandler) idle() bgp.FSMState { } } -func (h *FSMHandler) active() bgp.FSMState { +func (h *FSMHandler) active() (bgp.FSMState, FsmStateReason) { fsm := h.fsm for { select { case <-h.t.Dying(): - return 0 + return -1, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break @@ -396,15 +442,15 @@ func (h *FSMHandler) active() bgp.FSMState { } // we don't implement delayed open timer so move to opensent right // away. - return bgp.BGP_FSM_OPENSENT - case <-h.errorCh: - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_OPENSENT, 0 + case err := <-h.errorCh: + return bgp.BGP_FSM_IDLE, err case s := <-fsm.adminStateCh: err := h.changeAdminState(s) if err == nil { switch s { case ADMIN_STATE_DOWN: - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_ADMIN_DOWN case ADMIN_STATE_UP: log.WithFields(log.Fields{ "Topic": "Peer", @@ -453,7 +499,7 @@ func readAll(conn net.Conn, length int) ([]byte, error) { func (h *FSMHandler) recvMessageWithError() error { headerBuf, err := readAll(h.conn, bgp.BGP_HEADER_LENGTH) if err != nil { - h.errorCh <- true + h.errorCh <- FSM_READ_FAILED return err } @@ -478,7 +524,7 @@ func (h *FSMHandler) recvMessageWithError() error { bodyBuf, err := readAll(h.conn, int(hd.Len)-bgp.BGP_HEADER_LENGTH) if err != nil { - h.errorCh <- true + h.errorCh <- FSM_READ_FAILED return err } @@ -554,7 +600,7 @@ func (h *FSMHandler) recvMessageWithError() error { "Subcode": body.ErrorSubcode, "Data": body.Data, }).Warn("received notification") - h.reason = "Notification received" + h.errorCh <- FSM_NOTIFICATION_RECV return nil } } @@ -602,7 +648,7 @@ func open2Cap(open *bgp.BGPOpen, n *config.Neighbor) (map[bgp.BGPCapabilityCode] return capMap, rfMap } -func (h *FSMHandler) opensent() bgp.FSMState { +func (h *FSMHandler) opensent() (bgp.FSMState, FsmStateReason) { fsm := h.fsm m := buildopen(fsm.gConf, fsm.pConf) b, _ := m.Serialize() @@ -624,7 +670,7 @@ func (h *FSMHandler) opensent() bgp.FSMState { select { case <-h.t.Dying(): h.conn.Close() - return 0 + return -1, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break @@ -645,7 +691,7 @@ func (h *FSMHandler) opensent() bgp.FSMState { err := bgp.ValidateOpenMsg(body, fsm.pConf.Config.PeerAs) if err != nil { fsm.sendNotificatonFromErrorMsg(h.conn, err.(*bgp.MessageError)) - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } fsm.peerInfo.ID = body.ID fsm.capMap, fsm.rfMap = open2Cap(body, fsm.pConf) @@ -667,15 +713,15 @@ func (h *FSMHandler) opensent() bgp.FSMState { b, _ := msg.Serialize() fsm.conn.Write(b) fsm.bgpMessageStateUpdate(msg.Header.Type, false) - return bgp.BGP_FSM_OPENCONFIRM + return bgp.BGP_FSM_OPENCONFIRM, 0 } else { // send notification? h.conn.Close() - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } case *bgp.MessageError: fsm.sendNotificatonFromErrorMsg(h.conn, e.MsgData.(*bgp.MessageError)) - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG default: log.WithFields(log.Fields{ "Topic": "Peer", @@ -684,20 +730,20 @@ func (h *FSMHandler) opensent() bgp.FSMState { "Data": e.MsgData, }).Panic("unknown msg type") } - case <-h.errorCh: + case err := <-h.errorCh: h.conn.Close() - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, err case <-holdTimer.C: fsm.sendNotification(h.conn, bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired") h.t.Kill(nil) - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_HOLD_TIMER_EXPIRED case s := <-fsm.adminStateCh: err := h.changeAdminState(s) if err == nil { switch s { case ADMIN_STATE_DOWN: h.conn.Close() - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_ADMIN_DOWN case ADMIN_STATE_UP: log.WithFields(log.Fields{ "Topic": "Peer", @@ -725,7 +771,7 @@ func keepaliveTicker(fsm *FSM) *time.Ticker { return time.NewTicker(sec) } -func (h *FSMHandler) openconfirm() bgp.FSMState { +func (h *FSMHandler) openconfirm() (bgp.FSMState, FsmStateReason) { fsm := h.fsm ticker := keepaliveTicker(fsm) h.msgCh = make(chan *FsmMsg) @@ -746,7 +792,7 @@ func (h *FSMHandler) openconfirm() bgp.FSMState { select { case <-h.t.Dying(): h.conn.Close() - return 0 + return -1, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break @@ -774,10 +820,10 @@ func (h *FSMHandler) openconfirm() bgp.FSMState { // send notification ? h.conn.Close() } - return nextState + return nextState, 0 case *bgp.MessageError: fsm.sendNotificatonFromErrorMsg(h.conn, e.MsgData.(*bgp.MessageError)) - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG default: log.WithFields(log.Fields{ "Topic": "Peer", @@ -786,20 +832,20 @@ func (h *FSMHandler) openconfirm() bgp.FSMState { "Data": e.MsgData, }).Panic("unknown msg type") } - case <-h.errorCh: + case err := <-h.errorCh: h.conn.Close() - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, err case <-holdTimer.C: fsm.sendNotification(h.conn, bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired") h.t.Kill(nil) - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_HOLD_TIMER_EXPIRED case s := <-fsm.adminStateCh: err := h.changeAdminState(s) if err == nil { switch s { case ADMIN_STATE_DOWN: h.conn.Close() - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_ADMIN_DOWN case ADMIN_STATE_UP: log.WithFields(log.Fields{ "Topic": "Peer", @@ -811,12 +857,6 @@ func (h *FSMHandler) openconfirm() bgp.FSMState { } } } - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": fsm.pConf.Config.NeighborAddress, - "State": fsm.state, - }).Panic("code logic bug") - return 0 } func (h *FSMHandler) sendMessageloop() error { @@ -836,7 +876,7 @@ func (h *FSMHandler) sendMessageloop() error { return nil } if err := conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(fsm.negotiatedHoldTime))); err != nil { - h.errorCh <- true + h.errorCh <- FSM_WRITE_FAILED return fmt.Errorf("failed to set write deadline") } _, err = conn.Write(b) @@ -847,7 +887,7 @@ func (h *FSMHandler) sendMessageloop() error { "State": fsm.state, "Data": err, }).Warn("failed to send") - h.errorCh <- true + h.errorCh <- FSM_WRITE_FAILED return fmt.Errorf("closed") } fsm.bgpMessageStateUpdate(m.Header.Type, false) @@ -859,9 +899,7 @@ func (h *FSMHandler) sendMessageloop() error { "State": fsm.state, "Data": m, }).Warn("sent notification") - - h.errorCh <- true - h.reason = "Notificaiton sent" + h.errorCh <- FSM_NOTIFICATION_SENT return fmt.Errorf("closed") } else { log.WithFields(log.Fields{ @@ -914,7 +952,7 @@ func (h *FSMHandler) recvMessageloop() error { } } -func (h *FSMHandler) established() bgp.FSMState { +func (h *FSMHandler) established() (bgp.FSMState, FsmStateReason) { fsm := h.fsm h.conn = fsm.conn h.t.Go(h.sendMessageloop) @@ -931,7 +969,7 @@ func (h *FSMHandler) established() bgp.FSMState { for { select { case <-h.t.Dying(): - return 0 + return -1, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break @@ -942,11 +980,10 @@ func (h *FSMHandler) established() bgp.FSMState { "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state, }).Warn("Closed an accepted connection") - case <-h.errorCh: + case err := <-h.errorCh: h.conn.Close() h.t.Kill(nil) - h.reason = "Peer closed the session" - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, err case <-holdTimer.C: log.WithFields(log.Fields{ "Topic": "Peer", @@ -956,8 +993,7 @@ func (h *FSMHandler) established() bgp.FSMState { }).Warn("hold timer expired") m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil) h.outgoing <- m - h.reason = "HoldTimer expired" - return bgp.BGP_FSM_IDLE + return bgp.BGP_FSM_IDLE, FSM_HOLD_TIMER_EXPIRED case <-h.holdTimerResetCh: if fsm.negotiatedHoldTime != 0 { holdTimer.Reset(time.Second * time.Duration(fsm.negotiatedHoldTime)) @@ -974,7 +1010,6 @@ func (h *FSMHandler) established() bgp.FSMState { } } } - return 0 } func (h *FSMHandler) loop() error { @@ -983,22 +1018,23 @@ func (h *FSMHandler) loop() error { oldState := fsm.state f := func() error { - nextState := bgp.FSMState(0) + nextState := bgp.FSMState(-1) + var reason FsmStateReason switch fsm.state { case bgp.BGP_FSM_IDLE: - nextState = h.idle() + nextState, reason = h.idle() // case bgp.BGP_FSM_CONNECT: // nextState = h.connect() case bgp.BGP_FSM_ACTIVE: - nextState = h.active() + nextState, reason = h.active() case bgp.BGP_FSM_OPENSENT: - nextState = h.opensent() + nextState, reason = h.opensent() case bgp.BGP_FSM_OPENCONFIRM: - nextState = h.openconfirm() + nextState, reason = h.openconfirm() case bgp.BGP_FSM_ESTABLISHED: - nextState = h.established() + nextState, reason = h.established() } - + fsm.reason = reason ch <- nextState return nil } @@ -1020,7 +1056,7 @@ func (h *FSMHandler) loop() error { "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state, - "Reason": h.reason, + "Reason": fsm.reason, }).Info("Peer Down") } @@ -1030,7 +1066,7 @@ func (h *FSMHandler) loop() error { h.t.Wait() e.Stop() - // zero means that tomb.Dying() + // under zero means that tomb.Dying() if nextState >= bgp.BGP_FSM_IDLE { e := &FsmMsg{ MsgType: FSM_MSG_STATE_CHANGE, diff --git a/server/fsm_test.go b/server/fsm_test.go index 4eb907f3..5fa94641 100644 --- a/server/fsm_test.go +++ b/server/fsm_test.go @@ -170,7 +170,7 @@ func TestFSMHandlerOpensent_HoldTimerExpired(t *testing.T) { // set holdtime p.fsm.opensentHoldTime = 2 - state := h.opensent() + state, _ := h.opensent() assert.Equal(bgp.BGP_FSM_IDLE, state) lastMsg := m.sendBuf[len(m.sendBuf)-1] @@ -194,7 +194,7 @@ func TestFSMHandlerOpenconfirm_HoldTimerExpired(t *testing.T) { // set holdtime p.fsm.negotiatedHoldTime = 2 - state := h.openconfirm() + state, _ := h.openconfirm() assert.Equal(bgp.BGP_FSM_IDLE, state) lastMsg := m.sendBuf[len(m.sendBuf)-1] @@ -231,7 +231,7 @@ func TestFSMHandlerEstablish_HoldTimerExpired(t *testing.T) { p.fsm.negotiatedHoldTime = 2 go pushPackets() - state := h.established() + state, _ := h.established() time.Sleep(time.Second * 1) assert.Equal(bgp.BGP_FSM_IDLE, state) lastMsg := m.sendBuf[len(m.sendBuf)-1] @@ -300,7 +300,7 @@ func makePeerAndHandler() (*Peer, *FSMHandler) { h := &FSMHandler{ fsm: p.fsm, - errorCh: make(chan bool, 2), + errorCh: make(chan FsmStateReason, 2), incoming: incoming, outgoing: p.outgoing, } |