summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/fsm.go174
-rw-r--r--server/fsm_test.go59
-rw-r--r--server/peer.go51
-rw-r--r--server/peer_test.go409
-rw-r--r--server/server.go6
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]