diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 13:48:38 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 20:44:25 +0900 |
commit | c4775c42510d1f1ddd55036dc19e982712fa6a0b (patch) | |
tree | 6ec8b61d4338c809e239e3003a2d32d480898e22 /server/server.go | |
parent | b3079759aa13172fcb548a83da9a9653d8d5fed4 (diff) |
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout
Now you can see clearly what are private and public library code.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'server/server.go')
-rw-r--r-- | server/server.go | 3048 |
1 files changed, 0 insertions, 3048 deletions
diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 6faecba9..00000000 --- a/server/server.go +++ /dev/null @@ -1,3048 +0,0 @@ -// 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 ( - "bytes" - "fmt" - "net" - "strconv" - "sync" - "time" - - "github.com/eapache/channels" - uuid "github.com/satori/go.uuid" - log "github.com/sirupsen/logrus" - - "github.com/osrg/gobgp/config" - "github.com/osrg/gobgp/packet/bgp" - "github.com/osrg/gobgp/table" -) - -type TCPListener struct { - l *net.TCPListener - ch chan struct{} -} - -func (l *TCPListener) Close() error { - if err := l.l.Close(); err != nil { - return err - } - t := time.NewTicker(time.Second) - select { - case <-l.ch: - case <-t.C: - return fmt.Errorf("close timeout") - } - return nil -} - -// avoid mapped IPv6 address -func NewTCPListener(address string, port uint32, ch chan *net.TCPConn) (*TCPListener, error) { - proto := "tcp4" - if ip := net.ParseIP(address); ip == nil { - return nil, fmt.Errorf("can't listen on %s", address) - } else if ip.To4() == nil { - proto = "tcp6" - } - addr, err := net.ResolveTCPAddr(proto, net.JoinHostPort(address, strconv.Itoa(int(port)))) - if err != nil { - return nil, err - } - - l, err := net.ListenTCP(proto, addr) - if err != nil { - return nil, err - } - // Note: Set TTL=255 for incoming connection listener in order to accept - // connection in case for the neighbor has TTL Security settings. - if err := SetListenTcpTTLSockopt(l, 255); err != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": addr, - }).Warnf("cannot set TTL(=%d) for TCPListener: %s", 255, err) - } - - closeCh := make(chan struct{}) - go func() error { - for { - conn, err := l.AcceptTCP() - if err != nil { - close(closeCh) - log.WithFields(log.Fields{ - "Topic": "Peer", - "Error": err, - }).Warn("Failed to AcceptTCP") - return err - } - ch <- conn - } - }() - return &TCPListener{ - l: l, - ch: closeCh, - }, nil -} - -type BgpServer struct { - bgpConfig config.Bgp - fsmincomingCh *channels.InfiniteChannel - fsmStateCh chan *FsmMsg - acceptCh chan *net.TCPConn - - mgmtCh chan *mgmtOp - policy *table.RoutingPolicy - listeners []*TCPListener - neighborMap map[string]*Peer - peerGroupMap map[string]*PeerGroup - globalRib *table.TableManager - rsRib *table.TableManager - roaManager *roaManager - shutdownWG *sync.WaitGroup - watcherMap map[WatchEventType][]*Watcher - zclient *zebraClient - bmpManager *bmpClientManager - mrtManager *mrtManager - uuidMap map[uuid.UUID]string -} - -func NewBgpServer() *BgpServer { - roaManager, _ := NewROAManager(0) - s := &BgpServer{ - neighborMap: make(map[string]*Peer), - peerGroupMap: make(map[string]*PeerGroup), - policy: table.NewRoutingPolicy(), - roaManager: roaManager, - mgmtCh: make(chan *mgmtOp, 1), - watcherMap: make(map[WatchEventType][]*Watcher), - uuidMap: make(map[uuid.UUID]string), - } - s.bmpManager = newBmpClientManager(s) - s.mrtManager = newMrtManager(s) - return s -} - -func (server *BgpServer) Listeners(addr string) []*net.TCPListener { - list := make([]*net.TCPListener, 0, len(server.listeners)) - rhs := net.ParseIP(addr).To4() != nil - for _, l := range server.listeners { - host, _, _ := net.SplitHostPort(l.l.Addr().String()) - lhs := net.ParseIP(host).To4() != nil - if lhs == rhs { - list = append(list, l.l) - } - } - return list -} - -func (s *BgpServer) active() error { - if s.bgpConfig.Global.Config.As == 0 { - return fmt.Errorf("bgp server hasn't started yet") - } - return nil -} - -type mgmtOp struct { - f func() error - errCh chan error - checkActive bool // check BGP global setting is configured before calling f() -} - -func (server *BgpServer) handleMGMTOp(op *mgmtOp) { - if op.checkActive { - if err := server.active(); err != nil { - op.errCh <- err - return - } - } - op.errCh <- op.f() -} - -func (s *BgpServer) mgmtOperation(f func() error, checkActive bool) (err error) { - ch := make(chan error) - defer func() { err = <-ch }() - s.mgmtCh <- &mgmtOp{ - f: f, - errCh: ch, - checkActive: checkActive, - } - return -} - -func (server *BgpServer) Serve() { - server.listeners = make([]*TCPListener, 0, 2) - server.fsmincomingCh = channels.NewInfiniteChannel() - server.fsmStateCh = make(chan *FsmMsg, 4096) - - handleFsmMsg := func(e *FsmMsg) { - peer, found := server.neighborMap[e.MsgSrc] - if !found { - log.WithFields(log.Fields{ - "Topic": "Peer", - }).Warnf("Can't find the neighbor %s", e.MsgSrc) - return - } - if e.Version != peer.fsm.version { - log.WithFields(log.Fields{ - "Topic": "Peer", - }).Debug("FSM version inconsistent") - return - } - server.handleFSMMessage(peer, e) - } - - for { - passConn := func(conn *net.TCPConn) { - host, _, _ := net.SplitHostPort(conn.RemoteAddr().String()) - ipaddr, _ := net.ResolveIPAddr("ip", host) - remoteAddr := ipaddr.String() - peer, found := server.neighborMap[remoteAddr] - if found { - if peer.fsm.adminState != ADMIN_STATE_UP { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Remote Addr": remoteAddr, - "Admin State": peer.fsm.adminState, - }).Debug("New connection for non admin-state-up peer") - conn.Close() - return - } - localAddrValid := func(laddr string) bool { - if laddr == "0.0.0.0" || laddr == "::" { - return true - } - l := conn.LocalAddr() - if l == nil { - // already closed - return false - } - - host, _, _ := net.SplitHostPort(l.String()) - if host != laddr { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": remoteAddr, - "Configured addr": laddr, - "Addr": host, - }).Info("Mismatched local address") - return false - } - return true - }(peer.fsm.pConf.Transport.Config.LocalAddress) - - if !localAddrValid { - conn.Close() - return - } - - log.WithFields(log.Fields{ - "Topic": "Peer", - }).Debugf("Accepted a new passive connection from:%s", remoteAddr) - peer.PassConn(conn) - } else if pg := server.matchLongestDynamicNeighborPrefix(remoteAddr); pg != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - }).Debugf("Accepted a new dynamic neighbor from:%s", remoteAddr) - rib := server.globalRib - if pg.Conf.RouteServer.Config.RouteServerClient { - rib = server.rsRib - } - peer := newDynamicPeer(&server.bgpConfig.Global, remoteAddr, pg.Conf, rib, server.policy) - if peer == nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": remoteAddr, - }).Infof("Can't create new Dynamic Peer") - conn.Close() - return - } - server.policy.Reset(nil, map[string]config.ApplyPolicy{peer.ID(): peer.fsm.pConf.ApplyPolicy}) - server.neighborMap[remoteAddr] = peer - peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh) - server.broadcastPeerState(peer, bgp.BGP_FSM_ACTIVE, nil) - peer.PassConn(conn) - } else { - log.WithFields(log.Fields{ - "Topic": "Peer", - }).Infof("Can't find configuration for a new passive connection from:%s", remoteAddr) - conn.Close() - } - } - - select { - case op := <-server.mgmtCh: - server.handleMGMTOp(op) - case conn := <-server.acceptCh: - passConn(conn) - default: - } - - for { - select { - case e := <-server.fsmStateCh: - handleFsmMsg(e) - default: - goto CONT - } - } - CONT: - - select { - case op := <-server.mgmtCh: - server.handleMGMTOp(op) - case rmsg := <-server.roaManager.ReceiveROA(): - server.roaManager.HandleROAEvent(rmsg) - case conn := <-server.acceptCh: - passConn(conn) - case e, ok := <-server.fsmincomingCh.Out(): - if !ok { - continue - } - handleFsmMsg(e.(*FsmMsg)) - case e := <-server.fsmStateCh: - handleFsmMsg(e) - } - } -} - -func (server *BgpServer) matchLongestDynamicNeighborPrefix(a string) *PeerGroup { - ipAddr := net.ParseIP(a) - longestMask := net.CIDRMask(0, 32).String() - var longestPG *PeerGroup - for _, pg := range server.peerGroupMap { - for _, d := range pg.dynamicNeighbors { - _, netAddr, _ := net.ParseCIDR(d.Config.Prefix) - if netAddr.Contains(ipAddr) { - if netAddr.Mask.String() > longestMask { - longestMask = netAddr.Mask.String() - longestPG = pg - } - } - } - } - return longestPG -} - -func sendFsmOutgoingMsg(peer *Peer, paths []*table.Path, notification *bgp.BGPMessage, stayIdle bool) { - peer.outgoing.In() <- &FsmOutgoingMsg{ - Paths: paths, - Notification: notification, - StayIdle: stayIdle, - } -} - -func isASLoop(peer *Peer, path *table.Path) bool { - for _, as := range path.GetAsList() { - if as == peer.AS() { - return true - } - } - return false -} - -func filterpath(peer *Peer, path, old *table.Path) *table.Path { - if path == nil { - return nil - } - if _, ok := peer.fsm.rfMap[path.GetRouteFamily()]; !ok { - return nil - } - - //RFC4684 Constrained Route Distribution - if _, y := peer.fsm.rfMap[bgp.RF_RTC_UC]; y && path.GetRouteFamily() != bgp.RF_RTC_UC { - ignore := true - for _, ext := range path.GetExtCommunities() { - for _, p := range peer.adjRibIn.PathList([]bgp.RouteFamily{bgp.RF_RTC_UC}, true) { - rt := p.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget - // Note: nil RT means the default route target - if rt == nil || ext.String() == rt.String() { - ignore = false - break - } - } - if !ignore { - break - } - } - if ignore { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Data": path, - }).Debug("Filtered by Route Target Constraint, ignore") - return nil - } - } - - //iBGP handling - if peer.isIBGPPeer() { - ignore := false - if !path.IsLocal() { - ignore = true - info := path.GetSource() - //if the path comes from eBGP peer - if info.AS != peer.AS() { - ignore = false - } - // RFC4456 8. Avoiding Routing Information Loops - // A router that recognizes the ORIGINATOR_ID attribute SHOULD - // ignore a route received with its BGP Identifier as the ORIGINATOR_ID. - if id := path.GetOriginatorID(); peer.fsm.gConf.Config.RouterId == id.String() { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "OriginatorID": id, - "Data": path, - }).Debug("Originator ID is mine, ignore") - return nil - } - if info.RouteReflectorClient { - ignore = false - } - if peer.isRouteReflectorClient() { - // RFC4456 8. Avoiding Routing Information Loops - // If the local CLUSTER_ID is found in the CLUSTER_LIST, - // the advertisement received SHOULD be ignored. - for _, clusterID := range path.GetClusterList() { - if clusterID.Equal(peer.fsm.peerInfo.RouteReflectorClusterID) { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "ClusterID": clusterID, - "Data": path, - }).Debug("cluster list path attribute has local cluster id, ignore") - return nil - } - } - ignore = false - } - } - - if ignore { - if !path.IsWithdraw && old != nil { - oldSource := old.GetSource() - if old.IsLocal() || oldSource.Address.String() != peer.ID() && oldSource.AS != peer.AS() { - // In this case, we suppose this peer has the same prefix - // received from another iBGP peer. - // So we withdraw the old best which was injected locally - // (from CLI or gRPC for example) in order to avoid the - // old best left on peers. - // Also, we withdraw the eBGP route which is the old best. - // When we got the new best from iBGP, we don't advertise - // the new best and need to withdraw the old best. - return old.Clone(true) - } - } - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Data": path, - }).Debug("From same AS, ignore.") - return nil - } - } - - if path = peer.filterPathFromSourcePeer(path, old); path == nil { - return nil - } - - if !peer.isRouteServerClient() && isASLoop(peer, path) { - return nil - } - return path -} - -func (s *BgpServer) filterpath(peer *Peer, path, old *table.Path) *table.Path { - // Special handling for RTM NLRI. - if path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC && !path.IsWithdraw { - // If the given "path" is locally generated and the same with "old", we - // assumes "path" was already sent before. This assumption avoids the - // infinite UPDATE loop between Route Reflector and its clients. - if path.IsLocal() && path == old { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.fsm.pConf.State.NeighborAddress, - "Path": path, - }).Debug("given rtm nlri is already sent, skipping to advertise") - return nil - } - - if old != nil && old.IsLocal() { - // We assumes VRF with the specific RT is deleted. - path = old.Clone(true) - } else if peer.isRouteReflectorClient() { - // We need to send the path even if the peer is originator of the - // path in order to signal that the client should distribute route - // with the given RT. - } else { - // We send a path even if it is not the best path. See comments in - // (*Destination) GetChanges(). - dst := peer.localRib.GetDestination(path) - path = nil - for _, p := range dst.GetKnownPathList(peer.TableID(), peer.AS()) { - srcPeer := p.GetSource() - if peer.ID() != srcPeer.Address.String() { - if srcPeer.RouteReflectorClient { - // The path from a RR client is preferred than others - // for the case that RR and non RR client peering - // (e.g., peering of different RR clusters). - path = p - break - } else if path == nil { - path = p - } - } - } - } - } - - // 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 - } - - options := &table.PolicyOptions{ - Info: peer.fsm.peerInfo, - OldNextHop: path.GetNexthop(), - } - path = table.UpdatePathAttrs(peer.fsm.gConf, peer.fsm.pConf, peer.fsm.peerInfo, path) - - if v := s.roaManager.validate(path); v != nil { - options.ValidationResult = v - } - - path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options) - // When 'path' is filtered (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 clonePathList(pathList []*table.Path) []*table.Path { - l := make([]*table.Path, 0, len(pathList)) - for _, p := range pathList { - if p != nil { - l = append(l, p.Clone(p.IsWithdraw)) - } - } - return l -} - -func (server *BgpServer) notifyBestWatcher(best []*table.Path, multipath [][]*table.Path) { - if table.SelectionOptions.DisableBestPathSelection { - // Note: If best path selection disabled, no best path to notify. - return - } - clonedM := make([][]*table.Path, len(multipath)) - for i, pathList := range multipath { - clonedM[i] = clonePathList(pathList) - } - clonedB := clonePathList(best) - m := make(map[string]uint16) - for _, p := range clonedB { - switch p.GetRouteFamily() { - case bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN: - for _, vrf := range server.globalRib.Vrfs { - if vrf.Id != 0 && table.CanImportToVrf(vrf, p) { - m[p.GetNlri().String()] = uint16(vrf.Id) - } - } - } - } - w := &WatchEventBestPath{PathList: clonedB, MultiPathList: clonedM} - if len(m) > 0 { - w.Vrf = m - } - server.notifyWatcher(WATCH_EVENT_TYPE_BEST_PATH, w) -} - -func (s *BgpServer) ToConfig(peer *Peer, 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, af := range peer.fsm.pConf.AfiSafis { - conf.AfiSafis[i] = af - conf.AfiSafis[i].AddPaths.State.Receive = peer.isAddPathReceiveEnabled(af.State.Family) - if peer.isAddPathSendEnabled(af.State.Family) { - conf.AfiSafis[i].AddPaths.State.SendMax = af.AddPaths.State.SendMax - } else { - conf.AfiSafis[i].AddPaths.State.SendMax = 0 - } - } - - remoteCap := make([]bgp.ParameterCapabilityInterface, 0, len(peer.fsm.capMap)) - for _, caps := range peer.fsm.capMap { - for _, m := range caps { - // need to copy all values here - buf, _ := m.Serialize() - c, _ := bgp.DecodeCapability(buf) - remoteCap = append(remoteCap, c) - } - } - 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 := s.getBestFromLocal(peer, 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 (server *BgpServer) notifyPrePolicyUpdateWatcher(peer *Peer, pathList []*table.Path, msg *bgp.BGPMessage, timestamp time.Time, payload []byte) { - if !server.isWatched(WATCH_EVENT_TYPE_PRE_UPDATE) || peer == nil { - return - } - cloned := clonePathList(pathList) - if len(cloned) == 0 { - return - } - _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER] - l, _ := peer.fsm.LocalHostPort() - ev := &WatchEventUpdate{ - Message: msg, - PeerAS: peer.fsm.peerInfo.AS, - LocalAS: peer.fsm.peerInfo.LocalAS, - PeerAddress: peer.fsm.peerInfo.Address, - LocalAddress: net.ParseIP(l), - PeerID: peer.fsm.peerInfo.ID, - FourBytesAs: y, - Timestamp: timestamp, - Payload: payload, - PostPolicy: false, - PathList: cloned, - Neighbor: server.ToConfig(peer, false), - } - server.notifyWatcher(WATCH_EVENT_TYPE_PRE_UPDATE, ev) -} - -func (server *BgpServer) notifyPostPolicyUpdateWatcher(peer *Peer, pathList []*table.Path) { - if !server.isWatched(WATCH_EVENT_TYPE_POST_UPDATE) || peer == nil { - return - } - cloned := clonePathList(pathList) - if len(cloned) == 0 { - return - } - _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER] - l, _ := peer.fsm.LocalHostPort() - ev := &WatchEventUpdate{ - PeerAS: peer.fsm.peerInfo.AS, - LocalAS: peer.fsm.peerInfo.LocalAS, - PeerAddress: peer.fsm.peerInfo.Address, - LocalAddress: net.ParseIP(l), - PeerID: peer.fsm.peerInfo.ID, - FourBytesAs: y, - Timestamp: cloned[0].GetTimestamp(), - PostPolicy: true, - PathList: cloned, - Neighbor: server.ToConfig(peer, false), - } - server.notifyWatcher(WATCH_EVENT_TYPE_POST_UPDATE, ev) -} - -func newWatchEventPeerState(peer *Peer, m *FsmMsg) *WatchEventPeerState { - _, rport := peer.fsm.RemoteHostPort() - laddr, lport := peer.fsm.LocalHostPort() - sentOpen := buildopen(peer.fsm.gConf, peer.fsm.pConf) - recvOpen := peer.fsm.recvOpen - e := &WatchEventPeerState{ - PeerAS: peer.fsm.peerInfo.AS, - LocalAS: peer.fsm.peerInfo.LocalAS, - PeerAddress: peer.fsm.peerInfo.Address, - LocalAddress: net.ParseIP(laddr), - PeerPort: rport, - LocalPort: lport, - PeerID: peer.fsm.peerInfo.ID, - SentOpen: sentOpen, - RecvOpen: recvOpen, - State: peer.fsm.state, - AdminState: peer.fsm.adminState, - Timestamp: time.Now(), - PeerInterface: peer.fsm.pConf.Config.NeighborInterface, - } - - if m != nil { - e.StateReason = m.StateReason - } - return e -} - -func (server *BgpServer) broadcastPeerState(peer *Peer, oldState bgp.FSMState, e *FsmMsg) { - newState := peer.fsm.state - if oldState == bgp.BGP_FSM_ESTABLISHED || newState == bgp.BGP_FSM_ESTABLISHED { - server.notifyWatcher(WATCH_EVENT_TYPE_PEER_STATE, newWatchEventPeerState(peer, e)) - } -} - -func (server *BgpServer) notifyMessageWatcher(peer *Peer, timestamp time.Time, msg *bgp.BGPMessage, isSent bool) { - // validation should be done in the caller of this function - _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER] - l, _ := peer.fsm.LocalHostPort() - ev := &WatchEventMessage{ - Message: msg, - PeerAS: peer.fsm.peerInfo.AS, - LocalAS: peer.fsm.peerInfo.LocalAS, - PeerAddress: peer.fsm.peerInfo.Address, - LocalAddress: net.ParseIP(l), - PeerID: peer.fsm.peerInfo.ID, - FourBytesAs: y, - Timestamp: timestamp, - IsSent: isSent, - } - if !isSent { - server.notifyWatcher(WATCH_EVENT_TYPE_RECV_MSG, ev) - } -} - -func (server *BgpServer) notifyRecvMessageWatcher(peer *Peer, timestamp time.Time, msg *bgp.BGPMessage) { - if peer == nil || !server.isWatched(WATCH_EVENT_TYPE_RECV_MSG) { - return - } - server.notifyMessageWatcher(peer, timestamp, msg, false) -} - -func (s *BgpServer) getBestFromLocal(peer *Peer, rfList []bgp.RouteFamily) ([]*table.Path, []*table.Path) { - pathList := []*table.Path{} - filtered := []*table.Path{} - for _, family := range peer.toGlobalFamilies(rfList) { - pl := func() []*table.Path { - if peer.isAddPathSendEnabled(family) { - return peer.localRib.GetPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family}) - } - return peer.localRib.GetBestPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family}) - }() - for _, path := range pl { - if p := s.filterpath(peer, 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 (s *BgpServer) processOutgoingPaths(peer *Peer, 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 := s.filterpath(peer, path, old); p != nil { - outgoing = append(outgoing, p) - } - } - return outgoing -} - -func (s *BgpServer) handleRouteRefresh(peer *Peer, 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 := s.getBestFromLocal(peer, rfList) - for _, path := range filtered { - path.IsWithdraw = true - accepted = append(accepted, path) - } - return accepted -} - -func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) { - rs := peer != nil && peer.isRouteServerClient() - vrf := !rs && peer != nil && peer.fsm.pConf.Config.Vrf != "" - - tableId := table.GLOBAL_RIB_NAME - rib := server.globalRib - if rs { - tableId = peer.TableID() - rib = server.rsRib - } - - for _, path := range pathList { - if vrf { - path = path.ToGlobal(rib.Vrfs[peer.fsm.pConf.Config.Vrf]) - } - - policyOptions := &table.PolicyOptions{} - - if !rs && peer != nil { - policyOptions.Info = peer.fsm.peerInfo - } - if v := server.roaManager.validate(path); v != nil { - policyOptions.ValidationResult = v - } - - if p := server.policy.ApplyPolicy(tableId, table.POLICY_DIRECTION_IMPORT, path, policyOptions); p != nil { - path = p - } else { - path = path.Clone(true) - } - - if !rs { - server.notifyPostPolicyUpdateWatcher(peer, []*table.Path{path}) - - // RFC4684 Constrained Route Distribution 6. Operation - // - // When a BGP speaker receives a BGP UPDATE that advertises or withdraws - // a given Route Target membership NLRI, it should examine the RIB-OUTs - // of VPN NLRIs and re-evaluate the advertisement status of routes that - // match the Route Target in question. - // - // A BGP speaker should generate the minimum set of BGP VPN route - // updates (advertisements and/or withdraws) necessary to transition - // between the previous and current state of the route distribution - // graph that is derived from Route Target membership information. - if peer != nil && path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC { - rt := path.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget - fs := make([]bgp.RouteFamily, 0, len(peer.negotiatedRFList())) - for _, f := range peer.negotiatedRFList() { - if f != bgp.RF_RTC_UC { - fs = append(fs, f) - } - } - var candidates []*table.Path - if path.IsWithdraw { - // Note: The paths to be withdrawn are filtered because the - // given RT on RTM NLRI is already removed from adj-RIB-in. - _, candidates = server.getBestFromLocal(peer, fs) - } else { - candidates = server.globalRib.GetBestPathList(peer.TableID(), 0, fs) - } - paths := make([]*table.Path, 0, len(candidates)) - for _, p := range candidates { - for _, ext := range p.GetExtCommunities() { - if rt == nil || ext.String() == rt.String() { - if path.IsWithdraw { - p = p.Clone(true) - } - paths = append(paths, p) - break - } - } - } - if path.IsWithdraw { - // Skips filtering because the paths are already filtered - // and the withdrawal does not need the path attributes. - } else { - paths = server.processOutgoingPaths(peer, paths, nil) - } - sendFsmOutgoingMsg(peer, paths, nil, false) - } - } - - if dsts := rib.Update(path); len(dsts) > 0 { - server.propagateUpdateToNeighbors(peer, path, dsts, true) - } - } -} - -func (server *BgpServer) dropPeerAllRoutes(peer *Peer, families []bgp.RouteFamily) { - rib := server.globalRib - if peer.isRouteServerClient() { - rib = server.rsRib - } - for _, family := range peer.toGlobalFamilies(families) { - for _, path := range rib.GetPathListByPeer(peer.fsm.peerInfo, family) { - p := path.Clone(true) - if dsts := rib.Update(p); len(dsts) > 0 { - server.propagateUpdateToNeighbors(peer, p, dsts, false) - } - } - } -} - -func dstsToPaths(id string, as uint32, dsts []*table.Update) ([]*table.Path, []*table.Path, [][]*table.Path) { - bestList := make([]*table.Path, 0, len(dsts)) - oldList := make([]*table.Path, 0, len(dsts)) - mpathList := make([][]*table.Path, 0, len(dsts)) - - for _, dst := range dsts { - best, old, mpath := dst.GetChanges(id, as, false) - bestList = append(bestList, best) - oldList = append(oldList, old) - if mpath != nil { - mpathList = append(mpathList, mpath) - } - } - return bestList, oldList, mpathList -} - -func (server *BgpServer) propagateUpdateToNeighbors(source *Peer, newPath *table.Path, dsts []*table.Update, needOld bool) { - if table.SelectionOptions.DisableBestPathSelection { - return - } - var gBestList, gOldList, bestList, oldList []*table.Path - var mpathList [][]*table.Path - if source == nil || !source.isRouteServerClient() { - gBestList, gOldList, mpathList = dstsToPaths(table.GLOBAL_RIB_NAME, 0, dsts) - server.notifyBestWatcher(gBestList, mpathList) - } - family := newPath.GetRouteFamily() - for _, targetPeer := range server.neighborMap { - if (source == nil && targetPeer.isRouteServerClient()) || (source != nil && source.isRouteServerClient() != targetPeer.isRouteServerClient()) { - continue - } - f := func() bgp.RouteFamily { - if targetPeer.fsm.pConf.Config.Vrf != "" { - switch family { - case bgp.RF_IPv4_VPN: - return bgp.RF_IPv4_UC - case bgp.RF_IPv6_VPN: - return bgp.RF_IPv6_UC - } - } - return family - }() - if targetPeer.isAddPathSendEnabled(f) { - if newPath.IsWithdraw { - bestList = func() []*table.Path { - l := make([]*table.Path, 0, len(dsts)) - for _, d := range dsts { - l = append(l, d.GetWithdrawnPath()...) - } - return l - }() - } else { - bestList = []*table.Path{newPath} - if newPath.GetRouteFamily() == bgp.RF_RTC_UC { - // we assumes that new "path" nlri was already sent before. This assumption avoids the - // infinite UPDATE loop between Route Reflector and its clients. - for _, old := range dsts[0].OldKnownPathList { - if old.IsLocal() { - bestList = []*table.Path{} - break - } - } - } - } - oldList = nil - } else if targetPeer.isRouteServerClient() { - bestList, oldList, _ = dstsToPaths(targetPeer.TableID(), targetPeer.AS(), dsts) - } else { - bestList = gBestList - oldList = gOldList - } - if !needOld { - oldList = nil - } - if paths := server.processOutgoingPaths(targetPeer, bestList, oldList); len(paths) > 0 { - sendFsmOutgoingMsg(targetPeer, paths, nil, false) - } - } -} - -func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) { - switch e.MsgType { - case FSM_MSG_STATE_CHANGE: - nextState := e.MsgData.(bgp.FSMState) - oldState := bgp.FSMState(peer.fsm.pConf.State.SessionState.ToInt()) - peer.fsm.pConf.State.SessionState = config.IntToSessionStateMap[int(nextState)] - peer.fsm.StateChange(nextState) - - // PeerDown - if oldState == bgp.BGP_FSM_ESTABLISHED { - t := time.Now() - if t.Sub(time.Unix(peer.fsm.pConf.Timers.State.Uptime, 0)) < FLOP_THRESHOLD { - peer.fsm.pConf.State.Flops++ - } - var drop []bgp.RouteFamily - if peer.fsm.reason.Type == FSM_GRACEFUL_RESTART { - peer.fsm.pConf.GracefulRestart.State.PeerRestarting = true - var p []bgp.RouteFamily - p, drop = peer.forwardingPreservedFamilies() - server.propagateUpdate(peer, peer.StaleAll(p)) - } else { - drop = peer.configuredRFlist() - } - peer.prefixLimitWarned = make(map[bgp.RouteFamily]bool) - peer.DropAll(drop) - server.dropPeerAllRoutes(peer, drop) - if peer.fsm.pConf.Config.PeerAs == 0 { - peer.fsm.pConf.State.PeerAs = 0 - peer.fsm.peerInfo.AS = 0 - } - if peer.isDynamicNeighbor() { - peer.stopPeerRestarting() - go peer.stopFSM() - delete(server.neighborMap, peer.fsm.pConf.State.NeighborAddress) - server.broadcastPeerState(peer, oldState, e) - return - } - } else if peer.fsm.pConf.GracefulRestart.State.PeerRestarting && nextState == bgp.BGP_FSM_IDLE { - if peer.fsm.pConf.GracefulRestart.State.LongLivedEnabled { - llgr, no_llgr := peer.llgrFamilies() - - peer.DropAll(no_llgr) - server.dropPeerAllRoutes(peer, no_llgr) - - // attach LLGR_STALE community to paths in peer's adj-rib-in - // paths with NO_LLGR are deleted - pathList := peer.markLLGRStale(llgr) - - // calculate again - // wheh path with LLGR_STALE chosen as best, - // peer which doesn't support LLGR will drop the path - // if it is in adj-rib-out, do withdrawal - server.propagateUpdate(peer, pathList) - - for _, f := range llgr { - endCh := make(chan struct{}) - peer.llgrEndChs = append(peer.llgrEndChs, endCh) - go func(family bgp.RouteFamily, endCh chan struct{}) { - t := peer.llgrRestartTime(family) - timer := time.NewTimer(time.Second * time.Duration(t)) - - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Family": family, - }).Debugf("start LLGR restart timer (%d sec) for %s", t, family) - - select { - case <-timer.C: - server.mgmtOperation(func() error { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Family": family, - }).Debugf("LLGR restart timer (%d sec) for %s expired", t, family) - peer.DropAll([]bgp.RouteFamily{family}) - server.dropPeerAllRoutes(peer, []bgp.RouteFamily{family}) - - // when all llgr restart timer expired, stop PeerRestarting - if peer.llgrRestartTimerExpired(family) { - peer.stopPeerRestarting() - } - return nil - }, false) - case <-endCh: - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Family": family, - }).Debugf("stop LLGR restart timer (%d sec) for %s", t, family) - } - }(f, endCh) - } - } else { - // RFC 4724 4.2 - // If the session does not get re-established within the "Restart Time" - // that the peer advertised previously, the Receiving Speaker MUST - // delete all the stale routes from the peer that it is retaining. - peer.fsm.pConf.GracefulRestart.State.PeerRestarting = false - peer.DropAll(peer.configuredRFlist()) - server.dropPeerAllRoutes(peer, peer.configuredRFlist()) - } - } - - cleanInfiniteChannel(peer.outgoing) - peer.outgoing = channels.NewInfiniteChannel() - if nextState == bgp.BGP_FSM_ESTABLISHED { - // update for export policy - laddr, _ := peer.fsm.LocalHostPort() - // may include zone info - peer.fsm.pConf.Transport.State.LocalAddress = laddr - // exclude zone info - ipaddr, _ := net.ResolveIPAddr("ip", laddr) - peer.fsm.peerInfo.LocalAddress = ipaddr.IP - deferralExpiredFunc := func(family bgp.RouteFamily) func() { - return func() { - server.mgmtOperation(func() error { - server.softResetOut(peer.fsm.pConf.State.NeighborAddress, family, true) - return nil - }, false) - } - } - if !peer.fsm.pConf.GracefulRestart.State.LocalRestarting { - // When graceful-restart cap (which means intention - // of sending EOR) and route-target address family are negotiated, - // send route-target NLRIs first, and wait to send others - // till receiving EOR of route-target address family. - // This prevents sending uninterested routes to peers. - // - // However, when the peer is graceful restarting, give up - // waiting sending non-route-target NLRIs since the peer won't send - // any routes (and EORs) before we send ours (or deferral-timer expires). - var pathList []*table.Path - _, y := peer.fsm.rfMap[bgp.RF_RTC_UC] - if c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC); y && !peer.fsm.pConf.GracefulRestart.State.PeerRestarting && c.RouteTargetMembership.Config.DeferralTime > 0 { - pathList, _ = server.getBestFromLocal(peer, []bgp.RouteFamily{bgp.RF_RTC_UC}) - t := c.RouteTargetMembership.Config.DeferralTime - for _, f := range peer.negotiatedRFList() { - if f != bgp.RF_RTC_UC { - time.AfterFunc(time.Second*time.Duration(t), deferralExpiredFunc(f)) - } - } - } else { - pathList, _ = server.getBestFromLocal(peer, peer.negotiatedRFList()) - } - - if len(pathList) > 0 { - sendFsmOutgoingMsg(peer, pathList, nil, false) - } - } else { - // RFC 4724 4.1 - // Once the session between the Restarting Speaker and the Receiving - // Speaker is re-established, ...snip... it MUST defer route - // selection for an address family until it either (a) receives the - // End-of-RIB marker from all its peers (excluding the ones with the - // "Restart State" bit set in the received capability and excluding the - // ones that do not advertise the graceful restart capability) or (b) - // the Selection_Deferral_Timer referred to below has expired. - allEnd := func() bool { - for _, p := range server.neighborMap { - if !p.recvedAllEOR() { - return false - } - } - return true - }() - if allEnd { - for _, p := range server.neighborMap { - p.fsm.pConf.GracefulRestart.State.LocalRestarting = false - if !p.isGracefulRestartEnabled() { - continue - } - paths, _ := server.getBestFromLocal(p, p.configuredRFlist()) - if len(paths) > 0 { - sendFsmOutgoingMsg(p, paths, nil, false) - } - } - log.WithFields(log.Fields{ - "Topic": "Server", - }).Info("sync finished") - } else { - deferral := peer.fsm.pConf.GracefulRestart.Config.DeferralTime - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - }).Debugf("Now syncing, suppress sending updates. start deferral timer(%d)", deferral) - time.AfterFunc(time.Second*time.Duration(deferral), deferralExpiredFunc(bgp.RouteFamily(0))) - } - } - } else { - if server.shutdownWG != nil && nextState == bgp.BGP_FSM_IDLE { - die := true - for _, p := range server.neighborMap { - if p.fsm.state != bgp.BGP_FSM_IDLE { - die = false - break - } - } - if die { - server.shutdownWG.Done() - } - } - peer.fsm.pConf.Timers.State.Downtime = time.Now().Unix() - } - // clear counter - if peer.fsm.adminState == ADMIN_STATE_DOWN { - peer.fsm.pConf.State = config.NeighborState{} - peer.fsm.pConf.State.NeighborAddress = peer.fsm.pConf.Config.NeighborAddress - peer.fsm.pConf.State.PeerAs = peer.fsm.pConf.Config.PeerAs - peer.fsm.pConf.Timers.State = config.TimersState{} - } - peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh) - server.broadcastPeerState(peer, oldState, e) - case FSM_MSG_ROUTE_REFRESH: - if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED || e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime { - return - } - if paths := server.handleRouteRefresh(peer, e); len(paths) > 0 { - sendFsmOutgoingMsg(peer, paths, nil, false) - return - } - case FSM_MSG_BGP_MESSAGE: - switch m := e.MsgData.(type) { - case *bgp.MessageError: - sendFsmOutgoingMsg(peer, nil, bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data), false) - return - case *bgp.BGPMessage: - server.notifyRecvMessageWatcher(peer, e.timestamp, m) - if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED || e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime { - return - } - pathList, eor, notification := peer.handleUpdate(e) - if notification != nil { - sendFsmOutgoingMsg(peer, nil, notification, true) - return - } - if m.Header.Type == bgp.BGP_MSG_UPDATE { - server.notifyPrePolicyUpdateWatcher(peer, pathList, m, e.timestamp, e.payload) - } - - if len(pathList) > 0 { - server.propagateUpdate(peer, pathList) - } - - if len(eor) > 0 { - rtc := false - for _, f := range eor { - if f == bgp.RF_RTC_UC { - rtc = true - } - for i, a := range peer.fsm.pConf.AfiSafis { - if a.State.Family == f { - peer.fsm.pConf.AfiSafis[i].MpGracefulRestart.State.EndOfRibReceived = true - } - } - } - - // RFC 4724 4.1 - // Once the session between the Restarting Speaker and the Receiving - // Speaker is re-established, ...snip... it MUST defer route - // selection for an address family until it either (a) receives the - // End-of-RIB marker from all its peers (excluding the ones with the - // "Restart State" bit set in the received capability and excluding the - // ones that do not advertise the graceful restart capability) or ...snip... - if peer.fsm.pConf.GracefulRestart.State.LocalRestarting { - allEnd := func() bool { - for _, p := range server.neighborMap { - if !p.recvedAllEOR() { - return false - } - } - return true - }() - if allEnd { - for _, p := range server.neighborMap { - p.fsm.pConf.GracefulRestart.State.LocalRestarting = false - if !p.isGracefulRestartEnabled() { - continue - } - paths, _ := server.getBestFromLocal(p, p.negotiatedRFList()) - if len(paths) > 0 { - sendFsmOutgoingMsg(p, paths, nil, false) - } - } - log.WithFields(log.Fields{ - "Topic": "Server", - }).Info("sync finished") - - } - - // we don't delay non-route-target NLRIs when local-restarting - rtc = false - } - if peer.fsm.pConf.GracefulRestart.State.PeerRestarting { - if peer.recvedAllEOR() { - peer.stopPeerRestarting() - pathList := peer.adjRibIn.DropStale(peer.configuredRFlist()) - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.fsm.pConf.State.NeighborAddress, - }).Debugf("withdraw %d stale routes", len(pathList)) - server.propagateUpdate(peer, pathList) - } - - // we don't delay non-route-target NLRIs when peer is restarting - rtc = false - } - - // received EOR of route-target address family - // outbound filter is now ready, let's flash non-route-target NLRIs - if c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC); rtc && c != nil && c.RouteTargetMembership.Config.DeferralTime > 0 { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - }).Debug("received route-target eor. flash non-route-target NLRIs") - families := make([]bgp.RouteFamily, 0, len(peer.negotiatedRFList())) - for _, f := range peer.negotiatedRFList() { - if f != bgp.RF_RTC_UC { - families = append(families, f) - } - } - if paths, _ := server.getBestFromLocal(peer, families); len(paths) > 0 { - sendFsmOutgoingMsg(peer, paths, nil, false) - } - } - } - default: - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.fsm.pConf.State.NeighborAddress, - "Data": e.MsgData, - }).Panic("unknown msg type") - } - } -} - -func (s *BgpServer) AddCollector(c *config.CollectorConfig) error { - return s.mgmtOperation(func() error { - _, err := NewCollector(s, c.Url, c.DbName, c.TableDumpInterval) - return err - }, false) -} - -func (s *BgpServer) StartZebraClient(c *config.ZebraConfig) error { - return s.mgmtOperation(func() error { - if s.zclient != nil { - return fmt.Errorf("already connected to Zebra") - } - protos := make([]string, 0, len(c.RedistributeRouteTypeList)) - for _, p := range c.RedistributeRouteTypeList { - protos = append(protos, string(p)) - } - var err error - s.zclient, err = newZebraClient(s, c.Url, protos, c.Version, c.NexthopTriggerEnable, c.NexthopTriggerDelay) - return err - }, false) -} - -func (s *BgpServer) AddBmp(c *config.BmpServerConfig) error { - return s.mgmtOperation(func() error { - return s.bmpManager.addServer(c) - }, true) -} - -func (s *BgpServer) DeleteBmp(c *config.BmpServerConfig) error { - return s.mgmtOperation(func() error { - return s.bmpManager.deleteServer(c) - }, true) -} - -func (s *BgpServer) Shutdown() { - s.mgmtOperation(func() error { - s.shutdownWG = new(sync.WaitGroup) - s.shutdownWG.Add(1) - stateOp := AdminStateOperation{ - State: ADMIN_STATE_DOWN, - Communication: nil, - } - for _, p := range s.neighborMap { - p.fsm.adminStateCh <- stateOp - } - // TODO: call fsmincomingCh.Close() - return nil - }, false) - - // Waits for all goroutines per peer to stop. - // Note: This should not be wrapped with s.mgmtOperation() in order to - // avoid the deadlock in the main goroutine of BgpServer. - if s.shutdownWG != nil { - s.shutdownWG.Wait() - s.shutdownWG = nil - } -} - -func (s *BgpServer) UpdatePolicy(policy config.RoutingPolicy) error { - return s.mgmtOperation(func() error { - ap := make(map[string]config.ApplyPolicy, len(s.neighborMap)+1) - ap[table.GLOBAL_RIB_NAME] = s.bgpConfig.Global.ApplyPolicy - for _, peer := range s.neighborMap { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.fsm.pConf.State.NeighborAddress, - }).Info("call set policy") - ap[peer.ID()] = peer.fsm.pConf.ApplyPolicy - } - return s.policy.Reset(&policy, ap) - }, false) -} - -// EVPN MAC MOBILITY HANDLING -// -// We don't have multihoming function now, so ignore -// ESI comparison. -// -// RFC7432 15. MAC Mobility -// -// A PE detecting a locally attached MAC address for which it had -// previously received a MAC/IP Advertisement route with the same zero -// Ethernet segment identifier (single-homed scenarios) advertises it -// with a MAC Mobility extended community attribute with the sequence -// number set properly. In the case of single-homed scenarios, there -// is no need for ESI comparison. - -func getMacMobilityExtendedCommunity(etag uint32, mac net.HardwareAddr, evpnPaths []*table.Path) *bgp.MacMobilityExtended { - seqs := make([]struct { - seq int - isLocal bool - }, 0) - - for _, path := range evpnPaths { - nlri := path.GetNlri().(*bgp.EVPNNLRI) - target, ok := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute) - if !ok { - continue - } - if target.ETag == etag && bytes.Equal(target.MacAddress, mac) { - found := false - for _, ec := range path.GetExtCommunities() { - if t, st := ec.GetTypes(); t == bgp.EC_TYPE_EVPN && st == bgp.EC_SUBTYPE_MAC_MOBILITY { - seqs = append(seqs, struct { - seq int - isLocal bool - }{int(ec.(*bgp.MacMobilityExtended).Sequence), path.IsLocal()}) - found = true - break - } - } - - if !found { - seqs = append(seqs, struct { - seq int - isLocal bool - }{-1, path.IsLocal()}) - } - } - } - - if len(seqs) > 0 { - newSeq := -2 - var isLocal bool - for _, seq := range seqs { - if seq.seq > newSeq { - newSeq = seq.seq - isLocal = seq.isLocal - } - } - - if !isLocal { - newSeq += 1 - } - - if newSeq != -1 { - return &bgp.MacMobilityExtended{ - Sequence: uint32(newSeq), - } - } - } - return nil -} - -func (server *BgpServer) fixupApiPath(vrfId string, pathList []*table.Path) error { - pi := &table.PeerInfo{ - AS: server.bgpConfig.Global.Config.As, - LocalID: net.ParseIP(server.bgpConfig.Global.Config.RouterId).To4(), - } - - for _, path := range pathList { - if !path.IsWithdraw { - if _, err := path.GetOrigin(); err != nil { - return err - } - } - - if path.GetSource() == nil { - path.SetSource(pi) - } - - if vrfId != "" { - vrf := server.globalRib.Vrfs[vrfId] - if vrf == nil { - return fmt.Errorf("vrf %s not found", vrfId) - } - if err := vrf.ToGlobalPath(path); err != nil { - return err - } - } - - // Address Family specific Handling - switch nlri := path.GetNlri().(type) { - case *bgp.EVPNNLRI: - switch r := nlri.RouteTypeData.(type) { - case *bgp.EVPNMacIPAdvertisementRoute: - // MAC Mobility Extended Community - paths := server.globalRib.GetBestPathList(table.GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN}) - if m := getMacMobilityExtendedCommunity(r.ETag, r.MacAddress, paths); m != nil { - path.SetExtCommunities([]bgp.ExtendedCommunityInterface{m}, false) - } - case *bgp.EVPNEthernetSegmentRoute: - // RFC7432: BGP MPLS-Based Ethernet VPN - // 7.6. ES-Import Route Target - // The value is derived automatically for the ESI Types 1, 2, - // and 3, by encoding the high-order 6-octet portion of the 9-octet ESI - // Value, which corresponds to a MAC address, in the ES-Import Route - // Target. - // Note: If the given path already has the ES-Import Route Target, - // skips deriving a new one. - found := false - for _, extComm := range path.GetExtCommunities() { - if _, found = extComm.(*bgp.ESImportRouteTarget); found { - break - } - } - if !found { - switch r.ESI.Type { - case bgp.ESI_LACP, bgp.ESI_MSTP, bgp.ESI_MAC: - mac := net.HardwareAddr(r.ESI.Value[0:6]) - rt := &bgp.ESImportRouteTarget{ESImport: mac} - path.SetExtCommunities([]bgp.ExtendedCommunityInterface{rt}, false) - } - } - } - } - } - return nil -} - -func pathTokey(path *table.Path) string { - return fmt.Sprintf("%d:%s", path.GetNlri().PathIdentifier(), path.GetNlri().String()) -} - -func (s *BgpServer) AddPath(vrfId string, pathList []*table.Path) (uuidBytes []byte, err error) { - err = s.mgmtOperation(func() error { - if err := s.fixupApiPath(vrfId, pathList); err != nil { - return err - } - if len(pathList) == 1 { - path := pathList[0] - id, _ := uuid.NewV4() - s.uuidMap[id] = pathTokey(path) - uuidBytes = id.Bytes() - } - s.propagateUpdate(nil, pathList) - return nil - }, true) - return -} - -func (s *BgpServer) DeletePath(uuidBytes []byte, f bgp.RouteFamily, vrfId string, pathList []*table.Path) error { - return s.mgmtOperation(func() error { - deletePathList := make([]*table.Path, 0) - if len(uuidBytes) > 0 { - // Delete locally generated path which has the given UUID - path := func() *table.Path { - id, _ := uuid.FromBytes(uuidBytes) - if key, ok := s.uuidMap[id]; !ok { - return nil - } else { - for _, path := range s.globalRib.GetPathList(table.GLOBAL_RIB_NAME, 0, s.globalRib.GetRFlist()) { - if path.IsLocal() && key == pathTokey(path) { - delete(s.uuidMap, id) - return path - } - } - } - return nil - }() - if path == nil { - return fmt.Errorf("Can't find a specified path") - } - deletePathList = append(deletePathList, path.Clone(true)) - } else if len(pathList) == 0 { - // Delete all locally generated paths - families := s.globalRib.GetRFlist() - if f != 0 { - families = []bgp.RouteFamily{f} - } - for _, path := range s.globalRib.GetPathList(table.GLOBAL_RIB_NAME, 0, families) { - if path.IsLocal() { - deletePathList = append(deletePathList, path.Clone(true)) - } - } - s.uuidMap = make(map[uuid.UUID]string) - } else { - if err := s.fixupApiPath(vrfId, pathList); err != nil { - return err - } - deletePathList = pathList - } - s.propagateUpdate(nil, deletePathList) - return nil - }, true) -} - -func (s *BgpServer) UpdatePath(vrfId string, pathList []*table.Path) error { - err := s.mgmtOperation(func() error { - if err := s.fixupApiPath(vrfId, pathList); err != nil { - return err - } - s.propagateUpdate(nil, pathList) - return nil - }, true) - return err -} - -func (s *BgpServer) Start(c *config.Global) error { - return s.mgmtOperation(func() error { - if err := config.SetDefaultGlobalConfigValues(c); err != nil { - return err - } - - if c.Config.Port > 0 { - acceptCh := make(chan *net.TCPConn, 4096) - for _, addr := range c.Config.LocalAddressList { - l, err := NewTCPListener(addr, uint32(c.Config.Port), acceptCh) - if err != nil { - return err - } - s.listeners = append(s.listeners, l) - } - s.acceptCh = acceptCh - } - - rfs, _ := config.AfiSafis(c.AfiSafis).ToRfList() - s.globalRib = table.NewTableManager(rfs) - s.rsRib = table.NewTableManager(rfs) - - if err := s.policy.Reset(&config.RoutingPolicy{}, map[string]config.ApplyPolicy{}); err != nil { - return err - } - s.bgpConfig.Global = *c - // update route selection options - table.SelectionOptions = c.RouteSelectionOptions.Config - table.UseMultiplePaths = c.UseMultiplePaths.Config - - s.roaManager.SetAS(s.bgpConfig.Global.Config.As) - return nil - }, false) -} - -func (s *BgpServer) GetVrf() (l []*table.Vrf) { - s.mgmtOperation(func() error { - l = make([]*table.Vrf, 0, len(s.globalRib.Vrfs)) - for _, vrf := range s.globalRib.Vrfs { - l = append(l, vrf.Clone()) - } - return nil - }, true) - return l -} - -func (s *BgpServer) AddVrf(name string, id uint32, rd bgp.RouteDistinguisherInterface, im, ex []bgp.ExtendedCommunityInterface) error { - return s.mgmtOperation(func() error { - pi := &table.PeerInfo{ - AS: s.bgpConfig.Global.Config.As, - LocalID: net.ParseIP(s.bgpConfig.Global.Config.RouterId).To4(), - } - if pathList, err := s.globalRib.AddVrf(name, id, rd, im, ex, pi); err != nil { - return err - } else if len(pathList) > 0 { - s.propagateUpdate(nil, pathList) - } - return nil - }, true) -} - -func (s *BgpServer) DeleteVrf(name string) error { - return s.mgmtOperation(func() error { - for _, n := range s.neighborMap { - if n.fsm.pConf.Config.Vrf == name { - return fmt.Errorf("failed to delete VRF %s: neighbor %s is in use", name, n.ID()) - } - } - pathList, err := s.globalRib.DeleteVrf(name) - if err != nil { - return err - } - if len(pathList) > 0 { - s.propagateUpdate(nil, pathList) - } - return nil - }, true) -} - -func (s *BgpServer) Stop() error { - return s.mgmtOperation(func() error { - for k, _ := range s.neighborMap { - if err := s.deleteNeighbor(&config.Neighbor{Config: config.NeighborConfig{ - NeighborAddress: k}}, bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED); err != nil { - return err - } - } - for _, l := range s.listeners { - l.Close() - } - s.bgpConfig.Global = config.Global{} - return nil - }, true) -} - -func familiesForSoftreset(peer *Peer, family bgp.RouteFamily) []bgp.RouteFamily { - if family == bgp.RouteFamily(0) { - configured := peer.configuredRFlist() - families := make([]bgp.RouteFamily, 0, len(configured)) - for _, f := range configured { - if f != bgp.RF_RTC_UC { - families = append(families, f) - } - } - return families - } - return []bgp.RouteFamily{family} -} - -func (s *BgpServer) softResetIn(addr string, family bgp.RouteFamily) error { - peers, err := s.addrToPeers(addr) - if err != nil { - return err - } - for _, peer := range peers { - families := familiesForSoftreset(peer, family) - - pathList := make([]*table.Path, 0, peer.adjRibIn.Count(families)) - for _, path := range peer.adjRibIn.PathList(families, false) { - // RFC4271 9.1.2 Phase 2: Route Selection - // - // If the AS_PATH attribute of a BGP route contains an AS loop, the BGP - // route should be excluded from the Phase 2 decision function. - isLooped := false - if aspath := path.GetAsPath(); aspath != nil { - isLooped = hasOwnASLoop(peer.fsm.peerInfo.LocalAS, int(peer.fsm.pConf.AsPathOptions.Config.AllowOwnAs), aspath) - } - if path.IsAsLooped() != isLooped { - // can't modify the existing one. needs to create one - path = path.Clone(false) - path.SetAsLooped(isLooped) - // update accepted counter - peer.adjRibIn.Update([]*table.Path{path}) - } - if !path.IsAsLooped() { - pathList = append(pathList, path) - } - } - s.propagateUpdate(peer, pathList) - } - return err -} - -func (s *BgpServer) softResetOut(addr string, family bgp.RouteFamily, deferral bool) error { - peers, err := s.addrToPeers(addr) - if err != nil { - return err - } - for _, peer := range peers { - if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED { - continue - } - families := familiesForSoftreset(peer, family) - - if deferral { - _, y := peer.fsm.rfMap[bgp.RF_RTC_UC] - if peer.fsm.pConf.GracefulRestart.State.LocalRestarting { - peer.fsm.pConf.GracefulRestart.State.LocalRestarting = false - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Families": families, - }).Debug("deferral timer expired") - } else if c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC); y && !c.MpGracefulRestart.State.EndOfRibReceived { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "Families": families, - }).Debug("route-target deferral timer expired") - } else { - continue - } - } - - pathList, filtered := s.getBestFromLocal(peer, families) - if len(pathList) > 0 { - sendFsmOutgoingMsg(peer, pathList, nil, false) - } - if !deferral && len(filtered) > 0 { - withdrawnList := make([]*table.Path, 0, len(filtered)) - for _, p := range filtered { - withdrawnList = append(withdrawnList, p.Clone(true)) - } - sendFsmOutgoingMsg(peer, withdrawnList, nil, false) - } - } - return nil -} - -func (s *BgpServer) SoftResetIn(addr string, family bgp.RouteFamily) error { - return s.mgmtOperation(func() error { - log.WithFields(log.Fields{ - "Topic": "Operation", - "Key": addr, - }).Info("Neighbor soft reset in") - return s.softResetIn(addr, family) - }, true) -} - -func (s *BgpServer) SoftResetOut(addr string, family bgp.RouteFamily) error { - return s.mgmtOperation(func() error { - log.WithFields(log.Fields{ - "Topic": "Operation", - "Key": addr, - }).Info("Neighbor soft reset out") - return s.softResetOut(addr, family, false) - }, true) -} - -func (s *BgpServer) SoftReset(addr string, family bgp.RouteFamily) error { - return s.mgmtOperation(func() error { - log.WithFields(log.Fields{ - "Topic": "Operation", - "Key": addr, - }).Info("Neighbor soft reset") - err := s.softResetIn(addr, family) - if err != nil { - return err - } - return s.softResetOut(addr, family, false) - }, true) -} - -func (s *BgpServer) validateTable(r *table.Table) (v []*table.Validation) { - if s.roaManager.enabled() { - v = make([]*table.Validation, 0, len(r.GetDestinations())) - for _, d := range r.GetDestinations() { - for _, p := range d.GetAllKnownPathList() { - v = append(v, s.roaManager.validate(p)) - } - } - } - return -} - -func (s *BgpServer) GetRib(addr string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (rib *table.Table, v []*table.Validation, err error) { - err = s.mgmtOperation(func() error { - m := s.globalRib - id := table.GLOBAL_RIB_NAME - as := uint32(0) - if len(addr) > 0 { - peer, ok := s.neighborMap[addr] - if !ok { - return fmt.Errorf("Neighbor that has %v doesn't exist.", addr) - } - if !peer.isRouteServerClient() { - return fmt.Errorf("Neighbor %v doesn't have local rib", addr) - } - id = peer.ID() - as = peer.AS() - m = s.rsRib - } - af := bgp.RouteFamily(family) - tbl, ok := m.Tables[af] - if !ok { - return fmt.Errorf("address family: %s not supported", af) - } - rib, err = tbl.Select(table.TableSelectOption{ID: id, AS: as, LookupPrefixes: prefixes}) - v = s.validateTable(rib) - return err - }, true) - return -} - -func (s *BgpServer) GetVrfRib(name string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (rib *table.Table, err error) { - err = s.mgmtOperation(func() error { - m := s.globalRib - vrfs := m.Vrfs - if _, ok := vrfs[name]; !ok { - return fmt.Errorf("vrf %s not found", name) - } - var af bgp.RouteFamily - switch family { - case bgp.RF_IPv4_UC: - af = bgp.RF_IPv4_VPN - case bgp.RF_IPv6_UC: - af = bgp.RF_IPv6_VPN - case bgp.RF_EVPN: - af = bgp.RF_EVPN - } - tbl, ok := m.Tables[af] - if !ok { - return fmt.Errorf("address family: %s not supported", af) - } - rib, err = tbl.Select(table.TableSelectOption{VRF: vrfs[name], LookupPrefixes: prefixes}) - return err - }, true) - return -} - -func (s *BgpServer) GetAdjRib(addr string, family bgp.RouteFamily, in bool, prefixes []*table.LookupPrefix) (rib *table.Table, v []*table.Validation, err error) { - err = s.mgmtOperation(func() error { - peer, ok := s.neighborMap[addr] - if !ok { - return fmt.Errorf("Neighbor that has %v doesn't exist.", addr) - } - id := peer.ID() - as := peer.AS() - - var adjRib *table.AdjRib - if in { - adjRib = peer.adjRibIn - } else { - adjRib = table.NewAdjRib(peer.configuredRFlist()) - accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist()) - adjRib.Update(accepted) - } - rib, err = adjRib.Select(family, false, table.TableSelectOption{ID: id, AS: as, LookupPrefixes: prefixes}) - v = s.validateTable(rib) - return err - }, true) - return -} - -func (s *BgpServer) GetRibInfo(addr string, family bgp.RouteFamily) (info *table.TableInfo, err error) { - err = s.mgmtOperation(func() error { - m := s.globalRib - id := table.GLOBAL_RIB_NAME - as := uint32(0) - if len(addr) > 0 { - peer, ok := s.neighborMap[addr] - if !ok { - return fmt.Errorf("Neighbor that has %v doesn't exist.", addr) - } - if !peer.isRouteServerClient() { - return fmt.Errorf("Neighbor %v doesn't have local rib", addr) - } - id = peer.ID() - as = peer.AS() - m = s.rsRib - } - info, err = m.TableInfo(id, as, family) - return err - }, true) - return -} - -func (s *BgpServer) GetAdjRibInfo(addr string, family bgp.RouteFamily, in bool) (info *table.TableInfo, err error) { - err = s.mgmtOperation(func() error { - peer, ok := s.neighborMap[addr] - if !ok { - return fmt.Errorf("Neighbor that has %v doesn't exist.", addr) - } - - var adjRib *table.AdjRib - if in { - adjRib = peer.adjRibIn - } else { - adjRib = table.NewAdjRib(peer.configuredRFlist()) - accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist()) - adjRib.Update(accepted) - } - info, err = adjRib.TableInfo(family) - return err - }, true) - return -} - -func (s *BgpServer) GetServer() (c *config.Global) { - s.mgmtOperation(func() error { - g := s.bgpConfig.Global - c = &g - return nil - }, false) - return c -} - -func (s *BgpServer) GetNeighbor(address string, getAdvertised bool) (l []*config.Neighbor) { - s.mgmtOperation(func() error { - l = make([]*config.Neighbor, 0, len(s.neighborMap)) - for k, peer := range s.neighborMap { - if address != "" && address != k && address != peer.fsm.pConf.Config.NeighborInterface { - continue - } - l = append(l, s.ToConfig(peer, getAdvertised)) - } - return nil - }, false) - return l -} - -func (server *BgpServer) addPeerGroup(c *config.PeerGroup) error { - name := c.Config.PeerGroupName - if _, y := server.peerGroupMap[name]; y { - return fmt.Errorf("Can't overwrite the existing peer-group: %s", name) - } - - log.WithFields(log.Fields{ - "Topic": "Peer", - "Name": name, - }).Info("Add a peer group configuration") - - server.peerGroupMap[c.Config.PeerGroupName] = NewPeerGroup(c) - - return nil -} - -func (server *BgpServer) addNeighbor(c *config.Neighbor) error { - addr, err := c.ExtractNeighborAddress() - if err != nil { - return err - } - - if _, y := server.neighborMap[addr]; y { - return fmt.Errorf("Can't overwrite the existing peer: %s", addr) - } - - var pgConf *config.PeerGroup - if c.Config.PeerGroup != "" { - pg, ok := server.peerGroupMap[c.Config.PeerGroup] - if !ok { - return fmt.Errorf("no such peer-group: %s", c.Config.PeerGroup) - } - pgConf = pg.Conf - } - - if err := config.SetDefaultNeighborConfigValues(c, pgConf, &server.bgpConfig.Global); err != nil { - return err - } - - if vrf := c.Config.Vrf; vrf != "" { - if c.RouteServer.Config.RouteServerClient { - return fmt.Errorf("route server client can't be enslaved to VRF") - } - families, _ := config.AfiSafis(c.AfiSafis).ToRfList() - for _, f := range families { - if f != bgp.RF_IPv4_UC && f != bgp.RF_IPv6_UC { - return fmt.Errorf("%s is not supported for VRF enslaved neighbor", f) - } - } - _, y := server.globalRib.Vrfs[vrf] - if !y { - return fmt.Errorf("VRF not found: %s", vrf) - } - } - - if c.RouteServer.Config.RouteServerClient && c.RouteReflector.Config.RouteReflectorClient { - return fmt.Errorf("can't be both route-server-client and route-reflector-client") - } - - if server.bgpConfig.Global.Config.Port > 0 { - for _, l := range server.Listeners(addr) { - if c.Config.AuthPassword != "" { - if err := SetTcpMD5SigSockopt(l, addr, c.Config.AuthPassword); err != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": addr, - }).Warnf("failed to set md5: %s", err) - } - } - } - } - log.WithFields(log.Fields{ - "Topic": "Peer", - }).Infof("Add a peer configuration for:%s", addr) - - rib := server.globalRib - if c.RouteServer.Config.RouteServerClient { - rib = server.rsRib - } - peer := NewPeer(&server.bgpConfig.Global, c, rib, server.policy) - server.policy.Reset(nil, map[string]config.ApplyPolicy{peer.ID(): c.ApplyPolicy}) - server.neighborMap[addr] = peer - if name := c.Config.PeerGroup; name != "" { - server.peerGroupMap[name].AddMember(*c) - } - peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh) - server.broadcastPeerState(peer, bgp.BGP_FSM_IDLE, nil) - return nil -} - -func (s *BgpServer) AddPeerGroup(c *config.PeerGroup) error { - return s.mgmtOperation(func() error { - return s.addPeerGroup(c) - }, true) -} - -func (s *BgpServer) AddNeighbor(c *config.Neighbor) error { - return s.mgmtOperation(func() error { - return s.addNeighbor(c) - }, true) -} - -func (s *BgpServer) AddDynamicNeighbor(c *config.DynamicNeighbor) error { - return s.mgmtOperation(func() error { - s.peerGroupMap[c.Config.PeerGroup].AddDynamicNeighbor(c) - return nil - }, true) -} - -func (server *BgpServer) deletePeerGroup(pg *config.PeerGroup) error { - name := pg.Config.PeerGroupName - - if _, y := server.peerGroupMap[name]; !y { - return fmt.Errorf("Can't delete a peer-group %s which does not exist", name) - } - - log.WithFields(log.Fields{ - "Topic": "Peer", - "Name": name, - }).Info("Delete a peer group configuration") - - delete(server.peerGroupMap, name) - return nil -} - -func (server *BgpServer) deleteNeighbor(c *config.Neighbor, code, subcode uint8) error { - if c.Config.PeerGroup != "" { - _, y := server.peerGroupMap[c.Config.PeerGroup] - if y { - server.peerGroupMap[c.Config.PeerGroup].DeleteMember(*c) - } - } - - addr, err := c.ExtractNeighborAddress() - if err != nil { - return err - } - - if intf := c.Config.NeighborInterface; intf != "" { - var err error - addr, err = config.GetIPv6LinkLocalNeighborAddress(intf) - if err != nil { - return err - } - } - n, y := server.neighborMap[addr] - if !y { - return fmt.Errorf("Can't delete a peer configuration for %s", addr) - } - for _, l := range server.Listeners(addr) { - if err := SetTcpMD5SigSockopt(l, addr, ""); err != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": addr, - }).Warnf("failed to unset md5: %s", err) - } - } - log.WithFields(log.Fields{ - "Topic": "Peer", - }).Infof("Delete a peer configuration for:%s", addr) - - n.fsm.sendNotification(code, subcode, nil, "") - n.stopPeerRestarting() - - go n.stopFSM() - delete(server.neighborMap, addr) - server.dropPeerAllRoutes(n, n.configuredRFlist()) - return nil -} - -func (s *BgpServer) DeletePeerGroup(c *config.PeerGroup) error { - return s.mgmtOperation(func() error { - name := c.Config.PeerGroupName - for _, n := range s.neighborMap { - if n.fsm.pConf.Config.PeerGroup == name { - return fmt.Errorf("failed to delete peer-group %s: neighbor %s is in use", name, n.ID()) - } - } - return s.deletePeerGroup(c) - }, true) -} - -func (s *BgpServer) DeleteNeighbor(c *config.Neighbor) error { - return s.mgmtOperation(func() error { - return s.deleteNeighbor(c, bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED) - }, true) -} - -func (s *BgpServer) updatePeerGroup(pg *config.PeerGroup) (needsSoftResetIn bool, err error) { - name := pg.Config.PeerGroupName - - _, ok := s.peerGroupMap[name] - if !ok { - return false, fmt.Errorf("Peer-group %s doesn't exist.", name) - } - s.peerGroupMap[name].Conf = pg - - for _, n := range s.peerGroupMap[name].members { - c := n - u, err := s.updateNeighbor(&c) - if err != nil { - return needsSoftResetIn, err - } - needsSoftResetIn = needsSoftResetIn || u - } - return needsSoftResetIn, nil -} - -func (s *BgpServer) UpdatePeerGroup(pg *config.PeerGroup) (needsSoftResetIn bool, err error) { - err = s.mgmtOperation(func() error { - needsSoftResetIn, err = s.updatePeerGroup(pg) - return err - }, true) - return needsSoftResetIn, err -} - -func (s *BgpServer) updateNeighbor(c *config.Neighbor) (needsSoftResetIn bool, err error) { - if c.Config.PeerGroup != "" { - if pg, ok := s.peerGroupMap[c.Config.PeerGroup]; ok { - if err := config.SetDefaultNeighborConfigValues(c, pg.Conf, &s.bgpConfig.Global); err != nil { - return needsSoftResetIn, err - } - } else { - return needsSoftResetIn, fmt.Errorf("no such peer-group: %s", c.Config.PeerGroup) - } - } - - addr, err := c.ExtractNeighborAddress() - if err != nil { - return needsSoftResetIn, err - } - - peer, ok := s.neighborMap[addr] - if !ok { - return needsSoftResetIn, fmt.Errorf("Neighbor that has %v doesn't exist.", addr) - } - - if !peer.fsm.pConf.ApplyPolicy.Equal(&c.ApplyPolicy) { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": addr, - }).Info("Update ApplyPolicy") - s.policy.Reset(nil, map[string]config.ApplyPolicy{peer.ID(): c.ApplyPolicy}) - peer.fsm.pConf.ApplyPolicy = c.ApplyPolicy - needsSoftResetIn = true - } - original := peer.fsm.pConf - - if !original.AsPathOptions.Config.Equal(&c.AsPathOptions.Config) { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - }).Info("Update aspath options") - peer.fsm.pConf.AsPathOptions = c.AsPathOptions - needsSoftResetIn = true - } - - if original.NeedsResendOpenMessage(c) { - sub := uint8(bgp.BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE) - if original.Config.AdminDown != c.Config.AdminDown { - sub = bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN - state := "Admin Down" - - if !c.Config.AdminDown { - state = "Admin Up" - } - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - "State": state, - }).Info("Update admin-state configuration") - } else if original.Config.PeerAs != c.Config.PeerAs { - sub = bgp.BGP_ERROR_SUB_PEER_DECONFIGURED - } - if err = s.deleteNeighbor(peer.fsm.pConf, bgp.BGP_ERROR_CEASE, sub); err != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": addr, - }).Error(err) - return needsSoftResetIn, err - } - err = s.addNeighbor(c) - if err != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": addr, - }).Error(err) - } - return needsSoftResetIn, err - } - - if !original.Timers.Config.Equal(&c.Timers.Config) { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.ID(), - }).Info("Update timer configuration") - peer.fsm.pConf.Timers.Config = c.Timers.Config - } - - err = peer.updatePrefixLimitConfig(c.AfiSafis) - if err != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": addr, - }).Error(err) - // rollback to original state - peer.fsm.pConf = original - } - return needsSoftResetIn, err -} - -func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (needsSoftResetIn bool, err error) { - err = s.mgmtOperation(func() error { - needsSoftResetIn, err = s.updateNeighbor(c) - return err - }, true) - return needsSoftResetIn, err -} - -func (s *BgpServer) addrToPeers(addr string) (l []*Peer, err error) { - if len(addr) == 0 { - for _, p := range s.neighborMap { - l = append(l, p) - } - return l, nil - } - peer, found := s.neighborMap[addr] - if !found { - return l, fmt.Errorf("Neighbor that has %v doesn't exist.", addr) - } - return []*Peer{peer}, nil -} - -func (s *BgpServer) resetNeighbor(op, addr string, subcode uint8, data []byte) error { - log.WithFields(log.Fields{ - "Topic": "Operation", - "Key": addr, - }).Info(op) - - peers, err := s.addrToPeers(addr) - if err == nil { - m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, subcode, data) - for _, peer := range peers { - sendFsmOutgoingMsg(peer, nil, m, false) - } - } - return err -} - -func (s *BgpServer) ShutdownNeighbor(addr, communication string) error { - return s.mgmtOperation(func() error { - return s.resetNeighbor("Neighbor shutdown", addr, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, newAdministrativeCommunication(communication)) - }, true) -} - -func (s *BgpServer) ResetNeighbor(addr, communication string) error { - return s.mgmtOperation(func() error { - err := s.resetNeighbor("Neighbor reset", addr, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, newAdministrativeCommunication(communication)) - if err != nil { - return err - } - peers, _ := s.addrToPeers(addr) - for _, peer := range peers { - peer.fsm.idleHoldTime = peer.fsm.pConf.Timers.Config.IdleHoldTimeAfterReset - } - return nil - }, true) -} - -func (s *BgpServer) setAdminState(addr, communication string, enable bool) error { - peers, err := s.addrToPeers(addr) - if err != nil { - return err - } - for _, peer := range peers { - f := func(stateOp *AdminStateOperation, message string) { - select { - case peer.fsm.adminStateCh <- *stateOp: - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": peer.fsm.pConf.State.NeighborAddress, - }).Debug(message) - default: - log.Warning("previous request is still remaining. : ", peer.fsm.pConf.State.NeighborAddress) - } - } - if enable { - f(&AdminStateOperation{ADMIN_STATE_UP, nil}, "ADMIN_STATE_UP requested") - } else { - f(&AdminStateOperation{ADMIN_STATE_DOWN, newAdministrativeCommunication(communication)}, "ADMIN_STATE_DOWN requested") - } - } - return nil -} - -func (s *BgpServer) EnableNeighbor(addr string) error { - return s.mgmtOperation(func() error { - return s.setAdminState(addr, "", true) - }, true) -} - -func (s *BgpServer) DisableNeighbor(addr, communication string) error { - return s.mgmtOperation(func() error { - return s.setAdminState(addr, communication, false) - }, true) -} - -func (s *BgpServer) GetDefinedSet(typ table.DefinedType, name string) (sets *config.DefinedSets, err error) { - err = s.mgmtOperation(func() error { - sets, err = s.policy.GetDefinedSet(typ, name) - return nil - }, false) - return sets, err -} - -func (s *BgpServer) AddDefinedSet(a table.DefinedSet) error { - return s.mgmtOperation(func() error { - return s.policy.AddDefinedSet(a) - }, false) -} - -func (s *BgpServer) DeleteDefinedSet(a table.DefinedSet, all bool) error { - return s.mgmtOperation(func() error { - return s.policy.DeleteDefinedSet(a, all) - }, false) -} - -func (s *BgpServer) ReplaceDefinedSet(a table.DefinedSet) error { - return s.mgmtOperation(func() error { - return s.policy.ReplaceDefinedSet(a) - }, false) -} - -func (s *BgpServer) GetStatement() (l []*config.Statement) { - s.mgmtOperation(func() error { - l = s.policy.GetStatement() - return nil - }, false) - return l -} - -func (s *BgpServer) AddStatement(st *table.Statement) error { - return s.mgmtOperation(func() error { - return s.policy.AddStatement(st) - }, false) -} - -func (s *BgpServer) DeleteStatement(st *table.Statement, all bool) error { - return s.mgmtOperation(func() error { - return s.policy.DeleteStatement(st, all) - }, false) -} - -func (s *BgpServer) ReplaceStatement(st *table.Statement) error { - return s.mgmtOperation(func() error { - return s.policy.ReplaceStatement(st) - }, false) -} - -func (s *BgpServer) GetPolicy() (l []*config.PolicyDefinition) { - s.mgmtOperation(func() error { - l = s.policy.GetAllPolicy() - return nil - }, false) - return l -} - -func (s *BgpServer) AddPolicy(x *table.Policy, refer bool) error { - return s.mgmtOperation(func() error { - return s.policy.AddPolicy(x, refer) - }, false) -} - -func (s *BgpServer) DeletePolicy(x *table.Policy, all, preserve bool) error { - return s.mgmtOperation(func() error { - l := make([]string, 0, len(s.neighborMap)+1) - for _, peer := range s.neighborMap { - l = append(l, peer.ID()) - } - l = append(l, table.GLOBAL_RIB_NAME) - - return s.policy.DeletePolicy(x, all, preserve, l) - }, false) -} - -func (s *BgpServer) ReplacePolicy(x *table.Policy, refer, preserve bool) error { - return s.mgmtOperation(func() error { - return s.policy.ReplacePolicy(x, refer, preserve) - }, false) -} - -func (server *BgpServer) toPolicyInfo(name string, dir table.PolicyDirection) (string, error) { - if name == "" { - switch dir { - case table.POLICY_DIRECTION_IMPORT, table.POLICY_DIRECTION_EXPORT: - return table.GLOBAL_RIB_NAME, nil - } - return "", fmt.Errorf("invalid policy type") - } else { - peer, ok := server.neighborMap[name] - if !ok { - return "", fmt.Errorf("not found peer %s", name) - } - if !peer.isRouteServerClient() { - return "", fmt.Errorf("non-rs-client peer %s doesn't have per peer policy", name) - } - return peer.ID(), nil - } -} - -func (s *BgpServer) GetPolicyAssignment(name string, dir table.PolicyDirection) (rt table.RouteType, l []*config.PolicyDefinition, err error) { - err = s.mgmtOperation(func() error { - var id string - id, err = s.toPolicyInfo(name, dir) - if err != nil { - rt = table.ROUTE_TYPE_NONE - return err - } - rt, l, err = s.policy.GetPolicyAssignment(id, dir) - return nil - }, false) - return rt, l, err -} - -func (s *BgpServer) AddPolicyAssignment(name string, dir table.PolicyDirection, policies []*config.PolicyDefinition, def table.RouteType) error { - return s.mgmtOperation(func() error { - id, err := s.toPolicyInfo(name, dir) - if err != nil { - return err - } - return s.policy.AddPolicyAssignment(id, dir, policies, def) - }, false) -} - -func (s *BgpServer) DeletePolicyAssignment(name string, dir table.PolicyDirection, policies []*config.PolicyDefinition, all bool) error { - return s.mgmtOperation(func() error { - id, err := s.toPolicyInfo(name, dir) - if err != nil { - return err - } - return s.policy.DeletePolicyAssignment(id, dir, policies, all) - }, false) -} - -func (s *BgpServer) ReplacePolicyAssignment(name string, dir table.PolicyDirection, policies []*config.PolicyDefinition, def table.RouteType) error { - return s.mgmtOperation(func() error { - id, err := s.toPolicyInfo(name, dir) - if err != nil { - return err - } - return s.policy.ReplacePolicyAssignment(id, dir, policies, def) - }, false) -} - -func (s *BgpServer) EnableMrt(c *config.MrtConfig) error { - return s.mgmtOperation(func() error { - return s.mrtManager.enable(c) - }, false) -} - -func (s *BgpServer) DisableMrt(c *config.MrtConfig) error { - return s.mgmtOperation(func() error { - return s.mrtManager.disable(c) - }, false) -} - -func (s *BgpServer) GetRpki() (l []*config.RpkiServer, err error) { - err = s.mgmtOperation(func() error { - l = s.roaManager.GetServers() - return nil - }, false) - return l, err -} - -func (s *BgpServer) GetRoa(family bgp.RouteFamily) (l []*table.ROA, err error) { - s.mgmtOperation(func() error { - l, err = s.roaManager.GetRoa(family) - return nil - }, false) - return l, err -} - -func (s *BgpServer) AddRpki(c *config.RpkiServerConfig) error { - return s.mgmtOperation(func() error { - return s.roaManager.AddServer(net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port))), c.RecordLifetime) - }, false) -} - -func (s *BgpServer) DeleteRpki(c *config.RpkiServerConfig) error { - return s.mgmtOperation(func() error { - return s.roaManager.DeleteServer(c.Address) - }, false) -} - -func (s *BgpServer) EnableRpki(c *config.RpkiServerConfig) error { - return s.mgmtOperation(func() error { - return s.roaManager.Enable(c.Address) - }, false) -} - -func (s *BgpServer) DisableRpki(c *config.RpkiServerConfig) error { - return s.mgmtOperation(func() error { - return s.roaManager.Disable(c.Address) - }, false) -} - -func (s *BgpServer) ResetRpki(c *config.RpkiServerConfig) error { - return s.mgmtOperation(func() error { - return s.roaManager.Reset(c.Address) - }, false) -} - -func (s *BgpServer) SoftResetRpki(c *config.RpkiServerConfig) error { - return s.mgmtOperation(func() error { - return s.roaManager.SoftReset(c.Address) - }, false) -} - -type WatchEventType string - -const ( - WATCH_EVENT_TYPE_BEST_PATH WatchEventType = "bestpath" - WATCH_EVENT_TYPE_PRE_UPDATE WatchEventType = "preupdate" - WATCH_EVENT_TYPE_POST_UPDATE WatchEventType = "postupdate" - WATCH_EVENT_TYPE_PEER_STATE WatchEventType = "peerstate" - WATCH_EVENT_TYPE_TABLE WatchEventType = "table" - WATCH_EVENT_TYPE_RECV_MSG WatchEventType = "receivedmessage" -) - -type WatchEvent interface { -} - -type WatchEventUpdate struct { - Message *bgp.BGPMessage - PeerAS uint32 - LocalAS uint32 - PeerAddress net.IP - LocalAddress net.IP - PeerID net.IP - FourBytesAs bool - Timestamp time.Time - Payload []byte - PostPolicy bool - Init bool - PathList []*table.Path - Neighbor *config.Neighbor -} - -type WatchEventPeerState struct { - PeerAS uint32 - LocalAS uint32 - PeerAddress net.IP - LocalAddress net.IP - PeerPort uint16 - LocalPort uint16 - PeerID net.IP - SentOpen *bgp.BGPMessage - RecvOpen *bgp.BGPMessage - State bgp.FSMState - StateReason *FsmStateReason - AdminState AdminState - Timestamp time.Time - PeerInterface string -} - -type WatchEventAdjIn struct { - PathList []*table.Path -} - -type WatchEventTable struct { - RouterId string - PathList map[string][]*table.Path - Neighbor []*config.Neighbor -} - -type WatchEventBestPath struct { - PathList []*table.Path - MultiPathList [][]*table.Path - Vrf map[string]uint16 -} - -type WatchEventMessage struct { - Message *bgp.BGPMessage - PeerAS uint32 - LocalAS uint32 - PeerAddress net.IP - LocalAddress net.IP - PeerID net.IP - FourBytesAs bool - Timestamp time.Time - IsSent bool -} - -type watchOptions struct { - bestpath bool - preUpdate bool - postUpdate bool - peerState bool - initBest bool - initUpdate bool - initPostUpdate bool - initPeerState bool - tableName string - recvMessage bool -} - -type WatchOption func(*watchOptions) - -func WatchBestPath(current bool) WatchOption { - return func(o *watchOptions) { - o.bestpath = true - if current { - o.initBest = true - } - } -} - -func WatchUpdate(current bool) WatchOption { - return func(o *watchOptions) { - o.preUpdate = true - if current { - o.initUpdate = true - } - } -} - -func WatchPostUpdate(current bool) WatchOption { - return func(o *watchOptions) { - o.postUpdate = true - if current { - o.initPostUpdate = true - } - } -} - -func WatchPeerState(current bool) WatchOption { - return func(o *watchOptions) { - o.peerState = true - if current { - o.initPeerState = true - } - } -} - -func WatchTableName(name string) WatchOption { - return func(o *watchOptions) { - o.tableName = name - } -} - -func WatchMessage(isSent bool) WatchOption { - return func(o *watchOptions) { - if isSent { - log.WithFields(log.Fields{ - "Topic": "Server", - }).Warn("watch event for sent messages is not implemented yet") - // o.sentMessage = true - } else { - o.recvMessage = true - } - } -} - -type Watcher struct { - opts watchOptions - realCh chan WatchEvent - ch *channels.InfiniteChannel - s *BgpServer -} - -func (w *Watcher) Event() <-chan WatchEvent { - return w.realCh -} - -func (w *Watcher) Generate(t WatchEventType) error { - return w.s.mgmtOperation(func() error { - switch t { - case WATCH_EVENT_TYPE_PRE_UPDATE: - pathList := make([]*table.Path, 0) - for _, peer := range w.s.neighborMap { - pathList = append(pathList, peer.adjRibIn.PathList(peer.configuredRFlist(), false)...) - } - w.notify(&WatchEventAdjIn{PathList: clonePathList(pathList)}) - case WATCH_EVENT_TYPE_TABLE: - rib := w.s.globalRib - as := uint32(0) - id := table.GLOBAL_RIB_NAME - if len(w.opts.tableName) > 0 { - peer, ok := w.s.neighborMap[w.opts.tableName] - if !ok { - return fmt.Errorf("Neighbor that has %v doesn't exist.", w.opts.tableName) - } - if !peer.isRouteServerClient() { - return fmt.Errorf("Neighbor %v doesn't have local rib", w.opts.tableName) - } - id = peer.ID() - as = peer.AS() - rib = w.s.rsRib - } - - pathList := func() map[string][]*table.Path { - pathList := make(map[string][]*table.Path) - for _, t := range rib.Tables { - for _, dst := range t.GetDestinations() { - if paths := dst.GetKnownPathList(id, as); len(paths) > 0 { - pathList[dst.GetNlri().String()] = clonePathList(paths) - } - } - } - return pathList - }() - l := make([]*config.Neighbor, 0, len(w.s.neighborMap)) - for _, peer := range w.s.neighborMap { - l = append(l, w.s.ToConfig(peer, false)) - } - w.notify(&WatchEventTable{PathList: pathList, Neighbor: l}) - default: - return fmt.Errorf("unsupported type %v", t) - } - return nil - }, false) -} - -func (w *Watcher) notify(v WatchEvent) { - w.ch.In() <- v -} - -func (w *Watcher) loop() { - for ev := range w.ch.Out() { - w.realCh <- ev.(WatchEvent) - } - close(w.realCh) -} - -func (w *Watcher) Stop() { - w.s.mgmtOperation(func() error { - for k, l := range w.s.watcherMap { - for i, v := range l { - if w == v { - w.s.watcherMap[k] = append(l[:i], l[i+1:]...) - break - } - } - } - - cleanInfiniteChannel(w.ch) - // the loop function goroutine might be blocked for - // writing to realCh. make sure it finishes. - for range w.realCh { - } - return nil - }, false) -} - -func (s *BgpServer) isWatched(typ WatchEventType) bool { - return len(s.watcherMap[typ]) != 0 -} - -func (s *BgpServer) notifyWatcher(typ WatchEventType, ev WatchEvent) { - for _, w := range s.watcherMap[typ] { - w.notify(ev) - } -} - -func (s *BgpServer) Watch(opts ...WatchOption) (w *Watcher) { - s.mgmtOperation(func() error { - w = &Watcher{ - s: s, - realCh: make(chan WatchEvent, 8), - ch: channels.NewInfiniteChannel(), - } - - for _, opt := range opts { - opt(&w.opts) - } - - register := func(t WatchEventType, w *Watcher) { - s.watcherMap[t] = append(s.watcherMap[t], w) - } - - if w.opts.bestpath { - register(WATCH_EVENT_TYPE_BEST_PATH, w) - } - if w.opts.preUpdate { - register(WATCH_EVENT_TYPE_PRE_UPDATE, w) - } - if w.opts.postUpdate { - register(WATCH_EVENT_TYPE_POST_UPDATE, w) - } - if w.opts.peerState { - register(WATCH_EVENT_TYPE_PEER_STATE, w) - } - if w.opts.initPeerState { - for _, peer := range s.neighborMap { - if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED { - continue - } - w.notify(newWatchEventPeerState(peer, nil)) - } - } - if w.opts.initBest && s.active() == nil { - w.notify(&WatchEventBestPath{ - PathList: s.globalRib.GetBestPathList(table.GLOBAL_RIB_NAME, 0, nil), - MultiPathList: s.globalRib.GetBestMultiPathList(table.GLOBAL_RIB_NAME, nil), - }) - } - if w.opts.initUpdate { - for _, peer := range s.neighborMap { - if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED { - continue - } - configNeighbor := w.s.ToConfig(peer, false) - for _, rf := range peer.configuredRFlist() { - _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER] - l, _ := peer.fsm.LocalHostPort() - w.notify(&WatchEventUpdate{ - PeerAS: peer.fsm.peerInfo.AS, - LocalAS: peer.fsm.peerInfo.LocalAS, - PeerAddress: peer.fsm.peerInfo.Address, - LocalAddress: net.ParseIP(l), - PeerID: peer.fsm.peerInfo.ID, - FourBytesAs: y, - Init: true, - PostPolicy: false, - Neighbor: configNeighbor, - PathList: peer.adjRibIn.PathList([]bgp.RouteFamily{rf}, false), - }) - - eor := bgp.NewEndOfRib(rf) - eorBuf, _ := eor.Serialize() - w.notify(&WatchEventUpdate{ - Message: eor, - PeerAS: peer.fsm.peerInfo.AS, - LocalAS: peer.fsm.peerInfo.LocalAS, - PeerAddress: peer.fsm.peerInfo.Address, - LocalAddress: net.ParseIP(l), - PeerID: peer.fsm.peerInfo.ID, - FourBytesAs: y, - Timestamp: time.Now(), - Init: true, - Payload: eorBuf, - PostPolicy: false, - Neighbor: configNeighbor, - }) - } - } - } - if w.opts.initPostUpdate && s.active() == nil { - for _, rf := range s.globalRib.GetRFlist() { - if len(s.globalRib.Tables[rf].GetDestinations()) == 0 { - continue - } - pathsByPeer := make(map[*table.PeerInfo][]*table.Path) - for _, path := range s.globalRib.GetPathList(table.GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{rf}) { - pathsByPeer[path.GetSource()] = append(pathsByPeer[path.GetSource()], path) - } - for peerInfo, paths := range pathsByPeer { - // create copy which can be access to without mutex - var configNeighbor *config.Neighbor - if peer, ok := s.neighborMap[peerInfo.Address.String()]; ok { - configNeighbor = w.s.ToConfig(peer, false) - } - - w.notify(&WatchEventUpdate{ - PeerAS: peerInfo.AS, - PeerAddress: peerInfo.Address, - PeerID: peerInfo.ID, - PostPolicy: true, - Neighbor: configNeighbor, - PathList: paths, - Init: true, - }) - - eor := bgp.NewEndOfRib(rf) - eorBuf, _ := eor.Serialize() - w.notify(&WatchEventUpdate{ - Message: eor, - PeerAS: peerInfo.AS, - PeerAddress: peerInfo.Address, - PeerID: peerInfo.ID, - Timestamp: time.Now(), - Payload: eorBuf, - PostPolicy: true, - Neighbor: configNeighbor, - Init: true, - }) - } - } - } - if w.opts.recvMessage { - register(WATCH_EVENT_TYPE_RECV_MSG, w) - } - - go w.loop() - return nil - }, false) - return w -} |