Files
osrg-gobgp/server/server.go
T
IWASE Yusuke b2e07101e9 server: Fix outgoing rtfilter
Currently, the result of Route Target Constraint filter is unexpectedly
ignored when Route Reflector reflecting the VPN routes because "ignore"
flag can be overwritten without evaluated.

This patch fixes to return "nil" before "ignore" flag overwritten and
enable to filter outgoing VPN routes based on RTC.

Signed-off-by: IWASE Yusuke <[email protected]>
2018-04-18 09:18:13 +09:00

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
}