diff options
-rw-r--r-- | server/fsm.go | 174 | ||||
-rw-r--r-- | server/fsm_test.go | 59 | ||||
-rw-r--r-- | server/peer.go | 51 | ||||
-rw-r--r-- | server/peer_test.go | 409 | ||||
-rw-r--r-- | server/server.go | 6 |
5 files changed, 662 insertions, 37 deletions
diff --git a/server/fsm.go b/server/fsm.go index a8c41e9c..4649ef85 100644 --- a/server/fsm.go +++ b/server/fsm.go @@ -16,6 +16,7 @@ package server import ( + "fmt" log "github.com/Sirupsen/logrus" "github.com/osrg/gobgp/config" "github.com/osrg/gobgp/packet" @@ -41,6 +42,24 @@ const ( HOLDTIME_OPENSENT = 240 ) +type AdminState int + +const ( + ADMIN_STATE_UP AdminState = iota + ADMIN_STATE_DOWN +) + +func (s AdminState) String() string { + switch s { + case ADMIN_STATE_UP: + return "ADMIN_STATE_UP" + case ADMIN_STATE_DOWN: + return "ADMIN_STATE_DOWN" + default: + return "Unknown" + } +} + type FSM struct { globalConfig *config.GlobalType peerConfig *config.NeighborType @@ -51,6 +70,8 @@ type FSM struct { idleHoldTime float64 opensentHoldTime float64 negotiatedHoldTime float64 + adminState AdminState + adminStateCh chan AdminState } func (fsm *FSM) bgpMessageStateUpdate(MessageType uint8, isIn bool) { @@ -108,6 +129,8 @@ func NewFSM(gConfig *config.GlobalType, pConfig *config.NeighborType, connCh cha state: bgp.BGP_FSM_IDLE, passiveConnCh: connCh, opensentHoldTime: float64(HOLDTIME_OPENSENT), + adminState: ADMIN_STATE_UP, + adminStateCh: make(chan AdminState, 1), } } @@ -193,28 +216,67 @@ func (h *FSMHandler) idle() bgp.FSMState { "Key": fsm.peerConfig.NeighborAddress, }).Warn("Closed an accepted connection") case <-idleHoldTimer.C: - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": fsm.peerConfig.NeighborAddress, - "Duration": fsm.idleHoldTime, - }).Debug("IdleHoldTimer expired") - fsm.idleHoldTime = 0 - return bgp.BGP_FSM_ACTIVE + + if fsm.adminState == ADMIN_STATE_UP { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": fsm.peerConfig.NeighborAddress, + "Duration": fsm.idleHoldTime, + }).Debug("IdleHoldTimer expired") + fsm.idleHoldTime = 0 + return bgp.BGP_FSM_ACTIVE + + } else { + log.Debug("IdleHoldTimer expired, but stay at idle because the admin state is DOWN") + } + + case s := <-fsm.adminStateCh: + err := h.changeAdminState(s) + if err == nil { + switch s { + case ADMIN_STATE_DOWN: + // stop idle hold timer + idleHoldTimer.Stop() + + case ADMIN_STATE_UP: + // restart idle hold timer + idleHoldTimer.Reset(time.Second * time.Duration(fsm.idleHoldTime)) + } + } } } } func (h *FSMHandler) active() bgp.FSMState { fsm := h.fsm - select { - case <-h.t.Dying(): - return 0 - case conn := <-fsm.passiveConnCh: - fsm.passiveConn = conn + for { + select { + case <-h.t.Dying(): + return 0 + case conn := <-fsm.passiveConnCh: + fsm.passiveConn = conn + // 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 + case s := <-fsm.adminStateCh: + err := h.changeAdminState(s) + if err == nil { + switch s { + case ADMIN_STATE_DOWN: + return bgp.BGP_FSM_IDLE + case ADMIN_STATE_UP: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": fsm.peerConfig.NeighborAddress, + "State": fsm.state, + "AdminState": s.String(), + }).Panic("code logic bug") + } + } + } } - // we don't implement delayed open timer so move to opensent right - // away. - return bgp.BGP_FSM_OPENSENT } func buildopen(global *config.GlobalType, peerConf *config.NeighborType) *bgp.BGPMessage { @@ -391,6 +453,22 @@ func (h *FSMHandler) opensent() bgp.FSMState { fsm.sendNotification(h.conn, bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired") h.t.Kill(nil) return bgp.BGP_FSM_IDLE + 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 + case ADMIN_STATE_UP: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": fsm.peerConfig.NeighborAddress, + "State": fsm.state, + "AdminState": s.String(), + }).Panic("code logic bug") + } + } } } } @@ -455,6 +533,22 @@ func (h *FSMHandler) openconfirm() bgp.FSMState { fsm.sendNotification(h.conn, bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired") h.t.Kill(nil) return bgp.BGP_FSM_IDLE + 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 + case ADMIN_STATE_UP: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": fsm.peerConfig.NeighborAddress, + "State": fsm.state, + "AdminState": s.String(), + }).Panic("code logic bug") + } + } } } log.WithFields(log.Fields{ @@ -546,6 +640,16 @@ func (h *FSMHandler) established() bgp.FSMState { m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil) h.outgoing <- m return bgp.BGP_FSM_IDLE + case s := <-fsm.adminStateCh: + err := h.changeAdminState(s) + if err == nil { + switch s { + case ADMIN_STATE_DOWN: + m := bgp.NewBGPNotificationMessage( + bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, nil) + h.outgoing <- m + } + } } } return 0 @@ -579,3 +683,43 @@ func (h *FSMHandler) loop() error { } return nil } + +func (h *FSMHandler) changeAdminState(s AdminState) error { + fsm := h.fsm + if fsm.adminState != s { + + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": fsm.peerConfig.NeighborAddress, + "AdminState": s.String(), + }).Debug("admin state changed") + + fsm.adminState = s + + switch s { + case ADMIN_STATE_UP: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": h.fsm.peerConfig.NeighborAddress, + "FSMState": fsm.state.String(), + }).Info("Administrative start") + + case ADMIN_STATE_DOWN: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": h.fsm.peerConfig.NeighborAddress, + "FSMState": fsm.state.String(), + }).Info("Administrative shutdown") + } + + } else { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": fsm.peerConfig.NeighborAddress, + "FSMState": fsm.state.String(), + }).Warn("cannot change to the same state") + + return fmt.Errorf("cannot change to the same state.") + } + return nil +} diff --git a/server/fsm_test.go b/server/fsm_test.go index ae31cc5a..21659600 100644 --- a/server/fsm_test.go +++ b/server/fsm_test.go @@ -28,31 +28,52 @@ import ( type MockConnection struct { net.Conn - recvCh chan []byte + recvCh chan chan byte sendBuf [][]byte + currentCh chan byte readBytes int + isClosed bool + wait int } func NewMockConnection() *MockConnection { m := &MockConnection{ - recvCh: make(chan []byte, 128), - sendBuf: make([][]byte, 129), + recvCh: make(chan chan byte, 128), + sendBuf: make([][]byte, 128), + isClosed: false, } return m } +func (m *MockConnection) setData(data []byte) int { + dataChan := make(chan byte, 4096) + for _, b := range data { + dataChan <- b + } + m.recvCh <- dataChan + return len(dataChan) +} + func (m *MockConnection) Read(buf []byte) (int, error) { - data := <-m.recvCh - rest := len(buf) - m.readBytes - if len(data) > rest { - m.recvCh <- data[rest:] - data = data[:rest] + if m.isClosed { + return 0, fmt.Errorf("already closed") } - for _, val := range data { - buf[m.readBytes] = val - m.readBytes += 1 + if m.currentCh == nil { + m.currentCh = <-m.recvCh + } + + rest := len(buf) - m.readBytes + for i := 0; i < rest; i++ { + if len(m.currentCh) > 0 { + val := <-m.currentCh + buf[m.readBytes] = val + m.readBytes += 1 + } else { + m.currentCh = nil + break + } } length := 0 @@ -62,12 +83,12 @@ func (m *MockConnection) Read(buf []byte) (int, error) { } else { length = m.readBytes } - fmt.Printf("%d bytes read from peer\n", length) return length, nil } func (m *MockConnection) Write(buf []byte) (int, error) { + time.Sleep(time.Duration(m.wait) * time.Millisecond) m.sendBuf = append(m.sendBuf, buf) msg, _ := bgp.ParseBGPMessage(buf) fmt.Printf("%d bytes written by gobgp message type : %s\n", len(buf), showMessageType(msg.Header.Type)) @@ -92,6 +113,10 @@ func showMessageType(t uint8) string { func (m *MockConnection) Close() error { fmt.Printf("close called\n") + if !m.isClosed { + close(m.recvCh) + m.isClosed = true + } return nil } @@ -104,11 +129,11 @@ func TestReadAll(t *testing.T) { pushBytes := func() { fmt.Println("push 5 bytes") - m.recvCh <- expected1[0:5] + m.setData(expected1[0:5]) fmt.Println("push rest") - m.recvCh <- expected1[5:] + m.setData(expected1[5:]) fmt.Println("push bytes at once") - m.recvCh <- expected2 + m.setData(expected2) } go pushBytes() @@ -193,8 +218,8 @@ func TestFSMHandlerEstablish_HoldTimerExpired(t *testing.T) { pushPackets := func() { // first keepalive from peer - m.recvCh <- header - m.recvCh <- body + m.setData(header) + m.setData(body) } // set holdtime diff --git a/server/peer.go b/server/peer.go index d04947fa..61a229ee 100644 --- a/server/peer.go +++ b/server/peer.go @@ -199,7 +199,17 @@ func (peer *Peer) handleREST(restReq *api.RestRequest) { result := &api.RestResponse{} switch restReq.RequestType { case api.REQ_LOCAL_RIB: - j, _ := json.Marshal(peer.rib.Tables[peer.rf]) + var t table.Table + if peer.fsm.adminState == ADMIN_STATE_DOWN { + if peer.rf == bgp.RF_IPv4_UC { + t = table.NewIPv4Table(0) + } else { + t = table.NewIPv6Table(0) + } + } else { + t = peer.rib.Tables[peer.rf] + } + j, _ := json.Marshal(t) result.Data = j case api.REQ_NEIGHBOR_SHUTDOWN: peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, nil) @@ -215,9 +225,7 @@ func (peer *Peer) handleREST(restReq *api.RestRequest) { case api.REQ_NEIGHBOR_SOFT_RESET_OUT: pathList := peer.adjRib.GetOutPathList(peer.rf) peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList)) - case api.REQ_ADJ_RIB_IN: - fallthrough - case api.REQ_ADJ_RIB_OUT: + case api.REQ_ADJ_RIB_IN, api.REQ_ADJ_RIB_OUT: rfs := []bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv6_UC} adjrib := make(map[string][]table.Path) @@ -236,6 +244,35 @@ func (peer *Peer) handleREST(restReq *api.RestRequest) { } j, _ := json.Marshal(adjrib) result.Data = j + case api.REQ_NEIGHBOR_ENABLE, api.REQ_NEIGHBOR_DISABLE: + r := make(map[string]string) + if restReq.RequestType == api.REQ_NEIGHBOR_ENABLE { + select { + case peer.fsm.adminStateCh <- ADMIN_STATE_UP: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.peerConfig.NeighborAddress, + }).Debug("ADMIN_STATE_UP requested") + r["result"] = "ADMIN_STATE_UP" + default: + log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress) + r["result"] = "previous request is still remaining" + } + } else { + select { + case peer.fsm.adminStateCh <- ADMIN_STATE_DOWN: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.peerConfig.NeighborAddress, + }).Debug("ADMIN_STATE_DOWN requested") + r["result"] = "ADMIN_STATE_DOWN" + default: + log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress) + r["result"] = "previous request is still remaining" + } + } + j, _ := json.Marshal(r) + result.Data = j } restReq.ResponseCh <- result close(restReq.ResponseCh) @@ -352,6 +389,12 @@ func (peer *Peer) loop() error { s.peerMsgCh <- pm } } + + // clear counter + if h.fsm.adminState == ADMIN_STATE_DOWN { + h.fsm.peerConfig.BgpNeighborCommonState = config.BgpNeighborCommonStateType{} + } + case FSM_MSG_BGP_MESSAGE: switch m := e.MsgData.(type) { case *bgp.MessageError: diff --git a/server/peer_test.go b/server/peer_test.go index 4f310512..9e406e3e 100644 --- a/server/peer_test.go +++ b/server/peer_test.go @@ -18,12 +18,17 @@ package server import ( "fmt" //"encoding/json" + "encoding/json" + log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/config" "github.com/osrg/gobgp/packet" "github.com/osrg/gobgp/table" "github.com/stretchr/testify/assert" "net" "reflect" "testing" + "time" ) func peerRC3() *table.PeerInfo { @@ -93,3 +98,407 @@ func TestProcessBGPUpdate_fourbyteAS(t *testing.T) { assert.Equal(t, len(attrAS2.Value), 1) assert.Equal(t, attrAS2.Value[0].(*bgp.As4PathParam).AS, []uint32{66003, 4000, 70000}) } + +func TestPeerAdminShutdownWhileEstablished(t *testing.T) { + log.SetLevel(log.DebugLevel) + assert := assert.New(t) + m := NewMockConnection() + globalConfig := config.GlobalType{} + peerConfig := config.NeighborType{} + peerConfig.PeerAs = 100000 + peerConfig.Timers.KeepaliveInterval = 5 + peer := makePeer(globalConfig, peerConfig) + peer.fsm.opensentHoldTime = 10 + + peer.t.Go(peer.loop) + pushPackets := func() { + o, _ := open().Serialize() + m.setData(o) + k, _ := keepalive().Serialize() + m.setData(k) + } + go pushPackets() + + waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) + peer.acceptedConnCh <- m + waitUntil(assert, bgp.BGP_FSM_ESTABLISHED, peer, 1000) + + restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0") + msg := &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + + peer.serverMsgCh <- msg + result := <-restReq.ResponseCh + res := make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("ADMIN_STATE_DOWN", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) + + assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) + assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) + lastMsg := m.sendBuf[len(m.sendBuf)-1] + sent, _ := bgp.ParseBGPMessage(lastMsg) + assert.Equal(uint8(bgp.BGP_MSG_NOTIFICATION), sent.Header.Type) + assert.Equal(uint8(bgp.BGP_ERROR_CEASE), sent.Body.(*bgp.BGPNotification).ErrorCode) + assert.Equal(uint8(bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN), sent.Body.(*bgp.BGPNotification).ErrorSubcode) + assert.True(m.isClosed) + + // check counter + counter := peer.fsm.peerConfig.BgpNeighborCommonState + assertCounter(assert, counter) +} + +func TestPeerAdminShutdownWhileIdle(t *testing.T) { + log.SetLevel(log.DebugLevel) + assert := assert.New(t) + + globalConfig := config.GlobalType{} + peerConfig := config.NeighborType{} + peerConfig.PeerAs = 100000 + peerConfig.Timers.KeepaliveInterval = 5 + peer := makePeer(globalConfig, peerConfig) + peer.fsm.opensentHoldTime = 10 + peer.fsm.idleHoldTime = 5 + peer.t.Go(peer.loop) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) + + restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0") + msg := &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + + peer.serverMsgCh <- msg + result := <-restReq.ResponseCh + res := make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("ADMIN_STATE_DOWN", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) + assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) + assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) + + // check counter + counter := peer.fsm.peerConfig.BgpNeighborCommonState + assertCounter(assert, counter) +} + +func TestPeerAdminShutdownWhileActive(t *testing.T) { + log.SetLevel(log.DebugLevel) + assert := assert.New(t) + + globalConfig := config.GlobalType{} + peerConfig := config.NeighborType{} + peerConfig.PeerAs = 100000 + peerConfig.Timers.KeepaliveInterval = 5 + peer := makePeer(globalConfig, peerConfig) + peer.fsm.opensentHoldTime = 10 + peer.t.Go(peer.loop) + + waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) + + restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0") + msg := &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + + peer.serverMsgCh <- msg + result := <-restReq.ResponseCh + res := make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("ADMIN_STATE_DOWN", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) + assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) + assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) + + // check counter + counter := peer.fsm.peerConfig.BgpNeighborCommonState + assertCounter(assert, counter) +} + +func TestPeerAdminShutdownWhileOpensent(t *testing.T) { + log.SetLevel(log.DebugLevel) + assert := assert.New(t) + m := NewMockConnection() + globalConfig := config.GlobalType{} + peerConfig := config.NeighborType{} + peerConfig.PeerAs = 100000 + peerConfig.Timers.KeepaliveInterval = 5 + peer := makePeer(globalConfig, peerConfig) + peer.fsm.opensentHoldTime = 1 + peer.t.Go(peer.loop) + + waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) + peer.acceptedConnCh <- m + waitUntil(assert, bgp.BGP_FSM_OPENSENT, peer, 1000) + + restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0") + msg := &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + + peer.serverMsgCh <- msg + result := <-restReq.ResponseCh + res := make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("ADMIN_STATE_DOWN", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) + assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) + assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) + lastMsg := m.sendBuf[len(m.sendBuf)-1] + sent, _ := bgp.ParseBGPMessage(lastMsg) + assert.NotEqual(bgp.BGP_MSG_NOTIFICATION, sent.Header.Type) + assert.True(m.isClosed) + + // check counter + counter := peer.fsm.peerConfig.BgpNeighborCommonState + assertCounter(assert, counter) +} + +func TestPeerAdminShutdownWhileOpenconfirm(t *testing.T) { + log.SetLevel(log.DebugLevel) + assert := assert.New(t) + m := NewMockConnection() + globalConfig := config.GlobalType{} + peerConfig := config.NeighborType{} + peerConfig.PeerAs = 100000 + peerConfig.Timers.KeepaliveInterval = 5 + peer := makePeer(globalConfig, peerConfig) + peer.fsm.opensentHoldTime = 10 + peer.t.Go(peer.loop) + pushPackets := func() { + o, _ := open().Serialize() + m.setData(o) + } + go pushPackets() + waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) + peer.acceptedConnCh <- m + waitUntil(assert, bgp.BGP_FSM_OPENCONFIRM, peer, 1000) + + restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0") + msg := &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + + peer.serverMsgCh <- msg + result := <-restReq.ResponseCh + res := make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("ADMIN_STATE_DOWN", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) + assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) + assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) + lastMsg := m.sendBuf[len(m.sendBuf)-1] + sent, _ := bgp.ParseBGPMessage(lastMsg) + assert.NotEqual(bgp.BGP_MSG_NOTIFICATION, sent.Header.Type) + assert.True(m.isClosed) + + // check counter + counter := peer.fsm.peerConfig.BgpNeighborCommonState + assertCounter(assert, counter) + +} + +func TestPeerAdminEnable(t *testing.T) { + log.SetLevel(log.DebugLevel) + assert := assert.New(t) + m := NewMockConnection() + globalConfig := config.GlobalType{} + peerConfig := config.NeighborType{} + peerConfig.PeerAs = 100000 + peerConfig.Timers.KeepaliveInterval = 5 + peer := makePeer(globalConfig, peerConfig) + + peer.fsm.opensentHoldTime = 5 + peer.t.Go(peer.loop) + pushPackets := func() { + o, _ := open().Serialize() + m.setData(o) + k, _ := keepalive().Serialize() + m.setData(k) + } + go pushPackets() + + waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) + peer.acceptedConnCh <- m + waitUntil(assert, bgp.BGP_FSM_ESTABLISHED, peer, 1000) + + // shutdown peer at first + restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0") + msg := &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + peer.serverMsgCh <- msg + result := <-restReq.ResponseCh + res := make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("ADMIN_STATE_DOWN", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 100) + assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) + assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) + + // enable peer + restReq = api.NewRestRequest(api.REQ_NEIGHBOR_ENABLE, "0.0.0.0") + msg = &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + peer.serverMsgCh <- msg + result = <-restReq.ResponseCh + res = make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("ADMIN_STATE_UP", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) + assert.Equal(bgp.BGP_FSM_ACTIVE, peer.fsm.state) + + m2 := NewMockConnection() + pushPackets = func() { + o, _ := open().Serialize() + m2.setData(o) + k, _ := keepalive().Serialize() + m2.setData(k) + } + go pushPackets() + + peer.acceptedConnCh <- m2 + + waitUntil(assert, bgp.BGP_FSM_ESTABLISHED, peer, 1000) + assert.Equal(bgp.BGP_FSM_ESTABLISHED, peer.fsm.state) +} + +func TestPeerAdminShutdownReject(t *testing.T) { + log.SetLevel(log.DebugLevel) + assert := assert.New(t) + m := NewMockConnection() + m.wait = 500 + + globalConfig := config.GlobalType{} + peerConfig := config.NeighborType{} + peerConfig.PeerAs = 100000 + peerConfig.Timers.KeepaliveInterval = 5 + peer := makePeer(globalConfig, peerConfig) + peer.fsm.opensentHoldTime = 1 + peer.t.Go(peer.loop) + + waitUntil(assert, bgp.BGP_FSM_ACTIVE, peer, 1000) + peer.acceptedConnCh <- m + waitUntil(assert, bgp.BGP_FSM_OPENSENT, peer, 1000) + + restReq := api.NewRestRequest(api.REQ_NEIGHBOR_DISABLE, "0.0.0.0") + msg := &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + + peer.fsm.adminStateCh <- ADMIN_STATE_DOWN + + peer.serverMsgCh <- msg + result := <-restReq.ResponseCh + res := make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("previous request is still remaining", res["result"]) + + restReq = api.NewRestRequest(api.REQ_NEIGHBOR_ENABLE, "0.0.0.0") + msg = &serverMsg{ + msgType: SRV_MSG_API, + msgData: restReq, + } + + peer.serverMsgCh <- msg + result = <-restReq.ResponseCh + res = make(map[string]string) + json.Unmarshal(result.Data, &res) + assert.Equal("previous request is still remaining", res["result"]) + + waitUntil(assert, bgp.BGP_FSM_IDLE, peer, 1000) + assert.Equal(bgp.BGP_FSM_IDLE, peer.fsm.state) + assert.Equal(ADMIN_STATE_DOWN, peer.fsm.adminState) + +} + +func assertCounter(assert *assert.Assertions, counter config.BgpNeighborCommonStateType) { + assert.Equal(uint32(0), counter.OpenIn) + assert.Equal(uint32(0), counter.OpenOut) + assert.Equal(uint32(0), counter.UpdateIn) + assert.Equal(uint32(0), counter.UpdateOut) + assert.Equal(uint32(0), counter.KeepaliveIn) + assert.Equal(uint32(0), counter.KeepaliveOut) + assert.Equal(uint32(0), counter.NotifyIn) + assert.Equal(uint32(0), counter.NotifyOut) + assert.Equal(uint32(0), counter.EstablishedCount) + assert.Equal(uint32(0), counter.TotalIn) + assert.Equal(uint32(0), counter.TotalOut) + assert.Equal(uint32(0), counter.RefreshIn) + assert.Equal(uint32(0), counter.RefreshOut) + assert.Equal(uint32(0), counter.DynamicCapIn) + assert.Equal(uint32(0), counter.DynamicCapOut) + assert.Equal(uint32(0), counter.EstablishedCount) + assert.Equal(uint32(0), counter.DroppedCount) + assert.Equal(uint32(0), counter.Flops) +} + +func waitUntil(assert *assert.Assertions, state bgp.FSMState, peer *Peer, timeout int64) { + isTimeout := false + expire := func() { + isTimeout = true + } + time.AfterFunc((time.Duration)(timeout)*time.Millisecond, expire) + + for { + time.Sleep(1 * time.Millisecond) + + if peer.fsm.state == state || isTimeout { + assert.Equal(state, peer.fsm.state, "timeout") + break + } + } +} + +func makePeer(globalConfig config.GlobalType, peerConfig config.NeighborType) *Peer { + + sch := make(chan *serverMsg, 8) + pch := make(chan *peerMsg, 4096) + + p := &Peer{ + globalConfig: globalConfig, + peerConfig: peerConfig, + acceptedConnCh: make(chan net.Conn), + serverMsgCh: sch, + peerMsgCh: pch, + capMap: make(map[bgp.BGPCapabilityCode]bgp.ParameterCapabilityInterface), + } + p.siblings = make(map[string]*serverMsgDataPeer) + + p.fsm = NewFSM(&globalConfig, &peerConfig, p.acceptedConnCh) + peerConfig.BgpNeighborCommonState.State = uint32(bgp.BGP_FSM_IDLE) + peerConfig.BgpNeighborCommonState.Downtime = time.Now() + if peerConfig.NeighborAddress.To4() != nil { + p.rf = bgp.RF_IPv4_UC + } else { + p.rf = bgp.RF_IPv6_UC + } + + p.peerInfo = &table.PeerInfo{ + AS: peerConfig.PeerAs, + LocalID: globalConfig.RouterId, + RF: p.rf, + Address: peerConfig.NeighborAddress, + } + p.adjRib = table.NewAdjRib() + p.rib = table.NewTableManager(p.peerConfig.NeighborAddress.String()) + + return p +} diff --git a/server/server.go b/server/server.go index 14a1ac5f..9e03a270 100644 --- a/server/server.go +++ b/server/server.go @@ -241,7 +241,11 @@ func (server *BgpServer) handleRest(restReq *api.RestRequest) { } restReq.ResponseCh <- result close(restReq.ResponseCh) - case api.REQ_LOCAL_RIB, api.REQ_NEIGHBOR_SHUTDOWN, api.REQ_NEIGHBOR_RESET, api.REQ_NEIGHBOR_SOFT_RESET_IN, api.REQ_NEIGHBOR_SOFT_RESET_OUT, api.REQ_ADJ_RIB_IN, api.REQ_ADJ_RIB_OUT: + case api.REQ_LOCAL_RIB, api.REQ_NEIGHBOR_SHUTDOWN, api.REQ_NEIGHBOR_RESET, + api.REQ_NEIGHBOR_SOFT_RESET_IN, api.REQ_NEIGHBOR_SOFT_RESET_OUT, + api.REQ_ADJ_RIB_IN, api.REQ_ADJ_RIB_OUT, + api.REQ_NEIGHBOR_ENABLE, api.REQ_NEIGHBOR_DISABLE: + remoteAddr := restReq.RemoteAddr result := &api.RestResponse{} info, found := server.peerMap[remoteAddr] |