Files
Arthur de Kerhor 3ae91215be graceful restart: send initial paths list to all neighbors
Before this PR, when graceful restart was configured for a neighbor
and when the restart flag was set by the restarting speaker, if
the neighbor was not advertising the GR capability, the initial
paths list was never sent by the restarting speaker to its neighbor

This is a problem when the server is configured with graceful
restart for all its peers without knowing if the peer supports it.
If some of the peers don't support it, they may never receive the
routes from the restarting speaker, leading to an inconsistent
routing state.
2024-04-30 16:05:51 +02:00

4841 lines
130 KiB
Go

// Copyright (C) 2014-2021 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"
"context"
"errors"
"fmt"
"net"
"reflect"
"strconv"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/eapache/channels"
"github.com/google/uuid"
"google.golang.org/grpc"
api "github.com/osrg/gobgp/v3/api"
"github.com/osrg/gobgp/v3/internal/pkg/table"
"github.com/osrg/gobgp/v3/internal/pkg/version"
"github.com/osrg/gobgp/v3/pkg/apiutil"
"github.com/osrg/gobgp/v3/pkg/config/oc"
"github.com/osrg/gobgp/v3/pkg/log"
"github.com/osrg/gobgp/v3/pkg/packet/bgp"
"github.com/osrg/gobgp/v3/pkg/packet/bmp"
"github.com/osrg/gobgp/v3/pkg/zebra"
)
type tcpListener struct {
l *net.TCPListener
ch chan struct{}
cancel context.CancelFunc
}
func (l *tcpListener) Close() error {
if err := l.l.Close(); err != nil {
return err
}
l.cancel()
<-l.ch
return nil
}
// avoid mapped IPv6 address
func newTCPListener(logger log.Logger, address string, port uint32, bindToDev string, ch chan *net.TCPConn) (*tcpListener, error) {
proto := "tcp4"
family := syscall.AF_INET
if ip := net.ParseIP(address); ip == nil {
return nil, fmt.Errorf("can't listen on %s", address)
} else if ip.To4() == nil {
proto = "tcp6"
family = syscall.AF_INET6
}
addr := net.JoinHostPort(address, strconv.Itoa(int(port)))
var lc net.ListenConfig
lc.Control = func(network, address string, c syscall.RawConn) error {
if bindToDev != "" {
err := setBindToDevSockopt(c, bindToDev)
if err != nil {
logger.Warn("failed to bind Listener to device ",
log.Fields{
"Topic": "Peer",
"Key": addr,
"BindToDev": bindToDev,
"Error": err})
return err
}
}
// Note: Set TTL=255 for incoming connection listener in order to accept
// connection in case for the neighbor has TTL Security settings.
err := setsockoptIpTtl(c, family, 255)
if err != nil {
logger.Warn("cannot set TTL (255) for TCPListener",
log.Fields{
"Topic": "Peer",
"Key": addr,
"Err": err})
}
return nil
}
l, err := lc.Listen(context.Background(), proto, addr)
if err != nil {
return nil, err
}
listener, ok := l.(*net.TCPListener)
if !ok {
err = fmt.Errorf("unexpected connection listener (not for TCP)")
return nil, err
}
closeCh := make(chan struct{})
listenerCtx, listenerCancel := context.WithCancel(context.Background())
go func() error {
for {
conn, err := listener.AcceptTCP()
if err != nil {
close(closeCh)
if !errors.Is(err, net.ErrClosed) {
logger.Warn("Failed to AcceptTCP",
log.Fields{
"Topic": "Peer",
"Error": err,
})
}
return err
}
select {
case ch <- conn:
case <-listenerCtx.Done():
}
}
}()
return &tcpListener{
l: listener,
ch: closeCh,
cancel: listenerCancel,
}, nil
}
type options struct {
grpcAddress string
grpcOption []grpc.ServerOption
logger log.Logger
}
type ServerOption func(*options)
func GrpcListenAddress(addr string) ServerOption {
return func(o *options) {
o.grpcAddress = addr
}
}
func GrpcOption(opt []grpc.ServerOption) ServerOption {
return func(o *options) {
o.grpcOption = opt
}
}
func LoggerOption(logger log.Logger) ServerOption {
return func(o *options) {
o.logger = logger
}
}
type BgpServer struct {
apiServer *server
bgpConfig oc.Bgp
acceptCh chan *net.TCPConn
incomings []*channels.InfiniteChannel
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
roaTable *table.ROATable
uuidMap map[string]uuid.UUID
logger log.Logger
}
func NewBgpServer(opt ...ServerOption) *BgpServer {
opts := options{}
for _, o := range opt {
o(&opts)
}
logger := opts.logger
if logger == nil {
logger = log.NewDefaultLogger()
}
roaTable := table.NewROATable(logger)
s := &BgpServer{
neighborMap: make(map[string]*peer),
peerGroupMap: make(map[string]*peerGroup),
policy: table.NewRoutingPolicy(logger),
mgmtCh: make(chan *mgmtOp, 1),
watcherMap: make(map[watchEventType][]*watcher),
uuidMap: make(map[string]uuid.UUID),
roaManager: newROAManager(roaTable, logger),
roaTable: roaTable,
logger: logger,
}
s.bmpManager = newBmpClientManager(s)
s.mrtManager = newMrtManager(s)
if len(opts.grpcAddress) != 0 {
grpc.EnableTracing = false
s.apiServer = newAPIserver(s, grpc.NewServer(opts.grpcOption...), opts.grpcAddress)
go func() {
if err := s.apiServer.serve(); err != nil {
logger.Fatal("failed to listen grpc port",
log.Fields{"Err": err})
}
}()
}
return s
}
func (s *BgpServer) Stop() {
s.StopBgp(context.Background(), &api.StopBgpRequest{})
if s.apiServer != nil {
s.apiServer.grpcServer.Stop()
}
}
func (s *BgpServer) addIncoming(ch *channels.InfiniteChannel) {
s.incomings = append(s.incomings, ch)
}
func (s *BgpServer) delIncoming(ch *channels.InfiniteChannel) {
for i, c := range s.incomings {
if c == ch {
s.incomings = append(s.incomings[:i], s.incomings[i+1:]...)
return
}
}
}
func (s *BgpServer) listListeners(addr string) []*net.TCPListener {
list := make([]*net.TCPListener, 0, len(s.listeners))
rhs := net.ParseIP(addr).To4() != nil
for _, l := range s.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 (s *BgpServer) handleMGMTOp(op *mgmtOp) {
if op.checkActive {
if err := s.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 (s *BgpServer) passConnToPeer(conn *net.TCPConn) {
host, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
ipaddr, _ := net.ResolveIPAddr("ip", host)
remoteAddr := ipaddr.String()
peer, found := s.neighborMap[remoteAddr]
if found {
peer.fsm.lock.RLock()
adminStateNotUp := peer.fsm.adminState != adminStateUp
peer.fsm.lock.RUnlock()
if adminStateNotUp {
peer.fsm.lock.RLock()
s.logger.Debug("New connection for non admin-state-up peer",
log.Fields{
"Topic": "Peer",
"Remote Addr": remoteAddr,
"Admin State": peer.fsm.adminState,
})
peer.fsm.lock.RUnlock()
conn.Close()
return
}
peer.fsm.lock.RLock()
localAddr := peer.fsm.pConf.Transport.Config.LocalAddress
bindInterface := peer.fsm.pConf.Transport.Config.BindInterface
peer.fsm.lock.RUnlock()
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 && bindInterface == "" {
s.logger.Info("Mismatched local address",
log.Fields{
"Topic": "Peer",
"Key": remoteAddr,
"Configured addr": laddr,
"Addr": host,
"BindInterface": bindInterface})
return false
}
return true
}(localAddr)
if !localAddrValid {
conn.Close()
return
}
s.logger.Debug("Accepted a new passive connection",
log.Fields{
"Topic": "Peer",
"Key": remoteAddr})
peer.PassConn(conn)
} else if pg := s.matchLongestDynamicNeighborPrefix(remoteAddr); pg != nil {
s.logger.Debug("Accepted a new dynamic neighbor",
log.Fields{
"Topic": "Peer",
"Key": remoteAddr})
rib := s.globalRib
if pg.Conf.RouteServer.Config.RouteServerClient {
rib = s.rsRib
}
peer := newDynamicPeer(&s.bgpConfig.Global, remoteAddr, pg.Conf, rib, s.policy, s.logger)
if peer == nil {
s.logger.Info("Can't create new Dynamic Peer",
log.Fields{
"Topic": "Peer",
"Key": remoteAddr})
conn.Close()
return
}
s.addIncoming(peer.fsm.incomingCh)
peer.fsm.lock.RLock()
policy := peer.fsm.pConf.ApplyPolicy
peer.fsm.lock.RUnlock()
s.policy.SetPeerPolicy(peer.ID(), policy)
s.neighborMap[remoteAddr] = peer
peer.startFSMHandler()
s.broadcastPeerState(peer, bgp.BGP_FSM_ACTIVE, nil)
peer.PassConn(conn)
} else {
s.logger.Info("Can't find configuration for a new passive connection",
log.Fields{
"Topic": "Peer",
"Key": remoteAddr})
conn.Close()
}
}
const firstPeerCaseIndex = 3
func (s *BgpServer) Serve() {
s.listeners = make([]*tcpListener, 0, 2)
handlefsmMsg := func(e *fsmMsg) {
fsm := e.fsm
if fsm.h.ctx.Err() != nil {
// canceled
addr := fsm.pConf.State.NeighborAddress
state := fsm.state
fsm.h.wg.Wait()
s.logger.Debug("freed fsm.h",
log.Fields{
"Topic": "Peer",
"Key": addr,
"State": state})
if state == bgp.BGP_FSM_ACTIVE {
var conn net.Conn
select {
case conn = <-fsm.connCh:
default:
if fsm.conn != nil {
conn = fsm.conn
fsm.conn = nil
}
}
if conn != nil {
err := conn.Close()
if err != nil {
s.logger.Error("failed to close existing tcp connection",
log.Fields{
"Topic": "Peer",
"Key": addr,
"State": state})
}
}
}
close(fsm.connCh)
if fsm.state == bgp.BGP_FSM_ESTABLISHED {
s.notifyWatcher(watchEventTypePeerState, &watchEventPeer{
PeerAS: fsm.peerInfo.AS,
PeerAddress: fsm.peerInfo.Address,
PeerID: fsm.peerInfo.ID,
State: bgp.BGP_FSM_IDLE,
Timestamp: time.Now(),
StateReason: &fsmStateReason{
Type: fsmDeConfigured,
},
})
}
cleanInfiniteChannel(fsm.outgoingCh)
cleanInfiniteChannel(fsm.incomingCh)
s.delIncoming(fsm.incomingCh)
if s.shutdownWG != nil && len(s.incomings) == 0 {
s.shutdownWG.Done()
}
return
}
peer, found := s.neighborMap[e.MsgSrc]
if !found {
s.logger.Warn("Can't find the neighbor",
log.Fields{
"Topic": "Peer",
"Key": e.MsgSrc})
return
}
s.handleFSMMessage(peer, e)
}
for {
cases := make([]reflect.SelectCase, firstPeerCaseIndex+len(s.incomings))
cases[0] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(s.mgmtCh),
}
cases[1] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(s.acceptCh),
}
cases[2] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(s.roaManager.ReceiveROA()),
}
for i := firstPeerCaseIndex; i < len(cases); i++ {
cases[i] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(s.incomings[i-firstPeerCaseIndex].Out()),
}
}
chosen, value, ok := reflect.Select(cases)
switch chosen {
case 0:
op := value.Interface().(*mgmtOp)
s.handleMGMTOp(op)
case 1:
conn := value.Interface().(*net.TCPConn)
s.passConnToPeer(conn)
case 2:
ev := value.Interface().(*roaEvent)
s.roaManager.HandleROAEvent(ev)
default:
// in the case of dynamic peer, handleFSMMessage closed incoming channel so
// nil fsmMsg can happen here.
if ok {
e := value.Interface().(*fsmMsg)
handlefsmMsg(e)
}
}
}
}
func (s *BgpServer) matchLongestDynamicNeighborPrefix(a string) *peerGroup {
ipAddr := net.ParseIP(a)
longestMask := net.CIDRMask(0, 32).String()
var longestPG *peerGroup
for _, pg := range s.peerGroupMap {
for _, d := range pg.dynamicNeighbors {
_, netAddr, _ := net.ParseCIDR(d.Config.Prefix)
if netAddr.Contains(ipAddr) {
if netAddr.Mask.String() > longestMask ||
(netAddr.Mask.String() == longestMask && longestMask == net.CIDRMask(0, 32).String()) {
longestMask = netAddr.Mask.String()
longestPG = pg
}
}
}
}
return longestPG
}
func sendfsmOutgoingMsg(peer *peer, paths []*table.Path, notification *bgp.BGPMessage, stayIdle bool) {
peer.fsm.outgoingCh.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
}
peer.fsm.lock.RLock()
_, ok := peer.fsm.rfMap[path.GetRouteFamily()]
peer.fsm.lock.RUnlock()
if !ok {
return nil
}
//RFC4684 Constrained Route Distribution
peer.fsm.lock.RLock()
_, y := peer.fsm.rfMap[bgp.RF_RTC_UC]
peer.fsm.lock.RUnlock()
if 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 {
peer.fsm.logger.Debug("Filtered by Route Target Constraint, ignore",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Data": path})
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
}
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() {
peer.fsm.lock.RLock()
rrClusterID := peer.fsm.peerInfo.RouteReflectorClusterID
peer.fsm.lock.RUnlock()
if clusterID.Equal(rrClusterID) {
peer.fsm.logger.Debug("cluster list path attribute has local cluster id, ignore",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"ClusterID": clusterID,
"Data": path})
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)
}
}
if peer.fsm.logger.GetLevel() >= log.DebugLevel {
peer.fsm.logger.Debug("From same AS, ignore",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Path": path})
}
return nil
}
}
if path = peer.filterPathFromSourcePeer(path, old); path == nil {
return nil
}
if !peer.isRouteServerClient() && isASLoop(peer, path) && !path.IsLocal() {
return nil
}
return path
}
func (s *BgpServer) prePolicyFilterpath(peer *peer, path, old *table.Path) (*table.Path, *table.PolicyOptions, bool) {
// 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.Equal(old) {
peer.fsm.lock.RLock()
s.logger.Debug("given rtm nlri is already sent, skipping to advertise",
log.Fields{
"Topic": "Peer",
"Key": peer.fsm.pConf.State.NeighborAddress,
"Path": path})
peer.fsm.lock.RUnlock()
return nil, nil, true
}
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()
peer.fsm.lock.RLock()
peerVrf := peer.fsm.pConf.Config.Vrf
peer.fsm.lock.RUnlock()
if path != nil && peerVrf != "" {
if f := path.GetRouteFamily(); f != bgp.RF_IPv4_VPN && f != bgp.RF_IPv6_VPN && f != bgp.RF_FS_IPv4_VPN && f != bgp.RF_FS_IPv6_VPN {
return nil, nil, true
}
vrf := peer.localRib.Vrfs[peerVrf]
if table.CanImportToVrf(vrf, path) {
path = path.ToLocal()
} else {
return nil, nil, true
}
}
// replace-peer-as handling
peer.fsm.lock.RLock()
if path != nil && !path.IsWithdraw && peer.fsm.pConf.AsPathOptions.State.ReplacePeerAs {
path = path.ReplaceAS(peer.fsm.pConf.Config.LocalAs, peer.fsm.pConf.Config.PeerAs)
}
peer.fsm.lock.RUnlock()
if path = filterpath(peer, path, old); path == nil {
return nil, nil, true
}
peer.fsm.lock.RLock()
options := &table.PolicyOptions{
Info: peer.fsm.peerInfo,
OldNextHop: path.GetNexthop(),
}
path = table.UpdatePathAttrs(peer.fsm.logger, peer.fsm.gConf, peer.fsm.pConf, peer.fsm.peerInfo, path)
peer.fsm.lock.RUnlock()
return path, options, false
}
func (s *BgpServer) postFilterpath(peer *peer, path *table.Path) *table.Path {
// 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 (s *BgpServer) filterpath(peer *peer, path, old *table.Path) *table.Path {
path, options, stop := s.prePolicyFilterpath(peer, path, old)
if stop {
return nil
}
options.Validate = s.roaTable.Validate
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)
}
}
return s.postFilterpath(peer, 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 (s *BgpServer) setPathVrfIdMap(paths []*table.Path, m map[uint32]bool) {
for _, p := range paths {
switch p.GetRouteFamily() {
case bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN:
for _, vrf := range s.globalRib.Vrfs {
if vrf.Id != 0 && table.CanImportToVrf(vrf, p) {
m[uint32(vrf.Id)] = true
}
}
default:
m[zebra.DefaultVrf] = true
}
}
}
// Note: the destination would be the same for all the paths passed here
// The wather (only zapi) needs a unique list of vrf IDs
func (s *BgpServer) notifyBestWatcher(best []*table.Path, multipath [][]*table.Path) {
if table.SelectionOptions.DisableBestPathSelection {
// Note: If best path selection disabled, no best path to notify.
return
}
m := make(map[uint32]bool)
clonedM := make([][]*table.Path, len(multipath))
for i, pathList := range multipath {
clonedM[i] = clonePathList(pathList)
if table.UseMultiplePaths.Enabled {
s.setPathVrfIdMap(clonedM[i], m)
}
}
clonedB := clonePathList(best)
if !table.UseMultiplePaths.Enabled {
s.setPathVrfIdMap(clonedB, m)
}
w := &watchEventBestPath{PathList: clonedB, MultiPathList: clonedM}
if len(m) > 0 {
w.Vrf = m
}
s.notifyWatcher(watchEventTypeBestPath, w)
}
func (s *BgpServer) toConfig(peer *peer, getAdvertised bool) *oc.Neighbor {
// create copy which can be access to without mutex
peer.fsm.lock.RLock()
conf := *peer.fsm.pConf
peerAfiSafis := peer.fsm.pConf.AfiSafis
peerCapMap := peer.fsm.capMap
peer.fsm.lock.RUnlock()
conf.AfiSafis = make([]oc.AfiSafi, len(peerAfiSafis))
for i, af := range peerAfiSafis {
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(peerCapMap))
for _, caps := range peerCapMap {
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
peer.fsm.lock.Lock()
conf.State.LocalCapabilityList = capabilitiesFromConfig(peer.fsm.pConf)
peer.fsm.lock.Unlock()
peer.fsm.lock.RLock()
conf.State.SessionState = oc.IntToSessionStateMap[int(peer.fsm.state)]
conf.State.AdminState = oc.IntToAdminStateMap[int(peer.fsm.adminState)]
state := peer.fsm.state
peer.fsm.lock.RUnlock()
if state == bgp.BGP_FSM_ESTABLISHED {
peer.fsm.lock.RLock()
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()
peer.fsm.lock.RUnlock()
}
return &conf
}
func (s *BgpServer) notifyPrePolicyUpdateWatcher(peer *peer, pathList []*table.Path, msg *bgp.BGPMessage, timestamp time.Time, payload []byte) {
if !s.isWatched(watchEventTypePreUpdate) || peer == nil {
return
}
cloned := clonePathList(pathList)
if len(cloned) == 0 {
return
}
n := s.toConfig(peer, false)
peer.fsm.lock.RLock()
_, 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: n,
}
peer.fsm.lock.RUnlock()
s.notifyWatcher(watchEventTypePreUpdate, ev)
}
func (s *BgpServer) notifyPostPolicyUpdateWatcher(peer *peer, pathList []*table.Path) {
if !s.isWatched(watchEventTypePostUpdate) || peer == nil {
return
}
cloned := clonePathList(pathList)
if len(cloned) == 0 {
return
}
n := s.toConfig(peer, false)
peer.fsm.lock.RLock()
_, 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: n,
}
peer.fsm.lock.RUnlock()
s.notifyWatcher(watchEventTypePostUpdate, ev)
}
func newWatchEventPeer(peer *peer, m *fsmMsg, oldState bgp.FSMState, t PeerEventType) *watchEventPeer {
var laddr string
var rport, lport uint16
peer.fsm.lock.Lock()
sentOpen := buildopen(peer.fsm.gConf, peer.fsm.pConf)
peer.fsm.lock.Unlock()
peer.fsm.lock.RLock()
if peer.fsm.conn != nil {
_, rport = peer.fsm.RemoteHostPort()
laddr, lport = peer.fsm.LocalHostPort()
}
recvOpen := peer.fsm.recvOpen
e := &watchEventPeer{
Type: t,
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,
OldState: oldState,
AdminState: peer.fsm.adminState,
Timestamp: time.Now(),
PeerInterface: peer.fsm.pConf.Config.NeighborInterface,
}
peer.fsm.lock.RUnlock()
if m != nil {
e.StateReason = m.StateReason
}
return e
}
func (s *BgpServer) broadcastPeerState(peer *peer, oldState bgp.FSMState, e *fsmMsg) {
s.notifyWatcher(watchEventTypePeerState, newWatchEventPeer(peer, e, oldState, PEER_EVENT_STATE))
}
func (s *BgpServer) notifyMessageWatcher(peer *peer, timestamp time.Time, msg *bgp.BGPMessage, isSent bool) {
// validation should be done in the caller of this function
peer.fsm.lock.RLock()
_, 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,
}
peer.fsm.lock.RUnlock()
if !isSent {
s.notifyWatcher(watchEventTypeRecvMsg, ev)
}
}
func (s *BgpServer) notifyRecvMessageWatcher(peer *peer, timestamp time.Time, msg *bgp.BGPMessage) {
if peer == nil || !s.isWatched(watchEventTypeRecvMsg) {
return
}
s.notifyMessageWatcher(peer, timestamp, msg, false)
}
func (s *BgpServer) getPossibleBest(peer *peer, family bgp.RouteFamily) []*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})
}
func (s *BgpServer) getBestFromLocal(peer *peer, rfList []bgp.RouteFamily) ([]*table.Path, []*table.Path) {
pathList := []*table.Path{}
filtered := []*table.Path{}
if peer.isSecondaryRouteEnabled() {
for _, family := range peer.toGlobalFamilies(rfList) {
dsts := s.rsRib.Tables[family].GetDestinations()
dl := make([]*table.Update, 0, len(dsts))
for _, d := range dsts {
l := d.GetAllKnownPathList()
pl := make([]*table.Path, len(l))
copy(pl, l)
u := &table.Update{
KnownPathList: pl,
}
dl = append(dl, u)
}
pathList = append(pathList, s.sendSecondaryRoutes(peer, nil, dl)...)
}
return pathList, filtered
}
for _, family := range peer.toGlobalFamilies(rfList) {
for _, path := range s.getPossibleBest(peer, family) {
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 needToAdvertise(peer *peer) bool {
peer.fsm.lock.RLock()
notEstablished := peer.fsm.state != bgp.BGP_FSM_ESTABLISHED
localRestarting := peer.fsm.pConf.GracefulRestart.State.LocalRestarting
peer.fsm.lock.RUnlock()
if notEstablished {
return false
}
if localRestarting {
peer.fsm.lock.RLock()
peer.fsm.logger.Debug("now syncing, suppress sending updates",
log.Fields{
"Topic": "Peer",
"Key": peer.fsm.pConf.State.NeighborAddress})
peer.fsm.lock.RUnlock()
return false
}
return true
}
func (s *BgpServer) sendSecondaryRoutes(peer *peer, newPath *table.Path, dsts []*table.Update) []*table.Path {
if !needToAdvertise(peer) {
return nil
}
pl := make([]*table.Path, 0, len(dsts))
f := func(path, old *table.Path) *table.Path {
path, options, stop := s.prePolicyFilterpath(peer, path, old)
if stop {
return nil
}
options.Validate = s.roaTable.Validate
path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options)
if path != nil {
return s.postFilterpath(peer, path)
}
return nil
}
for _, dst := range dsts {
old := func() *table.Path {
for _, old := range dst.OldKnownPathList {
o := f(old, nil)
if o != nil {
return o
}
}
return nil
}()
path := func() *table.Path {
for _, known := range dst.KnownPathList {
path := f(known, old)
if path != nil {
return path
}
}
return nil
}()
if path != nil {
pl = append(pl, path)
} else if old != nil {
pl = append(pl, old.Clone(true))
}
}
return pl
}
func (s *BgpServer) processOutgoingPaths(peer *peer, paths, olds []*table.Path) []*table.Path {
if !needToAdvertise(peer) {
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)
peer.fsm.lock.RLock()
_, ok := peer.fsm.rfMap[rf]
peer.fsm.lock.RUnlock()
if !ok {
s.logger.Warn("Route family isn't supported",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Family": rf})
return nil
}
peer.fsm.lock.RLock()
_, ok = peer.fsm.capMap[bgp.BGP_CAP_ROUTE_REFRESH]
peer.fsm.lock.RUnlock()
if !ok {
s.logger.Warn("ROUTE_REFRESH received but the capability wasn't advertised",
log.Fields{
"Topic": "Peer",
"Key": peer.ID()})
return nil
}
rfList := []bgp.RouteFamily{rf}
accepted, _ := s.getBestFromLocal(peer, rfList)
return accepted
}
func (s *BgpServer) propagateUpdate(peer *peer, pathList []*table.Path) {
rs := peer != nil && peer.isRouteServerClient()
vrf := false
if peer != nil {
peer.fsm.lock.RLock()
vrf = !rs && peer.fsm.pConf.Config.Vrf != ""
peer.fsm.lock.RUnlock()
}
tableId := table.GLOBAL_RIB_NAME
rib := s.globalRib
if rs {
tableId = peer.TableID()
rib = s.rsRib
}
for _, path := range pathList {
if vrf {
peer.fsm.lock.RLock()
peerVrf := peer.fsm.pConf.Config.Vrf
peer.fsm.lock.RUnlock()
path = path.ToGlobal(rib.Vrfs[peerVrf])
if s.zclient != nil {
s.zclient.pathVrfMap[path] = rib.Vrfs[peerVrf].Id
}
}
policyOptions := &table.PolicyOptions{
Validate: s.roaTable.Validate,
}
if !rs && peer != nil {
peer.fsm.lock.RLock()
policyOptions.Info = peer.fsm.peerInfo
peer.fsm.lock.RUnlock()
}
if p := s.policy.ApplyPolicy(tableId, table.POLICY_DIRECTION_IMPORT, path, policyOptions); p != nil {
path = p
} else {
path = path.Clone(true)
}
if !rs {
s.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 = s.getBestFromLocal(peer, fs)
} else {
// https://github.com/osrg/gobgp/issues/1777
// Ignore duplicate Membership announcements
membershipsForSource := s.globalRib.GetPathListWithSource(table.GLOBAL_RIB_NAME, []bgp.RouteFamily{bgp.RF_RTC_UC}, path.GetSource())
found := false
for _, membership := range membershipsForSource {
if membership.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget.String() == rt.String() {
found = true
break
}
}
if !found {
candidates = s.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 = s.processOutgoingPaths(peer, paths, nil)
}
sendfsmOutgoingMsg(peer, paths, nil, false)
}
}
if dsts := rib.Update(path); len(dsts) > 0 {
s.propagateUpdateToNeighbors(rib, peer, path, dsts, true)
}
}
}
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 (s *BgpServer) propagateUpdateToNeighbors(rib *table.TableManager, 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)
s.notifyBestWatcher(gBestList, mpathList)
}
family := newPath.GetRouteFamily()
for _, targetPeer := range s.neighborMap {
if (source == nil && targetPeer.isRouteServerClient()) || (source != nil && source.isRouteServerClient() != targetPeer.isRouteServerClient()) {
continue
}
f := func() bgp.RouteFamily {
targetPeer.fsm.lock.RLock()
peerVrf := targetPeer.fsm.pConf.Config.Vrf
targetPeer.fsm.lock.RUnlock()
if peerVrf != "" {
switch family {
case bgp.RF_IPv4_VPN:
return bgp.RF_IPv4_UC
case bgp.RF_IPv6_VPN:
return bgp.RF_IPv6_UC
case bgp.RF_FS_IPv4_VPN:
return bgp.RF_FS_IPv4_UC
case bgp.RF_FS_IPv6_VPN:
return bgp.RF_FS_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 {
toDelete := d.GetWithdrawnPath()
toActuallyDelete := make([]*table.Path, 0, len(toDelete))
for _, p := range toDelete {
// the path was never advertized to the peer
if targetPeer.unsetPathSendMaxFiltered(p) {
continue
}
toActuallyDelete = append(toActuallyDelete, p)
}
if len(toActuallyDelete) == 0 {
continue
}
destination := rib.GetDestination(toActuallyDelete[0])
dstPrefix := toActuallyDelete[0].GetPrefix()
targetPeer.decrementRoutesCount(dstPrefix, f, uint8(len(toActuallyDelete)))
l = append(l, toActuallyDelete...)
// the destination has been removed from the table
// e.g. no more paths to it
if destination == nil {
continue
}
toAdd := targetPeer.getSendMaxFilteredPathList(destination, len(toActuallyDelete))
targetPeer.incrementRoutesCount(dstPrefix, f, uint8(len(toAdd)))
for _, p := range toAdd {
targetPeer.unsetPathSendMaxFiltered(p)
}
l = append(l, toAdd...)
}
return l
}()
} else if targetPeer.canSendPathWithinLimit(newPath) {
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
}
}
}
} else {
bestList = []*table.Path{}
s.logger.Warn("exceeding max routes for prefix",
log.Fields{
"Topic": "Peer",
"Key": targetPeer.ID(),
"Prefix": newPath.GetPrefix(),
})
}
oldList = nil
} else if targetPeer.isRouteServerClient() {
if targetPeer.isSecondaryRouteEnabled() {
if paths := s.sendSecondaryRoutes(targetPeer, newPath, dsts); len(paths) > 0 {
sendfsmOutgoingMsg(targetPeer, paths, nil, false)
}
continue
}
bestList, oldList, _ = dstsToPaths(targetPeer.TableID(), targetPeer.AS(), dsts)
} else {
bestList = gBestList
oldList = gOldList
}
if !needOld {
oldList = nil
}
if paths := s.processOutgoingPaths(targetPeer, bestList, oldList); len(paths) > 0 {
sendfsmOutgoingMsg(targetPeer, paths, nil, false)
}
}
}
func (s *BgpServer) deleteDynamicNeighbor(peer *peer, oldState bgp.FSMState, e *fsmMsg) {
peer.stopPeerRestarting()
peer.fsm.lock.RLock()
delete(s.neighborMap, peer.fsm.pConf.State.NeighborAddress)
peer.fsm.lock.RUnlock()
cleanInfiniteChannel(peer.fsm.outgoingCh)
cleanInfiniteChannel(peer.fsm.incomingCh)
s.delIncoming(peer.fsm.incomingCh)
s.broadcastPeerState(peer, oldState, e)
}
func (s *BgpServer) handleFSMMessage(peer *peer, e *fsmMsg) {
switch e.MsgType {
case fsmMsgStateChange:
nextState := e.MsgData.(bgp.FSMState)
peer.fsm.lock.Lock()
oldState := bgp.FSMState(peer.fsm.pConf.State.SessionState.ToInt())
peer.fsm.pConf.State.SessionState = oc.IntToSessionStateMap[int(nextState)]
peer.fsm.lock.Unlock()
peer.fsm.StateChange(nextState)
peer.fsm.lock.RLock()
nextStateIdle := peer.fsm.pConf.GracefulRestart.State.PeerRestarting && nextState == bgp.BGP_FSM_IDLE
peer.fsm.lock.RUnlock()
// PeerDown
if oldState == bgp.BGP_FSM_ESTABLISHED {
t := time.Now()
peer.fsm.lock.Lock()
if t.Sub(time.Unix(peer.fsm.pConf.Timers.State.Uptime, 0)) < flopThreshold {
peer.fsm.pConf.State.Flops++
}
graceful := peer.fsm.reason.Type == fsmGracefulRestart
peer.fsm.lock.Unlock()
var drop []bgp.RouteFamily
if graceful {
peer.fsm.lock.Lock()
peer.fsm.pConf.GracefulRestart.State.PeerRestarting = true
peer.fsm.lock.Unlock()
var p []bgp.RouteFamily
p, drop = peer.forwardingPreservedFamilies()
s.propagateUpdate(peer, peer.StaleAll(p))
} else {
drop = peer.configuredRFlist()
}
// Always clear EndOfRibReceived state on PeerDown
peer.fsm.lock.Lock()
for i := range peer.fsm.pConf.AfiSafis {
peer.fsm.pConf.AfiSafis[i].MpGracefulRestart.State.EndOfRibReceived = false
}
peer.fsm.lock.Unlock()
peer.prefixLimitWarned = make(map[bgp.RouteFamily]bool)
s.propagateUpdate(peer, peer.DropAll(drop))
peer.fsm.lock.Lock()
if peer.fsm.pConf.Config.PeerAs == 0 {
peer.fsm.pConf.State.PeerAs = 0
peer.fsm.peerInfo.AS = 0
}
peer.fsm.lock.Unlock()
if !graceful && peer.isDynamicNeighbor() {
s.deleteDynamicNeighbor(peer, oldState, e)
return
}
} else if nextStateIdle {
peer.fsm.lock.RLock()
longLivedEnabled := peer.fsm.pConf.GracefulRestart.State.LongLivedEnabled
peer.fsm.lock.RUnlock()
if longLivedEnabled {
llgr, no_llgr := peer.llgrFamilies()
s.propagateUpdate(peer, peer.DropAll(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
s.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))
s.logger.Info("LLGR restart timer started",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Family": family,
"Duration": t})
select {
case <-timer.C:
s.mgmtOperation(func() error {
s.logger.Info("LLGR restart timer expired",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Family": family,
"Duration": t})
s.propagateUpdate(peer, peer.DropAll([]bgp.RouteFamily{family}))
// when all llgr restart timer expired, stop PeerRestarting
if peer.llgrRestartTimerExpired(family) {
peer.stopPeerRestarting()
}
return nil
}, false)
case <-endCh:
s.logger.Info("LLGR restart timer stopped",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Family": family,
"Duration": t})
}
}(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.lock.Lock()
peer.fsm.pConf.GracefulRestart.State.PeerRestarting = false
peer.fsm.lock.Unlock()
s.propagateUpdate(peer, peer.DropAll(peer.configuredRFlist()))
if peer.isDynamicNeighbor() {
s.deleteDynamicNeighbor(peer, oldState, e)
return
}
}
}
cleanInfiniteChannel(peer.fsm.outgoingCh)
peer.fsm.outgoingCh = channels.NewInfiniteChannel()
if nextState == bgp.BGP_FSM_ESTABLISHED {
// update for export policy
laddr, _ := peer.fsm.LocalHostPort()
// may include zone info
peer.fsm.lock.Lock()
peer.fsm.pConf.Transport.State.LocalAddress = laddr
// exclude zone info
ipaddr, _ := net.ResolveIPAddr("ip", laddr)
peer.fsm.peerInfo.LocalAddress = ipaddr.IP
neighborAddress := peer.fsm.pConf.State.NeighborAddress
peer.fsm.lock.Unlock()
deferralExpiredFunc := func(family bgp.RouteFamily) func() {
return func() {
s.mgmtOperation(func() error {
s.softResetOut(neighborAddress, family, true)
return nil
}, false)
}
}
peer.fsm.lock.RLock()
notLocalRestarting := !peer.fsm.pConf.GracefulRestart.State.LocalRestarting
peer.fsm.lock.RUnlock()
if notLocalRestarting {
// 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
peer.fsm.lock.RLock()
_, y := peer.fsm.rfMap[bgp.RF_RTC_UC]
c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC)
notPeerRestarting := !peer.fsm.pConf.GracefulRestart.State.PeerRestarting
peer.fsm.lock.RUnlock()
if y && notPeerRestarting && c.RouteTargetMembership.Config.DeferralTime > 0 {
pathList, _ = s.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, _ = s.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 s.neighborMap {
if !p.recvedAllEOR() {
return false
}
}
return true
}()
if allEnd {
for _, p := range s.neighborMap {
p.fsm.lock.Lock()
peerLocalRestarting := p.fsm.pConf.GracefulRestart.State.LocalRestarting
p.fsm.pConf.GracefulRestart.State.LocalRestarting = false
p.fsm.lock.Unlock()
if !p.isGracefulRestartEnabled() && !peerLocalRestarting {
continue
}
paths, _ := s.getBestFromLocal(p, p.configuredRFlist())
if len(paths) > 0 {
sendfsmOutgoingMsg(p, paths, nil, false)
}
}
s.logger.Info("sync finished",
log.Fields{
"Topic": "Server",
"Key": peer.ID()})
} else {
peer.fsm.lock.RLock()
deferral := peer.fsm.pConf.GracefulRestart.Config.DeferralTime
peer.fsm.lock.RUnlock()
s.logger.Debug("Now syncing, suppress sending updates. start deferral timer",
log.Fields{
"Topic": "Server",
"Key": peer.ID(),
"Duration": deferral})
time.AfterFunc(time.Second*time.Duration(deferral), deferralExpiredFunc(bgp.RouteFamily(0)))
}
}
} else {
peer.fsm.lock.Lock()
peer.fsm.pConf.Timers.State.Downtime = time.Now().Unix()
peer.fsm.lock.Unlock()
}
// clear counter
peer.fsm.lock.RLock()
adminStateDown := peer.fsm.adminState == adminStateDown
peer.fsm.lock.RUnlock()
if adminStateDown {
peer.fsm.lock.Lock()
peer.fsm.pConf.State = oc.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 = oc.TimersState{}
peer.fsm.lock.Unlock()
}
peer.startFSMHandler()
s.broadcastPeerState(peer, oldState, e)
case fsmMsgRouteRefresh:
peer.fsm.lock.RLock()
notEstablished := peer.fsm.state != bgp.BGP_FSM_ESTABLISHED
beforeUptime := e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime
peer.fsm.lock.RUnlock()
if notEstablished || beforeUptime {
return
}
if paths := s.handleRouteRefresh(peer, e); len(paths) > 0 {
sendfsmOutgoingMsg(peer, paths, nil, false)
return
}
case fsmMsgBGPMessage:
switch m := e.MsgData.(type) {
case *bgp.MessageError:
sendfsmOutgoingMsg(peer, nil, bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data), false)
return
case *bgp.BGPMessage:
s.notifyRecvMessageWatcher(peer, e.timestamp, m)
peer.fsm.lock.RLock()
notEstablished := peer.fsm.state != bgp.BGP_FSM_ESTABLISHED
beforeUptime := e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime
peer.fsm.lock.RUnlock()
if notEstablished || beforeUptime {
return
}
pathList, eor, notification := peer.handleUpdate(e)
if notification != nil {
sendfsmOutgoingMsg(peer, nil, notification, true)
return
}
if m.Header.Type == bgp.BGP_MSG_UPDATE {
s.notifyPrePolicyUpdateWatcher(peer, pathList, m, e.timestamp, e.payload)
}
if len(pathList) > 0 {
s.propagateUpdate(peer, pathList)
}
peer.fsm.lock.RLock()
peerAfiSafis := peer.fsm.pConf.AfiSafis
peer.fsm.lock.RUnlock()
if len(eor) > 0 {
rtc := false
for _, f := range eor {
if f == bgp.RF_RTC_UC {
rtc = true
}
peer.fsm.lock.RLock()
peerInfo := &table.PeerInfo{
AS: peer.fsm.peerInfo.AS,
ID: peer.fsm.peerInfo.ID,
LocalAS: peer.fsm.peerInfo.LocalAS,
LocalID: peer.fsm.peerInfo.LocalID,
Address: peer.fsm.peerInfo.Address,
LocalAddress: peer.fsm.peerInfo.LocalAddress,
}
peer.fsm.lock.RUnlock()
ev := &watchEventEor{
Family: f,
PeerInfo: peerInfo,
}
s.notifyWatcher(watchEventTypeEor, ev)
for i, a := range peerAfiSafis {
if a.State.Family == f {
peer.fsm.lock.Lock()
peer.fsm.pConf.AfiSafis[i].MpGracefulRestart.State.EndOfRibReceived = true
peer.fsm.lock.Unlock()
}
}
}
// 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...
peer.fsm.lock.RLock()
localRestarting := peer.fsm.pConf.GracefulRestart.State.LocalRestarting
peer.fsm.lock.RUnlock()
if localRestarting {
allEnd := func() bool {
for _, p := range s.neighborMap {
if !p.recvedAllEOR() {
return false
}
}
return true
}()
if allEnd {
for _, p := range s.neighborMap {
p.fsm.lock.Lock()
peerLocalRestarting := p.fsm.pConf.GracefulRestart.State.LocalRestarting
p.fsm.pConf.GracefulRestart.State.LocalRestarting = false
p.fsm.lock.Unlock()
if !p.isGracefulRestartEnabled() && !peerLocalRestarting {
continue
}
paths, _ := s.getBestFromLocal(p, p.negotiatedRFList())
if len(paths) > 0 {
sendfsmOutgoingMsg(p, paths, nil, false)
}
}
s.logger.Info("sync finished",
log.Fields{
"Topic": "Server"})
}
// we don't delay non-route-target NLRIs when local-restarting
rtc = false
}
peer.fsm.lock.RLock()
peerRestarting := peer.fsm.pConf.GracefulRestart.State.PeerRestarting
peer.fsm.lock.RUnlock()
if peerRestarting {
if peer.recvedAllEOR() {
peer.stopPeerRestarting()
pathList := peer.adjRibIn.DropStale(peer.configuredRFlist())
peer.fsm.lock.RLock()
s.logger.Debug("withdraw stale routes",
log.Fields{
"Topic": "Peer",
"Key": peer.fsm.pConf.State.NeighborAddress,
"Numbers": len(pathList)})
peer.fsm.lock.RUnlock()
s.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
peer.fsm.lock.RLock()
c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC)
peer.fsm.lock.RUnlock()
if rtc && c != nil && c.RouteTargetMembership.Config.DeferralTime > 0 {
s.logger.Debug("received route-target eor. flash non-route-target NLRIs",
log.Fields{
"Topic": "Peer",
"Key": peer.ID()})
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, _ := s.getBestFromLocal(peer, families); len(paths) > 0 {
sendfsmOutgoingMsg(peer, paths, nil, false)
}
}
}
default:
s.logger.Fatal("unknown msg type",
log.Fields{
"Topic": "Peer",
"Key": peer.fsm.pConf.State.NeighborAddress,
"Data": e.MsgData})
}
}
}
func (s *BgpServer) EnableZebra(ctx context.Context, r *api.EnableZebraRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
if s.zclient != nil {
return fmt.Errorf("already connected to Zebra")
}
software := zebra.NewSoftware(uint8(r.Version), r.SoftwareName)
for _, p := range r.RouteTypes {
if _, err := zebra.RouteTypeFromString(p, uint8(r.Version), software); err != nil {
return err
}
}
protos := make([]string, 0, len(r.RouteTypes))
for _, p := range r.RouteTypes {
protos = append(protos, string(p))
}
var err error
s.zclient, err = newZebraClient(s, r.Url, protos, uint8(r.Version), r.NexthopTriggerEnable, uint8(r.NexthopTriggerDelay), r.MplsLabelRangeSize, software)
return err
}, false)
}
func (s *BgpServer) AddBmp(ctx context.Context, r *api.AddBmpRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
_, ok := api.AddBmpRequest_MonitoringPolicy_name[int32(r.Policy)]
if !ok {
return fmt.Errorf("invalid bmp route monitoring policy: %v", r.Policy)
}
port := r.Port
if port == 0 {
port = bmp.BMP_DEFAULT_PORT
}
sysname := r.SysName
if sysname == "" {
sysname = "GoBGP"
}
sysDescr := r.SysDescr
if sysDescr == "" {
sysDescr = version.Version()
}
s.logger.Debug("add bmp server", log.Fields{"address": r.Address, "port": r.Port, "policy": r.Policy})
return s.bmpManager.addServer(&oc.BmpServerConfig{
Address: r.Address,
Port: port,
SysName: sysname,
SysDescr: sysDescr,
RouteMonitoringPolicy: oc.IntToBmpRouteMonitoringPolicyTypeMap[int(r.Policy)],
StatisticsTimeout: uint16(r.StatisticsTimeout),
})
}, true)
}
func (s *BgpServer) DeleteBmp(ctx context.Context, r *api.DeleteBmpRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.bmpManager.deleteServer(&oc.BmpServerConfig{
Address: r.Address,
Port: r.Port,
})
}, true)
}
func (s *BgpServer) ListBmp(ctx context.Context, req *api.ListBmpRequest, fn func(*api.ListBmpResponse_BmpStation)) error {
if req == nil {
return fmt.Errorf("null request")
}
var stations []*api.ListBmpResponse_BmpStation
err := s.mgmtOperation(func() error {
for _, s := range s.bmpManager.clientMap {
stations = append(stations, &api.ListBmpResponse_BmpStation{
Conf: &api.ListBmpResponse_BmpStation_Conf{
Address: s.c.Address,
Port: s.c.Port,
},
State: &api.ListBmpResponse_BmpStation_State{
Uptime: oc.ProtoTimestamp(atomic.LoadInt64(&s.uptime)),
Downtime: oc.ProtoTimestamp(atomic.LoadInt64(&s.downtime)),
},
})
}
return nil
}, false)
if err == nil {
for _, rsp := range stations {
select {
case <-ctx.Done():
return nil
default:
fn(rsp)
}
}
}
return err
}
func (s *BgpServer) StopBgp(ctx context.Context, r *api.StopBgpRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
s.mgmtOperation(func() error {
names := make([]string, 0, len(s.neighborMap))
for k := range s.neighborMap {
names = append(names, k)
}
if len(names) != 0 {
s.shutdownWG = new(sync.WaitGroup)
s.shutdownWG.Add(1)
}
for _, name := range names {
if err := s.deleteNeighbor(&oc.Neighbor{Config: oc.NeighborConfig{
NeighborAddress: name}}, bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED); err != nil {
return err
}
}
for _, l := range s.listeners {
l.Close()
}
s.bgpConfig.Global = oc.Global{}
return nil
}, false)
if s.shutdownWG != nil {
s.shutdownWG.Wait()
s.shutdownWG = nil
}
return nil
}
func (s *BgpServer) SetPolicies(ctx context.Context, r *api.SetPoliciesRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
rp, err := newRoutingPolicyFromApiStruct(r)
if err != nil {
return err
}
getConfig := func(id string) (*oc.ApplyPolicy, error) {
f := func(id string, dir table.PolicyDirection) (oc.DefaultPolicyType, []string, error) {
rt, policies, err := s.policy.GetPolicyAssignment(id, dir)
if err != nil {
return oc.DEFAULT_POLICY_TYPE_REJECT_ROUTE, nil, err
}
names := make([]string, 0, len(policies))
for _, p := range policies {
names = append(names, p.Name)
}
t := oc.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE
if rt == table.ROUTE_TYPE_REJECT {
t = oc.DEFAULT_POLICY_TYPE_REJECT_ROUTE
}
return t, names, nil
}
c := &oc.ApplyPolicy{}
rt, policies, err := f(id, table.POLICY_DIRECTION_IMPORT)
if err != nil {
return nil, err
}
c.Config.ImportPolicyList = policies
c.Config.DefaultImportPolicy = rt
rt, policies, err = f(id, table.POLICY_DIRECTION_EXPORT)
if err != nil {
return nil, err
}
c.Config.ExportPolicyList = policies
c.Config.DefaultExportPolicy = rt
return c, nil
}
return s.mgmtOperation(func() error {
ap := make(map[string]oc.ApplyPolicy, len(s.neighborMap)+1)
a, err := getConfig(table.GLOBAL_RIB_NAME)
if err != nil {
return err
}
ap[table.GLOBAL_RIB_NAME] = *a
for _, peer := range s.neighborMap {
peer.fsm.lock.RLock()
s.logger.Info("call set policy",
log.Fields{
"Topic": "Peer",
"Key": peer.fsm.pConf.State.NeighborAddress})
peer.fsm.lock.RUnlock()
a, err := getConfig(peer.ID())
if err != nil {
return err
}
ap[peer.ID()] = *a
}
return s.policy.Reset(rp, 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 {
if path == nil {
continue
}
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 (s *BgpServer) fixupApiPath(vrfId string, pathList []*table.Path) error {
for _, path := range pathList {
if !path.IsWithdraw {
if _, err := path.GetOrigin(); err != nil {
return err
}
}
if vrfId != "" {
vrf := s.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
mac := path.GetNlri().(*bgp.EVPNNLRI).RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute).MacAddress
paths := s.globalRib.GetPathListWithMac(table.GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN}, mac)
if m := getMacMobilityExtendedCommunity(r.ETag, r.MacAddress, paths); m != nil {
pm := getMacMobilityExtendedCommunity(r.ETag, r.MacAddress, []*table.Path{path})
if pm == nil {
path.SetExtCommunities([]bgp.ExtendedCommunityInterface{m}, false)
} else if pm != nil && pm.Sequence < m.Sequence {
return fmt.Errorf("invalid MAC mobility sequence number")
}
}
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.GetPrefix())
}
func (s *BgpServer) addPathList(vrfId string, pathList []*table.Path) error {
err := s.fixupApiPath(vrfId, pathList)
if err == nil {
s.propagateUpdate(nil, pathList)
}
return err
}
func (s *BgpServer) addPathStream(vrfId string, pathList []*table.Path) error {
err := s.mgmtOperation(func() error {
return s.addPathList(vrfId, pathList)
}, true)
return err
}
func (s *BgpServer) AddPath(ctx context.Context, r *api.AddPathRequest) (*api.AddPathResponse, error) {
if r == nil || r.Path == nil {
return nil, fmt.Errorf("nil request")
}
var uuidBytes []byte
err := s.mgmtOperation(func() error {
id, err := uuid.NewRandom()
if err != nil {
return err
}
path, err := api2Path(r.TableType, r.Path, false)
if err != nil {
return err
}
err = s.addPathList(r.VrfId, []*table.Path{path})
if err != nil {
return err
}
s.uuidMap[pathTokey(path)] = id
uuidBytes, _ = id.MarshalBinary()
return nil
}, true)
return &api.AddPathResponse{Uuid: uuidBytes}, err
}
func (s *BgpServer) DeletePath(ctx context.Context, r *api.DeletePathRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
deletePathList := make([]*table.Path, 0)
pathList, err := func() ([]*table.Path, error) {
if r.Path != nil {
path, err := api2Path(r.TableType, r.Path, true)
return []*table.Path{path}, err
}
return []*table.Path{}, nil
}()
if err != nil {
return err
}
if len(r.Uuid) > 0 {
// Delete locally generated path which has the given UUID
path := func() *table.Path {
id, _ := uuid.FromBytes(r.Uuid)
for k, v := range s.uuidMap {
if v == id {
for _, path := range s.globalRib.GetPathList(table.GLOBAL_RIB_NAME, 0, s.globalRib.GetRFlist()) {
if path.IsLocal() && k == pathTokey(path) {
delete(s.uuidMap, k)
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 r.Family != nil {
families = []bgp.RouteFamily{bgp.AfiSafiToRouteFamily(uint16(r.Family.Afi), uint8(r.Family.Safi))}
}
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[string]uuid.UUID)
} else {
if err := s.fixupApiPath(r.VrfId, pathList); err != nil {
return err
}
deletePathList = pathList
for _, p := range deletePathList {
delete(s.uuidMap, pathTokey(p))
}
}
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) StartBgp(ctx context.Context, r *api.StartBgpRequest) error {
if r == nil || r.Global == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
g := r.Global
if net.ParseIP(g.RouterId) == nil {
return fmt.Errorf("invalid router-id format: %s", g.RouterId)
}
c := newGlobalFromAPIStruct(g)
if err := oc.SetDefaultGlobalConfigValues(c); err != nil {
return err
}
if c.Config.Port > 0 {
acceptCh := make(chan *net.TCPConn, 32)
for _, addr := range c.Config.LocalAddressList {
l, err := newTCPListener(s.logger, addr, uint32(c.Config.Port), g.BindToDevice, acceptCh)
if err != nil {
return err
}
s.listeners = append(s.listeners, l)
}
s.acceptCh = acceptCh
}
rfs, _ := oc.AfiSafis(c.AfiSafis).ToRfList()
s.globalRib = table.NewTableManager(s.logger, rfs)
s.rsRib = table.NewTableManager(s.logger, rfs)
if err := s.policy.Initialize(); err != nil {
return err
}
s.bgpConfig.Global = *c
// update route selection options
table.SelectionOptions = c.RouteSelectionOptions.Config
table.UseMultiplePaths = c.UseMultiplePaths.Config
return nil
}, false)
}
// TODO: delete this function
func (s *BgpServer) listVrf() (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) ListVrf(ctx context.Context, r *api.ListVrfRequest, fn func(*api.Vrf)) error {
if r == nil {
return fmt.Errorf("nil request")
}
toApi := func(v *table.Vrf) *api.Vrf {
d, _ := apiutil.MarshalRD(v.Rd)
irt, _ := apiutil.MarshalRTs(v.ImportRt)
ert, _ := apiutil.MarshalRTs(v.ExportRt)
return &api.Vrf{
Name: v.Name,
Rd: d,
Id: v.Id,
ImportRt: irt,
ExportRt: ert,
}
}
var l []*api.Vrf
s.mgmtOperation(func() error {
l = make([]*api.Vrf, 0, len(s.globalRib.Vrfs))
for name, vrf := range s.globalRib.Vrfs {
if r.Name != "" && r.Name != name {
continue
}
l = append(l, toApi(vrf.Clone()))
}
return nil
}, true)
for _, v := range l {
select {
case <-ctx.Done():
return nil
default:
fn(v)
}
}
return nil
}
func (s *BgpServer) AddVrf(ctx context.Context, r *api.AddVrfRequest) error {
if r == nil || r.Vrf == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
name := r.Vrf.Name
id := r.Vrf.Id
rd, err := apiutil.UnmarshalRD(r.Vrf.Rd)
if err != nil {
return err
}
im, err := apiutil.UnmarshalRTs(r.Vrf.ImportRt)
if err != nil {
return err
}
ex, err := apiutil.UnmarshalRTs(r.Vrf.ExportRt)
if err != nil {
return err
}
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)
}
if vrf, ok := s.globalRib.Vrfs[name]; ok {
if s.zclient != nil && s.zclient.mplsLabel.rangeSize > 0 {
s.zclient.assignAndSendVrfMplsLabel(vrf)
}
}
return nil
}, true)
}
func (s *BgpServer) DeleteVrf(ctx context.Context, r *api.DeleteVrfRequest) error {
if r == nil || r.Name == "" {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
name := r.Name
for _, n := range s.neighborMap {
n.fsm.lock.RLock()
peerVrf := n.fsm.pConf.Config.Vrf
n.fsm.lock.RUnlock()
if peerVrf == name {
return fmt.Errorf("failed to delete VRF %s: neighbor %s is in use", name, n.ID())
}
}
if vrf, ok := s.globalRib.Vrfs[name]; ok {
if vrf.MplsLabel > 0 {
s.zclient.releaseMplsLabel(vrf.MplsLabel)
}
}
pathList, err := s.globalRib.DeleteVrf(name)
if err != nil {
return err
}
if len(pathList) > 0 {
s.propagateUpdate(nil, pathList)
}
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 {
s.propagateUpdate(peer, peer.adjRibIn.PathList(familiesForSoftreset(peer, family), true))
}
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 {
peer.fsm.lock.RLock()
notEstablished := peer.fsm.state != bgp.BGP_FSM_ESTABLISHED
peer.fsm.lock.RUnlock()
if notEstablished {
continue
}
families := familiesForSoftreset(peer, family)
if deferral {
if family == bgp.RouteFamily(0) {
families = peer.configuredRFlist()
}
peer.fsm.lock.RLock()
_, y := peer.fsm.rfMap[bgp.RF_RTC_UC]
c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC)
restarting := peer.fsm.pConf.GracefulRestart.State.LocalRestarting
peer.fsm.lock.RUnlock()
if restarting {
peer.fsm.lock.Lock()
peer.fsm.pConf.GracefulRestart.State.LocalRestarting = false
peer.fsm.lock.Unlock()
s.logger.Debug("deferral timer expired",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Families": families})
} else if y && !c.MpGracefulRestart.State.EndOfRibReceived {
s.logger.Debug("route-target deferral timer expired",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Families": families})
} else {
continue
}
}
pathList, _ := s.getBestFromLocal(peer, families)
if len(pathList) > 0 {
if deferral {
pathList = func() []*table.Path {
l := make([]*table.Path, 0, len(pathList))
for _, p := range pathList {
if !p.IsWithdraw {
l = append(l, p)
}
}
return l
}()
}
sendfsmOutgoingMsg(peer, pathList, nil, false)
}
}
return nil
}
func (s *BgpServer) sResetIn(addr string, family bgp.RouteFamily) error {
s.logger.Info("Neighbor soft reset in",
log.Fields{
"Topic": "Operation",
"Key": addr})
return s.softResetIn(addr, family)
}
func (s *BgpServer) sResetOut(addr string, family bgp.RouteFamily) error {
s.logger.Info("Neighbor soft reset out",
log.Fields{
"Topic": "Operation",
"Key": addr})
return s.softResetOut(addr, family, false)
}
func (s *BgpServer) sReset(addr string, family bgp.RouteFamily) error {
s.logger.Info("Neighbor soft reset",
log.Fields{
"Topic": "Operation",
"Key": addr})
err := s.softResetIn(addr, family)
if err != nil {
return err
}
return s.softResetOut(addr, family, false)
}
func (s *BgpServer) validateTable(r *table.Table) (v map[*table.Path]*table.Validation) {
if s.roaManager.enabled() {
v = make(map[*table.Path]*table.Validation, len(r.GetDestinations()))
for _, d := range r.GetDestinations() {
for _, p := range d.GetAllKnownPathList() {
v[p] = s.roaTable.Validate(p)
}
}
}
return
}
func (s *BgpServer) getRib(addr string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (rib *table.Table, v map[*table.Path]*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_FS_IPv4_UC:
af = bgp.RF_FS_IPv4_VPN
case bgp.RF_FS_IPv6_UC:
af = bgp.RF_FS_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, enableFiltered bool, prefixes []*table.LookupPrefix) (rib *table.Table, filtered map[table.PathLocalKey]table.FilteredType, v map[*table.Path]*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
var toUpdate []*table.Path
filtered = make(map[table.PathLocalKey]table.FilteredType)
if in {
adjRib = peer.adjRibIn
if enableFiltered {
toUpdate = make([]*table.Path, 0)
for _, path := range peer.adjRibIn.PathList([]bgp.RouteFamily{family}, true) {
options := &table.PolicyOptions{
Validate: s.roaTable.Validate,
}
if p := s.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_IMPORT, path, options); p == nil {
filtered[path.GetLocalKey()] = table.PolicyFiltered
} else {
toUpdate = append(toUpdate, p)
}
}
}
} else {
adjRib = table.NewAdjRib(s.logger, peer.configuredRFlist())
pathList := make([]*table.Path, 0)
if enableFiltered {
for _, path := range s.getPossibleBest(peer, family) {
path, options, stop := s.prePolicyFilterpath(peer, path, nil)
if stop {
continue
}
options.Validate = s.roaTable.Validate
p := peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options)
if p == nil {
filtered[path.GetLocalKey()] = table.PolicyFiltered
}
pathList = append(pathList, path)
}
} else {
pathList, _ = s.getBestFromLocal(peer, peer.configuredRFlist())
}
toUpdate = make([]*table.Path, 0, len(pathList))
for _, p := range pathList {
pathLocalKey := p.GetLocalKey()
if peer.isPathSendMaxFiltered(p) {
filtered[pathLocalKey] = filtered[pathLocalKey] | table.SendMaxFiltered
}
toUpdate = append(toUpdate, p)
}
}
adjRib.Update(toUpdate)
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) ListPath(ctx context.Context, r *api.ListPathRequest, fn func(*api.Destination)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var tbl *table.Table
var v map[*table.Path]*table.Validation
var filtered map[table.PathLocalKey]table.FilteredType
f := func() []*table.LookupPrefix {
l := make([]*table.LookupPrefix, 0, len(r.Prefixes))
for _, p := range r.Prefixes {
l = append(l, &table.LookupPrefix{
Prefix: p.Prefix,
RD: p.Rd,
LookupOption: table.LookupOption(p.Type),
})
}
return l
}
in := false
family := bgp.RouteFamily(0)
if r.Family != nil {
family = bgp.AfiSafiToRouteFamily(uint16(r.Family.Afi), uint8(r.Family.Safi))
}
var err error
switch r.TableType {
case api.TableType_LOCAL, api.TableType_GLOBAL:
tbl, v, err = s.getRib(r.Name, family, f())
case api.TableType_ADJ_IN:
in = true
fallthrough
case api.TableType_ADJ_OUT:
tbl, filtered, v, err = s.getAdjRib(r.Name, family, in, r.EnableFiltered, f())
case api.TableType_VRF:
tbl, err = s.getVrfRib(r.Name, family, []*table.LookupPrefix{})
default:
return fmt.Errorf("unsupported resource type: %v", r.TableType)
}
if err != nil {
return err
}
err = func() error {
for _, dst := range tbl.GetDestinations() {
d := api.Destination{
Prefix: dst.GetNlri().String(),
Paths: make([]*api.Path, 0, len(dst.GetAllKnownPathList())),
}
knownPathList := dst.GetAllKnownPathList()
for i, path := range knownPathList {
p := toPathApi(path, getValidation(v, path), r.EnableOnlyBinary, r.EnableNlriBinary, r.EnableAttributeBinary)
if !table.SelectionOptions.DisableBestPathSelection {
if i == 0 {
switch r.TableType {
case api.TableType_LOCAL, api.TableType_GLOBAL:
p.Best = true
}
} else if s.bgpConfig.Global.UseMultiplePaths.Config.Enabled && path.Equal(knownPathList[i-1]) {
p.Best = true
}
}
d.Paths = append(d.Paths, p)
if r.EnableFiltered && filtered[path.GetLocalKey()]&table.PolicyFiltered > 0 {
p.Filtered = true
}
// we always want to know that some paths are filtered out
// by send-max attribute
if filtered[path.GetLocalKey()]&table.SendMaxFiltered > 0 {
p.SendMaxFiltered = true
}
}
select {
case <-ctx.Done():
return nil
default:
fn(&d)
}
}
return nil
}()
return err
}
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
}
af := bgp.RouteFamily(family)
tbl, ok := m.Tables[af]
if !ok {
return fmt.Errorf("address family: %s not supported", af)
}
info = tbl.Info(table.TableInfoOptions{ID: id, AS: as})
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(s.logger, peer.configuredRFlist())
accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist())
adjRib.UpdateAdjRibOut(accepted)
}
info, err = adjRib.TableInfo(family)
return err
}, true)
return
}
func (s *BgpServer) getVrfRibInfo(name string, family bgp.RouteFamily) (info *table.TableInfo, 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_FS_IPv4_UC:
af = bgp.RF_FS_IPv4_VPN
case bgp.RF_FS_IPv6_UC:
af = bgp.RF_FS_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)
}
info = tbl.Info(table.TableInfoOptions{VRF: vrfs[name]})
return err
}, true)
return
}
func (s *BgpServer) GetTable(ctx context.Context, r *api.GetTableRequest) (*api.GetTableResponse, error) {
if r == nil || r.Family == nil {
return nil, fmt.Errorf("nil request")
}
family := bgp.RouteFamily(0)
if r.Family != nil {
family = bgp.AfiSafiToRouteFamily(uint16(r.Family.Afi), uint8(r.Family.Safi))
}
var in bool
var err error
var info *table.TableInfo
switch r.TableType {
case api.TableType_GLOBAL, api.TableType_LOCAL:
info, err = s.getRibInfo(r.Name, family)
case api.TableType_ADJ_IN:
in = true
fallthrough
case api.TableType_ADJ_OUT:
info, err = s.getAdjRibInfo(r.Name, family, in)
case api.TableType_VRF:
info, err = s.getVrfRibInfo(r.Name, family)
default:
return nil, fmt.Errorf("unsupported resource type: %s", r.TableType)
}
if err != nil {
return nil, err
}
return &api.GetTableResponse{
NumDestination: uint64(info.NumDestination),
NumPath: uint64(info.NumPath),
NumAccepted: uint64(info.NumAccepted),
}, nil
}
func (s *BgpServer) GetBgp(ctx context.Context, r *api.GetBgpRequest) (*api.GetBgpResponse, error) {
if r == nil {
return nil, fmt.Errorf("nil request")
}
var rsp *api.GetBgpResponse
s.mgmtOperation(func() error {
g := s.bgpConfig.Global
rsp = &api.GetBgpResponse{
Global: &api.Global{
Asn: g.Config.As,
RouterId: g.Config.RouterId,
ListenPort: g.Config.Port,
ListenAddresses: g.Config.LocalAddressList,
UseMultiplePaths: g.UseMultiplePaths.Config.Enabled,
},
}
return nil
}, false)
return rsp, nil
}
func (s *BgpServer) ListDynamicNeighbor(ctx context.Context, r *api.ListDynamicNeighborRequest, fn func(neighbor *api.DynamicNeighbor)) error {
if r == nil {
return fmt.Errorf("nil request")
}
toApi := func(dn *oc.DynamicNeighbor) *api.DynamicNeighbor {
return &api.DynamicNeighbor{
Prefix: dn.Config.Prefix,
PeerGroup: dn.Config.PeerGroup,
}
}
var l []*api.DynamicNeighbor
s.mgmtOperation(func() error {
peerGroupName := r.PeerGroup
for k, group := range s.peerGroupMap {
if peerGroupName != "" && peerGroupName != k {
continue
}
for _, dn := range group.dynamicNeighbors {
l = append(l, toApi(dn))
}
}
return nil
}, false)
for _, dn := range l {
select {
case <-ctx.Done():
return nil
default:
fn(dn)
}
}
return nil
}
func (s *BgpServer) ListPeerGroup(ctx context.Context, r *api.ListPeerGroupRequest, fn func(*api.PeerGroup)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var l []*api.PeerGroup
s.mgmtOperation(func() error {
peerGroupName := r.PeerGroupName
l = make([]*api.PeerGroup, 0, len(s.peerGroupMap))
for k, group := range s.peerGroupMap {
if peerGroupName != "" && peerGroupName != k {
continue
}
pg := oc.NewPeerGroupFromConfigStruct(group.Conf)
l = append(l, pg)
}
return nil
}, false)
for _, pg := range l {
select {
case <-ctx.Done():
return nil
default:
fn(pg)
}
}
return nil
}
func (s *BgpServer) ListPeer(ctx context.Context, r *api.ListPeerRequest, fn func(*api.Peer)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var l []*api.Peer
s.mgmtOperation(func() error {
address := r.Address
getAdvertised := r.EnableAdvertised
l = make([]*api.Peer, 0, len(s.neighborMap))
for k, peer := range s.neighborMap {
peer.fsm.lock.RLock()
neighborIface := peer.fsm.pConf.Config.NeighborInterface
peer.fsm.lock.RUnlock()
if address != "" && address != k && address != neighborIface {
continue
}
// FIXME: should remove toConfig() conversion
p := oc.NewPeerFromConfigStruct(s.toConfig(peer, getAdvertised))
for _, family := range peer.configuredRFlist() {
for i, afisafi := range p.AfiSafis {
if !afisafi.Config.Enabled {
continue
}
afi, safi := bgp.RouteFamilyToAfiSafi(family)
c := afisafi.Config
if c.Family != nil && c.Family.Afi == api.Family_Afi(afi) && c.Family.Safi == api.Family_Safi(safi) {
flist := []bgp.RouteFamily{family}
received := uint64(peer.adjRibIn.Count(flist))
accepted := uint64(peer.adjRibIn.Accepted(flist))
advertised := uint64(0)
if getAdvertised {
pathList, _ := s.getBestFromLocal(peer, flist)
advertised = uint64(len(pathList))
}
p.AfiSafis[i].State = &api.AfiSafiState{
Family: c.Family,
Enabled: true,
Received: received,
Accepted: accepted,
Advertised: advertised,
}
}
}
}
l = append(l, p)
}
return nil
}, false)
for _, p := range l {
select {
case <-ctx.Done():
return nil
default:
fn(p)
}
}
return nil
}
func (s *BgpServer) addPeerGroup(c *oc.PeerGroup) error {
name := c.Config.PeerGroupName
if _, y := s.peerGroupMap[name]; y {
return fmt.Errorf("can't overwrite the existing peer-group: %s", name)
}
s.logger.Info("Add a peer group configuration",
log.Fields{
"Topic": "Peer",
"Name": name})
s.peerGroupMap[c.Config.PeerGroupName] = newPeerGroup(c)
return nil
}
func (s *BgpServer) addNeighbor(c *oc.Neighbor) error {
addr, err := c.ExtractNeighborAddress()
if err != nil {
return err
}
if _, y := s.neighborMap[addr]; y {
return fmt.Errorf("can't overwrite the existing peer: %s", addr)
}
var pgConf *oc.PeerGroup
if c.Config.PeerGroup != "" {
pg, ok := s.peerGroupMap[c.Config.PeerGroup]
if !ok {
return fmt.Errorf("no such peer-group: %s", c.Config.PeerGroup)
}
pgConf = pg.Conf
}
if err := oc.SetDefaultNeighborConfigValues(c, pgConf, &s.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, _ := oc.AfiSafis(c.AfiSafis).ToRfList()
for _, f := range families {
if f != bgp.RF_IPv4_UC && f != bgp.RF_IPv6_UC && f != bgp.RF_FS_IPv4_UC && f != bgp.RF_FS_IPv6_UC {
return fmt.Errorf("%s is not supported for VRF enslaved neighbor", f)
}
}
_, y := s.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 s.bgpConfig.Global.Config.Port > 0 {
for _, l := range s.listListeners(addr) {
if c.Config.AuthPassword != "" {
if err := setTCPMD5SigSockopt(l, addr, c.Config.AuthPassword); err != nil {
s.logger.Warn("failed to set md5",
log.Fields{
"Topic": "Peer",
"Key": addr,
"Err": err})
}
}
}
}
s.logger.Info("Add a peer configuration",
log.Fields{
"Topic": "Peer",
"Key": addr})
rib := s.globalRib
if c.RouteServer.Config.RouteServerClient {
rib = s.rsRib
}
peer := newPeer(&s.bgpConfig.Global, c, rib, s.policy, s.logger)
s.addIncoming(peer.fsm.incomingCh)
s.policy.SetPeerPolicy(peer.ID(), c.ApplyPolicy)
s.neighborMap[addr] = peer
if name := c.Config.PeerGroup; name != "" {
s.peerGroupMap[name].AddMember(*c)
}
peer.startFSMHandler()
s.broadcastPeerState(peer, bgp.BGP_FSM_IDLE, nil)
return nil
}
func (s *BgpServer) AddPeerGroup(ctx context.Context, r *api.AddPeerGroupRequest) error {
if r == nil || r.PeerGroup == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
c, err := newPeerGroupFromAPIStruct(r.PeerGroup)
if err != nil {
return err
}
return s.addPeerGroup(c)
}, true)
}
func (s *BgpServer) AddPeer(ctx context.Context, r *api.AddPeerRequest) error {
if r == nil || r.Peer == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
c, err := newNeighborFromAPIStruct(r.Peer)
if err != nil {
return err
}
return s.addNeighbor(c)
}, true)
}
func (s *BgpServer) AddDynamicNeighbor(ctx context.Context, r *api.AddDynamicNeighborRequest) error {
if r == nil || r.DynamicNeighbor == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
c := &oc.DynamicNeighbor{Config: oc.DynamicNeighborConfig{
Prefix: r.DynamicNeighbor.Prefix,
PeerGroup: r.DynamicNeighbor.PeerGroup},
}
s.peerGroupMap[c.Config.PeerGroup].AddDynamicNeighbor(c)
pConf := s.peerGroupMap[c.Config.PeerGroup].Conf
if pConf.Config.AuthPassword != "" {
prefix := r.DynamicNeighbor.Prefix
addr, _, _ := net.ParseCIDR(prefix)
for _, l := range s.listListeners(addr.String()) {
if err := setTCPMD5SigSockopt(l, prefix, pConf.Config.AuthPassword); err != nil {
s.logger.Warn("failed to set md5",
log.Fields{
"Topic": "Peer",
"Key": prefix,
"Err": err})
} else {
s.logger.Info("successfully set md5 for dynamic peer",
log.Fields{
"Topic": "Peer",
"Key": prefix,
},
)
}
}
}
return nil
}, true)
}
func (s *BgpServer) deletePeerGroup(name string) error {
if _, y := s.peerGroupMap[name]; !y {
return fmt.Errorf("can't delete a peer-group %s which does not exist", name)
}
s.logger.Info("Delete a peer group configuration",
log.Fields{
"Topic": "Peer",
"Name": name})
delete(s.peerGroupMap, name)
return nil
}
func (s *BgpServer) deleteNeighbor(c *oc.Neighbor, code, subcode uint8) error {
if c.Config.PeerGroup != "" {
_, y := s.peerGroupMap[c.Config.PeerGroup]
if y {
s.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 = oc.GetIPv6LinkLocalNeighborAddress(intf)
if err != nil {
return err
}
}
n, y := s.neighborMap[addr]
if !y {
return fmt.Errorf("can't delete a peer configuration for %s", addr)
}
for _, l := range s.listListeners(addr) {
if c.Config.AuthPassword != "" {
if err := setTCPMD5SigSockopt(l, addr, ""); err != nil {
s.logger.Warn("failed to unset md5",
log.Fields{
"Topic": "Peer",
"Key": addr,
"Err": err})
}
}
}
s.logger.Info("Delete a peer configuration",
log.Fields{
"Topic": "Peer",
"Key": addr})
n.stopPeerRestarting()
n.fsm.notification <- bgp.NewBGPNotificationMessage(code, subcode, nil)
n.fsm.h.ctxCancel()
delete(s.neighborMap, addr)
s.propagateUpdate(n, n.DropAll(n.configuredRFlist()))
return nil
}
func (s *BgpServer) DeletePeerGroup(ctx context.Context, r *api.DeletePeerGroupRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
name := r.Name
for _, n := range s.neighborMap {
n.fsm.lock.RLock()
peerGroup := n.fsm.pConf.Config.PeerGroup
n.fsm.lock.RUnlock()
if peerGroup == name {
return fmt.Errorf("failed to delete peer-group %s: neighbor %s is in use", name, n.ID())
}
}
return s.deletePeerGroup(name)
}, true)
}
func (s *BgpServer) DeletePeer(ctx context.Context, r *api.DeletePeerRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
c := &oc.Neighbor{Config: oc.NeighborConfig{
NeighborAddress: r.Address,
NeighborInterface: r.Interface,
}}
return s.deleteNeighbor(c, bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED)
}, true)
}
func (s *BgpServer) DeleteDynamicNeighbor(ctx context.Context, r *api.DeleteDynamicNeighborRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
s.peerGroupMap[r.PeerGroup].DeleteDynamicNeighbor(r.Prefix)
if pg, ok := s.peerGroupMap[r.PeerGroup]; ok {
pConf := pg.Conf
if pConf.Config.AuthPassword != "" {
prefix := r.Prefix
addr, _, perr := net.ParseCIDR(prefix)
if perr == nil {
for _, l := range s.listListeners(addr.String()) {
if err := setTCPMD5SigSockopt(l, prefix, ""); err != nil {
s.logger.Warn("failed to clear md5",
log.Fields{
"Topic": "Peer",
"Key": prefix,
"Err": err})
}
}
} else {
s.logger.Warn("Cannot clear up dynamic MD5, invalid prefix",
log.Fields{
"Topic": "Peer",
"Key": prefix,
"Err": perr,
})
}
}
}
return nil
}, true)
}
func (s *BgpServer) updatePeerGroup(pg *oc.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(ctx context.Context, r *api.UpdatePeerGroupRequest) (rsp *api.UpdatePeerGroupResponse, err error) {
if r == nil || r.PeerGroup == nil {
return nil, fmt.Errorf("nil request")
}
doSoftreset := false
err = s.mgmtOperation(func() error {
pg, err := newPeerGroupFromAPIStruct(r.PeerGroup)
if err != nil {
return err
}
doSoftreset, err = s.updatePeerGroup(pg)
return err
}, true)
return &api.UpdatePeerGroupResponse{NeedsSoftResetIn: doSoftreset}, err
}
func (s *BgpServer) updateNeighbor(c *oc.Neighbor) (needsSoftResetIn bool, err error) {
var pgConf *oc.PeerGroup
if c.Config.PeerGroup != "" {
if pg, ok := s.peerGroupMap[c.Config.PeerGroup]; ok {
pgConf = pg.Conf
} else {
return needsSoftResetIn, fmt.Errorf("no such peer-group: %s", c.Config.PeerGroup)
}
}
if err := oc.SetDefaultNeighborConfigValues(c, pgConf, &s.bgpConfig.Global); err != nil {
return needsSoftResetIn, err
}
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) {
s.logger.Info("Update ApplyPolicy",
log.Fields{
"Topic": "Peer",
"Key": addr})
s.policy.SetPeerPolicy(peer.ID(), c.ApplyPolicy)
peer.fsm.pConf.ApplyPolicy = c.ApplyPolicy
needsSoftResetIn = true
}
original := peer.fsm.pConf
if !original.AsPathOptions.Config.Equal(&c.AsPathOptions.Config) {
s.logger.Info("Update aspath options",
log.Fields{
"Topic": "Peer",
"Key": peer.ID()})
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"
}
s.logger.Info("Update admin-state configuration",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"State": state})
} 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 {
s.logger.Error("failed to delete neighbor",
log.Fields{
"Topic": "Peer",
"Key": addr,
"Err": err})
return needsSoftResetIn, err
}
err = s.addNeighbor(c)
if err != nil {
s.logger.Error("failed to add neighbor",
log.Fields{
"Topic": "Peer",
"Key": addr,
"Err": err})
}
return needsSoftResetIn, err
}
if !original.Timers.Config.Equal(&c.Timers.Config) {
s.logger.Info("Update timer configuration",
log.Fields{
"Topic": "Peer",
"Key": peer.ID(),
"Err": err})
peer.fsm.pConf.Timers.Config = c.Timers.Config
}
err = peer.updatePrefixLimitConfig(c.AfiSafis)
if err != nil {
s.logger.Error("failed to update prefixLimit",
log.Fields{
"Topic": "Peer",
"Key": addr,
"Err": err})
// rollback to original state
peer.fsm.pConf = original
return needsSoftResetIn, err
}
setTTL := false
if !original.EbgpMultihop.Config.Equal(&c.EbgpMultihop.Config) {
peer.fsm.pConf.EbgpMultihop.Config = c.EbgpMultihop.Config
setTTL = true
}
if !original.TtlSecurity.Config.Equal(&c.TtlSecurity.Config) {
peer.fsm.pConf.TtlSecurity.Config = c.TtlSecurity.Config
setTTL = true
}
if setTTL && peer.fsm.conn != nil {
if err := setPeerConnTTL(peer.fsm); err != nil {
s.logger.Error("failed to set peer connection TTL",
log.Fields{
"Topic": "Peer",
"Key": addr,
"Err": err})
// rollback to original state
peer.fsm.pConf = original
setPeerConnTTL(peer.fsm)
return needsSoftResetIn, err
}
}
return needsSoftResetIn, err
}
func (s *BgpServer) UpdatePeer(ctx context.Context, r *api.UpdatePeerRequest) (rsp *api.UpdatePeerResponse, err error) {
if r == nil || r.Peer == nil {
return nil, fmt.Errorf("nil request")
}
doSoftReset := false
err = s.mgmtOperation(func() error {
c, err := newNeighborFromAPIStruct(r.Peer)
if err != nil {
return err
}
doSoftReset, err = s.updateNeighbor(c)
return err
}, true)
return &api.UpdatePeerResponse{NeedsSoftResetIn: doSoftReset}, 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
}
p, found := s.neighborMap[addr]
if !found {
return l, fmt.Errorf("neighbor that has %v doesn't exist", addr)
}
return []*peer{p}, nil
}
func (s *BgpServer) sendNotification(op, addr string, subcode uint8, data []byte) error {
s.logger.Info("Send operation notification",
log.Fields{
"Topic": "Operation",
"Key": addr,
"Op": 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) ShutdownPeer(ctx context.Context, r *api.ShutdownPeerRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.sendNotification("Neighbor shutdown", r.Address, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, newAdministrativeCommunication(r.Communication))
}, true)
}
func (s *BgpServer) ResetPeer(ctx context.Context, r *api.ResetPeerRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
addr := r.Address
comm := r.Communication
if r.Soft {
var err error
if addr == "all" {
addr = ""
}
family := bgp.RouteFamily(0)
switch r.Direction {
case api.ResetPeerRequest_IN:
err = s.sResetIn(addr, family)
case api.ResetPeerRequest_OUT:
err = s.sResetOut(addr, family)
case api.ResetPeerRequest_BOTH:
err = s.sReset(addr, family)
default:
err = fmt.Errorf("unknown direction")
}
return err
}
err := s.sendNotification("Neighbor reset", addr, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, newAdministrativeCommunication(comm))
if err != nil {
return err
}
peers, _ := s.addrToPeers(addr)
for _, peer := range peers {
peer.fsm.lock.Lock()
peer.fsm.idleHoldTime = peer.fsm.pConf.Timers.Config.IdleHoldTimeAfterReset
peer.fsm.lock.Unlock()
}
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:
peer.fsm.lock.RLock()
s.logger.Debug("set admin state",
log.Fields{
"Topic": "Peer",
"Key": peer.fsm.pConf.State.NeighborAddress,
"Message": message})
peer.fsm.lock.RUnlock()
default:
peer.fsm.lock.RLock()
s.logger.Warn("previous setting admin state request is still remaining",
log.Fields{
"Topic": "Peer",
"Key": peer.fsm.pConf.State.NeighborAddress})
peer.fsm.lock.RUnlock()
}
}
if enable {
f(&adminStateOperation{adminStateUp, nil}, "adminStateUp requested")
} else {
f(&adminStateOperation{adminStateDown, newAdministrativeCommunication(communication)}, "adminStateDown requested")
}
}
return nil
}
func (s *BgpServer) EnablePeer(ctx context.Context, r *api.EnablePeerRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.setAdminState(r.Address, "", true)
}, true)
}
func (s *BgpServer) DisablePeer(ctx context.Context, r *api.DisablePeerRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.setAdminState(r.Address, r.Communication, false)
}, true)
}
func (s *BgpServer) ListDefinedSet(ctx context.Context, r *api.ListDefinedSetRequest, fn func(*api.DefinedSet)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var cd *oc.DefinedSets
var err error
err = s.mgmtOperation(func() error {
cd, err = s.policy.GetDefinedSet(table.DefinedType(r.DefinedType), r.Name)
return err
}, false)
if err != nil {
return err
}
exec := func(d *api.DefinedSet) bool {
select {
case <-ctx.Done():
return true
default:
fn(d)
}
return false
}
for _, cs := range cd.PrefixSets {
ad := &api.DefinedSet{
DefinedType: api.DefinedType_PREFIX,
Name: cs.PrefixSetName,
Prefixes: func() []*api.Prefix {
l := make([]*api.Prefix, 0, len(cs.PrefixList))
for _, p := range cs.PrefixList {
elems := _regexpPrefixMaskLengthRange.FindStringSubmatch(p.MasklengthRange)
min, _ := strconv.ParseUint(elems[1], 10, 32)
max, _ := strconv.ParseUint(elems[2], 10, 32)
l = append(l, &api.Prefix{IpPrefix: p.IpPrefix, MaskLengthMin: uint32(min), MaskLengthMax: uint32(max)})
}
return l
}(),
}
if exec(ad) {
return nil
}
}
for _, cs := range cd.NeighborSets {
ad := &api.DefinedSet{
DefinedType: api.DefinedType_NEIGHBOR,
Name: cs.NeighborSetName,
List: cs.NeighborInfoList,
}
if exec(ad) {
return nil
}
}
for _, cs := range cd.BgpDefinedSets.CommunitySets {
ad := &api.DefinedSet{
DefinedType: api.DefinedType_COMMUNITY,
Name: cs.CommunitySetName,
List: cs.CommunityList,
}
if exec(ad) {
return nil
}
}
for _, cs := range cd.BgpDefinedSets.ExtCommunitySets {
ad := &api.DefinedSet{
DefinedType: api.DefinedType_EXT_COMMUNITY,
Name: cs.ExtCommunitySetName,
List: cs.ExtCommunityList,
}
if exec(ad) {
return nil
}
}
for _, cs := range cd.BgpDefinedSets.LargeCommunitySets {
ad := &api.DefinedSet{
DefinedType: api.DefinedType_LARGE_COMMUNITY,
Name: cs.LargeCommunitySetName,
List: cs.LargeCommunityList,
}
if exec(ad) {
return nil
}
}
for _, cs := range cd.BgpDefinedSets.AsPathSets {
ad := &api.DefinedSet{
DefinedType: api.DefinedType_AS_PATH,
Name: cs.AsPathSetName,
List: cs.AsPathList,
}
if exec(ad) {
return nil
}
}
return nil
}
func (s *BgpServer) AddDefinedSet(ctx context.Context, r *api.AddDefinedSetRequest) error {
if r == nil || r.DefinedSet == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
set, err := newDefinedSetFromApiStruct(r.DefinedSet)
if err != nil {
return err
}
return s.policy.AddDefinedSet(set, r.GetReplace())
}, false)
}
func (s *BgpServer) DeleteDefinedSet(ctx context.Context, r *api.DeleteDefinedSetRequest) error {
if r == nil || r.DefinedSet == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
set, err := newDefinedSetFromApiStruct(r.DefinedSet)
if err != nil {
return err
}
return s.policy.DeleteDefinedSet(set, r.All)
}, false)
}
func (s *BgpServer) ListStatement(ctx context.Context, r *api.ListStatementRequest, fn func(*api.Statement)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var l []*api.Statement
s.mgmtOperation(func() error {
s := s.policy.GetStatement(r.Name)
l = make([]*api.Statement, 0, len(s))
for _, st := range s {
l = append(l, toStatementApi(st))
}
return nil
}, false)
for _, s := range l {
select {
case <-ctx.Done():
return nil
default:
fn(s)
}
}
return nil
}
func (s *BgpServer) AddStatement(ctx context.Context, r *api.AddStatementRequest) error {
if r == nil || r.Statement == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
st, err := newStatementFromApiStruct(r.Statement)
if err != nil {
return err
}
return s.policy.AddStatement(st)
}, false)
}
func (s *BgpServer) DeleteStatement(ctx context.Context, r *api.DeleteStatementRequest) error {
if r == nil || r.Statement == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
st, err := newStatementFromApiStruct(r.Statement)
if err == nil {
err = s.policy.DeleteStatement(st, r.All)
}
return err
}, false)
}
func (s *BgpServer) ListPolicy(ctx context.Context, r *api.ListPolicyRequest, fn func(*api.Policy)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var l []*api.Policy
s.mgmtOperation(func() error {
pl := s.policy.GetPolicy(r.Name)
l = make([]*api.Policy, 0, len(pl))
for _, p := range pl {
l = append(l, table.ToPolicyApi(p))
}
return nil
}, false)
for _, p := range l {
select {
case <-ctx.Done():
return nil
default:
fn(p)
}
}
return nil
}
func (s *BgpServer) AddPolicy(ctx context.Context, r *api.AddPolicyRequest) error {
if r == nil || r.Policy == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
p, err := newPolicyFromApiStruct(r.Policy)
if err == nil {
err = s.policy.AddPolicy(p, r.ReferExistingStatements)
}
return err
}, false)
}
func (s *BgpServer) DeletePolicy(ctx context.Context, r *api.DeletePolicyRequest) error {
if r == nil || r.Policy == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
p, err := newPolicyFromApiStruct(r.Policy)
if err != nil {
return err
}
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(p, r.All, r.PreserveStatements, l)
}, false)
}
func (s *BgpServer) toPolicyInfo(name string, dir api.PolicyDirection) (string, table.PolicyDirection, error) {
if name == "" {
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("empty table name")
}
if name == table.GLOBAL_RIB_NAME {
name = table.GLOBAL_RIB_NAME
} else {
peer, ok := s.neighborMap[name]
if !ok {
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("not found peer %s", name)
}
if !peer.isRouteServerClient() {
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("non-rs-client peer %s doesn't have per peer policy", name)
}
name = peer.ID()
}
switch dir {
case api.PolicyDirection_IMPORT:
return name, table.POLICY_DIRECTION_IMPORT, nil
case api.PolicyDirection_EXPORT:
return name, table.POLICY_DIRECTION_EXPORT, nil
}
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid policy type")
}
func (s *BgpServer) ListPolicyAssignment(ctx context.Context, r *api.ListPolicyAssignmentRequest, fn func(*api.PolicyAssignment)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var a []*api.PolicyAssignment
err := s.mgmtOperation(func() error {
names := make([]string, 0, len(s.neighborMap)+1)
if r.Name == "" {
names = append(names, table.GLOBAL_RIB_NAME)
for name, peer := range s.neighborMap {
if peer.isRouteServerClient() {
names = append(names, name)
}
}
} else {
names = append(names, r.Name)
}
dirs := make([]api.PolicyDirection, 0, 2)
if r.Direction == api.PolicyDirection_UNKNOWN {
dirs = []api.PolicyDirection{api.PolicyDirection_EXPORT, api.PolicyDirection_IMPORT}
} else {
dirs = append(dirs, r.Direction)
}
a = make([]*api.PolicyAssignment, 0, len(names))
for _, name := range names {
for _, dir := range dirs {
id, dir, err := s.toPolicyInfo(name, dir)
if err != nil {
return err
}
rt, policies, err := s.policy.GetPolicyAssignment(id, dir)
if err != nil {
return err
}
t := &table.PolicyAssignment{
Name: name,
Type: dir,
Default: rt,
Policies: policies,
}
a = append(a, table.NewAPIPolicyAssignmentFromTableStruct(t))
}
}
return nil
}, false)
if err == nil {
for _, p := range a {
select {
case <-ctx.Done():
return nil
default:
fn(p)
}
}
}
return err
}
func (s *BgpServer) AddPolicyAssignment(ctx context.Context, r *api.AddPolicyAssignmentRequest) error {
if r == nil || r.Assignment == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
id, dir, err := s.toPolicyInfo(r.Assignment.Name, r.Assignment.Direction)
if err != nil {
return err
}
return s.policy.AddPolicyAssignment(id, dir, toPolicyDefinition(r.Assignment.Policies), defaultRouteType(r.Assignment.DefaultAction))
}, false)
}
func (s *BgpServer) DeletePolicyAssignment(ctx context.Context, r *api.DeletePolicyAssignmentRequest) error {
if r == nil || r.Assignment == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
id, dir, err := s.toPolicyInfo(r.Assignment.Name, r.Assignment.Direction)
if err != nil {
return err
}
return s.policy.DeletePolicyAssignment(id, dir, toPolicyDefinition(r.Assignment.Policies), r.All)
}, false)
}
func (s *BgpServer) SetPolicyAssignment(ctx context.Context, r *api.SetPolicyAssignmentRequest) error {
if r == nil || r.Assignment == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
id, dir, err := s.toPolicyInfo(r.Assignment.Name, r.Assignment.Direction)
if err != nil {
return err
}
return s.policy.SetPolicyAssignment(id, dir, toPolicyDefinition(r.Assignment.Policies), defaultRouteType(r.Assignment.DefaultAction))
}, false)
}
func (s *BgpServer) EnableMrt(ctx context.Context, r *api.EnableMrtRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.mrtManager.enable(&oc.MrtConfig{
DumpInterval: r.DumpInterval,
RotationInterval: r.RotationInterval,
DumpType: oc.IntToMrtTypeMap[int(r.Type)],
FileName: r.Filename,
})
}, false)
}
func (s *BgpServer) DisableMrt(ctx context.Context, r *api.DisableMrtRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.mrtManager.disable(&oc.MrtConfig{})
}, false)
}
func (s *BgpServer) ListRpki(ctx context.Context, r *api.ListRpkiRequest, fn func(*api.Rpki)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var l []*api.Rpki
err := s.mgmtOperation(func() error {
for _, r := range s.roaManager.GetServers() {
received := &r.State.RpkiMessages.RpkiReceived
sent := &r.State.RpkiMessages.RpkiSent
rpki := &api.Rpki{
Conf: &api.RPKIConf{
Address: r.Config.Address,
RemotePort: uint32(r.Config.Port),
},
State: &api.RPKIState{
Uptime: oc.ProtoTimestamp(r.State.Uptime),
Downtime: oc.ProtoTimestamp(r.State.Downtime),
Up: r.State.Up,
RecordIpv4: r.State.RecordsV4,
RecordIpv6: r.State.RecordsV6,
PrefixIpv4: r.State.PrefixesV4,
PrefixIpv6: r.State.PrefixesV6,
Serial: r.State.SerialNumber,
ReceivedIpv4: received.Ipv4Prefix,
ReceivedIpv6: received.Ipv6Prefix,
SerialNotify: received.SerialNotify,
CacheReset: received.CacheReset,
CacheResponse: received.CacheResponse,
EndOfData: received.EndOfData,
Error: received.Error,
SerialQuery: sent.SerialQuery,
ResetQuery: sent.ResetQuery,
},
}
l = append(l, rpki)
}
return nil
}, false)
if err == nil {
for _, r := range l {
select {
case <-ctx.Done():
return nil
default:
fn(r)
}
}
}
return err
}
func (s *BgpServer) ListRpkiTable(ctx context.Context, r *api.ListRpkiTableRequest, fn func(*api.Roa)) error {
if r == nil {
return fmt.Errorf("nil request")
}
var l []*api.Roa
err := s.mgmtOperation(func() error {
family := bgp.RouteFamily(0)
if r.Family != nil {
family = bgp.AfiSafiToRouteFamily(uint16(r.Family.Afi), uint8(r.Family.Safi))
}
roas, err := s.roaTable.List(family)
if err == nil {
l = append(l, newRoaListFromTableStructList(roas)...)
}
return err
}, false)
if err == nil {
for _, roa := range l {
select {
case <-ctx.Done():
return nil
default:
fn(roa)
}
}
}
return err
}
func (s *BgpServer) AddRpki(ctx context.Context, r *api.AddRpkiRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.roaManager.AddServer(net.JoinHostPort(r.Address, strconv.Itoa(int(r.Port))), r.Lifetime)
}, false)
}
func (s *BgpServer) DeleteRpki(ctx context.Context, r *api.DeleteRpkiRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.roaManager.DeleteServer(r.Address)
}, false)
}
func (s *BgpServer) EnableRpki(ctx context.Context, r *api.EnableRpkiRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.roaManager.Enable(r.Address)
}, false)
}
func (s *BgpServer) DisableRpki(ctx context.Context, r *api.DisableRpkiRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
return s.roaManager.Disable(r.Address)
}, false)
}
func (s *BgpServer) ResetRpki(ctx context.Context, r *api.ResetRpkiRequest) error {
if r == nil {
return fmt.Errorf("nil request")
}
return s.mgmtOperation(func() error {
if r.Soft {
return s.roaManager.SoftReset(r.Address)
}
return s.roaManager.Reset(r.Address)
}, false)
}
func (s *BgpServer) WatchEvent(ctx context.Context, r *api.WatchEventRequest, fn func(*api.WatchEventResponse)) error {
if r == nil {
return fmt.Errorf("nil request")
}
opts := make([]watchOption, 0)
if r.GetPeer() != nil {
opts = append(opts, watchPeer())
}
if t := r.GetTable(); t != nil {
for _, filter := range t.Filters {
switch filter.Type {
case api.WatchEventRequest_Table_Filter_BEST:
opts = append(opts, watchBestPath(filter.Init))
case api.WatchEventRequest_Table_Filter_ADJIN:
opts = append(opts, watchUpdate(filter.Init, filter.PeerAddress, filter.PeerGroup))
case api.WatchEventRequest_Table_Filter_POST_POLICY:
opts = append(opts, watchPostUpdate(filter.Init, filter.PeerAddress, filter.PeerGroup))
case api.WatchEventRequest_Table_Filter_EOR:
opts = append(opts, watchEor(filter.Init))
}
}
}
if len(opts) == 0 {
return fmt.Errorf("no events to watch")
}
w := s.watch(opts...)
go func() {
defer func() {
w.Stop()
}()
for {
select {
case ev := <-w.Event():
switch msg := ev.(type) {
case *watchEventUpdate:
paths := make([]*api.Path, 0)
for _, path := range msg.PathList {
paths = append(paths, toPathApi(path, nil, false, false, false))
}
fn(&api.WatchEventResponse{
Event: &api.WatchEventResponse_Table{
Table: &api.WatchEventResponse_TableEvent{
Paths: paths,
},
},
})
case *watchEventBestPath:
var pl []*api.Path
if len(msg.MultiPathList) > 0 {
l := make([]*table.Path, 0)
for _, p := range msg.MultiPathList {
l = append(l, p...)
}
for _, p := range l {
pl = append(pl, toPathApi(p, nil, false, false, false))
}
} else {
for _, p := range msg.PathList {
pl = append(pl, toPathApi(p, nil, false, false, false))
}
}
fn(&api.WatchEventResponse{
Event: &api.WatchEventResponse_Table{
Table: &api.WatchEventResponse_TableEvent{
Paths: pl,
},
},
})
case *watchEventEor:
eor := table.NewEOR(msg.Family)
eor.SetSource(msg.PeerInfo)
path := eorToPathAPI(eor)
fn(&api.WatchEventResponse{
Event: &api.WatchEventResponse_Table{
Table: &api.WatchEventResponse_TableEvent{
Paths: []*api.Path{path},
},
},
})
case *watchEventPeer:
fn(&api.WatchEventResponse{
Event: &api.WatchEventResponse_Peer{
Peer: &api.WatchEventResponse_PeerEvent{
Type: api.WatchEventResponse_PeerEvent_Type(msg.Type),
Peer: &api.Peer{
Conf: &api.PeerConf{
PeerAsn: msg.PeerAS,
LocalAsn: msg.LocalAS,
NeighborAddress: msg.PeerAddress.String(),
NeighborInterface: msg.PeerInterface,
},
State: &api.PeerState{
PeerAsn: msg.PeerAS,
LocalAsn: msg.LocalAS,
NeighborAddress: msg.PeerAddress.String(),
SessionState: api.PeerState_SessionState(int(msg.State) + 1),
AdminState: api.PeerState_AdminState(msg.AdminState),
RouterId: msg.PeerID.String(),
},
Transport: &api.Transport{
LocalAddress: msg.LocalAddress.String(),
LocalPort: uint32(msg.LocalPort),
RemotePort: uint32(msg.PeerPort),
},
},
},
},
})
}
case <-ctx.Done():
return
}
}
}()
return nil
}
func (s *BgpServer) SetLogLevel(ctx context.Context, r *api.SetLogLevelRequest) error {
oldLevel := uint32(s.logger.GetLevel())
newLevel := uint32(r.Level)
if oldLevel == newLevel {
s.logger.Info("Logging level unchanged",
log.Fields{
"Topic": "Config",
"OldLevel": oldLevel})
} else {
s.logger.SetLevel(log.LogLevel(newLevel))
s.logger.Info("Logging level changed",
log.Fields{
"Topic": "Config",
"OldLevel": oldLevel,
"NewLevel": newLevel})
}
return nil
}
func (s *BgpServer) Log() log.Logger {
return s.logger
}
type watchEventType string
const (
watchEventTypeBestPath watchEventType = "bestpath"
watchEventTypePreUpdate watchEventType = "preupdate"
watchEventTypePostUpdate watchEventType = "postupdate"
watchEventTypePeerState watchEventType = "peerstate"
watchEventTypeTable watchEventType = "table"
watchEventTypeRecvMsg watchEventType = "receivedmessage"
watchEventTypeEor watchEventType = "eor"
)
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 *oc.Neighbor
}
type PeerEventType uint32
const (
PEER_EVENT_UNKNOWN PeerEventType = 0
PEER_EVENT_INIT PeerEventType = 1
PEER_EVENT_END_OF_INIT PeerEventType = 2
PEER_EVENT_STATE PeerEventType = 3
)
type watchEventPeer struct {
Type PeerEventType
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
OldState 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 []*oc.Neighbor
}
type watchEventBestPath struct {
PathList []*table.Path
MultiPathList [][]*table.Path
Vrf map[uint32]bool
}
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 watchEventEor struct {
Family bgp.RouteFamily
PeerInfo *table.PeerInfo
}
type watchOptions struct {
bestPath bool
preUpdate bool
preUpdateFilter func(w watchEvent) bool
postUpdate bool
postUpdateFilter func(w watchEvent) bool
peerState bool
initBest bool
initUpdate bool
initPostUpdate bool
tableName string
recvMessage bool
initEor bool
eor 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, peerAddress string, peerGroup string) watchOption {
return func(o *watchOptions) {
o.preUpdate = true
if current {
o.initUpdate = true
}
if peerAddress != "" || peerGroup != "" {
o.preUpdateFilter = func(w watchEvent) bool {
ev, ok := w.(*watchEventUpdate)
if !ok || ev == nil {
return false
}
if len(peerAddress) > 0 && ev.Neighbor.State.NeighborAddress == peerAddress {
return true
}
if len(peerGroup) > 0 && ev.Neighbor.State.PeerGroup == peerGroup {
return true
}
return false
}
}
}
}
func watchPostUpdate(current bool, peerAddress string, peerGroup string) watchOption {
return func(o *watchOptions) {
o.postUpdate = true
if current {
o.initPostUpdate = true
}
if peerAddress != "" || peerGroup != "" {
o.postUpdateFilter = func(w watchEvent) bool {
ev, ok := w.(*watchEventUpdate)
if !ok || ev == nil {
return false
}
if len(peerAddress) > 0 && ev.Neighbor.State.NeighborAddress == peerAddress {
return true
}
if len(peerGroup) > 0 && ev.Neighbor.State.PeerGroup == peerGroup {
return true
}
return false
}
}
}
}
func watchEor(current bool) watchOption {
return func(o *watchOptions) {
o.eor = true
if current {
o.initEor = true
}
}
}
func watchPeer() watchOption {
return func(o *watchOptions) {
o.peerState = 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
// filters are used for notifyWatcher by using the filter for the given watchEvent,
// call notify method for skipping filtering.
filters map[watchEventType]func(w watchEvent) bool
}
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 watchEventTypePreUpdate:
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 watchEventTypeTable:
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([]*oc.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() {
if ev == nil {
break
}
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
}
// notifyWatcher notifies all watchers of the watchEventType about the event.
// If the filter is set(and not nil) for the watchEventType, it will be used for filtering.
// Otherwise, all events will be processed without any filtering.
func (s *BgpServer) notifyWatcher(typ watchEventType, ev watchEvent) {
for _, w := range s.watcherMap[typ] {
if f := w.filters[typ]; f != nil && !f(ev) {
// Filter is set and the event doesn't pass it.
continue
}
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(),
filters: make(map[watchEventType]func(w watchEvent) bool),
}
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(watchEventTypeBestPath, w)
}
if w.opts.preUpdate {
if w.opts.preUpdateFilter != nil {
w.filters[watchEventTypePreUpdate] = w.opts.preUpdateFilter
}
register(watchEventTypePreUpdate, w)
}
if w.opts.postUpdate {
if w.opts.postUpdateFilter != nil {
w.filters[watchEventTypePostUpdate] = w.opts.postUpdateFilter
}
register(watchEventTypePostUpdate, w)
}
if w.opts.eor {
register(watchEventTypeEor, w)
}
if w.opts.peerState {
for _, p := range s.neighborMap {
w.notify(newWatchEventPeer(p, nil, p.fsm.state, PEER_EVENT_INIT))
}
w.notify(&watchEventPeer{Type: PEER_EVENT_END_OF_INIT})
register(watchEventTypePeerState, w)
}
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.initEor && s.active() == nil {
for _, p := range s.neighborMap {
func() {
p.fsm.lock.RLock()
defer p.fsm.lock.RUnlock()
for _, a := range p.fsm.pConf.AfiSafis {
if s := a.MpGracefulRestart.State; s.EndOfRibReceived {
family := a.State.Family
peerInfo := &table.PeerInfo{
AS: p.fsm.peerInfo.AS,
ID: p.fsm.peerInfo.ID,
LocalAS: p.fsm.peerInfo.LocalAS,
LocalID: p.fsm.peerInfo.LocalID,
Address: p.fsm.peerInfo.Address,
LocalAddress: p.fsm.peerInfo.LocalAddress,
}
w.notify(&watchEventEor{
Family: family,
PeerInfo: peerInfo,
})
}
}
}()
}
}
if w.opts.initUpdate {
for _, peer := range s.neighborMap {
peer.fsm.lock.RLock()
notEstablished := peer.fsm.state != bgp.BGP_FSM_ESTABLISHED
peer.fsm.lock.RUnlock()
if notEstablished {
continue
}
configNeighbor := w.s.toConfig(peer, false)
if w.opts.preUpdateFilter != nil {
ev := &watchEventUpdate{ // use fake event to check filter.
Neighbor: configNeighbor,
}
if !w.opts.preUpdateFilter(ev) {
continue
}
}
for _, rf := range peer.configuredRFlist() {
peer.fsm.lock.RLock()
_, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
l, _ := peer.fsm.LocalHostPort()
update := &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),
}
peer.fsm.lock.RUnlock()
w.notify(update)
eor := bgp.NewEndOfRib(rf)
eorBuf, _ := eor.Serialize()
peer.fsm.lock.RLock()
update = &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,
}
peer.fsm.lock.RUnlock()
w.notify(update)
}
}
}
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 *oc.Neighbor
peerAddress := peerInfo.Address.String()
if peer, ok := s.neighborMap[peerAddress]; ok {
configNeighbor = w.s.toConfig(peer, false)
}
ev := &watchEventUpdate{
PeerAS: peerInfo.AS,
PeerAddress: peerInfo.Address,
PeerID: peerInfo.ID,
PostPolicy: true,
Neighbor: configNeighbor,
PathList: paths,
Init: true,
}
if w.opts.postUpdateFilter != nil && !w.opts.postUpdateFilter(ev) {
continue
}
w.notify(ev)
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(watchEventTypeRecvMsg, w)
}
go w.loop()
return nil
}, false)
return w
}