Skip to content

Commit

Permalink
BMP: Allow ignoring BGP sessions based on peer ASN (#379)
Browse files Browse the repository at this point in the history
  • Loading branch information
taktv6 authored Sep 7, 2022
1 parent 634c76c commit bb8b173
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 13 deletions.
3 changes: 2 additions & 1 deletion cmd/ris/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (

// RISConfig is the config of RIS instance
type RISConfig struct {
BMPServers []BMPServer `yaml:"bmp_servers"`
BMPServers []BMPServer `yaml:"bmp_servers"`
IgnorePeerASNs []uint32 `yaml:"ignore_peer_asns"`
}

// BMPServer represent a BMP enable Router
Expand Down
1 change: 1 addition & 0 deletions cmd/ris/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func main() {
b := server.NewServer(server.BMPServerConfig{
KeepalivePeriod: time.Duration(*tcpKeepaliveInterval) * time.Second,
AcceptAny: *allowAny,
IgnorePeerASNs: cfg.IgnorePeerASNs,
})

if *bmpListenAddr != "" {
Expand Down
54 changes: 44 additions & 10 deletions protocols/bgp/server/bmp_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ type Router struct {
ribClients map[afiClient]struct{}
ribClientsMu sync.Mutex
adjRIBInFactory adjRIBInFactoryI
ignorePeerASNs []uint32
ignoredPeers map[bnet.IP]struct{}

counters routerCounters
}
Expand All @@ -72,7 +74,7 @@ type neighbor struct {
opt *packet.DecodeOptions
}

func newRouter(addr net.IP, port uint16, passive bool, arif adjRIBInFactoryI) *Router {
func newRouter(addr net.IP, port uint16, passive bool, arif adjRIBInFactoryI, ignorePeerASNs []uint32) *Router {
return &Router{
address: addr,
port: port,
Expand All @@ -86,6 +88,8 @@ func newRouter(addr net.IP, port uint16, passive bool, arif adjRIBInFactoryI) *R
stop: make(chan struct{}),
ribClients: make(map[afiClient]struct{}),
adjRIBInFactory: arif,
ignorePeerASNs: ignorePeerASNs,
ignoredPeers: make(map[bnet.IP]struct{}),
}
}

Expand Down Expand Up @@ -214,6 +218,10 @@ func (r *Router) processRouteMonitoringMsg(msg *bmppkt.RouteMonitoringMsg) {
return
}

if _, exists := r.ignoredPeers[peerAddrToBNetAddr(msg.PerPeerHeader.PeerAddress, msg.PerPeerHeader.GetIPVersion())]; exists {
return
}

n := r.neighborManager.getNeighbor(msg.PerPeerHeader.PeerDistinguisher, msg.PerPeerHeader.PeerAddress)
if n == nil {
log.Errorf("Received route monitoring message for non-existent neighbor %d/%v on %s", msg.PerPeerHeader.PeerDistinguisher, msg.PerPeerHeader.PeerAddress, r.address.String())
Expand Down Expand Up @@ -306,12 +314,39 @@ func (r *Router) processPeerDownNotification(msg *bmppkt.PeerDownNotification) {
}).Infof("peer down notification received")
atomic.AddUint64(&r.counters.peerDownNotificationMessages, 1)

peerAddr := peerAddrToBNetAddr(msg.PerPeerHeader.PeerAddress, msg.PerPeerHeader.GetIPVersion())
if _, exists := r.ignoredPeers[peerAddr]; exists {
delete(r.ignoredPeers, peerAddr)
return
}

err := r.neighborManager.neighborDown(msg.PerPeerHeader.PeerDistinguisher, msg.PerPeerHeader.PeerAddress)
if err != nil {
log.Errorf("Failed to process peer down notification: %v", err)
}
}

func peerAddrToBNetAddr(a [16]byte, ipVersion uint8) bnet.IP {
addrLen := net.IPv4len
if ipVersion == 6 {
addrLen = net.IPv6len
}

// bnet.IPFromBytes can only fail if length of argument is not 4 or 16. However, length is ensured here.
ip, _ := bnet.IPFromBytes(a[16-addrLen:])
return ip
}

func (r *Router) isIgnoredPeerASN(asn uint32) bool {
for _, x := range r.ignorePeerASNs {
if x == asn {
return true
}
}

return false
}

func (r *Router) processPeerUpNotification(msg *bmppkt.PeerUpNotification) error {
atomic.AddUint64(&r.counters.peerUpNotificationMessages, 1)
log.WithFields(log.Fields{
Expand All @@ -321,6 +356,14 @@ func (r *Router) processPeerUpNotification(msg *bmppkt.PeerUpNotification) error
"peer_address": addrToNetIP(msg.PerPeerHeader.PeerAddress).String(),
}).Infof("peer up notification received")

peerAddress := peerAddrToBNetAddr(msg.PerPeerHeader.PeerAddress, msg.PerPeerHeader.GetIPVersion())
localAddress := peerAddrToBNetAddr(msg.LocalAddress, msg.PerPeerHeader.GetIPVersion())

if r.isIgnoredPeerASN(msg.PerPeerHeader.PeerAS) {
r.ignoredPeers[peerAddress] = struct{}{}
return nil
}

if len(msg.SentOpenMsg) < packet.MinOpenLen {
return fmt.Errorf("received peer up notification for %v: Invalid sent open message: %v", msg.PerPeerHeader.PeerAddress, msg.SentOpenMsg)
}
Expand All @@ -339,15 +382,6 @@ func (r *Router) processPeerUpNotification(msg *bmppkt.PeerUpNotification) error
return fmt.Errorf("unable to decode received open message sent from %v to %v: %w", msg.PerPeerHeader.PeerAddress, r.address.String(), err)
}

addrLen := net.IPv4len
if msg.PerPeerHeader.GetIPVersion() == 6 {
addrLen = net.IPv6len
}

// bnet.IPFromBytes can only fail if length of argument is not 4 or 16. However, length is ensured here.
peerAddress, _ := bnet.IPFromBytes(msg.PerPeerHeader.PeerAddress[16-addrLen:])
localAddress, _ := bnet.IPFromBytes(msg.LocalAddress[16-addrLen:])

fsm := &FSM{
isBMP: true,
ribsInitialized: true,
Expand Down
5 changes: 4 additions & 1 deletion protocols/bgp/server/bmp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ type BMPServer struct {
listener net.Listener
adjRIBInFactory adjRIBInFactoryI
acceptAny bool
ignorePeerASNs []uint32
}

type BMPServerConfig struct {
KeepalivePeriod time.Duration
AcceptAny bool
IgnorePeerASNs []uint32
}

type afiClient struct {
Expand All @@ -54,6 +56,7 @@ func NewServer(cfg BMPServerConfig) *BMPServer {
keepalivePeriod: cfg.KeepalivePeriod,
adjRIBInFactory: adjRIBInFactory{},
acceptAny: cfg.AcceptAny,
ignorePeerASNs: cfg.IgnorePeerASNs,
}

b.metrics = &bmpMetricsService{b}
Expand Down Expand Up @@ -226,7 +229,7 @@ func (b *BMPServer) AddRouter(addr net.IP, port uint16, passive bool, dynamic bo
}

func (b *BMPServer) _addRouter(addr net.IP, port uint16, passive bool, dynamic bool) error {
r := newRouter(addr, port, passive, b.adjRIBInFactory)
r := newRouter(addr, port, passive, b.adjRIBInFactory, b.ignorePeerASNs)
if _, exists := b.routers[r.address.String()]; exists {
return fmt.Errorf("router %s already configured,", r.address.String())
}
Expand Down
94 changes: 93 additions & 1 deletion protocols/bgp/server/bmp_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import (
"time"

bnet "github.com/bio-routing/bio-rd/net"
"github.com/stretchr/testify/assert"
)

func TestBMPServer(t *testing.T) {
srv := NewServer(BMPServerConfig{
KeepalivePeriod: time.Second,
})

rtr := newRouter(net.IP{10, 0, 255, 1}, 30119, false, &adjRIBInFactory{})
rtr := newRouter(net.IP{10, 0, 255, 1}, 30119, false, &adjRIBInFactory{}, []uint32{13335})
_, pipe := net.Pipe()
rtr.con = pipe
srv.routers[rtr.address.String()] = rtr
Expand Down Expand Up @@ -176,6 +177,97 @@ func TestBMPServer(t *testing.T) {
}
rtr.processMsg(peerUpC)

peerUpD := []byte{
3, // Version
0, 0, 0, 126, // Length
3, // Msg Type (peer up)

0, // Peer Type (global instance peer)
0, // Peer Flags
0, 0, 0, 0, 0, 0, 0, 0, // Peer Distinguisher
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 1, 2, 100, // Peer Address (10.1.2.100)
0, 0, 52, 23, // Peer AS = 13335
0, 0, 0, 233, // Peer BGP ID = 240
0, 0, 0, 0, // Timestamp seconds
0, 0, 0, 0, // Timestamp microseconds

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 1, 2, 200, // Local Address (10.1.2.200)
0, 222, // Local Port
0, 179, // Remote Port

// Sent OPEN
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // Marker
0, 29, // Length
1, // Type (OPEN)
4, // BGP Version
0, 100, // ASN
0, 180, // Hold Time
1, 0, 0, 100, // BGP ID
0, // Ops Param Len

// Received OPEN
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // Marker
0, 29, // Length
1, // Type (OPEN)
4, // BGP Version
0, 233, // ASN
0, 180, // Hold Time
1, 0, 0, 222, // BGP ID
0, // Ops Param Len
}
rtr.processMsg(peerUpD)

assert.Equal(t, rtr.ignoredPeers, map[bnet.IP]struct{}{
bnet.IPv4FromOctets(10, 1, 2, 100): {},
})

updateD1 := []byte{
3, // Version
0, 0, 0, 100, // Length
0, // Msg Type (route monitoring)

0, // Peer Type (global instance peer)
0b01100000, // Peer Flags
0, 0, 0, 0, 0, 0, 0, 123, // Peer Distinguisher
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 1, 2, 100, // Peer Address (10.1.2.100)
0, 0, 0, 200, // Peer AS = 200
0, 0, 0, 200, // Peer BGP ID = 200
0, 0, 0, 0, // Timestamp seconds
0, 0, 0, 0, // Timestamp microseconds

255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // Marker
0, 52, // Length
2, // Type (UPDATE)

0, 0, // Withdraw length
0, 27, // Total Path Attribute Length

0, // Attribute flags
3, // Attribute Type code (Next Hop)
4, // Length
10, 0, 0, 0,

255, // Attribute flags
1, // Attribute Type code (ORIGIN)
0, 1, // Length
2, // INCOMPLETE

0, // Attribute flags
2, // Attribute Type code (AS Path)
12, // Length
2, // Type = AS_SEQUENCE
2, // Path Segment Length
59, 65, // AS15169
12, 248, // AS3320
1, // Type = AS_SET
2, // Path Segment Length
59, 65, // AS15169
12, 248, // AS3320

8, 20, // 20.0.0.0/8
}
rtr.processMsg(updateD1)

aaaaVRFs = aaaa.GetVRFs()
if len(aaaaVRFs) != 2 {
t.Errorf("Unexpected VRF count for router AAAA: %d", len(aaaaVRFs))
Expand Down

0 comments on commit bb8b173

Please sign in to comment.