summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>2016-01-23 13:53:41 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-01-25 04:57:11 -0800
commit3817b82f9d57ce02adb025f5acff6f06b54c6641 (patch)
tree1fdb7a47d07cf7601b8688edcae7a61230d2f4cd
parent38bd31856b5eff046a4874e83b53cb7d3e45cdaf (diff)
fsm: add type FsmStateReason to track reason of state transition
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
-rw-r--r--server/fsm.go166
-rw-r--r--server/fsm_test.go8
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,
}