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