// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // implied. // See the License for the specific language governing permissions and // limitations under the License. package server import ( "fmt" "github.com/eapache/channels" "github.com/osrg/gobgp/config" "github.com/osrg/gobgp/packet/bgp" "github.com/osrg/gobgp/table" log "github.com/sirupsen/logrus" "net" "time" ) const ( FLOP_THRESHOLD = time.Second * 30 MIN_CONNECT_RETRY = 10 ) type PeerGroup struct { Conf *config.PeerGroup members map[string]config.Neighbor } func NewPeerGroup(c *config.PeerGroup) *PeerGroup { return &PeerGroup{ Conf: c, members: make(map[string]config.Neighbor, 0), } } func (pg *PeerGroup) AddMember(c config.Neighbor) { pg.members[c.State.NeighborAddress] = c } func (pg *PeerGroup) DeleteMember(c config.Neighbor) { delete(pg.members, c.State.NeighborAddress) } type Peer struct { tableId string fsm *FSM adjRibIn *table.AdjRib outgoing *channels.InfiniteChannel policy *table.RoutingPolicy localRib *table.TableManager prefixLimitWarned map[bgp.RouteFamily]bool llgrEndChs []chan struct{} } func NewPeer(g *config.Global, conf *config.Neighbor, loc *table.TableManager, policy *table.RoutingPolicy) *Peer { peer := &Peer{ outgoing: channels.NewInfiniteChannel(), localRib: loc, policy: policy, fsm: NewFSM(g, conf, policy), prefixLimitWarned: make(map[bgp.RouteFamily]bool), } if peer.isRouteServerClient() { peer.tableId = conf.State.NeighborAddress } else { peer.tableId = table.GLOBAL_RIB_NAME } rfs, _ := config.AfiSafis(conf.AfiSafis).ToRfList() peer.adjRibIn = table.NewAdjRib(peer.ID(), rfs) return peer } func (peer *Peer) ID() string { return peer.fsm.pConf.State.NeighborAddress } func (peer *Peer) TableID() string { return peer.tableId } func (peer *Peer) isIBGPPeer() bool { return peer.fsm.pConf.State.PeerAs == peer.fsm.gConf.Config.As } func (peer *Peer) isRouteServerClient() bool { return peer.fsm.pConf.RouteServer.Config.RouteServerClient } func (peer *Peer) isRouteReflectorClient() bool { return peer.fsm.pConf.RouteReflector.Config.RouteReflectorClient } func (peer *Peer) isGracefulRestartEnabled() bool { return peer.fsm.pConf.GracefulRestart.State.Enabled } func (peer *Peer) recvedAllEOR() bool { for _, a := range peer.fsm.pConf.AfiSafis { if s := a.MpGracefulRestart.State; s.Enabled && !s.EndOfRibReceived { return false } } return true } func (peer *Peer) configuredRFlist() []bgp.RouteFamily { rfs, _ := config.AfiSafis(peer.fsm.pConf.AfiSafis).ToRfList() return rfs } func (peer *Peer) toGlobalFamilies(families []bgp.RouteFamily) []bgp.RouteFamily { if peer.fsm.pConf.Config.Vrf != "" { fs := make([]bgp.RouteFamily, 0, len(families)) for _, f := range families { switch f { case bgp.RF_IPv4_UC: fs = append(fs, bgp.RF_IPv4_VPN) case bgp.RF_IPv6_UC: fs = append(fs, bgp.RF_IPv6_VPN) default: log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), "Family": f, "VRF": peer.fsm.pConf.Config.Vrf, }).Warn("invalid family configured for vrfed neighbor") } } families = fs } return families } func classifyFamilies(all, part []bgp.RouteFamily) ([]bgp.RouteFamily, []bgp.RouteFamily) { a := []bgp.RouteFamily{} b := []bgp.RouteFamily{} for _, f := range all { p := true for _, g := range part { if f == g { p = false a = append(a, f) break } } if p { b = append(b, f) } } return a, b } func (peer *Peer) forwardingPreservedFamilies() ([]bgp.RouteFamily, []bgp.RouteFamily) { list := []bgp.RouteFamily{} for _, a := range peer.fsm.pConf.AfiSafis { if s := a.MpGracefulRestart.State; s.Enabled && s.Received { f, _ := bgp.GetRouteFamily(string(a.Config.AfiSafiName)) list = append(list, f) } } return classifyFamilies(peer.configuredRFlist(), list) } func (peer *Peer) llgrFamilies() ([]bgp.RouteFamily, []bgp.RouteFamily) { list := []bgp.RouteFamily{} for _, a := range peer.fsm.pConf.AfiSafis { if a.LongLivedGracefulRestart.State.Enabled { f, _ := bgp.GetRouteFamily(string(a.Config.AfiSafiName)) list = append(list, f) } } return classifyFamilies(peer.configuredRFlist(), list) } func (peer *Peer) isLLGREnabledFamily(family bgp.RouteFamily) bool { if !peer.fsm.pConf.GracefulRestart.Config.LongLivedEnabled { return false } fs, _ := peer.llgrFamilies() for _, f := range fs { if f == family { return true } } return false } func (peer *Peer) llgrRestartTime(family bgp.RouteFamily) uint32 { for _, a := range peer.fsm.pConf.AfiSafis { if f, _ := bgp.GetRouteFamily(string(a.Config.AfiSafiName)); f == family { return a.LongLivedGracefulRestart.State.PeerRestartTime } } return 0 } func (peer *Peer) llgrRestartTimerExpired(family bgp.RouteFamily) bool { all := true for _, a := range peer.fsm.pConf.AfiSafis { if f, _ := bgp.GetRouteFamily(string(a.Config.AfiSafiName)); f == family { a.LongLivedGracefulRestart.State.PeerRestartTimerExpired = true } s := a.LongLivedGracefulRestart.State if s.Received && !s.PeerRestartTimerExpired { all = false } } return all } func (peer *Peer) markLLGRStale(fs []bgp.RouteFamily) []*table.Path { paths := peer.adjRibIn.PathList(fs, true) for i, p := range paths { doStale := true for _, c := range p.GetCommunities() { if c == bgp.COMMUNITY_NO_LLGR { doStale = false p = p.Clone(true) break } } if doStale { p = p.Clone(false) p.SetCommunities([]uint32{bgp.COMMUNITY_LLGR_STALE}, false) } paths[i] = p } return paths } func (peer *Peer) stopPeerRestarting() { peer.fsm.pConf.GracefulRestart.State.PeerRestarting = false for _, ch := range peer.llgrEndChs { close(ch) } peer.llgrEndChs = make([]chan struct{}, 0) } func (peer *Peer) getAccepted(rfList []bgp.RouteFamily) []*table.Path { return peer.adjRibIn.PathList(rfList, true) } func (peer *Peer) filterpath(path, old *table.Path) *table.Path { // special handling for RTC nlri // see comments in (*Destination).Calculate() if path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC && !path.IsWithdraw { // If we already sent the same nlri, send unnecessary // update. Fix this after the API change between table // and server packages. dst := peer.localRib.GetDestination(path) path = nil // we send a path even if it is not a best path for _, p := range dst.GetKnownPathList(peer.TableID()) { // just take care not to send back it if peer.ID() != p.GetSource().Address.String() { path = p break } } } // only allow vpnv4 and vpnv6 paths to be advertised to VRFed neighbors. // also check we can import this path using table.CanImportToVrf() // if we can, make it local path by calling (*Path).ToLocal() if path != nil && peer.fsm.pConf.Config.Vrf != "" { if f := path.GetRouteFamily(); f != bgp.RF_IPv4_VPN && f != bgp.RF_IPv6_VPN { return nil } vrf := peer.localRib.Vrfs[peer.fsm.pConf.Config.Vrf] if table.CanImportToVrf(vrf, path) { path = path.ToLocal() } else { return nil } } // replace-peer-as handling if path != nil && !path.IsWithdraw && peer.fsm.pConf.AsPathOptions.State.ReplacePeerAs { path = path.ReplaceAS(peer.fsm.pConf.Config.LocalAs, peer.fsm.pConf.Config.PeerAs) } if path = filterpath(peer, path, old); path == nil { return nil } path = table.UpdatePathAttrs(peer.fsm.gConf, peer.fsm.pConf, peer.fsm.peerInfo, path) options := &table.PolicyOptions{ Info: peer.fsm.peerInfo, } path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options) // When 'path' is filetered (path == nil), check 'old' has been sent to this peer. // If it has, send withdrawal to the peer. if path == nil && old != nil { o := peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, old, options) if o != nil { path = old.Clone(true) } } // draft-uttaro-idr-bgp-persistence-02 // 4.3. Processing LLGR_STALE Routes // // The route SHOULD NOT be advertised to any neighbor from which the // Long-lived Graceful Restart Capability has not been received. The // exception is described in the Optional Partial Deployment // Procedure section (Section 4.7). Note that this requirement // implies that such routes should be withdrawn from any such neighbor. if path != nil && !path.IsWithdraw && !peer.isLLGREnabledFamily(path.GetRouteFamily()) && path.IsLLGRStale() { // we send unnecessary withdrawn even if we didn't // sent the route. path = path.Clone(true) } // remove local-pref attribute // we should do this after applying export policy since policy may // set local-preference if path != nil && !peer.isIBGPPeer() && !peer.isRouteServerClient() { path.RemoveLocalPref() } return path } func (peer *Peer) getBestFromLocal(rfList []bgp.RouteFamily) ([]*table.Path, []*table.Path) { pathList := []*table.Path{} filtered := []*table.Path{} for _, path := range peer.localRib.GetBestPathList(peer.TableID(), peer.toGlobalFamilies(rfList)) { if p := peer.filterpath(path, nil); p != nil { pathList = append(pathList, p) } else { filtered = append(filtered, path) } } if peer.isGracefulRestartEnabled() { for _, family := range rfList { pathList = append(pathList, table.NewEOR(family)) } } return pathList, filtered } func (peer *Peer) processOutgoingPaths(paths, olds []*table.Path) []*table.Path { if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED { return nil } if peer.fsm.pConf.GracefulRestart.State.LocalRestarting { log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.fsm.pConf.State.NeighborAddress, }).Debug("now syncing, suppress sending updates") return nil } outgoing := make([]*table.Path, 0, len(paths)) for idx, path := range paths { var old *table.Path if olds != nil { old = olds[idx] } if p := peer.filterpath(path, old); p != nil { outgoing = append(outgoing, p) } } return outgoing } func (peer *Peer) handleRouteRefresh(e *FsmMsg) []*table.Path { m := e.MsgData.(*bgp.BGPMessage) rr := m.Body.(*bgp.BGPRouteRefresh) rf := bgp.AfiSafiToRouteFamily(rr.AFI, rr.SAFI) if _, ok := peer.fsm.rfMap[rf]; !ok { log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), "Data": rf, }).Warn("Route family isn't supported") return nil } if _, ok := peer.fsm.capMap[bgp.BGP_CAP_ROUTE_REFRESH]; !ok { log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), }).Warn("ROUTE_REFRESH received but the capability wasn't advertised") return nil } rfList := []bgp.RouteFamily{rf} accepted, filtered := peer.getBestFromLocal(rfList) for _, path := range filtered { path.IsWithdraw = true accepted = append(accepted, path) } return accepted } func (peer *Peer) doPrefixLimit(k bgp.RouteFamily, c *config.PrefixLimitConfig) *bgp.BGPMessage { if maxPrefixes := int(c.MaxPrefixes); maxPrefixes > 0 { count := peer.adjRibIn.Count([]bgp.RouteFamily{k}) pct := int(c.ShutdownThresholdPct) if pct > 0 && !peer.prefixLimitWarned[k] && count > (maxPrefixes*pct/100) { peer.prefixLimitWarned[k] = true log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), "AddressFamily": k.String(), }).Warnf("prefix limit %d%% reached", pct) } if count > maxPrefixes { log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), "AddressFamily": k.String(), }).Warnf("prefix limit reached") return bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED, nil) } } return nil } func (peer *Peer) updatePrefixLimitConfig(c []config.AfiSafi) error { x := peer.fsm.pConf.AfiSafis y := c if len(x) != len(y) { return fmt.Errorf("changing supported afi-safi is not allowed") } m := make(map[bgp.RouteFamily]config.PrefixLimitConfig) for _, e := range x { k, err := bgp.GetRouteFamily(string(e.Config.AfiSafiName)) if err != nil { return err } m[k] = e.PrefixLimit.Config } for _, e := range y { k, err := bgp.GetRouteFamily(string(e.Config.AfiSafiName)) if err != nil { return err } if p, ok := m[k]; !ok { return fmt.Errorf("changing supported afi-safi is not allowed") } else if !p.Equal(&e.PrefixLimit.Config) { log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), "AddressFamily": e.Config.AfiSafiName, "OldMaxPrefixes": p.MaxPrefixes, "NewMaxPrefixes": e.PrefixLimit.Config.MaxPrefixes, "OldShutdownThresholdPct": p.ShutdownThresholdPct, "NewShutdownThresholdPct": e.PrefixLimit.Config.ShutdownThresholdPct, }).Warnf("update prefix limit configuration") peer.prefixLimitWarned[k] = false if msg := peer.doPrefixLimit(k, &e.PrefixLimit.Config); msg != nil { sendFsmOutgoingMsg(peer, nil, msg, true) } } } peer.fsm.pConf.AfiSafis = c return nil } func (peer *Peer) handleUpdate(e *FsmMsg) ([]*table.Path, []bgp.RouteFamily, *bgp.BGPMessage) { m := e.MsgData.(*bgp.BGPMessage) update := m.Body.(*bgp.BGPUpdate) log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.fsm.pConf.State.NeighborAddress, "nlri": update.NLRI, "withdrawals": update.WithdrawnRoutes, "attributes": update.PathAttributes, }).Debug("received update") peer.fsm.pConf.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.Config.AfiSafiName)) if msg := peer.doPrefixLimit(k, &family.PrefixLimit.Config); msg != nil { return nil, nil, msg } } paths := make([]*table.Path, 0, len(e.PathList)) eor := []bgp.RouteFamily{} for _, path := range e.PathList { if path.IsEOR() { family := path.GetRouteFamily() log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), "AddressFamily": family, }).Debug("EOR received") eor = append(eor, family) continue } if path.Filtered(peer.ID()) != table.POLICY_DIRECTION_IN { paths = append(paths, path) } else { paths = append(paths, path.Clone(true)) } } return paths, eor, nil } return nil, nil, nil } func (peer *Peer) startFSMHandler(incoming *channels.InfiniteChannel, stateCh chan *FsmMsg) { peer.fsm.h = NewFSMHandler(peer.fsm, incoming, stateCh, peer.outgoing) } func (peer *Peer) StaleAll(rfList []bgp.RouteFamily) { peer.adjRibIn.StaleAll(rfList) } func (peer *Peer) PassConn(conn *net.TCPConn) { select { case peer.fsm.connCh <- conn: default: conn.Close() log.WithFields(log.Fields{ "Topic": "Peer", "Key": peer.ID(), }).Warn("accepted conn is closed to avoid be blocked") } } func (peer *Peer) ToConfig(getAdvertised bool) *config.Neighbor { // create copy which can be access to without mutex conf := *peer.fsm.pConf conf.AfiSafis = make([]config.AfiSafi, len(peer.fsm.pConf.AfiSafis)) for i := 0; i < len(peer.fsm.pConf.AfiSafis); i++ { conf.AfiSafis[i] = peer.fsm.pConf.AfiSafis[i] } remoteCap := make([]bgp.ParameterCapabilityInterface, 0, len(peer.fsm.capMap)) for _, c := range peer.fsm.capMap { for _, m := range c { // need to copy all values here buf, _ := m.Serialize() cap, _ := bgp.DecodeCapability(buf) remoteCap = append(remoteCap, cap) } } conf.State.RemoteCapabilityList = remoteCap conf.State.LocalCapabilityList = capabilitiesFromConfig(peer.fsm.pConf) conf.State.SessionState = config.IntToSessionStateMap[int(peer.fsm.state)] conf.State.AdminState = config.IntToAdminStateMap[int(peer.fsm.adminState)] if peer.fsm.state == bgp.BGP_FSM_ESTABLISHED { rfList := peer.configuredRFlist() if getAdvertised { pathList, filtered := peer.getBestFromLocal(rfList) conf.State.AdjTable.Advertised = uint32(len(pathList)) conf.State.AdjTable.Filtered = uint32(len(filtered)) } else { conf.State.AdjTable.Advertised = 0 } conf.State.AdjTable.Received = uint32(peer.adjRibIn.Count(rfList)) conf.State.AdjTable.Accepted = uint32(peer.adjRibIn.Accepted(rfList)) conf.Transport.State.LocalAddress, conf.Transport.State.LocalPort = peer.fsm.LocalHostPort() _, conf.Transport.State.RemotePort = peer.fsm.RemoteHostPort() buf, _ := peer.fsm.recvOpen.Serialize() // need to copy all values here conf.State.ReceivedOpenMessage, _ = bgp.ParseBGPMessage(buf) conf.State.RemoteRouterId = peer.fsm.peerInfo.ID.To4().String() } return &conf } func (peer *Peer) DropAll(rfList []bgp.RouteFamily) { peer.adjRibIn.Drop(rfList) }