diff options
-rw-r--r-- | docs/sources/configuration.md | 3 | ||||
-rw-r--r-- | gobgp/cmd/neighbor.go | 5 | ||||
-rw-r--r-- | server/fsm.go | 16 | ||||
-rw-r--r-- | server/peer.go | 48 | ||||
-rw-r--r-- | server/server.go | 7 | ||||
-rw-r--r-- | test/lib/base.py | 5 | ||||
-rw-r--r-- | test/lib/gobgp.py | 4 | ||||
-rw-r--r-- | test/scenario_test/route_server_test.py | 2 |
8 files changed, 65 insertions, 25 deletions
diff --git a/docs/sources/configuration.md b/docs/sources/configuration.md index 89aa2c2c..72c527a3 100644 --- a/docs/sources/configuration.md +++ b/docs/sources/configuration.md @@ -64,6 +64,9 @@ route-reflector-cluster-id = "192.168.0.1" [[neighbors.afi-safis]] afi-safi-name = "ipv4-unicast" + [neighbors.afi-safis.prefix-limit.config] + max-prefixes = 1000 + shutdown-threshold-pct = 80 [[neighbors.afi-safis]] afi-safi-name = "ipv6-unicast" [[neighbors.afi-safis]] diff --git a/gobgp/cmd/neighbor.go b/gobgp/cmd/neighbor.go index f20f7c88..bd6f3a1a 100644 --- a/gobgp/cmd/neighbor.go +++ b/gobgp/cmd/neighbor.go @@ -113,8 +113,11 @@ func showNeighbors() error { format += " %-11s |%11s %8s %8s\n" fmt.Printf(format, "Peer", "AS", "Up/Down", "State", "#Advertised", "Received", "Accepted") format_fsm := func(admin, fsm string) string { - if admin == "ADMIN_STATE_DOWN" { + switch admin { + case "ADMIN_STATE_DOWN": return "Idle(Admin)" + case "ADMIN_STATE_PFX_CT": + return "Idle(PfxCt)" } if fsm == "BGP_FSM_IDLE" { diff --git a/server/fsm.go b/server/fsm.go index 48f4e095..01587668 100644 --- a/server/fsm.go +++ b/server/fsm.go @@ -109,6 +109,7 @@ type AdminState int const ( ADMIN_STATE_UP AdminState = iota ADMIN_STATE_DOWN + ADMIN_STATE_PFX_CT ) func (s AdminState) String() string { @@ -117,6 +118,8 @@ func (s AdminState) String() string { return "ADMIN_STATE_UP" case ADMIN_STATE_DOWN: return "ADMIN_STATE_DOWN" + case ADMIN_STATE_PFX_CT: + return "ADMIN_STATE_PFX_CT" default: return "Unknown" } @@ -1086,15 +1089,14 @@ func (h *FSMHandler) sendMessageloop() error { } } if m.Notification != nil { + if m.StayIdle { + // current user is only prefix-limit + // fix me if this is not the case + h.changeAdminState(ADMIN_STATE_PFX_CT) + } if err := send(m.Notification); err != nil { return nil } - if m.StayIdle { - select { - case h.fsm.adminStateCh <- ADMIN_STATE_DOWN: - default: - } - } } case <-ticker.C: if err := send(bgp.NewBGPKeepAliveMessage()); err != nil { @@ -1272,7 +1274,7 @@ func (h *FSMHandler) changeAdminState(s AdminState) error { "State": fsm.state, }).Info("Administrative start") - case ADMIN_STATE_DOWN: + case ADMIN_STATE_DOWN, ADMIN_STATE_PFX_CT: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, diff --git a/server/peer.go b/server/peer.go index 950526dd..cc71063a 100644 --- a/server/peer.go +++ b/server/peer.go @@ -33,15 +33,16 @@ const ( ) type Peer struct { - tableId string - gConf config.Global - conf config.Neighbor - fsm *FSM - adjRibIn *table.AdjRib - adjRibOut *table.AdjRib - outgoing chan *FsmOutgoingMsg - policy *table.RoutingPolicy - localRib *table.TableManager + tableId string + gConf config.Global + conf config.Neighbor + fsm *FSM + adjRibIn *table.AdjRib + adjRibOut *table.AdjRib + outgoing chan *FsmOutgoingMsg + policy *table.RoutingPolicy + localRib *table.TableManager + prefixLimitWarned bool } func NewPeer(g config.Global, conf config.Neighbor, loc *table.TableManager, policy *table.RoutingPolicy) *Peer { @@ -227,7 +228,7 @@ func (peer *Peer) handleRouteRefresh(e *FsmMsg) []*table.Path { return accepted } -func (peer *Peer) handleUpdate(e *FsmMsg) ([]*table.Path, []bgp.RouteFamily) { +func (peer *Peer) handleUpdate(e *FsmMsg) ([]*table.Path, []bgp.RouteFamily, *bgp.BGPMessage) { m := e.MsgData.(*bgp.BGPMessage) log.WithFields(log.Fields{ "Topic": "Peer", @@ -237,6 +238,29 @@ func (peer *Peer) handleUpdate(e *FsmMsg) ([]*table.Path, []bgp.RouteFamily) { peer.conf.Timers.State.UpdateRecvTime = time.Now().Unix() if len(e.PathList) > 0 { peer.adjRibIn.Update(e.PathList) + for _, family := range peer.fsm.pConf.AfiSafis { + k, _ := bgp.GetRouteFamily(string(family.AfiSafiName)) + count := peer.adjRibIn.Count([]bgp.RouteFamily{k}) + if maxPrefixes := int(family.PrefixLimit.Config.MaxPrefixes); maxPrefixes > 0 { + pct := int(family.PrefixLimit.Config.ShutdownThresholdPct) + if pct > 0 && !peer.prefixLimitWarned && count > (maxPrefixes*pct/100) { + peer.prefixLimitWarned = true + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.conf.Config.NeighborAddress, + "AddressFamily": family.AfiSafiName, + }).Warnf("prefix limit %d%% reached", pct) + } + if count > maxPrefixes { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.conf.Config.NeighborAddress, + "AddressFamily": family.AfiSafiName, + }).Warnf("prefix limit reached") + return nil, nil, bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED, nil) + } + } + } paths := make([]*table.Path, 0, len(e.PathList)) eor := []bgp.RouteFamily{} for _, path := range e.PathList { @@ -254,9 +278,9 @@ func (peer *Peer) handleUpdate(e *FsmMsg) ([]*table.Path, []bgp.RouteFamily) { paths = append(paths, path) } } - return paths, eor + return paths, eor, nil } - return nil, nil + return nil, nil, nil } func (peer *Peer) startFSMHandler(incoming *channels.InfiniteChannel, stateCh chan *FsmMsg) { diff --git a/server/server.go b/server/server.go index af88f3ca..1b59402d 100644 --- a/server/server.go +++ b/server/server.go @@ -774,6 +774,7 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg { } else { drop = peer.configuredRFlist() } + peer.prefixLimitWarned = false peer.DropAll(drop) msgs = server.dropPeerAllRoutes(peer, drop) } else if peer.fsm.pConf.GracefulRestart.State.PeerRestarting && nextState == bgp.BGP_FSM_IDLE { @@ -847,8 +848,10 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg { case *bgp.MessageError: return []*SenderMsg{newSenderMsg(peer, nil, bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data), false)} case *bgp.BGPMessage: - pathList, eor := peer.handleUpdate(e) - + pathList, eor, notification := peer.handleUpdate(e) + if notification != nil { + return []*SenderMsg{newSenderMsg(peer, nil, notification, true)} + } if m.Header.Type == bgp.BGP_MSG_UPDATE && server.watchers.watching(WATCHER_EVENT_UPDATE_MSG) { _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER] l, _ := peer.fsm.LocalHostPort() diff --git a/test/lib/base.py b/test/lib/base.py index 29920451..d5dfc221 100644 --- a/test/lib/base.py +++ b/test/lib/base.py @@ -254,7 +254,7 @@ class BGPContainer(Container): policies=None, passive=False, is_rr_client=False, cluster_id=None, flowspec=False, bridge='', reload_config=True, as2=False, - graceful_restart=None, local_as=None): + graceful_restart=None, local_as=None, prefix_limit=None): neigh_addr = '' local_addr = '' for me, you in itertools.product(self.ip_addrs, peer.ip_addrs): @@ -283,7 +283,8 @@ class BGPContainer(Container): 'local_addr': local_addr, 'as2': as2, 'graceful_restart': graceful_restart, - 'local_as': local_as} + 'local_as': local_as, + 'prefix_limit': prefix_limit} if self.is_running and reload_config: self.create_config() self.reload_config() diff --git a/test/lib/gobgp.py b/test/lib/gobgp.py index b8413f40..36b4b86e 100644 --- a/test/lib/gobgp.py +++ b/test/lib/gobgp.py @@ -243,6 +243,10 @@ class GoBGPContainer(BGPContainer): if info['local_as']: n['config']['local-as'] = info['local_as'] + if info['prefix_limit']: + for v in afi_safi_list: + v['prefix-limit'] = {'config': {'max-prefixes': info['prefix_limit'], 'shutdown-threshold-pct': 80 }} + if info['graceful_restart'] is not None: n['graceful-restart'] = {'config': {'enabled': True, 'restart-time': 20}} for afi_safi in afi_safi_list: diff --git a/test/scenario_test/route_server_test.py b/test/scenario_test/route_server_test.py index ab305e44..3c8f9356 100644 --- a/test/scenario_test/route_server_test.py +++ b/test/scenario_test/route_server_test.py @@ -59,7 +59,7 @@ class GoBGPTestBase(unittest.TestCase): time.sleep(initial_wait_time) for rs_client in rs_clients: - g1.add_peer(rs_client, is_rs_client=True, passwd='passwd', passive=True) + g1.add_peer(rs_client, is_rs_client=True, passwd='passwd', passive=True, prefix_limit=10) rs_client.add_peer(g1, passwd='passwd') cls.gobgp = g1 |