mirror of
https://github.com/osrg/gobgp.git
synced 2024-05-11 05:55:10 +00:00
Changing some of configuration leads to sending a notification. So the reason of the BGP state change should be the notification. The main goroutine sends a notification, closes the connection, kill goroutines for the peer. So With the current code, the reason could be "rx failure", "tx failure", or "dying". To avoid that, this patch adds kinda workaround. Signed-off-by: FUJITA Tomonori <[email protected]>
3253 lines
94 KiB
Go
3253 lines
94 KiB
Go
// Copyright (C) 2014 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"
|
|
"sync"
|
|
"time"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/armon/go-radix"
|
|
"github.com/eapache/channels"
|
|
api "github.com/osrg/gobgp/api"
|
|
"github.com/osrg/gobgp/config"
|
|
"github.com/osrg/gobgp/packet/bgp"
|
|
"github.com/osrg/gobgp/packet/bmp"
|
|
"github.com/osrg/gobgp/table"
|
|
"github.com/osrg/gobgp/zebra"
|
|
"github.com/satori/go.uuid"
|
|
)
|
|
|
|
var policyMutex sync.RWMutex
|
|
|
|
type SenderMsg struct {
|
|
ch chan *FsmOutgoingMsg
|
|
msg *FsmOutgoingMsg
|
|
}
|
|
|
|
type broadcastMsg interface {
|
|
send()
|
|
}
|
|
|
|
type broadcastGrpcMsg struct {
|
|
req *GrpcRequest
|
|
result *GrpcResponse
|
|
done bool
|
|
}
|
|
|
|
func (m *broadcastGrpcMsg) send() {
|
|
m.req.ResponseCh <- m.result
|
|
if m.done == true {
|
|
close(m.req.ResponseCh)
|
|
}
|
|
}
|
|
|
|
type broadcastBGPMsg struct {
|
|
message *bgp.BGPMessage
|
|
peerAS uint32
|
|
localAS uint32
|
|
peerAddress net.IP
|
|
localAddress net.IP
|
|
fourBytesAs bool
|
|
ch chan *broadcastBGPMsg
|
|
}
|
|
|
|
func (m *broadcastBGPMsg) send() {
|
|
m.ch <- m
|
|
}
|
|
|
|
type Watchers map[watcherType]watcher
|
|
|
|
func (ws Watchers) watching(typ watcherEventType) bool {
|
|
for _, w := range ws {
|
|
for _, ev := range w.watchingEventTypes() {
|
|
if ev == typ {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
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
|
|
}
|
|
closeCh := make(chan struct{})
|
|
go func() error {
|
|
for {
|
|
conn, err := l.AcceptTCP()
|
|
if err != nil {
|
|
close(closeCh)
|
|
log.Warn(err)
|
|
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
|
|
zapiMsgCh chan *zebra.Message
|
|
|
|
GrpcReqCh chan *GrpcRequest
|
|
policy *table.RoutingPolicy
|
|
broadcastReqs []*GrpcRequest
|
|
broadcastMsgs []broadcastMsg
|
|
listeners []*TCPListener
|
|
neighborMap map[string]*Peer
|
|
globalRib *table.TableManager
|
|
zclient *zebra.Client
|
|
roaManager *roaManager
|
|
shutdown bool
|
|
watchers Watchers
|
|
}
|
|
|
|
func NewBgpServer() *BgpServer {
|
|
roaManager, _ := NewROAManager(0)
|
|
return &BgpServer{
|
|
GrpcReqCh: make(chan *GrpcRequest, 1),
|
|
neighborMap: make(map[string]*Peer),
|
|
watchers: Watchers(make(map[watcherType]watcher)),
|
|
policy: table.NewRoutingPolicy(),
|
|
roaManager: roaManager,
|
|
}
|
|
}
|
|
|
|
func (server *BgpServer) notify2watchers(typ watcherEventType, ev watcherEvent) error {
|
|
for _, watcher := range server.watchers {
|
|
if ch := watcher.notify(typ); ch != nil {
|
|
server.broadcastMsgs = append(server.broadcastMsgs, &broadcastWatcherMsg{
|
|
ch: ch,
|
|
event: ev,
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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 (server *BgpServer) Serve() {
|
|
w, _ := newGrpcIncomingWatcher()
|
|
server.watchers[WATCHER_GRPC_INCOMING] = w
|
|
|
|
senderCh := make(chan *SenderMsg, 1<<16)
|
|
go func(ch chan *SenderMsg) {
|
|
w := func(c chan *FsmOutgoingMsg, msg *FsmOutgoingMsg) {
|
|
// nasty but the peer could already become non established state before here.
|
|
defer func() { recover() }()
|
|
c <- msg
|
|
}
|
|
|
|
for m := range ch {
|
|
// TODO: must be more clever. Slow peer makes other peers slow too.
|
|
w(m.ch, m.msg)
|
|
}
|
|
|
|
}(senderCh)
|
|
|
|
broadcastCh := make(chan broadcastMsg, 8)
|
|
go func(ch chan broadcastMsg) {
|
|
for {
|
|
m := <-ch
|
|
m.send()
|
|
}
|
|
}(broadcastCh)
|
|
|
|
server.listeners = make([]*TCPListener, 0, 2)
|
|
server.fsmincomingCh = channels.NewInfiniteChannel()
|
|
server.fsmStateCh = make(chan *FsmMsg, 4096)
|
|
var senderMsgs []*SenderMsg
|
|
|
|
handleFsmMsg := func(e *FsmMsg) {
|
|
peer, found := server.neighborMap[e.MsgSrc]
|
|
if !found {
|
|
log.Warn("Can't find the neighbor ", e.MsgSrc)
|
|
return
|
|
}
|
|
if e.Version != peer.fsm.version {
|
|
log.Debug("FSM Version inconsistent")
|
|
return
|
|
}
|
|
m := server.handleFSMMessage(peer, e)
|
|
if len(m) > 0 {
|
|
senderMsgs = append(senderMsgs, m...)
|
|
}
|
|
}
|
|
|
|
for {
|
|
var firstMsg *SenderMsg
|
|
var sCh chan *SenderMsg
|
|
if len(senderMsgs) > 0 {
|
|
sCh = senderCh
|
|
firstMsg = senderMsgs[0]
|
|
}
|
|
var firstBroadcastMsg broadcastMsg
|
|
var bCh chan broadcastMsg
|
|
if len(server.broadcastMsgs) > 0 {
|
|
bCh = broadcastCh
|
|
firstBroadcastMsg = server.broadcastMsgs[0]
|
|
}
|
|
|
|
passConn := func(conn *net.TCPConn) {
|
|
remoteAddr, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
peer, found := server.neighborMap[remoteAddr]
|
|
if found {
|
|
if peer.fsm.adminState != ADMIN_STATE_UP {
|
|
log.Debug("new connection for non admin-state-up peer ", remoteAddr, peer.fsm.adminState)
|
|
conn.Close()
|
|
return
|
|
}
|
|
localAddrValid := func(laddr net.IP) bool {
|
|
if laddr == nil {
|
|
return true
|
|
}
|
|
l := conn.LocalAddr()
|
|
if l == nil {
|
|
// already closed
|
|
return false
|
|
}
|
|
|
|
host, _, _ := net.SplitHostPort(l.String())
|
|
if host != laddr.String() {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": remoteAddr,
|
|
"Configured addr": laddr.String(),
|
|
"Addr": host,
|
|
}).Info("Mismatched local address")
|
|
return false
|
|
}
|
|
return true
|
|
}(net.ParseIP(peer.fsm.pConf.Transport.Config.LocalAddress))
|
|
if localAddrValid == false {
|
|
conn.Close()
|
|
return
|
|
}
|
|
log.Debug("accepted a new passive connection from ", remoteAddr)
|
|
peer.PassConn(conn)
|
|
} else {
|
|
log.Info("can't find configuration for a new passive connection from ", remoteAddr)
|
|
conn.Close()
|
|
}
|
|
}
|
|
|
|
select {
|
|
case grpcReq := <-server.GrpcReqCh:
|
|
m := server.handleGrpc(grpcReq)
|
|
if len(m) > 0 {
|
|
senderMsgs = append(senderMsgs, m...)
|
|
}
|
|
case conn := <-server.acceptCh:
|
|
passConn(conn)
|
|
default:
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case e := <-server.fsmStateCh:
|
|
handleFsmMsg(e)
|
|
default:
|
|
goto CONT
|
|
}
|
|
}
|
|
CONT:
|
|
|
|
select {
|
|
case rmsg := <-server.roaManager.ReceiveROA():
|
|
server.roaManager.HandleROAEvent(rmsg)
|
|
case zmsg := <-server.zapiMsgCh:
|
|
m := handleZapiMsg(zmsg, server)
|
|
if len(m) > 0 {
|
|
senderMsgs = append(senderMsgs, m...)
|
|
}
|
|
case conn := <-server.acceptCh:
|
|
passConn(conn)
|
|
case e, ok := <-server.fsmincomingCh.Out():
|
|
if !ok {
|
|
continue
|
|
}
|
|
handleFsmMsg(e.(*FsmMsg))
|
|
case e := <-server.fsmStateCh:
|
|
handleFsmMsg(e)
|
|
case sCh <- firstMsg:
|
|
senderMsgs = senderMsgs[1:]
|
|
case bCh <- firstBroadcastMsg:
|
|
server.broadcastMsgs = server.broadcastMsgs[1:]
|
|
case grpcReq := <-server.GrpcReqCh:
|
|
m := server.handleGrpc(grpcReq)
|
|
if len(m) > 0 {
|
|
senderMsgs = append(senderMsgs, m...)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func newSenderMsg(peer *Peer, paths []*table.Path, notification *bgp.BGPMessage, stayIdle bool) *SenderMsg {
|
|
return &SenderMsg{
|
|
ch: peer.outgoing,
|
|
msg: &FsmOutgoingMsg{
|
|
Paths: paths,
|
|
Notification: notification,
|
|
StayIdle: stayIdle,
|
|
},
|
|
}
|
|
}
|
|
|
|
func isASLoop(peer *Peer, path *table.Path) bool {
|
|
for _, as := range path.GetAsList() {
|
|
if as == peer.fsm.pConf.Config.PeerAs {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func filterpath(peer *Peer, path *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 peer.fsm.rfMap[bgp.RF_RTC_UC] && path.GetRouteFamily() != bgp.RF_RTC_UC {
|
|
ignore = true
|
|
for _, ext := range path.GetExtCommunities() {
|
|
for _, path := range peer.adjRibIn.PathList([]bgp.RouteFamily{bgp.RF_RTC_UC}, true) {
|
|
rt := path.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget
|
|
if ext.String() == rt.String() {
|
|
ignore = false
|
|
break
|
|
}
|
|
}
|
|
if !ignore {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if !path.IsLocal() {
|
|
ignore = true
|
|
info := path.GetSource()
|
|
//if the path comes from eBGP peer
|
|
if info.AS != peer.fsm.pConf.Config.PeerAs {
|
|
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 {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.ID(),
|
|
"Data": path,
|
|
}).Debug("From same AS, ignore.")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if peer.ID() == path.GetSource().Address.String() {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.ID(),
|
|
"Data": path,
|
|
}).Debug("From me, ignore.")
|
|
return nil
|
|
}
|
|
|
|
if !peer.isRouteServerClient() && isASLoop(peer, path) {
|
|
return nil
|
|
}
|
|
return path
|
|
}
|
|
|
|
func (server *BgpServer) dropPeerAllRoutes(peer *Peer, families []bgp.RouteFamily) []*SenderMsg {
|
|
ids := make([]string, 0, len(server.neighborMap))
|
|
msgs := make([]*SenderMsg, 0, len(server.neighborMap))
|
|
if peer.isRouteServerClient() {
|
|
for _, targetPeer := range server.neighborMap {
|
|
if !targetPeer.isRouteServerClient() || targetPeer == peer || targetPeer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
|
|
continue
|
|
}
|
|
ids = append(ids, targetPeer.TableID())
|
|
}
|
|
} else {
|
|
ids = append(ids, table.GLOBAL_RIB_NAME)
|
|
}
|
|
for _, rf := range families {
|
|
best, _ := server.globalRib.DeletePathsByPeer(ids, peer.fsm.peerInfo, rf)
|
|
|
|
if !peer.isRouteServerClient() {
|
|
server.broadcastBests(best[table.GLOBAL_RIB_NAME])
|
|
}
|
|
|
|
for _, targetPeer := range server.neighborMap {
|
|
if peer.isRouteServerClient() != targetPeer.isRouteServerClient() || targetPeer == peer {
|
|
continue
|
|
}
|
|
if paths := targetPeer.processOutgoingPaths(best[targetPeer.TableID()], nil); len(paths) > 0 {
|
|
msgs = append(msgs, newSenderMsg(targetPeer, paths, nil, false))
|
|
}
|
|
}
|
|
}
|
|
return msgs
|
|
}
|
|
|
|
func (server *BgpServer) broadcastBests(bests []*table.Path) {
|
|
for _, path := range bests {
|
|
if path == nil {
|
|
continue
|
|
}
|
|
if !path.IsFromExternal() {
|
|
z := newBroadcastZapiBestMsg(server.zclient, path)
|
|
if z != nil {
|
|
server.broadcastMsgs = append(server.broadcastMsgs, z)
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Server",
|
|
"Client": z.client,
|
|
"Message": z.msg,
|
|
}).Debug("Default policy applied and rejected.")
|
|
}
|
|
}
|
|
|
|
rf := path.GetRouteFamily()
|
|
|
|
result := &GrpcResponse{
|
|
Data: &api.Destination{
|
|
Prefix: path.GetNlri().String(),
|
|
Paths: []*api.Path{path.ToApiStruct(table.GLOBAL_RIB_NAME)},
|
|
},
|
|
}
|
|
remainReqs := make([]*GrpcRequest, 0, len(server.broadcastReqs))
|
|
for _, req := range server.broadcastReqs {
|
|
select {
|
|
case <-req.EndCh:
|
|
continue
|
|
default:
|
|
}
|
|
if req.RequestType != REQ_MONITOR_GLOBAL_BEST_CHANGED {
|
|
remainReqs = append(remainReqs, req)
|
|
continue
|
|
}
|
|
if req.RouteFamily == bgp.RouteFamily(0) || req.RouteFamily == rf {
|
|
m := &broadcastGrpcMsg{
|
|
req: req,
|
|
result: result,
|
|
}
|
|
server.broadcastMsgs = append(server.broadcastMsgs, m)
|
|
}
|
|
remainReqs = append(remainReqs, req)
|
|
}
|
|
server.broadcastReqs = remainReqs
|
|
}
|
|
}
|
|
|
|
func (server *BgpServer) broadcastPeerState(peer *Peer, oldState bgp.FSMState) {
|
|
result := &GrpcResponse{
|
|
Data: peer.ToApiStruct(),
|
|
}
|
|
remainReqs := make([]*GrpcRequest, 0, len(server.broadcastReqs))
|
|
for _, req := range server.broadcastReqs {
|
|
select {
|
|
case <-req.EndCh:
|
|
continue
|
|
default:
|
|
}
|
|
ignore := req.RequestType != REQ_MONITOR_NEIGHBOR_PEER_STATE
|
|
ignore = ignore || (req.Name != "" && req.Name != peer.fsm.pConf.Config.NeighborAddress)
|
|
if ignore {
|
|
remainReqs = append(remainReqs, req)
|
|
continue
|
|
}
|
|
m := &broadcastGrpcMsg{
|
|
req: req,
|
|
result: result,
|
|
}
|
|
server.broadcastMsgs = append(server.broadcastMsgs, m)
|
|
remainReqs = append(remainReqs, req)
|
|
}
|
|
server.broadcastReqs = remainReqs
|
|
newState := peer.fsm.state
|
|
if oldState == bgp.BGP_FSM_ESTABLISHED || newState == bgp.BGP_FSM_ESTABLISHED {
|
|
if server.watchers.watching(WATCHER_EVENT_STATE_CHANGE) {
|
|
_, rport := peer.fsm.RemoteHostPort()
|
|
laddr, lport := peer.fsm.LocalHostPort()
|
|
sentOpen := buildopen(peer.fsm.gConf, peer.fsm.pConf)
|
|
recvOpen := peer.fsm.recvOpen
|
|
ev := &watcherEventStateChangedMsg{
|
|
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: newState,
|
|
timestamp: time.Now(),
|
|
}
|
|
server.notify2watchers(WATCHER_EVENT_STATE_CHANGE, ev)
|
|
}
|
|
}
|
|
}
|
|
|
|
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) ([]*SenderMsg, []*table.Path) {
|
|
rib := server.globalRib
|
|
var alteredPathList, withdrawn []*table.Path
|
|
var best map[string][]*table.Path
|
|
msgs := make([]*SenderMsg, 0, len(server.neighborMap))
|
|
|
|
if peer != nil && peer.isRouteServerClient() {
|
|
for _, path := range pathList {
|
|
path.Filter(peer.ID(), table.POLICY_DIRECTION_IMPORT)
|
|
path.Filter(table.GLOBAL_RIB_NAME, 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)...)
|
|
}
|
|
isTarget := func(p *Peer) bool {
|
|
return p.isRouteServerClient() && p.fsm.state == bgp.BGP_FSM_ESTABLISHED && !p.fsm.pConf.GracefulRestart.State.LocalRestarting
|
|
}
|
|
|
|
ids := make([]string, 0, len(server.neighborMap))
|
|
for _, targetPeer := range server.neighborMap {
|
|
if isTarget(targetPeer) {
|
|
ids = append(ids, targetPeer.TableID())
|
|
}
|
|
}
|
|
best, withdrawn = rib.ProcessPaths(ids, append(pathList, moded...))
|
|
} else {
|
|
for idx, path := range pathList {
|
|
path = server.policy.ApplyPolicy(table.GLOBAL_RIB_NAME, table.POLICY_DIRECTION_IMPORT, path, nil)
|
|
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 withdrawls) 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.adjRibOut.PathList(fs, false)
|
|
} else {
|
|
candidates = rib.GetBestPathList(peer.TableID(), fs)
|
|
}
|
|
paths := make([]*table.Path, 0, len(pathList))
|
|
for _, p := range candidates {
|
|
t := false
|
|
for _, ext := range p.GetExtCommunities() {
|
|
if ext.String() == rt.String() {
|
|
t = true
|
|
break
|
|
}
|
|
}
|
|
if t {
|
|
paths = append(paths, p.Clone(path.IsWithdraw))
|
|
}
|
|
}
|
|
msgs = append(msgs, newSenderMsg(peer, paths, nil, false))
|
|
}
|
|
}
|
|
alteredPathList = pathList
|
|
best, withdrawn = rib.ProcessPaths([]string{table.GLOBAL_RIB_NAME}, pathList)
|
|
if len(best[table.GLOBAL_RIB_NAME]) == 0 {
|
|
return nil, alteredPathList
|
|
}
|
|
server.broadcastBests(best[table.GLOBAL_RIB_NAME])
|
|
}
|
|
|
|
for _, targetPeer := range server.neighborMap {
|
|
if (peer == nil && targetPeer.isRouteServerClient()) || (peer != nil && peer.isRouteServerClient() != targetPeer.isRouteServerClient()) {
|
|
continue
|
|
}
|
|
if paths := targetPeer.processOutgoingPaths(best[targetPeer.TableID()], withdrawn); len(paths) > 0 {
|
|
msgs = append(msgs, newSenderMsg(targetPeer, paths, nil, false))
|
|
}
|
|
}
|
|
return msgs, alteredPathList
|
|
}
|
|
|
|
func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg {
|
|
var msgs []*SenderMsg
|
|
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)
|
|
msgs = server.dropPeerAllRoutes(peer, drop)
|
|
} else if peer.fsm.pConf.GracefulRestart.State.PeerRestarting && nextState == bgp.BGP_FSM_IDLE {
|
|
// 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())
|
|
msgs = server.dropPeerAllRoutes(peer, peer.configuredRFlist())
|
|
}
|
|
|
|
close(peer.outgoing)
|
|
peer.outgoing = make(chan *FsmOutgoingMsg, 128)
|
|
if nextState == bgp.BGP_FSM_ESTABLISHED {
|
|
// update for export policy
|
|
laddr, _ := peer.fsm.LocalHostPort()
|
|
peer.fsm.pConf.Transport.State.LocalAddress = laddr
|
|
deferralExpiredFunc := func(family bgp.RouteFamily) func() {
|
|
return func() {
|
|
req := NewGrpcRequest(REQ_DEFERRAL_TIMER_EXPIRED, peer.ID(), family, nil)
|
|
server.GrpcReqCh <- req
|
|
<-req.ResponseCh
|
|
}
|
|
}
|
|
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
|
|
if c := config.GetAfiSafi(peer.fsm.pConf, bgp.RF_RTC_UC); !peer.fsm.pConf.GracefulRestart.State.PeerRestarting && peer.fsm.rfMap[bgp.RF_RTC_UC] && 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 {
|
|
peer.adjRibOut.Update(pathList)
|
|
msgs = []*SenderMsg{newSenderMsg(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.Timers.State = config.TimersState{}
|
|
}
|
|
peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh)
|
|
server.broadcastPeerState(peer, oldState)
|
|
case FSM_MSG_ROUTE_REFRESH:
|
|
if paths := peer.handleRouteRefresh(e); len(paths) > 0 {
|
|
return []*SenderMsg{newSenderMsg(peer, paths, nil, false)}
|
|
}
|
|
case FSM_MSG_BGP_MESSAGE:
|
|
switch m := e.MsgData.(type) {
|
|
case *bgp.MessageError:
|
|
return []*SenderMsg{newSenderMsg(peer, nil, bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data), false)}
|
|
case *bgp.BGPMessage:
|
|
server.roaManager.validate(e.PathList)
|
|
pathList, eor, notification := peer.handleUpdate(e)
|
|
if notification != nil {
|
|
return []*SenderMsg{newSenderMsg(peer, nil, notification, true)}
|
|
}
|
|
if m.Header.Type == bgp.BGP_MSG_UPDATE && server.watchers.watching(WATCHER_EVENT_UPDATE_MSG) {
|
|
_, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
|
|
l, _ := peer.fsm.LocalHostPort()
|
|
ev := &watcherEventUpdateMsg{
|
|
message: m,
|
|
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: e.timestamp,
|
|
payload: e.payload,
|
|
postPolicy: false,
|
|
pathList: pathList,
|
|
}
|
|
server.notify2watchers(WATCHER_EVENT_UPDATE_MSG, ev)
|
|
}
|
|
|
|
if len(pathList) > 0 {
|
|
var altered []*table.Path
|
|
msgs, altered = server.propagateUpdate(peer, pathList)
|
|
if server.watchers.watching(WATCHER_EVENT_POST_POLICY_UPDATE_MSG) {
|
|
_, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
|
|
l, _ := peer.fsm.LocalHostPort()
|
|
ev := &watcherEventUpdateMsg{
|
|
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: e.timestamp,
|
|
postPolicy: true,
|
|
pathList: altered,
|
|
}
|
|
for _, u := range table.CreateUpdateMsgFromPaths(altered) {
|
|
payload, _ := u.Serialize()
|
|
ev.payload = payload
|
|
server.notify2watchers(WATCHER_EVENT_POST_POLICY_UPDATE_MSG, ev)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 g, _ := bgp.GetRouteFamily(string(a.Config.AfiSafiName)); f == g {
|
|
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 {
|
|
p.adjRibOut.Update(paths)
|
|
msgs = append(msgs, newSenderMsg(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.fsm.pConf.GracefulRestart.State.PeerRestarting = false
|
|
pathList := peer.adjRibIn.DropStale(peer.configuredRFlist())
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.fsm.pConf.Config.NeighborAddress,
|
|
}).Debugf("withdraw %d stale routes", len(pathList))
|
|
m, _ := server.propagateUpdate(peer, pathList)
|
|
msgs = append(msgs, m...)
|
|
}
|
|
|
|
// 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 := config.GetAfiSafi(peer.fsm.pConf, 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 {
|
|
peer.adjRibOut.Update(paths)
|
|
msgs = append(msgs, newSenderMsg(peer, paths, nil, false))
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.fsm.pConf.Config.NeighborAddress,
|
|
"Data": e.MsgData,
|
|
}).Panic("unknown msg type")
|
|
}
|
|
}
|
|
return msgs
|
|
}
|
|
|
|
func (server *BgpServer) SetGlobalType(g config.Global) error {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_START_SERVER,
|
|
Data: &g,
|
|
ResponseCh: ch,
|
|
}
|
|
if err := (<-ch).Err(); err != nil {
|
|
return err
|
|
}
|
|
if g.Zebra.Enabled {
|
|
cli, err := NewZclient(g.Zebra.Url, g.Zebra.RedistributeRouteTypeList)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
server.zclient = cli
|
|
server.zapiMsgCh = server.zclient.Receive()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (server *BgpServer) SetRpkiConfig(c []config.RpkiServer) error {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_INITIALIZE_RPKI,
|
|
Data: &server.bgpConfig.Global,
|
|
ResponseCh: ch,
|
|
}
|
|
if err := (<-ch).Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, s := range c {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_ADD_RPKI,
|
|
Data: &api.AddRpkiRequest{
|
|
Address: s.Config.Address,
|
|
Port: s.Config.Port,
|
|
Lifetime: s.Config.RecordLifetime,
|
|
},
|
|
ResponseCh: ch,
|
|
}
|
|
if err := (<-ch).Err(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (server *BgpServer) SetBmpConfig(c []config.BmpServer) error {
|
|
for _, s := range c {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_ADD_BMP,
|
|
Data: &s.Config,
|
|
ResponseCh: ch,
|
|
}
|
|
if err := (<-ch).Err(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (server *BgpServer) SetMrtConfig(c []config.Mrt) error {
|
|
for _, s := range c {
|
|
if s.FileName != "" {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_ENABLE_MRT,
|
|
Data: &api.EnableMrtRequest{
|
|
DumpType: int32(s.DumpType.ToInt()),
|
|
Filename: s.FileName,
|
|
Interval: s.Interval,
|
|
},
|
|
ResponseCh: ch,
|
|
}
|
|
if err := (<-ch).Err(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (server *BgpServer) PeerAdd(peer config.Neighbor) error {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_ADD_NEIGHBOR,
|
|
Data: &peer,
|
|
ResponseCh: ch,
|
|
}
|
|
return (<-ch).Err()
|
|
}
|
|
|
|
func (server *BgpServer) PeerDelete(peer config.Neighbor) error {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_DEL_NEIGHBOR,
|
|
Data: &peer,
|
|
ResponseCh: ch,
|
|
}
|
|
return (<-ch).Err()
|
|
}
|
|
|
|
func (server *BgpServer) PeerUpdate(peer config.Neighbor) (bool, error) {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_UPDATE_NEIGHBOR,
|
|
Data: &peer,
|
|
ResponseCh: ch,
|
|
}
|
|
res := <-ch
|
|
return res.Data.(bool), res.Err()
|
|
}
|
|
|
|
func (server *BgpServer) Shutdown() {
|
|
server.shutdown = true
|
|
for _, p := range server.neighborMap {
|
|
p.fsm.adminStateCh <- ADMIN_STATE_DOWN
|
|
}
|
|
// TODO: call fsmincomingCh.Close()
|
|
}
|
|
|
|
func (server *BgpServer) UpdatePolicy(policy config.RoutingPolicy) {
|
|
ch := make(chan *GrpcResponse)
|
|
server.GrpcReqCh <- &GrpcRequest{
|
|
RequestType: REQ_RELOAD_POLICY,
|
|
Data: policy,
|
|
ResponseCh: ch,
|
|
}
|
|
<-ch
|
|
}
|
|
|
|
func (server *BgpServer) setPolicyByConfig(id string, c config.ApplyPolicy) {
|
|
for _, dir := range []table.PolicyDirection{table.POLICY_DIRECTION_IN, table.POLICY_DIRECTION_IMPORT, table.POLICY_DIRECTION_EXPORT} {
|
|
ps, def, err := server.policy.GetAssignmentFromConfig(dir, c)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Policy",
|
|
"Dir": dir,
|
|
}).Errorf("failed to get policy info: %s", err)
|
|
continue
|
|
}
|
|
server.policy.SetDefaultPolicy(id, dir, def)
|
|
server.policy.SetPolicy(id, dir, ps)
|
|
}
|
|
}
|
|
|
|
func (server *BgpServer) SetRoutingPolicy(pl config.RoutingPolicy) error {
|
|
if err := server.policy.Reload(pl); err != nil {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Policy",
|
|
}).Errorf("failed to create routing policy: %s", err)
|
|
return err
|
|
}
|
|
server.setPolicyByConfig(table.GLOBAL_RIB_NAME, server.bgpConfig.Global.ApplyPolicy)
|
|
return nil
|
|
}
|
|
|
|
func (server *BgpServer) handlePolicy(pl config.RoutingPolicy) error {
|
|
if err := server.SetRoutingPolicy(pl); err != nil {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Policy",
|
|
}).Errorf("failed to set new policy: %s", err)
|
|
return err
|
|
}
|
|
for _, peer := range server.neighborMap {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.fsm.pConf.Config.NeighborAddress,
|
|
}).Info("call set policy")
|
|
server.setPolicyByConfig(peer.ID(), peer.fsm.pConf.ApplyPolicy)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (server *BgpServer) checkNeighborRequest(grpcReq *GrpcRequest) (*Peer, error) {
|
|
remoteAddr := grpcReq.Name
|
|
peer, found := server.neighborMap[remoteAddr]
|
|
if !found {
|
|
result := &GrpcResponse{}
|
|
result.ResponseErr = fmt.Errorf("Neighbor that has %v doesn't exist.", remoteAddr)
|
|
grpcReq.ResponseCh <- result
|
|
close(grpcReq.ResponseCh)
|
|
return nil, result.ResponseErr
|
|
}
|
|
return peer, nil
|
|
}
|
|
|
|
// 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) Api2PathList(resource api.Resource, name string, ApiPathList []*api.Path) ([]*table.Path, error) {
|
|
var nlri bgp.AddrPrefixInterface
|
|
var nexthop string
|
|
var pi *table.PeerInfo
|
|
|
|
paths := make([]*table.Path, 0, len(ApiPathList))
|
|
|
|
for _, path := range ApiPathList {
|
|
seen := make(map[bgp.BGPAttrType]bool)
|
|
|
|
pattr := make([]bgp.PathAttributeInterface, 0)
|
|
extcomms := make([]bgp.ExtendedCommunityInterface, 0)
|
|
|
|
if path.SourceAsn != 0 {
|
|
pi = &table.PeerInfo{
|
|
AS: path.SourceAsn,
|
|
LocalID: net.ParseIP(path.SourceId),
|
|
}
|
|
} else {
|
|
pi = &table.PeerInfo{
|
|
AS: server.bgpConfig.Global.Config.As,
|
|
LocalID: net.ParseIP(server.bgpConfig.Global.Config.RouterId).To4(),
|
|
}
|
|
}
|
|
|
|
if len(path.Nlri) > 0 {
|
|
nlri = &bgp.IPAddrPrefix{}
|
|
err := nlri.DecodeFromBytes(path.Nlri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
for _, attr := range path.Pattrs {
|
|
p, err := bgp.GetPathAttribute(attr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = p.DecodeFromBytes(attr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, ok := seen[p.GetType()]; !ok {
|
|
seen[p.GetType()] = true
|
|
} else {
|
|
return nil, fmt.Errorf("the path attribute apears twice. Type : " + strconv.Itoa(int(p.GetType())))
|
|
}
|
|
switch p.GetType() {
|
|
case bgp.BGP_ATTR_TYPE_NEXT_HOP:
|
|
nexthop = p.(*bgp.PathAttributeNextHop).Value.String()
|
|
case bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES:
|
|
value := p.(*bgp.PathAttributeExtendedCommunities).Value
|
|
if len(value) > 0 {
|
|
extcomms = append(extcomms, value...)
|
|
}
|
|
case bgp.BGP_ATTR_TYPE_MP_REACH_NLRI:
|
|
mpreach := p.(*bgp.PathAttributeMpReachNLRI)
|
|
if len(mpreach.Value) != 1 {
|
|
return nil, fmt.Errorf("include only one route in mp_reach_nlri")
|
|
}
|
|
nlri = mpreach.Value[0]
|
|
nexthop = mpreach.Nexthop.String()
|
|
default:
|
|
pattr = append(pattr, p)
|
|
}
|
|
}
|
|
|
|
if nlri == nil || nexthop == "" {
|
|
return nil, fmt.Errorf("not found nlri or nexthop")
|
|
}
|
|
|
|
rf := bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI())
|
|
|
|
if resource == api.Resource_VRF {
|
|
label, err := server.globalRib.GetNextLabel(name, nexthop, path.IsWithdraw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vrf := server.globalRib.Vrfs[name]
|
|
switch rf {
|
|
case bgp.RF_IPv4_UC:
|
|
n := nlri.(*bgp.IPAddrPrefix)
|
|
nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(label), vrf.Rd)
|
|
case bgp.RF_IPv6_UC:
|
|
n := nlri.(*bgp.IPv6AddrPrefix)
|
|
nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(label), vrf.Rd)
|
|
case bgp.RF_EVPN:
|
|
n := nlri.(*bgp.EVPNNLRI)
|
|
switch n.RouteType {
|
|
case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT:
|
|
n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute).RD = vrf.Rd
|
|
case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG:
|
|
n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute).RD = vrf.Rd
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("unsupported route family for vrf: %s", rf)
|
|
}
|
|
extcomms = append(extcomms, vrf.ExportRt...)
|
|
}
|
|
|
|
if resource != api.Resource_VRF && rf == bgp.RF_IPv4_UC {
|
|
pattr = append(pattr, bgp.NewPathAttributeNextHop(nexthop))
|
|
} else {
|
|
pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
|
|
}
|
|
|
|
if rf == bgp.RF_EVPN {
|
|
evpnNlri := nlri.(*bgp.EVPNNLRI)
|
|
if evpnNlri.RouteType == bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
|
|
macIpAdv := evpnNlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
|
|
etag := macIpAdv.ETag
|
|
mac := macIpAdv.MacAddress
|
|
paths := server.globalRib.GetBestPathList(table.GLOBAL_RIB_NAME, []bgp.RouteFamily{bgp.RF_EVPN})
|
|
if m := getMacMobilityExtendedCommunity(etag, mac, paths); m != nil {
|
|
extcomms = append(extcomms, m)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(extcomms) > 0 {
|
|
pattr = append(pattr, bgp.NewPathAttributeExtendedCommunities(extcomms))
|
|
}
|
|
newPath := table.NewPath(pi, nlri, path.IsWithdraw, pattr, time.Now(), path.NoImplicitWithdraw)
|
|
newPath.SetIsFromExternal(path.IsFromExternal)
|
|
paths = append(paths, newPath)
|
|
|
|
}
|
|
return paths, nil
|
|
}
|
|
|
|
func (server *BgpServer) handleAddPathRequest(grpcReq *GrpcRequest) []*table.Path {
|
|
var err error
|
|
var uuidBytes []byte
|
|
paths := make([]*table.Path, 0, 1)
|
|
arg, ok := grpcReq.Data.(*api.AddPathRequest)
|
|
if !ok {
|
|
err = fmt.Errorf("type assertion failed")
|
|
} else {
|
|
paths, err = server.Api2PathList(arg.Resource, arg.VrfId, []*api.Path{arg.Path})
|
|
if err == nil {
|
|
u := uuid.NewV4()
|
|
uuidBytes = u.Bytes()
|
|
paths[0].SetUUID(uuidBytes)
|
|
}
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: &api.AddPathResponse{
|
|
Uuid: uuidBytes,
|
|
},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
return paths
|
|
}
|
|
|
|
func (server *BgpServer) handleDeletePathRequest(grpcReq *GrpcRequest) []*table.Path {
|
|
var err error
|
|
paths := make([]*table.Path, 0, 1)
|
|
arg, ok := grpcReq.Data.(*api.DeletePathRequest)
|
|
if !ok {
|
|
err = fmt.Errorf("type assertion failed")
|
|
} else {
|
|
if len(arg.Uuid) > 0 {
|
|
path := func() *table.Path {
|
|
for _, path := range server.globalRib.GetPathList(table.GLOBAL_RIB_NAME, server.globalRib.GetRFlist()) {
|
|
if len(path.UUID()) > 0 && bytes.Equal(path.UUID(), arg.Uuid) {
|
|
return path
|
|
}
|
|
}
|
|
return nil
|
|
}()
|
|
if path != nil {
|
|
paths = append(paths, path.Clone(true))
|
|
} else {
|
|
err = fmt.Errorf("Can't find a specified path")
|
|
}
|
|
} else if arg.Path != nil {
|
|
arg.Path.IsWithdraw = true
|
|
paths, err = server.Api2PathList(arg.Resource, arg.VrfId, []*api.Path{arg.Path})
|
|
} else {
|
|
// delete all paths
|
|
families := server.globalRib.GetRFlist()
|
|
if arg.Family != 0 {
|
|
families = []bgp.RouteFamily{bgp.RouteFamily(arg.Family)}
|
|
}
|
|
for _, path := range server.globalRib.GetPathList(table.GLOBAL_RIB_NAME, families) {
|
|
paths = append(paths, path.Clone(true))
|
|
}
|
|
}
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: &api.DeletePathResponse{},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
return paths
|
|
}
|
|
|
|
func (server *BgpServer) handleInjectMrtRequest(grpcReq *GrpcRequest) []*table.Path {
|
|
var err error
|
|
var paths []*table.Path
|
|
arg, ok := grpcReq.Data.(*api.InjectMrtRequest)
|
|
if !ok {
|
|
err = fmt.Errorf("type assertion failed")
|
|
}
|
|
if err == nil {
|
|
paths, err = server.Api2PathList(arg.Resource, arg.VrfId, arg.Paths)
|
|
if err == nil {
|
|
return paths
|
|
}
|
|
}
|
|
result := &GrpcResponse{
|
|
ResponseErr: err,
|
|
}
|
|
grpcReq.ResponseCh <- result
|
|
close(grpcReq.ResponseCh)
|
|
return []*table.Path{}
|
|
|
|
}
|
|
|
|
func (server *BgpServer) handleAddVrfRequest(grpcReq *GrpcRequest) ([]*table.Path, error) {
|
|
arg, _ := grpcReq.Data.(*api.AddVrfRequest)
|
|
rib := server.globalRib
|
|
rd := bgp.GetRouteDistinguisher(arg.Vrf.Rd)
|
|
f := func(bufs [][]byte) ([]bgp.ExtendedCommunityInterface, error) {
|
|
ret := make([]bgp.ExtendedCommunityInterface, 0, len(bufs))
|
|
for _, rt := range bufs {
|
|
r, err := bgp.ParseExtended(rt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret = append(ret, r)
|
|
}
|
|
return ret, nil
|
|
}
|
|
importRt, err := f(arg.Vrf.ImportRt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
exportRt, err := f(arg.Vrf.ExportRt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pi := &table.PeerInfo{
|
|
AS: server.bgpConfig.Global.Config.As,
|
|
LocalID: net.ParseIP(server.bgpConfig.Global.Config.RouterId).To4(),
|
|
}
|
|
return rib.AddVrf(arg.Vrf.Name, rd, importRt, exportRt, pi)
|
|
}
|
|
|
|
func (server *BgpServer) handleDeleteVrfRequest(grpcReq *GrpcRequest) ([]*table.Path, error) {
|
|
arg, _ := grpcReq.Data.(*api.DeleteVrfRequest)
|
|
rib := server.globalRib
|
|
return rib.DeleteVrf(arg.Vrf.Name)
|
|
}
|
|
|
|
func (server *BgpServer) handleVrfRequest(req *GrpcRequest) []*table.Path {
|
|
var msgs []*table.Path
|
|
result := &GrpcResponse{}
|
|
|
|
switch req.RequestType {
|
|
case REQ_VRF:
|
|
arg := req.Data.(*api.GetRibRequest)
|
|
name := arg.Table.Name
|
|
rib := server.globalRib
|
|
vrfs := rib.Vrfs
|
|
if _, ok := vrfs[name]; !ok {
|
|
result.ResponseErr = fmt.Errorf("vrf %s not found", name)
|
|
break
|
|
}
|
|
var rf bgp.RouteFamily
|
|
switch bgp.RouteFamily(arg.Table.Family) {
|
|
case bgp.RF_IPv4_UC:
|
|
rf = bgp.RF_IPv4_VPN
|
|
case bgp.RF_IPv6_UC:
|
|
rf = bgp.RF_IPv6_VPN
|
|
case bgp.RF_EVPN:
|
|
rf = bgp.RF_EVPN
|
|
default:
|
|
result.ResponseErr = fmt.Errorf("unsupported route family: %s", bgp.RouteFamily(arg.Table.Family))
|
|
break
|
|
}
|
|
paths := rib.GetPathList(table.GLOBAL_RIB_NAME, []bgp.RouteFamily{rf})
|
|
dsts := make([]*api.Destination, 0, len(paths))
|
|
for _, path := range paths {
|
|
ok := table.CanImportToVrf(vrfs[name], path)
|
|
if !ok {
|
|
continue
|
|
}
|
|
dsts = append(dsts, &api.Destination{
|
|
Prefix: path.GetNlri().String(),
|
|
Paths: []*api.Path{path.ToApiStruct(table.GLOBAL_RIB_NAME)},
|
|
})
|
|
}
|
|
req.ResponseCh <- &GrpcResponse{
|
|
Data: &api.GetRibResponse{
|
|
Table: &api.Table{
|
|
Type: arg.Table.Type,
|
|
Family: arg.Table.Family,
|
|
Destinations: dsts,
|
|
},
|
|
},
|
|
}
|
|
goto END
|
|
case REQ_GET_VRF:
|
|
l := make([]*api.Vrf, 0, len(server.globalRib.Vrfs))
|
|
for _, vrf := range server.globalRib.Vrfs {
|
|
l = append(l, vrf.ToApiStruct())
|
|
}
|
|
result.Data = &api.GetVrfResponse{Vrfs: l}
|
|
case REQ_ADD_VRF:
|
|
msgs, result.ResponseErr = server.handleAddVrfRequest(req)
|
|
result.Data = &api.AddVrfResponse{}
|
|
case REQ_DELETE_VRF:
|
|
msgs, result.ResponseErr = server.handleDeleteVrfRequest(req)
|
|
result.Data = &api.DeleteVrfResponse{}
|
|
default:
|
|
result.ResponseErr = fmt.Errorf("unknown request type: %d", req.RequestType)
|
|
}
|
|
|
|
req.ResponseCh <- result
|
|
END:
|
|
close(req.ResponseCh)
|
|
return msgs
|
|
}
|
|
|
|
func (server *BgpServer) handleModConfig(grpcReq *GrpcRequest) error {
|
|
var c *config.Global
|
|
switch arg := grpcReq.Data.(type) {
|
|
case *api.StartServerRequest:
|
|
g := arg.Global
|
|
if net.ParseIP(g.RouterId) == nil {
|
|
return fmt.Errorf("invalid router-id format: %s", g.RouterId)
|
|
}
|
|
families := make([]config.AfiSafi, 0, len(g.Families))
|
|
for _, f := range g.Families {
|
|
name := config.AfiSafiType(bgp.RouteFamily(f).String())
|
|
families = append(families, config.AfiSafi{
|
|
Config: config.AfiSafiConfig{
|
|
AfiSafiName: name,
|
|
Enabled: true,
|
|
},
|
|
State: config.AfiSafiState{
|
|
AfiSafiName: name,
|
|
},
|
|
})
|
|
}
|
|
b := &config.BgpConfigSet{
|
|
Global: config.Global{
|
|
Config: config.GlobalConfig{
|
|
As: g.As,
|
|
RouterId: g.RouterId,
|
|
Port: g.ListenPort,
|
|
LocalAddressList: g.ListenAddresses,
|
|
},
|
|
MplsLabelRange: config.MplsLabelRange{
|
|
MinLabel: g.MplsLabelMin,
|
|
MaxLabel: g.MplsLabelMax,
|
|
},
|
|
AfiSafis: families,
|
|
},
|
|
}
|
|
if err := config.SetDefaultConfigValues(nil, b); err != nil {
|
|
return err
|
|
}
|
|
c = &b.Global
|
|
case *config.Global:
|
|
c = arg
|
|
case *api.StopServerRequest:
|
|
for k, _ := range server.neighborMap {
|
|
_, err := server.handleDeleteNeighborRequest(&GrpcRequest{
|
|
Data: &api.DeleteNeighborRequest{
|
|
Peer: &api.Peer{
|
|
Conf: &api.PeerConf{
|
|
NeighborAddress: k,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, l := range server.listeners {
|
|
l.Close()
|
|
}
|
|
server.bgpConfig.Global = config.Global{}
|
|
return nil
|
|
}
|
|
|
|
if server.bgpConfig.Global.Config.As != 0 {
|
|
return fmt.Errorf("gobgp is already started")
|
|
}
|
|
|
|
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
|
|
}
|
|
server.listeners = append(server.listeners, l)
|
|
}
|
|
server.acceptCh = acceptCh
|
|
}
|
|
|
|
rfs, _ := config.AfiSafis(c.AfiSafis).ToRfList()
|
|
server.globalRib = table.NewTableManager(rfs, c.MplsLabelRange.MinLabel, c.MplsLabelRange.MaxLabel)
|
|
|
|
p := config.RoutingPolicy{}
|
|
if err := server.SetRoutingPolicy(p); err != nil {
|
|
return err
|
|
}
|
|
server.bgpConfig.Global = *c
|
|
// update route selection options
|
|
table.SelectionOptions = c.RouteSelectionOptions.Config
|
|
return nil
|
|
}
|
|
|
|
func sendMultipleResponses(grpcReq *GrpcRequest, results []*GrpcResponse) {
|
|
defer close(grpcReq.ResponseCh)
|
|
for _, r := range results {
|
|
select {
|
|
case grpcReq.ResponseCh <- r:
|
|
case <-grpcReq.EndCh:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg {
|
|
var msgs []*SenderMsg
|
|
|
|
logOp := func(addr string, action string) {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Operation",
|
|
"Key": addr,
|
|
}).Info(action)
|
|
}
|
|
|
|
reqToPeers := func(grpcReq *GrpcRequest) ([]*Peer, error) {
|
|
peers := make([]*Peer, 0)
|
|
if grpcReq.Name == "all" {
|
|
for _, p := range server.neighborMap {
|
|
peers = append(peers, p)
|
|
}
|
|
return peers, nil
|
|
}
|
|
peer, err := server.checkNeighborRequest(grpcReq)
|
|
return []*Peer{peer}, err
|
|
}
|
|
|
|
sortedDsts := func(id string, t *table.Table) []*api.Destination {
|
|
results := make([]*api.Destination, 0, len(t.GetDestinations()))
|
|
|
|
r := radix.New()
|
|
for _, dst := range t.GetDestinations() {
|
|
if d := dst.ToApiStruct(id); d != nil {
|
|
r.Insert(dst.RadixKey, d)
|
|
}
|
|
}
|
|
r.Walk(func(s string, v interface{}) bool {
|
|
results = append(results, v.(*api.Destination))
|
|
return false
|
|
})
|
|
|
|
return results
|
|
}
|
|
|
|
if server.bgpConfig.Global.Config.As == 0 && grpcReq.RequestType != REQ_START_SERVER {
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: fmt.Errorf("bgpd main loop is not started yet"),
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
|
|
switch grpcReq.RequestType {
|
|
case REQ_GET_SERVER:
|
|
g := server.bgpConfig.Global
|
|
result := &GrpcResponse{
|
|
Data: &api.GetServerResponse{
|
|
Global: &api.Global{
|
|
As: g.Config.As,
|
|
RouterId: g.Config.RouterId,
|
|
ListenPort: g.Config.Port,
|
|
ListenAddresses: g.Config.LocalAddressList,
|
|
MplsLabelMin: g.MplsLabelRange.MinLabel,
|
|
MplsLabelMax: g.MplsLabelRange.MaxLabel,
|
|
},
|
|
},
|
|
}
|
|
grpcReq.ResponseCh <- result
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_START_SERVER:
|
|
err := server.handleModConfig(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: &api.StartServerResponse{},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_STOP_SERVER:
|
|
err := server.handleModConfig(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: &api.StopServerResponse{},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_GLOBAL_RIB, REQ_LOCAL_RIB:
|
|
arg := grpcReq.Data.(*api.GetRibRequest)
|
|
d := &api.Table{
|
|
Type: arg.Table.Type,
|
|
Family: arg.Table.Family,
|
|
}
|
|
rib := server.globalRib
|
|
id := table.GLOBAL_RIB_NAME
|
|
if grpcReq.RequestType == REQ_LOCAL_RIB {
|
|
peer, ok := server.neighborMap[arg.Table.Name]
|
|
if !ok {
|
|
err = fmt.Errorf("Neighbor that has %v doesn't exist.", arg.Table.Name)
|
|
goto ERROR
|
|
}
|
|
if !peer.isRouteServerClient() {
|
|
err = fmt.Errorf("Neighbor %v doesn't have local rib", arg.Table.Name)
|
|
goto ERROR
|
|
}
|
|
id = peer.ID()
|
|
}
|
|
af := bgp.RouteFamily(arg.Table.Family)
|
|
if _, ok := rib.Tables[af]; !ok {
|
|
err = fmt.Errorf("address family: %s not supported", af)
|
|
goto ERROR
|
|
}
|
|
|
|
switch af {
|
|
case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC:
|
|
if len(arg.Table.Destinations) > 0 {
|
|
dsts := []*api.Destination{}
|
|
f := func(id, cidr string) (bool, error) {
|
|
_, prefix, err := net.ParseCIDR(cidr)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if dst := rib.Tables[af].GetDestination(prefix.String()); dst != nil {
|
|
if d := dst.ToApiStruct(id); d != nil {
|
|
dsts = append(dsts, d)
|
|
}
|
|
return true, nil
|
|
} else {
|
|
return false, nil
|
|
}
|
|
}
|
|
for _, dst := range arg.Table.Destinations {
|
|
key := dst.Prefix
|
|
if _, err := f(id, key); err != nil {
|
|
if host := net.ParseIP(key); host != nil {
|
|
masklen := 32
|
|
if af == bgp.RF_IPv6_UC {
|
|
masklen = 128
|
|
}
|
|
for i := masklen; i > 0; i-- {
|
|
if y, _ := f(id, fmt.Sprintf("%s/%d", key, i)); y {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} else if dst.LongerPrefixes {
|
|
_, prefix, _ := net.ParseCIDR(key)
|
|
ones, bits := prefix.Mask.Size()
|
|
for i := ones + 1; i <= bits; i++ {
|
|
prefix.Mask = net.CIDRMask(i, bits)
|
|
f(id, prefix.String())
|
|
}
|
|
}
|
|
}
|
|
d.Destinations = dsts
|
|
} else {
|
|
d.Destinations = sortedDsts(id, rib.Tables[af])
|
|
}
|
|
default:
|
|
d.Destinations = make([]*api.Destination, 0, len(rib.Tables[af].GetDestinations()))
|
|
for _, dst := range rib.Tables[af].GetDestinations() {
|
|
if s := dst.ToApiStruct(id); s != nil {
|
|
d.Destinations = append(d.Destinations, s)
|
|
}
|
|
}
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: &api.GetRibResponse{Table: d},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_BMP_GLOBAL:
|
|
paths := server.globalRib.GetBestPathList(table.GLOBAL_RIB_NAME, server.globalRib.GetRFlist())
|
|
bmpmsgs := make([]*bmp.BMPMessage, 0, len(paths))
|
|
for _, path := range paths {
|
|
msgs := table.CreateUpdateMsgFromPaths([]*table.Path{path})
|
|
buf, _ := msgs[0].Serialize()
|
|
bmpmsgs = append(bmpmsgs, bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, true, 0, path.GetSource(), path.GetTimestamp().Unix(), buf))
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: bmpmsgs,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_ADD_PATH:
|
|
pathList := server.handleAddPathRequest(grpcReq)
|
|
if len(pathList) > 0 {
|
|
msgs, _ = server.propagateUpdate(nil, pathList)
|
|
}
|
|
case REQ_DELETE_PATH:
|
|
pathList := server.handleDeletePathRequest(grpcReq)
|
|
if len(pathList) > 0 {
|
|
msgs, _ = server.propagateUpdate(nil, pathList)
|
|
}
|
|
case REQ_BMP_NEIGHBORS:
|
|
//TODO: merge REQ_NEIGHBORS and REQ_BMP_NEIGHBORS
|
|
msgs := make([]*bmp.BMPMessage, 0, len(server.neighborMap))
|
|
for _, peer := range server.neighborMap {
|
|
if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
|
|
continue
|
|
}
|
|
laddr, lport := peer.fsm.LocalHostPort()
|
|
_, rport := peer.fsm.RemoteHostPort()
|
|
sentOpen := buildopen(peer.fsm.gConf, peer.fsm.pConf)
|
|
info := peer.fsm.peerInfo
|
|
timestamp := peer.fsm.pConf.Timers.State.Uptime
|
|
msg := bmpPeerUp(laddr, lport, rport, sentOpen, peer.fsm.recvOpen, bmp.BMP_PEER_TYPE_GLOBAL, false, 0, info, timestamp)
|
|
msgs = append(msgs, msg)
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: msgs,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_NEIGHBOR:
|
|
l := []*api.Peer{}
|
|
for _, peer := range server.neighborMap {
|
|
l = append(l, peer.ToApiStruct())
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: &api.GetNeighborResponse{
|
|
Peers: l,
|
|
},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_ADJ_RIB_IN, REQ_ADJ_RIB_OUT:
|
|
arg := grpcReq.Data.(*api.GetRibRequest)
|
|
d := &api.Table{
|
|
Type: arg.Table.Type,
|
|
Family: arg.Table.Family,
|
|
}
|
|
|
|
peer, ok := server.neighborMap[arg.Table.Name]
|
|
if !ok {
|
|
err = fmt.Errorf("Neighbor that has %v doesn't exist.", arg.Table.Name)
|
|
goto ERROR
|
|
}
|
|
|
|
rf := bgp.RouteFamily(arg.Table.Family)
|
|
var paths []*table.Path
|
|
if grpcReq.RequestType == REQ_ADJ_RIB_IN {
|
|
paths = peer.adjRibIn.PathList([]bgp.RouteFamily{rf}, false)
|
|
log.Debugf("RouteFamily=%v adj-rib-in found : %d", rf.String(), len(paths))
|
|
} else {
|
|
paths = peer.adjRibOut.PathList([]bgp.RouteFamily{rf}, false)
|
|
log.Debugf("RouteFamily=%v adj-rib-out found : %d", rf.String(), len(paths))
|
|
}
|
|
|
|
results := make([]*api.Destination, 0, len(paths))
|
|
switch rf {
|
|
case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC:
|
|
r := radix.New()
|
|
for _, p := range paths {
|
|
key := p.GetNlri().String()
|
|
found := true
|
|
for _, dst := range arg.Table.Destinations {
|
|
found = false
|
|
if dst.Prefix == key {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if found {
|
|
b, _ := r.Get(table.CidrToRadixkey(key))
|
|
if b == nil {
|
|
r.Insert(table.CidrToRadixkey(key), &api.Destination{
|
|
Prefix: key,
|
|
Paths: []*api.Path{p.ToApiStruct(peer.TableID())},
|
|
})
|
|
} else {
|
|
d := b.(*api.Destination)
|
|
d.Paths = append(d.Paths, p.ToApiStruct(peer.TableID()))
|
|
}
|
|
}
|
|
}
|
|
r.Walk(func(s string, v interface{}) bool {
|
|
results = append(results, v.(*api.Destination))
|
|
return false
|
|
})
|
|
default:
|
|
for _, p := range paths {
|
|
results = append(results, &api.Destination{
|
|
Prefix: p.GetNlri().String(),
|
|
Paths: []*api.Path{p.ToApiStruct(peer.TableID())},
|
|
})
|
|
}
|
|
}
|
|
d.Destinations = results
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: &api.GetRibResponse{Table: d},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_BMP_ADJ_IN:
|
|
bmpmsgs := make([]*bmp.BMPMessage, 0)
|
|
for _, peer := range server.neighborMap {
|
|
if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
|
|
continue
|
|
}
|
|
for _, path := range peer.adjRibIn.PathList(peer.configuredRFlist(), false) {
|
|
msgs := table.CreateUpdateMsgFromPaths([]*table.Path{path})
|
|
buf, _ := msgs[0].Serialize()
|
|
bmpmsgs = append(bmpmsgs, bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, false, 0, peer.fsm.peerInfo, path.GetTimestamp().Unix(), buf))
|
|
}
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: bmpmsgs,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_NEIGHBOR_SHUTDOWN:
|
|
peers, err := reqToPeers(grpcReq)
|
|
if err != nil {
|
|
break
|
|
}
|
|
logOp(grpcReq.Name, "Neighbor shutdown")
|
|
m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, nil)
|
|
for _, peer := range peers {
|
|
msgs = append(msgs, newSenderMsg(peer, nil, m, false))
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{Data: &api.ShutdownNeighborResponse{}}
|
|
close(grpcReq.ResponseCh)
|
|
|
|
case REQ_NEIGHBOR_RESET:
|
|
peers, err := reqToPeers(grpcReq)
|
|
if err != nil {
|
|
break
|
|
}
|
|
logOp(grpcReq.Name, "Neighbor reset")
|
|
m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, nil)
|
|
for _, peer := range peers {
|
|
peer.fsm.idleHoldTime = peer.fsm.pConf.Timers.Config.IdleHoldTimeAfterReset
|
|
msgs = append(msgs, newSenderMsg(peer, nil, m, false))
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{Data: &api.ResetNeighborResponse{}}
|
|
close(grpcReq.ResponseCh)
|
|
|
|
case REQ_NEIGHBOR_SOFT_RESET, REQ_NEIGHBOR_SOFT_RESET_IN:
|
|
peers, err := reqToPeers(grpcReq)
|
|
if err != nil {
|
|
break
|
|
}
|
|
if grpcReq.RequestType == REQ_NEIGHBOR_SOFT_RESET {
|
|
logOp(grpcReq.Name, "Neighbor soft reset")
|
|
} else {
|
|
logOp(grpcReq.Name, "Neighbor soft reset in")
|
|
}
|
|
|
|
for _, peer := range peers {
|
|
pathList := []*table.Path{}
|
|
families := []bgp.RouteFamily{grpcReq.RouteFamily}
|
|
if families[0] == 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)
|
|
if server.policy.ApplyPolicy(peer.ID(), table.POLICY_DIRECTION_IN, path, nil) != nil {
|
|
pathList = append(pathList, path.Clone(false))
|
|
} 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)
|
|
m, _ := server.propagateUpdate(peer, pathList)
|
|
msgs = append(msgs, m...)
|
|
}
|
|
|
|
if grpcReq.RequestType == REQ_NEIGHBOR_SOFT_RESET_IN {
|
|
grpcReq.ResponseCh <- &GrpcResponse{Data: &api.SoftResetNeighborResponse{}}
|
|
close(grpcReq.ResponseCh)
|
|
break
|
|
}
|
|
fallthrough
|
|
case REQ_NEIGHBOR_SOFT_RESET_OUT, REQ_DEFERRAL_TIMER_EXPIRED:
|
|
peers, err := reqToPeers(grpcReq)
|
|
if err != nil {
|
|
break
|
|
}
|
|
if grpcReq.RequestType == REQ_NEIGHBOR_SOFT_RESET_OUT {
|
|
logOp(grpcReq.Name, "Neighbor soft reset out")
|
|
}
|
|
for _, peer := range peers {
|
|
if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
|
|
continue
|
|
}
|
|
|
|
families := []bgp.RouteFamily{grpcReq.RouteFamily}
|
|
if families[0] == bgp.RouteFamily(0) {
|
|
families = peer.configuredRFlist()
|
|
}
|
|
|
|
if grpcReq.RequestType == REQ_DEFERRAL_TIMER_EXPIRED {
|
|
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 := config.GetAfiSafi(peer.fsm.pConf, bgp.RF_RTC_UC); peer.fsm.rfMap[bgp.RF_RTC_UC] && !c.MpGracefulRestart.State.EndOfRibReceived {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.ID(),
|
|
"Families": families,
|
|
}).Debug("route-target deferral timer expired")
|
|
} else {
|
|
continue
|
|
}
|
|
}
|
|
|
|
sentPathList := peer.adjRibOut.PathList(families, false)
|
|
peer.adjRibOut.Drop(families)
|
|
pathList, filtered := peer.getBestFromLocal(families)
|
|
if len(pathList) > 0 {
|
|
peer.adjRibOut.Update(pathList)
|
|
msgs = append(msgs, newSenderMsg(peer, pathList, nil, false))
|
|
}
|
|
if grpcReq.RequestType != REQ_DEFERRAL_TIMER_EXPIRED && len(filtered) > 0 {
|
|
withdrawnList := make([]*table.Path, 0, len(filtered))
|
|
for _, p := range filtered {
|
|
found := false
|
|
for _, sentPath := range sentPathList {
|
|
if p.GetNlri() == sentPath.GetNlri() {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
withdrawnList = append(withdrawnList, p.Clone(true))
|
|
}
|
|
}
|
|
msgs = append(msgs, newSenderMsg(peer, withdrawnList, nil, false))
|
|
}
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{Data: &api.SoftResetNeighborResponse{}}
|
|
close(grpcReq.ResponseCh)
|
|
|
|
case REQ_NEIGHBOR_ENABLE, REQ_NEIGHBOR_DISABLE:
|
|
peer, err1 := server.checkNeighborRequest(grpcReq)
|
|
if err1 != nil {
|
|
break
|
|
}
|
|
result := &GrpcResponse{}
|
|
if grpcReq.RequestType == REQ_NEIGHBOR_ENABLE {
|
|
select {
|
|
case peer.fsm.adminStateCh <- ADMIN_STATE_UP:
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.fsm.pConf.Config.NeighborAddress,
|
|
}).Debug("ADMIN_STATE_UP requested")
|
|
default:
|
|
log.Warning("previous request is still remaining. : ", peer.fsm.pConf.Config.NeighborAddress)
|
|
result.ResponseErr = fmt.Errorf("previous request is still remaining %v", peer.fsm.pConf.Config.NeighborAddress)
|
|
}
|
|
result.Data = &api.EnableNeighborResponse{}
|
|
} else {
|
|
select {
|
|
case peer.fsm.adminStateCh <- ADMIN_STATE_DOWN:
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": peer.fsm.pConf.Config.NeighborAddress,
|
|
}).Debug("ADMIN_STATE_DOWN requested")
|
|
default:
|
|
log.Warning("previous request is still remaining. : ", peer.fsm.pConf.Config.NeighborAddress)
|
|
result.ResponseErr = fmt.Errorf("previous request is still remaining %v", peer.fsm.pConf.Config.NeighborAddress)
|
|
}
|
|
result.Data = &api.DisableNeighborResponse{}
|
|
}
|
|
grpcReq.ResponseCh <- result
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_GRPC_ADD_NEIGHBOR:
|
|
_, err := server.handleAddNeighborRequest(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: &api.AddNeighborResponse{},
|
|
ResponseErr: err,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_GRPC_DELETE_NEIGHBOR:
|
|
m, err := server.handleDeleteNeighborRequest(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: &api.DeleteNeighborResponse{},
|
|
ResponseErr: err,
|
|
}
|
|
if len(m) > 0 {
|
|
msgs = append(msgs, m...)
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_ADD_NEIGHBOR:
|
|
_, err := server.handleAddNeighbor(grpcReq.Data.(*config.Neighbor))
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_DEL_NEIGHBOR:
|
|
m, err := server.handleDelNeighbor(grpcReq.Data.(*config.Neighbor), bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
}
|
|
if len(m) > 0 {
|
|
msgs = append(msgs, m...)
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_UPDATE_NEIGHBOR:
|
|
m, policyUpdated, err := server.handleUpdateNeighbor(grpcReq.Data.(*config.Neighbor))
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: policyUpdated,
|
|
ResponseErr: err,
|
|
}
|
|
if len(m) > 0 {
|
|
msgs = append(msgs, m...)
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_GET_DEFINED_SET:
|
|
rsp, err := server.handleGrpcGetDefinedSet(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: rsp,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_ADD_DEFINED_SET:
|
|
rsp, err := server.handleGrpcAddDefinedSet(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: rsp,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_DELETE_DEFINED_SET:
|
|
rsp, err := server.handleGrpcDeleteDefinedSet(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: rsp,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_REPLACE_DEFINED_SET:
|
|
rsp, err := server.handleGrpcReplaceDefinedSet(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: rsp,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_GET_STATEMENT:
|
|
rsp, err := server.handleGrpcGetStatement(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: rsp,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_ADD_STATEMENT:
|
|
data, err := server.handleGrpcAddStatement(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_DELETE_STATEMENT:
|
|
data, err := server.handleGrpcDeleteStatement(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_REPLACE_STATEMENT:
|
|
data, err := server.handleGrpcReplaceStatement(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_GET_POLICY:
|
|
rsp, err := server.handleGrpcGetPolicy(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: rsp,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_ADD_POLICY:
|
|
data, err := server.handleGrpcAddPolicy(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_DELETE_POLICY:
|
|
data, err := server.handleGrpcDeletePolicy(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_REPLACE_POLICY:
|
|
data, err := server.handleGrpcReplacePolicy(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_GET_POLICY_ASSIGNMENT:
|
|
data, err := server.handleGrpcGetPolicyAssignment(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_ADD_POLICY_ASSIGNMENT:
|
|
data, err := server.handleGrpcAddPolicyAssignment(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_DELETE_POLICY_ASSIGNMENT:
|
|
data, err := server.handleGrpcDeletePolicyAssignment(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_REPLACE_POLICY_ASSIGNMENT:
|
|
data, err := server.handleGrpcReplacePolicyAssignment(grpcReq)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: data,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_MONITOR_GLOBAL_BEST_CHANGED, REQ_MONITOR_NEIGHBOR_PEER_STATE:
|
|
server.broadcastReqs = append(server.broadcastReqs, grpcReq)
|
|
case REQ_MONITOR_INCOMING:
|
|
if grpcReq.Name != "" {
|
|
if _, err = server.checkNeighborRequest(grpcReq); err != nil {
|
|
break
|
|
}
|
|
}
|
|
w := server.watchers[WATCHER_GRPC_INCOMING]
|
|
go w.(*grpcIncomingWatcher).addRequest(grpcReq)
|
|
case REQ_ENABLE_MRT:
|
|
server.handleEnableMrtRequest(grpcReq)
|
|
case REQ_DISABLE_MRT:
|
|
server.handleDisableMrtRequest(grpcReq)
|
|
case REQ_INJECT_MRT:
|
|
pathList := server.handleInjectMrtRequest(grpcReq)
|
|
if len(pathList) > 0 {
|
|
msgs, _ = server.propagateUpdate(nil, pathList)
|
|
grpcReq.ResponseCh <- &GrpcResponse{}
|
|
close(grpcReq.ResponseCh)
|
|
}
|
|
case REQ_ADD_BMP:
|
|
server.handleAddBmp(grpcReq)
|
|
case REQ_DELETE_BMP:
|
|
server.handleDeleteBmp(grpcReq)
|
|
case REQ_VALIDATE_RIB:
|
|
server.handleValidateRib(grpcReq)
|
|
case REQ_INITIALIZE_RPKI:
|
|
g := grpcReq.Data.(*config.Global)
|
|
grpcDone(grpcReq, server.roaManager.SetAS(g.Config.As))
|
|
case REQ_ADD_RPKI, REQ_DELETE_RPKI, REQ_ENABLE_RPKI, REQ_DISABLE_RPKI, REQ_RESET_RPKI, REQ_SOFT_RESET_RPKI:
|
|
server.handleModRpki(grpcReq)
|
|
case REQ_ROA, REQ_GET_RPKI:
|
|
rsp := server.roaManager.handleGRPC(grpcReq)
|
|
grpcReq.ResponseCh <- rsp
|
|
close(grpcReq.ResponseCh)
|
|
case REQ_VRF, REQ_GET_VRF, REQ_ADD_VRF, REQ_DELETE_VRF:
|
|
pathList := server.handleVrfRequest(grpcReq)
|
|
if len(pathList) > 0 {
|
|
msgs, _ = server.propagateUpdate(nil, pathList)
|
|
}
|
|
case REQ_RELOAD_POLICY:
|
|
err := server.handlePolicy(grpcReq.Data.(config.RoutingPolicy))
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
default:
|
|
err = fmt.Errorf("Unknown request type: %v", grpcReq.RequestType)
|
|
goto ERROR
|
|
}
|
|
return msgs
|
|
ERROR:
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
return msgs
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcGetDefinedSet(grpcReq *GrpcRequest) (*api.GetDefinedSetResponse, error) {
|
|
arg := grpcReq.Data.(*api.GetDefinedSetRequest)
|
|
typ := table.DefinedType(arg.Type)
|
|
set, ok := server.policy.DefinedSetMap[typ]
|
|
if !ok {
|
|
return &api.GetDefinedSetResponse{}, fmt.Errorf("invalid defined-set type: %d", typ)
|
|
}
|
|
sets := make([]*api.DefinedSet, 0)
|
|
for _, s := range set {
|
|
sets = append(sets, s.ToApiStruct())
|
|
}
|
|
return &api.GetDefinedSetResponse{Sets: sets}, nil
|
|
}
|
|
|
|
func (server *BgpServer) handleAddNeighbor(c *config.Neighbor) ([]*SenderMsg, error) {
|
|
addr := c.Config.NeighborAddress
|
|
if _, y := server.neighborMap[addr]; y {
|
|
return nil, fmt.Errorf("Can't overwrite the exising peer: %s", addr)
|
|
}
|
|
|
|
if server.bgpConfig.Global.Config.Port > 0 {
|
|
for _, l := range server.Listeners(addr) {
|
|
SetTcpMD5SigSockopts(l, addr, c.Config.AuthPassword)
|
|
}
|
|
}
|
|
log.Info("Add a peer configuration for ", addr)
|
|
|
|
peer := NewPeer(&server.bgpConfig.Global, c, server.globalRib, server.policy)
|
|
server.setPolicyByConfig(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.globalRib.ProcessPaths(nil, moded)
|
|
}
|
|
}
|
|
server.neighborMap[addr] = peer
|
|
peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh)
|
|
server.broadcastPeerState(peer, bgp.BGP_FSM_IDLE)
|
|
return nil, nil
|
|
}
|
|
|
|
func (server *BgpServer) handleDelNeighbor(c *config.Neighbor, code, subcode uint8) ([]*SenderMsg, error) {
|
|
addr := c.Config.NeighborAddress
|
|
n, y := server.neighborMap[addr]
|
|
if !y {
|
|
return nil, fmt.Errorf("Can't delete a peer configuration for %s", addr)
|
|
}
|
|
for _, l := range server.Listeners(addr) {
|
|
SetTcpMD5SigSockopts(l, addr, "")
|
|
}
|
|
log.Info("Delete a peer configuration for ", addr)
|
|
|
|
n.fsm.sendNotification(code, subcode, nil, "")
|
|
|
|
go func(addr string) {
|
|
t := time.AfterFunc(time.Minute*5, func() { log.Fatal("failed to free the fsm.h.t for ", addr) })
|
|
n.fsm.h.t.Kill(nil)
|
|
n.fsm.h.t.Wait()
|
|
t.Stop()
|
|
t = time.AfterFunc(time.Minute*5, func() { log.Fatal("failed to free the fsm.h for ", addr) })
|
|
n.fsm.t.Kill(nil)
|
|
n.fsm.t.Wait()
|
|
t.Stop()
|
|
}(addr)
|
|
delete(server.neighborMap, addr)
|
|
m := server.dropPeerAllRoutes(n, n.configuredRFlist())
|
|
return m, nil
|
|
}
|
|
|
|
func (server *BgpServer) handleUpdateNeighbor(c *config.Neighbor) ([]*SenderMsg, bool, error) {
|
|
addr := c.Config.NeighborAddress
|
|
peer := server.neighborMap[addr]
|
|
policyUpdated := false
|
|
|
|
if !peer.fsm.pConf.ApplyPolicy.Equal(&c.ApplyPolicy) {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": addr,
|
|
}).Info("Update ApplyPolicy")
|
|
server.setPolicyByConfig(peer.ID(), c.ApplyPolicy)
|
|
peer.fsm.pConf.ApplyPolicy = c.ApplyPolicy
|
|
policyUpdated = true
|
|
}
|
|
original := peer.fsm.pConf
|
|
|
|
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
|
|
}
|
|
msgs, err := server.handleDelNeighbor(peer.fsm.pConf, bgp.BGP_ERROR_CEASE, sub)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": addr,
|
|
}).Error(err)
|
|
return msgs, policyUpdated, err
|
|
}
|
|
msgs2, err := server.handleAddNeighbor(c)
|
|
msgs = append(msgs, msgs2...)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Peer",
|
|
"Key": addr,
|
|
}).Error(err)
|
|
}
|
|
return msgs, policyUpdated, 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
|
|
}
|
|
|
|
msgs, 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 nil, policyUpdated, err
|
|
}
|
|
return msgs, policyUpdated, nil
|
|
}
|
|
|
|
func (server *BgpServer) handleAddNeighborRequest(grpcReq *GrpcRequest) ([]*SenderMsg, error) {
|
|
arg, ok := grpcReq.Data.(*api.AddNeighborRequest)
|
|
if !ok {
|
|
return []*SenderMsg{}, fmt.Errorf("AddNeighborRequest type assertion failed")
|
|
} else {
|
|
apitoConfig := func(a *api.Peer) (*config.Neighbor, error) {
|
|
pconf := &config.Neighbor{}
|
|
if a.Conf != nil {
|
|
pconf.Config.NeighborAddress = a.Conf.NeighborAddress
|
|
pconf.Config.PeerAs = a.Conf.PeerAs
|
|
if a.Conf.LocalAs == 0 {
|
|
pconf.Config.LocalAs = server.bgpConfig.Global.Config.As
|
|
} else {
|
|
pconf.Config.LocalAs = a.Conf.LocalAs
|
|
}
|
|
if pconf.Config.PeerAs != pconf.Config.LocalAs {
|
|
pconf.Config.PeerType = config.PEER_TYPE_EXTERNAL
|
|
} else {
|
|
pconf.Config.PeerType = config.PEER_TYPE_INTERNAL
|
|
}
|
|
pconf.Config.AuthPassword = a.Conf.AuthPassword
|
|
pconf.Config.RemovePrivateAs = config.RemovePrivateAsOption(a.Conf.RemovePrivateAs)
|
|
pconf.Config.RouteFlapDamping = a.Conf.RouteFlapDamping
|
|
pconf.Config.SendCommunity = config.CommunityType(a.Conf.SendCommunity)
|
|
pconf.Config.Description = a.Conf.Description
|
|
pconf.Config.PeerGroup = a.Conf.PeerGroup
|
|
pconf.Config.NeighborAddress = a.Conf.NeighborAddress
|
|
}
|
|
if a.Timers != nil {
|
|
if a.Timers.Config != nil {
|
|
pconf.Timers.Config.ConnectRetry = float64(a.Timers.Config.ConnectRetry)
|
|
pconf.Timers.Config.HoldTime = float64(a.Timers.Config.HoldTime)
|
|
pconf.Timers.Config.KeepaliveInterval = float64(a.Timers.Config.KeepaliveInterval)
|
|
pconf.Timers.Config.MinimumAdvertisementInterval = float64(a.Timers.Config.MinimumAdvertisementInterval)
|
|
}
|
|
} else {
|
|
pconf.Timers.Config.ConnectRetry = float64(config.DEFAULT_CONNECT_RETRY)
|
|
pconf.Timers.Config.HoldTime = float64(config.DEFAULT_HOLDTIME)
|
|
pconf.Timers.Config.KeepaliveInterval = float64(config.DEFAULT_HOLDTIME / 3)
|
|
}
|
|
if a.RouteReflector != nil {
|
|
pconf.RouteReflector.Config.RouteReflectorClusterId = config.RrClusterIdType(a.RouteReflector.RouteReflectorClusterId)
|
|
pconf.RouteReflector.Config.RouteReflectorClient = a.RouteReflector.RouteReflectorClient
|
|
}
|
|
if a.RouteServer != nil {
|
|
pconf.RouteServer.Config.RouteServerClient = a.RouteServer.RouteServerClient
|
|
}
|
|
if a.ApplyPolicy != nil {
|
|
if a.ApplyPolicy.ImportPolicy != nil {
|
|
pconf.ApplyPolicy.Config.DefaultImportPolicy = config.DefaultPolicyType(a.ApplyPolicy.ImportPolicy.Default)
|
|
for _, p := range a.ApplyPolicy.ImportPolicy.Policies {
|
|
pconf.ApplyPolicy.Config.ImportPolicyList = append(pconf.ApplyPolicy.Config.ImportPolicyList, p.Name)
|
|
}
|
|
}
|
|
if a.ApplyPolicy.ExportPolicy != nil {
|
|
pconf.ApplyPolicy.Config.DefaultExportPolicy = config.DefaultPolicyType(a.ApplyPolicy.ExportPolicy.Default)
|
|
for _, p := range a.ApplyPolicy.ExportPolicy.Policies {
|
|
pconf.ApplyPolicy.Config.ExportPolicyList = append(pconf.ApplyPolicy.Config.ExportPolicyList, p.Name)
|
|
}
|
|
}
|
|
if a.ApplyPolicy.InPolicy != nil {
|
|
pconf.ApplyPolicy.Config.DefaultInPolicy = config.DefaultPolicyType(a.ApplyPolicy.InPolicy.Default)
|
|
for _, p := range a.ApplyPolicy.InPolicy.Policies {
|
|
pconf.ApplyPolicy.Config.InPolicyList = append(pconf.ApplyPolicy.Config.InPolicyList, p.Name)
|
|
}
|
|
}
|
|
}
|
|
if a.Families != nil {
|
|
for _, family := range a.Families {
|
|
name, ok := bgp.AddressFamilyNameMap[bgp.RouteFamily(family)]
|
|
if !ok {
|
|
return pconf, fmt.Errorf("invalid address family: %d", family)
|
|
}
|
|
cAfiSafi := config.AfiSafi{
|
|
Config: config.AfiSafiConfig{
|
|
AfiSafiName: config.AfiSafiType(name),
|
|
},
|
|
}
|
|
pconf.AfiSafis = append(pconf.AfiSafis, cAfiSafi)
|
|
}
|
|
} else {
|
|
if net.ParseIP(a.Conf.NeighborAddress).To4() != nil {
|
|
pconf.AfiSafis = []config.AfiSafi{
|
|
config.AfiSafi{
|
|
Config: config.AfiSafiConfig{
|
|
AfiSafiName: "ipv4-unicast",
|
|
},
|
|
},
|
|
}
|
|
} else {
|
|
pconf.AfiSafis = []config.AfiSafi{
|
|
config.AfiSafi{
|
|
Config: config.AfiSafiConfig{
|
|
AfiSafiName: "ipv6-unicast",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
if a.Transport != nil {
|
|
pconf.Transport.Config.LocalAddress = a.Transport.LocalAddress
|
|
pconf.Transport.Config.PassiveMode = a.Transport.PassiveMode
|
|
}
|
|
if a.EbgpMultihop != nil {
|
|
pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled
|
|
pconf.EbgpMultihop.Config.MultihopTtl = uint8(a.EbgpMultihop.MultihopTtl)
|
|
}
|
|
return pconf, nil
|
|
}
|
|
c, err := apitoConfig(arg.Peer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return server.handleAddNeighbor(c)
|
|
}
|
|
}
|
|
|
|
func (server *BgpServer) handleDeleteNeighborRequest(grpcReq *GrpcRequest) ([]*SenderMsg, error) {
|
|
arg := grpcReq.Data.(*api.DeleteNeighborRequest)
|
|
return server.handleDelNeighbor(&config.Neighbor{
|
|
Config: config.NeighborConfig{
|
|
NeighborAddress: arg.Peer.Conf.NeighborAddress,
|
|
},
|
|
}, bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED)
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcAddDefinedSet(grpcReq *GrpcRequest) (*api.AddDefinedSetResponse, error) {
|
|
arg := grpcReq.Data.(*api.AddDefinedSetRequest)
|
|
set := arg.Set
|
|
typ := table.DefinedType(set.Type)
|
|
name := set.Name
|
|
var err error
|
|
m, ok := server.policy.DefinedSetMap[typ]
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid defined-set type: %d", typ)
|
|
}
|
|
d, ok := m[name]
|
|
s, err := table.NewDefinedSetFromApiStruct(set)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
err = d.Append(s)
|
|
} else {
|
|
m[name] = s
|
|
}
|
|
return &api.AddDefinedSetResponse{}, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcDeleteDefinedSet(grpcReq *GrpcRequest) (*api.DeleteDefinedSetResponse, error) {
|
|
arg := grpcReq.Data.(*api.DeleteDefinedSetRequest)
|
|
set := arg.Set
|
|
typ := table.DefinedType(set.Type)
|
|
name := set.Name
|
|
var err error
|
|
m, ok := server.policy.DefinedSetMap[typ]
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid defined-set type: %d", typ)
|
|
}
|
|
d, ok := m[name]
|
|
if !ok {
|
|
return nil, fmt.Errorf("not found defined-set: %s", name)
|
|
}
|
|
s, err := table.NewDefinedSetFromApiStruct(set)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if arg.All {
|
|
if server.policy.InUse(d) {
|
|
return nil, fmt.Errorf("can't delete. defined-set %s is in use", name)
|
|
}
|
|
delete(m, name)
|
|
} else {
|
|
err = d.Remove(s)
|
|
}
|
|
return &api.DeleteDefinedSetResponse{}, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcReplaceDefinedSet(grpcReq *GrpcRequest) (*api.ReplaceDefinedSetResponse, error) {
|
|
arg := grpcReq.Data.(*api.ReplaceDefinedSetRequest)
|
|
set := arg.Set
|
|
typ := table.DefinedType(set.Type)
|
|
name := set.Name
|
|
var err error
|
|
m, ok := server.policy.DefinedSetMap[typ]
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid defined-set type: %d", typ)
|
|
}
|
|
d, ok := m[name]
|
|
if !ok {
|
|
return nil, fmt.Errorf("not found defined-set: %s", name)
|
|
}
|
|
s, err := table.NewDefinedSetFromApiStruct(set)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &api.ReplaceDefinedSetResponse{}, d.Replace(s)
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcGetStatement(grpcReq *GrpcRequest) (*api.GetStatementResponse, error) {
|
|
l := make([]*api.Statement, 0)
|
|
for _, s := range server.policy.StatementMap {
|
|
l = append(l, s.ToApiStruct())
|
|
}
|
|
return &api.GetStatementResponse{Statements: l}, nil
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcAddStatement(grpcReq *GrpcRequest) (*api.AddStatementResponse, error) {
|
|
var err error
|
|
arg := grpcReq.Data.(*api.AddStatementRequest)
|
|
s, err := table.NewStatementFromApiStruct(arg.Statement, server.policy.DefinedSetMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m := server.policy.StatementMap
|
|
name := s.Name
|
|
if d, ok := m[name]; ok {
|
|
err = d.Add(s)
|
|
} else {
|
|
m[name] = s
|
|
}
|
|
return &api.AddStatementResponse{}, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcDeleteStatement(grpcReq *GrpcRequest) (*api.DeleteStatementResponse, error) {
|
|
var err error
|
|
arg := grpcReq.Data.(*api.DeleteStatementRequest)
|
|
s, err := table.NewStatementFromApiStruct(arg.Statement, server.policy.DefinedSetMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m := server.policy.StatementMap
|
|
name := s.Name
|
|
if d, ok := m[name]; ok {
|
|
if arg.All {
|
|
if server.policy.StatementInUse(d) {
|
|
err = fmt.Errorf("can't delete. statement %s is in use", name)
|
|
} else {
|
|
delete(m, name)
|
|
}
|
|
} else {
|
|
err = d.Remove(s)
|
|
}
|
|
} else {
|
|
err = fmt.Errorf("not found statement: %s", name)
|
|
}
|
|
return &api.DeleteStatementResponse{}, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcReplaceStatement(grpcReq *GrpcRequest) (*api.ReplaceStatementResponse, error) {
|
|
var err error
|
|
arg := grpcReq.Data.(*api.ReplaceStatementRequest)
|
|
s, err := table.NewStatementFromApiStruct(arg.Statement, server.policy.DefinedSetMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m := server.policy.StatementMap
|
|
name := s.Name
|
|
if d, ok := m[name]; ok {
|
|
err = d.Replace(s)
|
|
} else {
|
|
err = fmt.Errorf("not found statement: %s", name)
|
|
}
|
|
return &api.ReplaceStatementResponse{}, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcGetPolicy(grpcReq *GrpcRequest) (*api.GetPolicyResponse, error) {
|
|
policies := make([]*api.Policy, 0, len(server.policy.PolicyMap))
|
|
for _, s := range server.policy.PolicyMap {
|
|
policies = append(policies, s.ToApiStruct())
|
|
}
|
|
return &api.GetPolicyResponse{Policies: policies}, nil
|
|
}
|
|
|
|
func (server *BgpServer) policyInUse(x *table.Policy) bool {
|
|
for _, peer := range server.neighborMap {
|
|
for _, dir := range []table.PolicyDirection{table.POLICY_DIRECTION_IN, table.POLICY_DIRECTION_EXPORT, table.POLICY_DIRECTION_EXPORT} {
|
|
for _, y := range server.policy.GetPolicy(peer.ID(), dir) {
|
|
if x.Name() == y.Name() {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, dir := range []table.PolicyDirection{table.POLICY_DIRECTION_EXPORT, table.POLICY_DIRECTION_EXPORT} {
|
|
for _, y := range server.policy.GetPolicy(table.GLOBAL_RIB_NAME, dir) {
|
|
if x.Name() == y.Name() {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcAddPolicy(grpcReq *GrpcRequest) (*api.AddPolicyResponse, error) {
|
|
policyMutex.Lock()
|
|
defer policyMutex.Unlock()
|
|
rsp := &api.AddPolicyResponse{}
|
|
arg := grpcReq.Data.(*api.AddPolicyRequest)
|
|
x, err := table.NewPolicyFromApiStruct(arg.Policy, server.policy.DefinedSetMap)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
pMap := server.policy.PolicyMap
|
|
sMap := server.policy.StatementMap
|
|
name := x.Name()
|
|
y, ok := pMap[name]
|
|
if arg.ReferExistingStatements {
|
|
err = x.FillUp(sMap)
|
|
} else {
|
|
for _, s := range x.Statements {
|
|
if _, ok := sMap[s.Name]; ok {
|
|
return rsp, fmt.Errorf("statement %s already defined", s.Name)
|
|
}
|
|
sMap[s.Name] = s
|
|
}
|
|
}
|
|
if ok {
|
|
err = y.Add(x)
|
|
} else {
|
|
pMap[name] = x
|
|
}
|
|
return &api.AddPolicyResponse{}, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcDeletePolicy(grpcReq *GrpcRequest) (*api.DeletePolicyResponse, error) {
|
|
policyMutex.Lock()
|
|
defer policyMutex.Unlock()
|
|
rsp := &api.DeletePolicyResponse{}
|
|
arg := grpcReq.Data.(*api.DeletePolicyRequest)
|
|
x, err := table.NewPolicyFromApiStruct(arg.Policy, server.policy.DefinedSetMap)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
pMap := server.policy.PolicyMap
|
|
sMap := server.policy.StatementMap
|
|
name := x.Name()
|
|
y, ok := pMap[name]
|
|
if !ok {
|
|
return rsp, fmt.Errorf("not found policy: %s", name)
|
|
}
|
|
if arg.All {
|
|
if server.policyInUse(y) {
|
|
return rsp, fmt.Errorf("can't delete. policy %s is in use", name)
|
|
}
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Policy",
|
|
"Key": name,
|
|
}).Debug("delete policy")
|
|
delete(pMap, name)
|
|
} else {
|
|
err = y.Remove(x)
|
|
}
|
|
if err == nil && !arg.PreserveStatements {
|
|
for _, s := range y.Statements {
|
|
if !server.policy.StatementInUse(s) {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Policy",
|
|
"Key": s.Name,
|
|
}).Debug("delete unused statement")
|
|
delete(sMap, s.Name)
|
|
}
|
|
}
|
|
}
|
|
return rsp, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcReplacePolicy(grpcReq *GrpcRequest) (*api.ReplacePolicyResponse, error) {
|
|
policyMutex.Lock()
|
|
defer policyMutex.Unlock()
|
|
rsp := &api.ReplacePolicyResponse{}
|
|
arg := grpcReq.Data.(*api.ReplacePolicyRequest)
|
|
x, err := table.NewPolicyFromApiStruct(arg.Policy, server.policy.DefinedSetMap)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
pMap := server.policy.PolicyMap
|
|
sMap := server.policy.StatementMap
|
|
name := x.Name()
|
|
y, ok := pMap[name]
|
|
if !ok {
|
|
return rsp, fmt.Errorf("not found policy: %s", name)
|
|
}
|
|
if arg.ReferExistingStatements {
|
|
if err = x.FillUp(sMap); err != nil {
|
|
return rsp, err
|
|
}
|
|
} else {
|
|
for _, s := range x.Statements {
|
|
if _, ok := sMap[s.Name]; ok {
|
|
return rsp, fmt.Errorf("statement %s already defined", s.Name)
|
|
}
|
|
sMap[s.Name] = s
|
|
}
|
|
}
|
|
|
|
err = y.Replace(x)
|
|
if err == nil && !arg.PreserveStatements {
|
|
for _, s := range y.Statements {
|
|
if !server.policy.StatementInUse(s) {
|
|
log.WithFields(log.Fields{
|
|
"Topic": "Policy",
|
|
"Key": s.Name,
|
|
}).Debug("delete unused statement")
|
|
delete(sMap, s.Name)
|
|
}
|
|
}
|
|
}
|
|
return rsp, err
|
|
}
|
|
|
|
func (server *BgpServer) getPolicyInfo(a *api.PolicyAssignment) (string, table.PolicyDirection, error) {
|
|
switch a.Resource {
|
|
case api.Resource_GLOBAL:
|
|
switch a.Type {
|
|
case api.PolicyType_IMPORT:
|
|
return table.GLOBAL_RIB_NAME, table.POLICY_DIRECTION_IMPORT, nil
|
|
case api.PolicyType_EXPORT:
|
|
return table.GLOBAL_RIB_NAME, table.POLICY_DIRECTION_EXPORT, nil
|
|
default:
|
|
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid policy type")
|
|
}
|
|
case api.Resource_LOCAL:
|
|
peer, ok := server.neighborMap[a.Name]
|
|
if !ok {
|
|
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("not found peer %s", a.Name)
|
|
}
|
|
if !peer.isRouteServerClient() {
|
|
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("non-rs-client peer %s doesn't have per peer policy", a.Name)
|
|
}
|
|
switch a.Type {
|
|
case api.PolicyType_IN:
|
|
return peer.ID(), table.POLICY_DIRECTION_IN, nil
|
|
case api.PolicyType_IMPORT:
|
|
return peer.ID(), table.POLICY_DIRECTION_IMPORT, nil
|
|
case api.PolicyType_EXPORT:
|
|
return peer.ID(), table.POLICY_DIRECTION_EXPORT, nil
|
|
default:
|
|
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid policy type")
|
|
}
|
|
default:
|
|
return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid resource type")
|
|
}
|
|
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcGetPolicyAssignment(grpcReq *GrpcRequest) (*api.GetPolicyAssignmentResponse, error) {
|
|
rsp := &api.GetPolicyAssignmentResponse{}
|
|
id, dir, err := server.getPolicyInfo(grpcReq.Data.(*api.GetPolicyAssignmentRequest).Assignment)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
rsp.Assignment.Default = server.policy.GetDefaultPolicy(id, dir).ToApiStruct()
|
|
ps := server.policy.GetPolicy(id, dir)
|
|
rsp.Assignment.Policies = make([]*api.Policy, 0, len(ps))
|
|
for _, x := range ps {
|
|
rsp.Assignment.Policies = append(rsp.Assignment.Policies, x.ToApiStruct())
|
|
}
|
|
return rsp, nil
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcAddPolicyAssignment(grpcReq *GrpcRequest) (*api.AddPolicyAssignmentResponse, error) {
|
|
var err error
|
|
var dir table.PolicyDirection
|
|
var id string
|
|
rsp := &api.AddPolicyAssignmentResponse{}
|
|
policyMutex.Lock()
|
|
defer policyMutex.Unlock()
|
|
arg := grpcReq.Data.(*api.AddPolicyAssignmentRequest)
|
|
assignment := arg.Assignment
|
|
id, dir, err = server.getPolicyInfo(assignment)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
ps := make([]*table.Policy, 0, len(assignment.Policies))
|
|
seen := make(map[string]bool)
|
|
for _, x := range assignment.Policies {
|
|
p, ok := server.policy.PolicyMap[x.Name]
|
|
if !ok {
|
|
return rsp, fmt.Errorf("not found policy %s", x.Name)
|
|
}
|
|
if seen[x.Name] {
|
|
return rsp, fmt.Errorf("duplicated policy %s", x.Name)
|
|
}
|
|
seen[x.Name] = true
|
|
ps = append(ps, p)
|
|
}
|
|
cur := server.policy.GetPolicy(id, dir)
|
|
if cur == nil {
|
|
err = server.policy.SetPolicy(id, dir, ps)
|
|
} else {
|
|
seen = make(map[string]bool)
|
|
ps = append(cur, ps...)
|
|
for _, x := range ps {
|
|
if seen[x.Name()] {
|
|
return rsp, fmt.Errorf("duplicated policy %s", x.Name())
|
|
}
|
|
seen[x.Name()] = true
|
|
}
|
|
err = server.policy.SetPolicy(id, dir, ps)
|
|
}
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
|
|
switch assignment.Default {
|
|
case api.RouteAction_ACCEPT:
|
|
err = server.policy.SetDefaultPolicy(id, dir, table.ROUTE_TYPE_ACCEPT)
|
|
case api.RouteAction_REJECT:
|
|
err = server.policy.SetDefaultPolicy(id, dir, table.ROUTE_TYPE_REJECT)
|
|
}
|
|
return rsp, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcDeletePolicyAssignment(grpcReq *GrpcRequest) (*api.DeletePolicyAssignmentResponse, error) {
|
|
var err error
|
|
var dir table.PolicyDirection
|
|
var id string
|
|
policyMutex.Lock()
|
|
defer policyMutex.Unlock()
|
|
rsp := &api.DeletePolicyAssignmentResponse{}
|
|
arg := grpcReq.Data.(*api.DeletePolicyAssignmentRequest)
|
|
assignment := arg.Assignment
|
|
id, dir, err = server.getPolicyInfo(assignment)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
ps := make([]*table.Policy, 0, len(assignment.Policies))
|
|
seen := make(map[string]bool)
|
|
for _, x := range assignment.Policies {
|
|
p, ok := server.policy.PolicyMap[x.Name]
|
|
if !ok {
|
|
return rsp, fmt.Errorf("not found policy %s", x.Name)
|
|
}
|
|
if seen[x.Name] {
|
|
return rsp, fmt.Errorf("duplicated policy %s", x.Name)
|
|
}
|
|
seen[x.Name] = true
|
|
ps = append(ps, p)
|
|
}
|
|
cur := server.policy.GetPolicy(id, dir)
|
|
|
|
if arg.All {
|
|
err = server.policy.SetPolicy(id, dir, nil)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
err = server.policy.SetDefaultPolicy(id, dir, table.ROUTE_TYPE_NONE)
|
|
} else {
|
|
n := make([]*table.Policy, 0, len(cur)-len(ps))
|
|
for _, y := range cur {
|
|
found := false
|
|
for _, x := range ps {
|
|
if x.Name() == y.Name() {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
n = append(n, y)
|
|
}
|
|
}
|
|
err = server.policy.SetPolicy(id, dir, n)
|
|
}
|
|
return rsp, err
|
|
}
|
|
|
|
func (server *BgpServer) handleGrpcReplacePolicyAssignment(grpcReq *GrpcRequest) (*api.ReplacePolicyAssignmentResponse, error) {
|
|
var err error
|
|
var dir table.PolicyDirection
|
|
var id string
|
|
policyMutex.Lock()
|
|
defer policyMutex.Unlock()
|
|
rsp := &api.ReplacePolicyAssignmentResponse{}
|
|
arg := grpcReq.Data.(*api.ReplacePolicyAssignmentRequest)
|
|
assignment := arg.Assignment
|
|
id, dir, err = server.getPolicyInfo(assignment)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
ps := make([]*table.Policy, 0, len(assignment.Policies))
|
|
seen := make(map[string]bool)
|
|
for _, x := range assignment.Policies {
|
|
p, ok := server.policy.PolicyMap[x.Name]
|
|
if !ok {
|
|
return rsp, fmt.Errorf("not found policy %s", x.Name)
|
|
}
|
|
if seen[x.Name] {
|
|
return rsp, fmt.Errorf("duplicated policy %s", x.Name)
|
|
}
|
|
seen[x.Name] = true
|
|
ps = append(ps, p)
|
|
}
|
|
server.policy.GetPolicy(id, dir)
|
|
err = server.policy.SetPolicy(id, dir, ps)
|
|
if err != nil {
|
|
return rsp, err
|
|
}
|
|
switch assignment.Default {
|
|
case api.RouteAction_ACCEPT:
|
|
err = server.policy.SetDefaultPolicy(id, dir, table.ROUTE_TYPE_ACCEPT)
|
|
case api.RouteAction_REJECT:
|
|
err = server.policy.SetDefaultPolicy(id, dir, table.ROUTE_TYPE_REJECT)
|
|
}
|
|
return rsp, err
|
|
}
|
|
|
|
func grpcDone(grpcReq *GrpcRequest, e error) {
|
|
result := &GrpcResponse{
|
|
ResponseErr: e,
|
|
}
|
|
grpcReq.ResponseCh <- result
|
|
close(grpcReq.ResponseCh)
|
|
}
|
|
|
|
func (server *BgpServer) handleEnableMrtRequest(grpcReq *GrpcRequest) {
|
|
arg := grpcReq.Data.(*api.EnableMrtRequest)
|
|
if _, y := server.watchers[WATCHER_MRT]; y {
|
|
grpcDone(grpcReq, fmt.Errorf("already enabled"))
|
|
return
|
|
}
|
|
if arg.Interval != 0 && arg.Interval < 30 {
|
|
log.Info("minimum mrt dump interval is 30 seconds")
|
|
arg.Interval = 30
|
|
}
|
|
w, err := newMrtWatcher(arg.DumpType, arg.Filename, arg.Interval)
|
|
if err == nil {
|
|
server.watchers[WATCHER_MRT] = w
|
|
}
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: &api.EnableMrtResponse{},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
}
|
|
|
|
func (server *BgpServer) handleDisableMrtRequest(grpcReq *GrpcRequest) {
|
|
w, y := server.watchers[WATCHER_MRT]
|
|
if !y {
|
|
grpcDone(grpcReq, fmt.Errorf("not enabled yet"))
|
|
return
|
|
}
|
|
|
|
delete(server.watchers, WATCHER_MRT)
|
|
w.stop()
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
Data: &api.DisableMrtResponse{},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
}
|
|
|
|
func (server *BgpServer) handleAddBmp(grpcReq *GrpcRequest) {
|
|
var c *config.BmpServerConfig
|
|
switch arg := grpcReq.Data.(type) {
|
|
case *api.AddBmpRequest:
|
|
c = &config.BmpServerConfig{
|
|
Address: arg.Address,
|
|
Port: arg.Port,
|
|
RouteMonitoringPolicy: config.BmpRouteMonitoringPolicyType(arg.Type),
|
|
}
|
|
case *config.BmpServerConfig:
|
|
c = arg
|
|
}
|
|
|
|
w, y := server.watchers[WATCHER_BMP]
|
|
if !y {
|
|
w, _ = newBmpWatcher(server.GrpcReqCh)
|
|
server.watchers[WATCHER_BMP] = w
|
|
}
|
|
|
|
err := w.(*bmpWatcher).addServer(*c)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: &api.AddBmpResponse{},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
}
|
|
|
|
func (server *BgpServer) handleDeleteBmp(grpcReq *GrpcRequest) {
|
|
var c *config.BmpServerConfig
|
|
switch arg := grpcReq.Data.(type) {
|
|
case *api.DeleteBmpRequest:
|
|
c = &config.BmpServerConfig{
|
|
Address: arg.Address,
|
|
Port: arg.Port,
|
|
}
|
|
case *config.BmpServerConfig:
|
|
c = arg
|
|
}
|
|
|
|
if w, y := server.watchers[WATCHER_BMP]; y {
|
|
err := w.(*bmpWatcher).deleteServer(*c)
|
|
grpcReq.ResponseCh <- &GrpcResponse{
|
|
ResponseErr: err,
|
|
Data: &api.DeleteBmpResponse{},
|
|
}
|
|
close(grpcReq.ResponseCh)
|
|
} else {
|
|
grpcDone(grpcReq, fmt.Errorf("bmp not configured"))
|
|
}
|
|
}
|
|
|
|
func (server *BgpServer) handleValidateRib(grpcReq *GrpcRequest) {
|
|
arg := grpcReq.Data.(*api.ValidateRibRequest)
|
|
for _, rf := range server.globalRib.GetRFlist() {
|
|
if t, ok := server.globalRib.Tables[rf]; ok {
|
|
dsts := t.GetDestinations()
|
|
if arg.Prefix != "" {
|
|
_, prefix, _ := net.ParseCIDR(arg.Prefix)
|
|
if dst := t.GetDestination(prefix.String()); dst != nil {
|
|
dsts = map[string]*table.Destination{prefix.String(): dst}
|
|
}
|
|
}
|
|
for _, dst := range dsts {
|
|
server.roaManager.validate(dst.GetAllKnownPathList())
|
|
}
|
|
}
|
|
}
|
|
result := &GrpcResponse{
|
|
Data: &api.ValidateRibResponse{},
|
|
}
|
|
grpcReq.ResponseCh <- result
|
|
close(grpcReq.ResponseCh)
|
|
}
|
|
|
|
func (server *BgpServer) handleModRpki(grpcReq *GrpcRequest) {
|
|
done := func(grpcReq *GrpcRequest, data interface{}, e error) {
|
|
result := &GrpcResponse{
|
|
ResponseErr: e,
|
|
Data: data,
|
|
}
|
|
grpcReq.ResponseCh <- result
|
|
close(grpcReq.ResponseCh)
|
|
}
|
|
|
|
switch arg := grpcReq.Data.(type) {
|
|
case *api.AddRpkiRequest:
|
|
done(grpcReq, &api.AddRpkiResponse{}, server.roaManager.AddServer(net.JoinHostPort(arg.Address, strconv.Itoa(int(arg.Port))), arg.Lifetime))
|
|
case *api.DeleteRpkiRequest:
|
|
done(grpcReq, &api.DeleteRpkiResponse{}, server.roaManager.DeleteServer(arg.Address))
|
|
case *api.EnableRpkiRequest:
|
|
done(grpcReq, &api.EnableRpkiResponse{}, server.roaManager.Enable(arg.Address))
|
|
case *api.DisableRpkiRequest:
|
|
done(grpcReq, &api.DisableRpkiResponse{}, server.roaManager.Disable(arg.Address))
|
|
case *api.ResetRpkiRequest:
|
|
done(grpcReq, &api.ResetRpkiResponse{}, server.roaManager.Reset(arg.Address))
|
|
case *api.SoftResetRpkiRequest:
|
|
done(grpcReq, &api.SoftResetRpkiResponse{}, server.roaManager.SoftReset(arg.Address))
|
|
}
|
|
}
|