diff --git a/examples/bmp/BUILD.bazel b/examples/bmp/BUILD.bazel index 63c52adaba65907513eda97e260a79955d622d34..66cf5d6efb5b53bdb99944da2591fc4997b753b6 100644 --- a/examples/bmp/BUILD.bazel +++ b/examples/bmp/BUILD.bazel @@ -6,7 +6,7 @@ go_library( importpath = "github.com/bio-routing/bio-rd/examples/bmp", visibility = ["//visibility:private"], deps = [ - "//protocols/bmp/server:go_default_library", + "//protocols/bgp/server:go_default_library", "//routingtable/locRIB:go_default_library", "//vendor/github.com/sirupsen/logrus:go_default_library", ], diff --git a/examples/bmp/main_bmp.go b/examples/bmp/main_bmp.go index 146fa4244f267d3fadfcf7fb1b5815524cfcc909..63de954fed0cbefea47ddd398d522b5d96e00b29 100644 --- a/examples/bmp/main_bmp.go +++ b/examples/bmp/main_bmp.go @@ -5,7 +5,7 @@ import ( "net" "time" - "github.com/bio-routing/bio-rd/protocols/bmp/server" + "github.com/bio-routing/bio-rd/protocols/bgp/server" "github.com/bio-routing/bio-rd/routingtable/locRIB" "github.com/sirupsen/logrus" ) @@ -13,13 +13,14 @@ import ( func main() { logrus.Printf("This is a BMP speaker\n") - rib := locRIB.New() + rib4 := locRIB.New() + rib6 := locRIB.New() b := server.NewServer() - b.AddRouter(net.IP{127, 0, 0, 1}, 1234, rib, nil) + b.AddRouter(net.IP{10, 0, 255, 0}, 30119, rib4, rib6) go func() { for { - fmt.Printf("LocRIB count: %d\n", rib.Count()) + fmt.Printf("LocRIB4 count: %d\n", rib4.Count()) time.Sleep(time.Second * 10) } }() diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go index 736137582bf1d73f5b474297cdaaf3c6cd8ba167..5ad26c878dfb728531f2a4bef87a3894c50640ed 100644 --- a/protocols/bgp/packet/decoder.go +++ b/protocols/bgp/packet/decoder.go @@ -30,7 +30,7 @@ func Decode(buf *bytes.Buffer, opt *DecodeOptions) (*BGPMessage, error) { func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *DecodeOptions) (interface{}, error) { switch msgType { case OpenMsg: - return decodeOpenMsg(buf) + return DecodeOpenMsg(buf) case UpdateMsg: return decodeUpdateMsg(buf, l, opt) case KeepaliveMsg: @@ -128,7 +128,8 @@ func invalidErrCode(n *BGPNotification) (*BGPNotification, error) { return n, fmt.Errorf("Invalid error sub code: %d/%d", n.ErrorCode, n.ErrorSubcode) } -func decodeOpenMsg(buf *bytes.Buffer) (*BGPOpen, error) { +// DecodeOpenMsg decodes a BGP OPEN message +func DecodeOpenMsg(buf *bytes.Buffer) (*BGPOpen, error) { msg, err := _decodeOpenMsg(buf) if err != nil { return nil, fmt.Errorf("Unable to decode OPEN message: %v", err) @@ -241,13 +242,13 @@ func decodeCapability(buf *bytes.Buffer) (Capability, error) { case AddPathCapabilityCode: addPathCap, err := decodeAddPathCapability(buf) if err != nil { - return cap, fmt.Errorf("Unable to decode add path capability") + return cap, fmt.Errorf("Unable to decode add path capability: %v", err) } cap.Value = addPathCap case ASN4CapabilityCode: asn4Cap, err := decodeASN4Capability(buf) if err != nil { - return cap, fmt.Errorf("Unable to decode 4 octet ASN capability") + return cap, fmt.Errorf("Unable to decode 4 octet ASN capability: %v", err) } cap.Value = asn4Cap default: diff --git a/protocols/bgp/packet/nlri_test.go b/protocols/bgp/packet/nlri_test.go index 1efb81eaf44e758685923c2e553d3890a92c5592..e9e786bc77299bedbc5320fc3801ceb135c4a904 100644 --- a/protocols/bgp/packet/nlri_test.go +++ b/protocols/bgp/packet/nlri_test.go @@ -60,6 +60,42 @@ func TestDecodeNLRIs(t *testing.T) { } } +func TestDecodeNLRIv6(t *testing.T) { + tests := []struct { + name string + input []byte + addPath bool + wantFail bool + expected *NLRI + }{ + { + name: "IPv6 default", + input: []byte{ + 0, + }, + wantFail: false, + expected: &NLRI{ + Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0), 0), + }, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(test.input) + res, _, err := decodeNLRI(buf, IPv6AFI, test.addPath) + + if test.wantFail && err == nil { + t.Errorf("Expected error did not happen for test %q", test.name) + } + + if !test.wantFail && err != nil { + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + } + + assert.Equal(t, test.expected, res) + } +} + func TestDecodeNLRI(t *testing.T) { tests := []struct { name string diff --git a/protocols/bgp/server/BUILD.bazel b/protocols/bgp/server/BUILD.bazel index bd45a72d0abd1f419175febf884094109f6eb982..ea2b4ea6a2c5d575297404967aa5c6eee28eb900 100644 --- a/protocols/bgp/server/BUILD.bazel +++ b/protocols/bgp/server/BUILD.bazel @@ -3,6 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ + "bmp_router.go", + "bmp_server.go", "fake_conn.go", "fsm.go", "fsm_active.go", @@ -29,6 +31,7 @@ go_library( "//net:go_default_library", "//protocols/bgp/packet:go_default_library", "//protocols/bgp/types:go_default_library", + "//protocols/bmp/packet:go_default_library", "//route:go_default_library", "//routingtable:go_default_library", "//routingtable/adjRIBIn:go_default_library", @@ -36,12 +39,15 @@ go_library( "//routingtable/filter:go_default_library", "//routingtable/locRIB:go_default_library", "//vendor/github.com/sirupsen/logrus:go_default_library", + "//vendor/github.com/taktv6/tflow2/convert:go_default_library", ], ) go_test( name = "go_default_test", srcs = [ + "bmp_router_test.go", + "bmp_server_test.go", "fsm_address_family_test.go", "fsm_open_sent_test.go", "fsm_test.go", @@ -55,11 +61,14 @@ go_test( "//net:go_default_library", "//protocols/bgp/packet:go_default_library", "//protocols/bgp/types:go_default_library", + "//protocols/bmp/packet:go_default_library", "//route:go_default_library", "//routingtable:go_default_library", + "//routingtable/adjRIBIn:go_default_library", "//routingtable/filter:go_default_library", "//routingtable/locRIB:go_default_library", "//testing:go_default_library", + "//vendor/github.com/sirupsen/logrus:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", ], ) diff --git a/protocols/bgp/server/bmp_router.go b/protocols/bgp/server/bmp_router.go new file mode 100644 index 0000000000000000000000000000000000000000..84f5c668508137ea3ab13ed93e7051b2d282e0d7 --- /dev/null +++ b/protocols/bgp/server/bmp_router.go @@ -0,0 +1,404 @@ +package server + +import ( + "bytes" + "fmt" + "net" + "sync" + "time" + + bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + bmppkt "github.com/bio-routing/bio-rd/protocols/bmp/packet" + "github.com/bio-routing/bio-rd/routingtable" + "github.com/bio-routing/bio-rd/routingtable/filter" + "github.com/bio-routing/bio-rd/routingtable/locRIB" + log "github.com/sirupsen/logrus" + "github.com/taktv6/tflow2/convert" +) + +type router struct { + name string + address net.IP + port uint16 + con net.Conn + reconnectTimeMin int + reconnectTimeMax int + reconnectTime int + reconnectTimer *time.Timer + rib4 *locRIB.LocRIB + rib6 *locRIB.LocRIB + neighbors map[[16]byte]*neighbor + neighborsMu sync.Mutex + logger *log.Logger + runMu sync.Mutex + stop chan struct{} + + ribClients map[afiClient]struct{} + ribClientsMu sync.Mutex +} + +type neighbor struct { + localAS uint32 + peerAS uint32 + peerAddress [16]byte + routerID uint32 + fsm *FSM + opt *packet.DecodeOptions +} + +func newRouter(addr net.IP, port uint16, rib4 *locRIB.LocRIB, rib6 *locRIB.LocRIB) *router { + return &router{ + address: addr, + port: port, + reconnectTimeMin: 30, // Suggested by RFC 7854 + reconnectTimeMax: 720, // Suggested by RFC 7854 + reconnectTimer: time.NewTimer(time.Duration(0)), + rib4: rib4, + rib6: rib6, + neighbors: make(map[[16]byte]*neighbor), + logger: log.New(), + stop: make(chan struct{}), + ribClients: make(map[afiClient]struct{}), + } +} + +func (r *router) subscribeRIBs(client routingtable.RouteTableClient, afi uint8) { + ac := afiClient{ + afi: afi, + client: client, + } + + r.ribClientsMu.Lock() + defer r.ribClientsMu.Unlock() + if _, ok := r.ribClients[ac]; ok { + return + } + r.ribClients[ac] = struct{}{} + + r.neighborsMu.Lock() + defer r.neighborsMu.Unlock() + for _, n := range r.neighbors { + if afi == packet.IPv4AFI { + n.fsm.ipv4Unicast.adjRIBIn.Register(client) + } + if afi == packet.IPv6AFI { + n.fsm.ipv6Unicast.adjRIBIn.Register(client) + } + } +} + +func (r *router) unsubscribeRIBs(client routingtable.RouteTableClient, afi uint8) { + ac := afiClient{ + afi: afi, + client: client, + } + + r.ribClientsMu.Lock() + defer r.ribClientsMu.Unlock() + if _, ok := r.ribClients[ac]; !ok { + return + } + delete(r.ribClients, ac) + + r.neighborsMu.Lock() + defer r.neighborsMu.Unlock() + for _, n := range r.neighbors { + if !n.fsm.ribsInitialized { + continue + } + if afi == packet.IPv4AFI { + n.fsm.ipv4Unicast.adjRIBIn.Unregister(client) + } + if afi == packet.IPv6AFI { + n.fsm.ipv6Unicast.adjRIBIn.Unregister(client) + } + } +} + +func (r *router) serve(con net.Conn) { + r.con = con + r.runMu.Lock() + defer r.con.Close() + defer r.runMu.Unlock() + + for { + select { + case <-r.stop: + return + default: + } + + msg, err := recvBMPMsg(r.con) + if err != nil { + r.logger.Errorf("Unable to get message: %v", err) + return + } + + bmpMsg, err := bmppkt.Decode(msg) + if err != nil { + r.logger.Errorf("Unable to decode BMP message: %v", err) + return + } + + switch bmpMsg.MsgType() { + case bmppkt.PeerUpNotificationType: + err = r.processPeerUpNotification(bmpMsg.(*bmppkt.PeerUpNotification)) + if err != nil { + r.logger.Errorf("Unable to process peer up notification: %v", err) + } + case bmppkt.PeerDownNotificationType: + r.processPeerDownNotification(bmpMsg.(*bmppkt.PeerDownNotification)) + case bmppkt.InitiationMessageType: + r.processInitiationMsg(bmpMsg.(*bmppkt.InitiationMessage)) + case bmppkt.TerminationMessageType: + r.processTerminationMsg(bmpMsg.(*bmppkt.TerminationMessage)) + return + case bmppkt.RouteMonitoringType: + r.processRouteMonitoringMsg(bmpMsg.(*bmppkt.RouteMonitoringMsg)) + } + } +} + +func (r *router) processRouteMonitoringMsg(msg *bmppkt.RouteMonitoringMsg) { + r.neighborsMu.Lock() + defer r.neighborsMu.Unlock() + + if _, ok := r.neighbors[msg.PerPeerHeader.PeerAddress]; !ok { + r.logger.Errorf("Received route monitoring message for non-existent neighbor %v on %s", msg.PerPeerHeader.PeerAddress, r.address.String()) + return + } + + n := r.neighbors[msg.PerPeerHeader.PeerAddress] + s := n.fsm.state.(*establishedState) + s.msgReceived(msg.BGPUpdate, s.fsm.decodeOptions()) +} + +func (r *router) processInitiationMsg(msg *bmppkt.InitiationMessage) { + const ( + stringType = 0 + sysDescrType = 1 + sysNameType = 2 + ) + + logMsg := fmt.Sprintf("Received initiation message from %s:", r.address.String()) + + for _, tlv := range msg.TLVs { + switch tlv.InformationType { + case stringType: + logMsg += fmt.Sprintf(" Message: %q", string(tlv.Information)) + case sysDescrType: + logMsg += fmt.Sprintf(" sysDescr.: %s", string(tlv.Information)) + case sysNameType: + r.name = string(tlv.Information) + logMsg += fmt.Sprintf(" sysName.: %s", string(tlv.Information)) + } + } + + r.logger.Info(logMsg) +} + +func (r *router) processTerminationMsg(msg *bmppkt.TerminationMessage) { + const ( + stringType = 0 + reasonType = 1 + + adminDown = 0 + unspecReason = 1 + outOfRes = 2 + redundantCon = 3 + permAdminDown = 4 + ) + + logMsg := fmt.Sprintf("Received termination message from %s: ", r.address.String()) + for _, tlv := range msg.TLVs { + switch tlv.InformationType { + case stringType: + logMsg += fmt.Sprintf("Message: %q", string(tlv.Information)) + case reasonType: + reason := convert.Uint16b(tlv.Information[:2]) + switch reason { + case adminDown: + logMsg += "Session administratively down" + case unspecReason: + logMsg += "Unespcified reason" + case outOfRes: + logMsg += "Out of resources" + case redundantCon: + logMsg += "Redundant connection" + case permAdminDown: + logMsg += "Session permanently administratively closed" + } + } + } + + r.logger.Warning(logMsg) + + r.con.Close() + for n := range r.neighbors { + r.peerDown(n) + } +} + +func (r *router) processPeerDownNotification(msg *bmppkt.PeerDownNotification) { + r.neighborsMu.Lock() + defer r.neighborsMu.Unlock() + + if _, ok := r.neighbors[msg.PerPeerHeader.PeerAddress]; !ok { + r.logger.Warningf("Received peer down notification for %v: Peer doesn't exist.", msg.PerPeerHeader.PeerAddress) + return + } + + r.peerDown(msg.PerPeerHeader.PeerAddress) +} + +func (r *router) peerDown(addr [16]byte) { + if r.neighbors[addr].fsm != nil { + if r.neighbors[addr].fsm.ipv4Unicast != nil { + r.neighbors[addr].fsm.ipv4Unicast.bmpDispose() + } + + if r.neighbors[addr].fsm.ipv6Unicast != nil { + r.neighbors[addr].fsm.ipv6Unicast.bmpDispose() + } + } + + delete(r.neighbors, addr) +} + +func (r *router) processPeerUpNotification(msg *bmppkt.PeerUpNotification) error { + r.neighborsMu.Lock() + defer r.neighborsMu.Unlock() + + if _, ok := r.neighbors[msg.PerPeerHeader.PeerAddress]; ok { + return fmt.Errorf("Received peer up notification for %v: Peer exists already", msg.PerPeerHeader.PeerAddress) + } + + 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) + } + + sentOpen, err := packet.DecodeOpenMsg(bytes.NewBuffer(msg.SentOpenMsg[packet.HeaderLen:])) + if err != nil { + return fmt.Errorf("Unable to decode sent open message sent from %v to %v: %v", r.address.String(), msg.PerPeerHeader.PeerAddress, err) + } + + if len(msg.ReceivedOpenMsg) < packet.MinOpenLen { + return fmt.Errorf("Received peer up notification for %v: Invalid received open message: %v", msg.PerPeerHeader.PeerAddress, msg.ReceivedOpenMsg) + } + + recvOpen, err := packet.DecodeOpenMsg(bytes.NewBuffer(msg.ReceivedOpenMsg[packet.HeaderLen:])) + if err != nil { + return fmt.Errorf("Unable to decode received open message sent from %v to %v: %v", 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, + peer: &peer{ + routerID: sentOpen.BGPIdentifier, + addr: peerAddress, + localAddr: localAddress, + peerASN: msg.PerPeerHeader.PeerAS, + localASN: uint32(sentOpen.ASN), + ipv4: &peerAddressFamily{}, + ipv6: &peerAddressFamily{}, + }, + } + + fsm.peer.configureBySentOpen(sentOpen) + + fsm.ipv4Unicast = newFSMAddressFamily(packet.IPv4AFI, packet.UnicastSAFI, &peerAddressFamily{ + rib: r.rib4, + importFilter: filter.NewAcceptAllFilter(), + }, fsm) + fsm.ipv4Unicast.bmpInit() + + fsm.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, &peerAddressFamily{ + rib: r.rib6, + importFilter: filter.NewAcceptAllFilter(), + }, fsm) + fsm.ipv6Unicast.bmpInit() + + fsm.state = newOpenSentState(fsm) + openSent := fsm.state.(*openSentState) + openSent.openMsgReceived(recvOpen) + + fsm.state = newEstablishedState(fsm) + n := &neighbor{ + localAS: fsm.peer.localASN, + peerAS: msg.PerPeerHeader.PeerAS, + peerAddress: msg.PerPeerHeader.PeerAddress, + routerID: recvOpen.BGPIdentifier, + fsm: fsm, + opt: fsm.decodeOptions(), + } + + r.neighbors[msg.PerPeerHeader.PeerAddress] = n + + r.ribClientsMu.Lock() + defer r.ribClientsMu.Unlock() + n.registerClients(r.ribClients) + + return nil +} + +func (n *neighbor) registerClients(clients map[afiClient]struct{}) { + for ac := range clients { + if ac.afi == packet.IPv4AFI { + n.fsm.ipv4Unicast.adjRIBIn.Register(ac.client) + } + if ac.afi == packet.IPv6AFI { + n.fsm.ipv6Unicast.adjRIBIn.Register(ac.client) + } + } +} + +func (p *peer) configureBySentOpen(msg *packet.BGPOpen) { + caps := getCaps(msg.OptParams) + for _, cap := range caps { + switch cap.Code { + case packet.AddPathCapabilityCode: + addPathCap := cap.Value.(packet.AddPathCapability) + peerFamily := p.addressFamily(addPathCap.AFI, addPathCap.SAFI) + if peerFamily == nil { + continue + } + switch addPathCap.SendReceive { + case packet.AddPathSend: + peerFamily.addPathSend = routingtable.ClientOptions{ + MaxPaths: 10, + } + case packet.AddPathReceive: + peerFamily.addPathReceive = true + case packet.AddPathSendReceive: + peerFamily.addPathReceive = true + peerFamily.addPathSend = routingtable.ClientOptions{ + MaxPaths: 10, + } + } + case packet.ASN4CapabilityCode: + asn4Cap := cap.Value.(packet.ASN4Capability) + p.localASN = asn4Cap.ASN4 + } + } +} + +func getCaps(optParams []packet.OptParam) packet.Capabilities { + for _, optParam := range optParams { + if optParam.Type != packet.CapabilitiesParamType { + continue + } + + return optParam.Value.(packet.Capabilities) + } + return nil +} diff --git a/protocols/bgp/server/bmp_router_test.go b/protocols/bgp/server/bmp_router_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3c74e6b82b358c964b5ed58f6a2a8d3b28944de9 --- /dev/null +++ b/protocols/bgp/server/bmp_router_test.go @@ -0,0 +1,2746 @@ +package server + +import ( + "bytes" + "net" + "testing" + "time" + + bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + bmppkt "github.com/bio-routing/bio-rd/protocols/bmp/packet" + "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/routingtable" + "github.com/bio-routing/bio-rd/routingtable/adjRIBIn" + "github.com/bio-routing/bio-rd/routingtable/filter" + "github.com/bio-routing/bio-rd/routingtable/locRIB" + biotesting "github.com/bio-routing/bio-rd/testing" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestBMPRouterServe(t *testing.T) { + tests := []struct { + name string + msg []byte + wantFail bool + }{ + { + name: "Test #1", + msg: []byte{1, 2, 3}, + wantFail: true, + }, + } + + for _, test := range tests { + addr := net.IP{10, 20, 30, 40} + port := uint16(123) + rib4 := locRIB.New() + rib6 := locRIB.New() + conA, conB := net.Pipe() + + r := newRouter(addr, port, rib4, rib6) + buf := bytes.NewBuffer(nil) + r.logger.Out = buf + go r.serve(conA) + + conB.Write(test.msg) + + assert.Equalf(t, 0, len(buf.Bytes()), "Test %q", test.name) + } +} + +func TestStartStopBMP(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(123) + rib4 := locRIB.New() + rib6 := locRIB.New() + + con := biotesting.NewMockConn() + + r := newRouter(addr, port, rib4, rib6) + go r.serve(con) + + r.stop <- struct{}{} + + r.runMu.Lock() + assert.Equal(t, true, r.con.(*biotesting.MockConn).Closed) +} + +func TestConfigureBySentOpen(t *testing.T) { + tests := []struct { + name string + p *peer + openMsg *packet.BGPOpen + expected *peer + }{ + { + name: "Test 32bit ASN", + p: &peer{}, + openMsg: &packet.BGPOpen{ + OptParams: []packet.OptParam{ + { + Type: 2, + Value: packet.Capabilities{ + { + Code: packet.ASN4CapabilityCode, + Value: packet.ASN4Capability{ + ASN4: 201701, + }, + }, + }, + }, + }, + }, + expected: &peer{ + localASN: 201701, + }, + }, + { + name: "Test Add Path TX", + p: &peer{ + ipv4: &peerAddressFamily{}, + }, + openMsg: &packet.BGPOpen{ + OptParams: []packet.OptParam{ + { + Type: 2, + Value: packet.Capabilities{ + { + Code: packet.AddPathCapabilityCode, + Value: packet.AddPathCapability{ + AFI: 1, + SAFI: 1, + SendReceive: 2, + }, + }, + }, + }, + }, + }, + expected: &peer{ + ipv4: &peerAddressFamily{ + addPathSend: routingtable.ClientOptions{ + MaxPaths: 10, + }, + }, + }, + }, + { + name: "Test Add Path RX", + p: &peer{ + ipv4: &peerAddressFamily{}, + }, + openMsg: &packet.BGPOpen{ + OptParams: []packet.OptParam{ + { + Type: 2, + Value: packet.Capabilities{ + { + Code: packet.AddPathCapabilityCode, + Value: packet.AddPathCapability{ + AFI: 1, + SAFI: 1, + SendReceive: 1, + }, + }, + }, + }, + }, + }, + expected: &peer{ + ipv4: &peerAddressFamily{ + addPathReceive: true, + }, + }, + }, + { + name: "Test Add Path RX/TX", + p: &peer{ + ipv4: &peerAddressFamily{}, + }, + openMsg: &packet.BGPOpen{ + OptParams: []packet.OptParam{ + { + Type: 2, + Value: packet.Capabilities{ + { + Code: packet.AddPathCapabilityCode, + Value: packet.AddPathCapability{ + AFI: 1, + SAFI: 1, + SendReceive: 3, + }, + }, + }, + }, + }, + }, + expected: &peer{ + ipv4: &peerAddressFamily{ + addPathSend: routingtable.ClientOptions{ + MaxPaths: 10, + }, + addPathReceive: true, + }, + }, + }, + } + + for _, test := range tests { + test.p.configureBySentOpen(test.openMsg) + + assert.Equalf(t, test.expected, test.p, "Test %q", test.name) + } +} + +func TestProcessPeerUpNotification(t *testing.T) { + tests := []struct { + name string + router *router + pkt *bmppkt.PeerUpNotification + wantFail bool + expected *router + }{ + { + name: "Invalid sent open message", + router: &router{ + neighbors: make(map[[16]byte]*neighbor), + }, + pkt: &bmppkt.PeerUpNotification{ + PerPeerHeader: &bmppkt.PerPeerHeader{ + PeerType: 0, + PeerFlags: 0, + PeerDistinguisher: 0, + PeerAddress: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 1}, + PeerAS: 51324, + PeerBGPID: 100, + }, + LocalAddress: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 0}, + LocalPort: 179, + RemotePort: 34542, + SentOpenMsg: []byte{}, + ReceivedOpenMsg: []byte{}, + Information: []byte{}, + }, + wantFail: true, + }, + { + name: "Invalid received open message", + router: &router{ + neighbors: make(map[[16]byte]*neighbor), + }, + pkt: &bmppkt.PeerUpNotification{ + PerPeerHeader: &bmppkt.PerPeerHeader{ + PeerType: 0, + PeerFlags: 0, + PeerDistinguisher: 0, + PeerAddress: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 1}, + PeerAS: 51324, + PeerBGPID: 100, + }, + LocalAddress: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 0}, + LocalPort: 179, + RemotePort: 34542, + SentOpenMsg: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, + 4, + 100, 200, + 20, 0, + 10, 20, 30, 40, + 0, + }, + ReceivedOpenMsg: []byte{}, + Information: []byte{}, + }, + wantFail: true, + }, + { + name: "Regular BGP by RFC4271", + router: &router{ + rib4: locRIB.New(), + rib6: locRIB.New(), + neighbors: make(map[[16]byte]*neighbor), + }, + pkt: &bmppkt.PeerUpNotification{ + PerPeerHeader: &bmppkt.PerPeerHeader{ + PeerType: 0, + PeerFlags: 0, + PeerDistinguisher: 0, + PeerAddress: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 1}, + PeerAS: 100, + PeerBGPID: 100, + }, + LocalAddress: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 0}, + LocalPort: 179, + RemotePort: 34542, + SentOpenMsg: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, + 4, + 0, 200, + 20, 0, + 10, 20, 30, 40, + 0, + }, + ReceivedOpenMsg: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, + 4, + 0, 100, + 20, 0, + 10, 20, 30, 50, + 0, + }, + Information: []byte{}, + }, + wantFail: false, + expected: &router{ + rib4: locRIB.New(), + rib6: locRIB.New(), + neighbors: map[[16]byte]*neighbor{ + [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 1}: { + localAS: 200, + peerAS: 100, + peerAddress: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 255, 1}, + routerID: 169090610, + opt: &packet.DecodeOptions{ + AddPath: false, + Use32BitASN: false, + }, + fsm: &FSM{ + isBMP: true, + neighborID: 169090610, + state: &establishedState{}, + peer: &peer{ + routerID: 169090600, + addr: bnet.IPv4FromOctets(10, 0, 255, 1), + localAddr: bnet.IPv4FromOctets(10, 0, 255, 0), + peerASN: 100, + localASN: 200, + ipv4: &peerAddressFamily{}, + ipv6: &peerAddressFamily{}, + }, + ipv4Unicast: &fsmAddressFamily{ + afi: 1, + safi: 1, + adjRIBIn: adjRIBIn.New(filter.NewAcceptAllFilter(), &routingtable.ContributingASNs{}, 169090600, 0, false), + importFilter: filter.NewAcceptAllFilter(), + }, + ipv6Unicast: &fsmAddressFamily{ + afi: 2, + safi: 1, + adjRIBIn: adjRIBIn.New(filter.NewAcceptAllFilter(), &routingtable.ContributingASNs{}, 169090600, 0, false), + importFilter: filter.NewAcceptAllFilter(), + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + err := test.router.processPeerUpNotification(test.pkt) + if err != nil { + if test.wantFail { + continue + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", test.name) + continue + } + + test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.state = &establishedState{fsm: test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm} + + if test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv4Unicast != nil { + test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv4Unicast.rib = test.router.rib4 + test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv4Unicast.fsm = test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm + test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv4Unicast.adjRIBIn.Register(test.router.rib4) + } + + if test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv6Unicast != nil { + test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv6Unicast.rib = test.router.rib6 + test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv6Unicast.fsm = test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm + test.expected.neighbors[test.pkt.PerPeerHeader.PeerAddress].fsm.ipv6Unicast.adjRIBIn.Register(test.router.rib6) + } + + assert.Equalf(t, test.expected, test.router, "Test %q", test.name) + } + +} + +func TestProcessRouteMonitoringMsg(t *testing.T) { + tests := []struct { + name string + r *router + msg *bmppkt.RouteMonitoringMsg + expectedLogBuf string + logOnly bool + expected *router + }{ + { + name: "Unknown peer address", + r: &router{ + address: net.IP{10, 20, 30, 40}, + logger: log.New(), + }, + msg: &bmppkt.RouteMonitoringMsg{ + PerPeerHeader: &bmppkt.PerPeerHeader{ + PeerAddress: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + }, + }, + expectedLogBuf: "level=error msg=\"Received route monitoring message for non-existent neighbor [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] on 10.20.30.40\"", + logOnly: true, + expected: &router{}, + }, + } + + for _, test := range tests { + logBuf := bytes.NewBuffer(nil) + test.r.logger.Out = logBuf + test.r.logger.Formatter = biotesting.NewLogFormatter() + + test.expected.logger = test.r.logger + + test.r.processRouteMonitoringMsg(test.msg) + + assert.Equalf(t, test.expectedLogBuf, string(logBuf.Bytes()), "Test %q", test.name) + if test.logOnly { + continue + } + + assert.Equalf(t, test.expected, test.r, "Test %q", test.name) + } +} + +func TestProcessInitiationMsg(t *testing.T) { + tests := []struct { + name string + r *router + msg *bmppkt.InitiationMessage + expectedLog string + }{ + { + name: "Test #1", + r: &router{ + address: net.IP{10, 20, 30, 40}, + logger: log.New(), + }, + msg: &bmppkt.InitiationMessage{ + TLVs: []*bmppkt.InformationTLV{ + { + InformationType: 0, + Information: []byte("Foo Bar"), + }, + { + InformationType: 1, + Information: []byte("SYS DESCR"), + }, + { + InformationType: 2, + Information: []byte("core01.fra01"), + }, + }, + }, + expectedLog: "level=info msg=\"Received initiation message from 10.20.30.40: Message: \\\"Foo Bar\\\" sysDescr.: SYS DESCR sysName.: core01.fra01\"", + }, + } + + for _, test := range tests { + logBuf := bytes.NewBuffer(nil) + test.r.logger.Out = logBuf + test.r.logger.Formatter = biotesting.NewLogFormatter() + + test.r.processInitiationMsg(test.msg) + + assert.Equalf(t, test.expectedLog, string(logBuf.Bytes()), "Test %q", test.name) + } +} + +func TestProcessTerminationMsg(t *testing.T) { + tests := []struct { + name string + r *router + msg *bmppkt.TerminationMessage + expectedLog string + expected *router + }{ + { + name: "Test shutdown", + r: &router{ + con: &biotesting.MockConn{}, + address: net.IP{10, 20, 30, 40}, + logger: log.New(), + neighbors: map[[16]byte]*neighbor{ + [16]byte{1, 2, 3}: &neighbor{}, + }, + }, + msg: &bmppkt.TerminationMessage{ + TLVs: []*bmppkt.InformationTLV{ + { + InformationType: 0, // string type + Information: []byte("Foo Bar"), + }, + }, + }, + expectedLog: "level=warning msg=\"Received termination message from 10.20.30.40: Message: \\\"Foo Bar\\\"\"", + expected: &router{ + con: &biotesting.MockConn{ + Closed: true, + }, + address: net.IP{10, 20, 30, 40}, + neighbors: map[[16]byte]*neighbor{}, + }, + }, + { + name: "Test logs", + r: &router{ + con: &biotesting.MockConn{}, + address: net.IP{10, 20, 30, 40}, + logger: log.New(), + neighbors: map[[16]byte]*neighbor{ + [16]byte{1, 2, 3}: &neighbor{}, + }, + }, + msg: &bmppkt.TerminationMessage{ + TLVs: []*bmppkt.InformationTLV{ + { + InformationType: 1, // reason type + Information: []byte{0, 0}, + }, + { + InformationType: 1, // reason type + Information: []byte{0, 1}, + }, + { + InformationType: 1, // reason type + Information: []byte{0, 2}, + }, + { + InformationType: 1, // reason type + Information: []byte{0, 3}, + }, + { + InformationType: 1, // reason type + Information: []byte{0, 4}, + }, + }, + }, + expectedLog: "level=warning msg=\"Received termination message from 10.20.30.40: Session administratively downUnespcified reasonOut of resourcesRedundant connectionSession permanently administratively closed\"", + expected: &router{ + con: &biotesting.MockConn{ + Closed: true, + }, + address: net.IP{10, 20, 30, 40}, + neighbors: map[[16]byte]*neighbor{}, + }, + }, + } + + for _, test := range tests { + logBuf := bytes.NewBuffer(nil) + test.r.logger.Out = logBuf + test.r.logger.Formatter = biotesting.NewLogFormatter() + + test.expected.logger = test.r.logger + + test.r.processTerminationMsg(test.msg) + + assert.Equalf(t, test.expectedLog, string(logBuf.Bytes()), "Test %q", test.name) + assert.Equalf(t, test.expected, test.r, "Test %q", test.name) + } +} + +func TestProcessPeerDownNotification(t *testing.T) { + tests := []struct { + name string + r *router + msg *bmppkt.PeerDownNotification + expectedLog string + expected *router + }{ + { + name: "Peer down notification for existing peer", + r: &router{ + address: net.IP{10, 20, 30, 40}, + logger: log.New(), + neighbors: map[[16]byte]*neighbor{ + [16]byte{1, 2, 3}: &neighbor{}, + }, + }, + msg: &bmppkt.PeerDownNotification{ + PerPeerHeader: &bmppkt.PerPeerHeader{ + PeerAddress: [16]byte{1, 2, 3}, + }, + }, + expectedLog: "", + expected: &router{ + address: net.IP{10, 20, 30, 40}, + neighbors: map[[16]byte]*neighbor{}, + }, + }, + { + name: "Peer down notification for non-existing peer", + r: &router{ + address: net.IP{10, 20, 30, 40}, + logger: log.New(), + neighbors: map[[16]byte]*neighbor{ + [16]byte{1, 2, 3}: &neighbor{}, + }, + }, + msg: &bmppkt.PeerDownNotification{ + PerPeerHeader: &bmppkt.PerPeerHeader{ + PeerAddress: [16]byte{10, 20, 30}, + }, + }, + expectedLog: "level=warning msg=\"Received peer down notification for [10 20 30 0 0 0 0 0 0 0 0 0 0 0 0 0]: Peer doesn't exist.\"", + expected: &router{ + address: net.IP{10, 20, 30, 40}, + neighbors: map[[16]byte]*neighbor{ + [16]byte{1, 2, 3}: &neighbor{}, + }, + }, + }, + } + + for _, test := range tests { + logBuf := bytes.NewBuffer(nil) + test.r.logger.Out = logBuf + test.r.logger.Formatter = biotesting.NewLogFormatter() + + test.expected.logger = test.r.logger + + test.r.processPeerDownNotification(test.msg) + + assert.Equalf(t, test.expectedLog, string(logBuf.Bytes()), "Test %q", test.name) + assert.Equalf(t, test.expected, test.r, "Test %q", test.name) + } +} + +func TestRegisterClients(t *testing.T) { + n := &neighbor{ + fsm: &FSM{ + ipv4Unicast: &fsmAddressFamily{ + adjRIBIn: locRIB.New(), + }, + ipv6Unicast: &fsmAddressFamily{ + adjRIBIn: locRIB.New(), + }, + }, + } + + client4 := locRIB.New() + client6 := locRIB.New() + ac4 := afiClient{ + afi: packet.IPv4AFI, + client: client4, + } + ac6 := afiClient{ + afi: packet.IPv6AFI, + client: client6, + } + clients4 := map[afiClient]struct{}{ + ac4: struct{}{}, + } + clients6 := map[afiClient]struct{}{ + ac6: struct{}{}, + } + + n.registerClients(clients4) + n.registerClients(clients6) + n.fsm.ipv4Unicast.adjRIBIn.AddPath(bnet.NewPfx(bnet.IPv4(0), 0), &route.Path{ + BGPPath: &route.BGPPath{ + LocalPref: 100, + }, + }) + n.fsm.ipv6Unicast.adjRIBIn.AddPath(bnet.NewPfx(bnet.IPv6(0, 0), 0), &route.Path{ + BGPPath: &route.BGPPath{ + LocalPref: 200, + }, + }) + + assert.Equal(t, int64(1), n.fsm.ipv4Unicast.adjRIBIn.RouteCount()) + assert.Equal(t, int64(1), n.fsm.ipv6Unicast.adjRIBIn.RouteCount()) +} + +func TestIntegrationPeerUpRouteMonitor(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + conA, conB := net.Pipe() + + go r.serve(conB) + + // Peer Up Notification + _, err := conA.Write([]byte{ + // Common Header + 3, // Version + 0, 0, 0, 126, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 0, // Opt param length + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 0, // Opt param length + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + }) + + if err != nil { + panic("write failed") + } + + time.Sleep(time.Millisecond * 50) + assert.NotEmpty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib4.RouteCount() + if count != 1 { + t.Errorf("Unexpected route count. Expected: 1 Got: %d", count) + } + + conA.Close() +} + +func TestIntegrationPeerUpRouteMonitorIPv6IPv4(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + conA, conB := net.Pipe() + + go r.serve(conB) + + // Peer Up Notification + _, err := conA.Write([]byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 138, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 90, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 67, // Total Path Attribute Length + + 255, + 14, // MP REACH NLRI + 0, 22, // Length + 0, 2, // IPv6 + 1, // Unicast + 16, // IPv6 Next Hop + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, // Reserved + 0, // Pfxlen /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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // THIRD MESSAGE + // Common Header + 3, // Version + 0, 0, 0, 113, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 65, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + 0, // /0 + }) + + if err != nil { + panic("write failed") + } + + time.Sleep(time.Millisecond * 50) + assert.NotEmpty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib6.RouteCount() + if count != 1 { + t.Errorf("Unexpected IPv6 route count. Expected: 1 Got: %d", count) + } + + count = rib4.RouteCount() + if count != 1 { + t.Errorf("Unexpected IPv4 route count. Expected: 1 Got: %d", count) + } + + conA.Close() +} + +func TestIntegrationPeerUpRouteMonitorIPv4IPv6(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + conA, conB := net.Pipe() + + go r.serve(conB) + + // Peer Up Notification + _, err := conA.Write([]byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 113, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 65, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + 0, // /0 + + // THIRD MESSAGE + + // Common Header + 3, // Version + 0, 0, 0, 138, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 90, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 67, // Total Path Attribute Length + + 255, + 14, // MP REACH NLRI + 0, 22, // Length + 0, 2, // IPv6 + 1, // Unicast + 16, // IPv6 Next Hop + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, // Reserved + 0, // Pfxlen /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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + }) + + if err != nil { + panic("write failed") + } + + time.Sleep(time.Millisecond * 50) + assert.NotEmpty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib6.RouteCount() + if count != 1 { + t.Errorf("Unexpected IPv6 route count. Expected: 1 Got: %d", count) + } + + count = rib4.RouteCount() + if count != 1 { + t.Errorf("Unexpected IPv4 route count. Expected: 1 Got: %d", count) + } + + conA.Close() +} + +func TestIntegrationPeerUpRouteMonitorIPv6(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + conA, conB := net.Pipe() + + go r.serve(conB) + + // Peer Up Notification + _, err := conA.Write([]byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 138, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 90, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 67, // Total Path Attribute Length + + 255, + 14, // MP REACH NLRI + 0, 22, // Length + 0, 2, // IPv6 + 1, // Unicast + 16, // IPv6 Next Hop + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, // Reserved + 0, // Pfxlen /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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + }) + + if err != nil { + panic("write failed") + } + + time.Sleep(time.Millisecond * 50) + assert.NotEmpty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib6.RouteCount() + if count != 1 { + t.Errorf("Unexpected IPv6 route count. Expected: 1 Got: %d", count) + } + + count = rib4.RouteCount() + if count != 0 { + t.Errorf("Unexpected IPv4 route count. Expected: 0 Got: %d", count) + } + + conA.Close() +} + +func TestIntegrationIncompleteBMPMsg(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + con := biotesting.NewMockConn() + + // Peer Up Notification with invalid version number (4) + con.Write([]byte{ + // Common Header + 4, // Version + 0, 0, 0, 126, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 0, // Opt param length + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 0, // Opt param length + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + }) + + r.serve(con) + + if !con.Closed { + t.Errorf("Connection not closed although failure should have happened") + } +} + +func TestBMPFullRunWithWithdraw(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + con := biotesting.NewMockConn() + + go r.serve(con) + + con.Write([]byte{ + // ##################################################################### + // Initiation Message + // ##################################################################### + // Common Header + 3, + 0, 0, 0, 23, + 4, + 0, 1, // sysDescr + 0, 4, // Length + 42, 42, 42, 42, // AAAA + 0, 2, //sysName + 0, 5, // Length + 43, 43, 43, 43, 43, // BBBBB + + // ##################################################################### + // Peer UP Notification + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 126, // Message Length + 3, // Msg Type = Peer Up Notification + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 0, // Opt param length + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 0, // Opt param length + + // ##################################################################### + // Route Monitoring Message #1 + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + + // ##################################################################### + // Route Monitoring Message #2 (withdraw of prefix from #1) + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 75, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 27, // Length + 2, // Update + + 0, 4, // Withdrawn Routes Length + 24, + 192, 168, 0, + 0, 0, // Total Path Attribute Length + }) + + time.Sleep(time.Millisecond * 50) + assert.NotEmpty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib4.RouteCount() + if count != 0 { + t.Errorf("Unexpected route count. Expected: 0 Got: %d", count) + } + +} + +func TestBMPFullRunWithPeerDownNotification(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + con := biotesting.NewMockConn() + + go r.serve(con) + + con.Write([]byte{ + // ##################################################################### + // Initiation Message + // ##################################################################### + // Common Header + 3, + 0, 0, 0, 23, + 4, + 0, 1, // sysDescr + 0, 4, // Length + 42, 42, 42, 42, // AAAA + 0, 2, //sysName + 0, 5, // Length + 43, 43, 43, 43, 43, // BBBBB + + // ##################################################################### + // Peer UP Notification + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 126, // Message Length + 3, // Msg Type = Peer Up Notification + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 0, // Opt param length + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 0, // Opt param length + + // ##################################################################### + // Route Monitoring Message #1 + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + + // ##################################################################### + // Peer Down Notification + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 49, // Message Length + 2, // Msg Type = Peer Down Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + 4, // Reason + }) + + time.Sleep(time.Millisecond * 50) + assert.Empty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib4.RouteCount() + if count != 0 { + t.Errorf("Unexpected route count. Expected: 0 Got: %d", count) + } +} + +func TestBMPFullRunWithTerminationMessage(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + con := biotesting.NewMockConn() + + go r.serve(con) + + con.Write([]byte{ + // ##################################################################### + // Initiation Message + // ##################################################################### + // Common Header + 3, + 0, 0, 0, 23, + 4, + 0, 1, // sysDescr + 0, 4, // Length + 42, 42, 42, 42, // AAAA + 0, 2, //sysName + 0, 5, // Length + 43, 43, 43, 43, 43, // BBBBB + + // ##################################################################### + // Peer UP Notification + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 126, // Message Length + 3, // Msg Type = Peer Up Notification + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 0, // Opt param length + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 0, // Opt param length + + // ##################################################################### + // Route Monitoring Message #1 + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + + // ##################################################################### + // Termination Message + // ##################################################################### + // Common Header + 3, // Version + 0, 0, 0, 12, // Message Length + 5, // Msg Type = Termination Message + + // TLV + 0, 0, 0, 2, + 42, 42, + }) + + time.Sleep(time.Millisecond * 50) + assert.Empty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib4.RouteCount() + if count != 0 { + t.Errorf("Unexpected route count. Expected: 0 Got: %d", count) + } +} + +func TestIntegrationPeerUpRouteMonitorIPv6WithClientAtEnd(t *testing.T) { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + conA, conB := net.Pipe() + + go r.serve(conB) + + // Peer Up Notification + _, err := conA.Write([]byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 138, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 90, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 67, // Total Path Attribute Length + + 255, + 14, // MP REACH NLRI + 0, 22, // Length + 0, 2, // IPv6 + 1, // Unicast + 16, // IPv6 Next Hop + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, // Reserved + 0, // Pfxlen /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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + }) + + if err != nil { + panic("write failed") + } + + time.Sleep(time.Millisecond * 50) + assert.NotEmpty(t, r.neighbors) + + time.Sleep(time.Millisecond * 50) + + count := rib6.RouteCount() + if count != 1 { + t.Errorf("Unexpected IPv6 route count. Expected: 1 Got: %d", count) + } + + count = rib4.RouteCount() + if count != 0 { + t.Errorf("Unexpected IPv4 route count. Expected: 0 Got: %d", count) + } + + client6 := locRIB.New() + r.subscribeRIBs(client6, packet.IPv6AFI) + + count = client6.RouteCount() + if count != 1 { + t.Errorf("Unexpected IPv6 route count. Expected: 1 Got: %d", count) + } + + conA.Close() +} + +func TestIntegrationPeerUpRouteMonitorIPv6WithClientBeforeBMPPeer(t *testing.T) { + tests := []struct { + name string + afi uint8 + unregister bool + doubleSubscribe bool + doubleUnsubscribe bool + input []byte + expectedRouteCount int + }{ + { + name: "IPv4 without unregister", + afi: packet.IPv4AFI, + unregister: false, + input: []byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + }, + expectedRouteCount: 1, + }, + { + name: "IPv4 with unregister", + afi: packet.IPv4AFI, + unregister: true, + input: []byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + }, + expectedRouteCount: 1, + }, + { + name: "IPv4 with double unregister", + afi: packet.IPv4AFI, + doubleUnsubscribe: true, + unregister: true, + input: []byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + }, + expectedRouteCount: 1, + }, + { + name: "IPv4 with double register", + afi: packet.IPv4AFI, + doubleSubscribe: true, + unregister: true, + input: []byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 116, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 68, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 41, // Total Path Attribute Length + + 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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + + // NLRI + 24, + 192, 168, 0, + }, + expectedRouteCount: 1, + }, + { + name: "IPv6 without unregister", + afi: packet.IPv6AFI, + unregister: false, + input: []byte{ + // Common Header + 3, // Version + 0, 0, 0, 142, // Message Length + 3, // Msg Type = Peer Up Notification + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // Peer Up Notification + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 41, // 10.20.30.41 local address + 0, 123, // Local Port + 0, 234, // Remote Port + + // Sent OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 200, // AS + 0, 180, // Hold Time + 1, 0, 0, 1, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // Received OPEN message + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 37, // Length + 1, // Open message type + 4, // BGP Version + 0, 100, // AS + 0, 180, // Hold Time + 1, 0, 0, 255, // BGP Identifier + 8, // Opt param length + 2, 6, + 1, 4, // MP BGP + 0, 2, // IPv6 + 0, 1, // Unicast + + // SECOND MESSAGE: + + // Common Header + 3, // Version + 0, 0, 0, 138, // Message Length + 0, // Msg Type = Route Monitoring Message + + // Per Peer Header + 0, // Peer Type + 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, 20, 30, 40, // 10.20.30.40 peer address + 0, 0, 0, 100, // Peer AS + 0, 0, 0, 255, // Peer BGP ID + 0, 0, 0, 0, // Timestamp s + 0, 0, 0, 0, // Timestamp µs + + // BGP Update + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 90, // Length + 2, // Update + + 0, 0, // Withdrawn Routes Length + 0, 67, // Total Path Attribute Length + + 255, + 14, // MP REACH NLRI + 0, 22, // Length + 0, 2, // IPv6 + 1, // Unicast + 16, // IPv6 Next Hop + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, // Reserved + 0, // Pfxlen /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 + + 0, // Attribute flags + 3, // Attribute Type code (Next Hop) + 4, // Length + 10, 11, 12, 13, // Next Hop + + 0, // Attribute flags + 4, // Attribute Type code (MED) + 4, // Length + 0, 0, 1, 0, // MED 256 + + 0, // Attribute flags + 5, // Attribute Type code (Local Pref) + 4, // Length + 0, 0, 1, 0, // Local Pref 256 + }, + expectedRouteCount: 1, + }, + } + + for _, test := range tests { + addr := net.IP{10, 20, 30, 40} + port := uint16(12346) + + rib4 := locRIB.New() + rib6 := locRIB.New() + + r := newRouter(addr, port, rib4, rib6) + conA, conB := net.Pipe() + + client := locRIB.New() + r.subscribeRIBs(client, test.afi) + if test.doubleSubscribe { + r.subscribeRIBs(client, test.afi) + } + + if test.unregister { + r.unsubscribeRIBs(client, test.afi) + if test.doubleUnsubscribe { + r.unsubscribeRIBs(client, test.afi) + } + } + + go r.serve(conB) + + _, err := conA.Write(test.input) + + if err != nil { + panic("write failed") + } + + time.Sleep(time.Millisecond * 50) + + expectedCount := int64(1) + if test.unregister { + expectedCount = 0 + } + + count := client.RouteCount() + if count != expectedCount { + t.Errorf("Unexpected route count for test %q. Expected: %d Got: %d", test.name, expectedCount, count) + } + + conA.Close() + } +} diff --git a/protocols/bgp/server/bmp_server.go b/protocols/bgp/server/bmp_server.go new file mode 100644 index 0000000000000000000000000000000000000000..d49355ee030d4d68b192c8ad33a7cf751f8c10cb --- /dev/null +++ b/protocols/bgp/server/bmp_server.go @@ -0,0 +1,152 @@ +package server + +import ( + "fmt" + "io" + "net" + "sync" + "time" + + bmppkt "github.com/bio-routing/bio-rd/protocols/bmp/packet" + "github.com/bio-routing/bio-rd/routingtable" + "github.com/bio-routing/bio-rd/routingtable/locRIB" + log "github.com/sirupsen/logrus" + "github.com/taktv6/tflow2/convert" +) + +const ( + defaultBufferLen = 4096 +) + +// BMPServer represents a BMP server +type BMPServer struct { + routers map[string]*router + ribClients map[string]map[afiClient]struct{} + gloablMu sync.RWMutex +} + +type afiClient struct { + afi uint8 + client routingtable.RouteTableClient +} + +// NewServer creates a new BMP server +func NewServer() *BMPServer { + return &BMPServer{ + routers: make(map[string]*router), + ribClients: make(map[string]map[afiClient]struct{}), + } +} + +// SubscribeRIBs subscribes c for all RIB updates of router rtr +func (b *BMPServer) SubscribeRIBs(client routingtable.RouteTableClient, rtr net.IP, afi uint8) { + b.gloablMu.Lock() + defer b.gloablMu.Unlock() + + rtrStr := rtr.String() + if _, ok := b.ribClients[rtrStr]; !ok { + b.ribClients[rtrStr] = make(map[afiClient]struct{}) + } + + ac := afiClient{ + afi: afi, + client: client, + } + if _, ok := b.ribClients[rtrStr][ac]; ok { + return + } + + b.ribClients[rtrStr][ac] = struct{}{} + + if _, ok := b.routers[rtrStr]; !ok { + return + } + + b.routers[rtrStr].subscribeRIBs(client, afi) +} + +// UnsubscribeRIBs unsubscribes client from RIBs of address family afi +func (b *BMPServer) UnsubscribeRIBs(client routingtable.RouteTableClient, rtr net.IP, afi uint8) { + b.gloablMu.Lock() + defer b.gloablMu.Unlock() + + rtrStr := rtr.String() + if _, ok := b.ribClients[rtrStr]; !ok { + return + } + + ac := afiClient{ + afi: afi, + client: client, + } + if _, ok := b.ribClients[rtrStr][ac]; !ok { + return + } + + delete(b.ribClients[rtrStr], ac) + b.routers[rtrStr].unsubscribeRIBs(client, afi) +} + +// AddRouter adds a router to which we connect with BMP +func (b *BMPServer) AddRouter(addr net.IP, port uint16, rib4 *locRIB.LocRIB, rib6 *locRIB.LocRIB) { + b.gloablMu.Lock() + defer b.gloablMu.Unlock() + + r := newRouter(addr, port, rib4, rib6) + b.routers[fmt.Sprintf("%s", r.address.String())] = r + + go func(r *router) { + for { + <-r.reconnectTimer.C + c, err := net.Dial("tcp", fmt.Sprintf("%s:%d", r.address.String(), r.port)) + if err != nil { + log.Infof("Unable to connect to BMP router: %v", err) + if r.reconnectTime == 0 { + r.reconnectTime = r.reconnectTimeMin + } else if r.reconnectTime < r.reconnectTimeMax { + r.reconnectTime *= 2 + } + r.reconnectTimer = time.NewTimer(time.Second * time.Duration(r.reconnectTime)) + continue + } + + r.reconnectTime = 0 + log.Infof("Connected to %s", r.address.String()) + r.serve(c) + } + }(r) +} + +// RemoveRouter removes a BMP monitored router +func (b *BMPServer) RemoveRouter(addr net.IP, port uint16) { + b.gloablMu.Lock() + defer b.gloablMu.Unlock() + + id := addr.String() + r := b.routers[id] + r.stop <- struct{}{} + delete(b.routers, id) +} + +func recvBMPMsg(c net.Conn) (msg []byte, err error) { + buffer := make([]byte, defaultBufferLen) + _, err = io.ReadFull(c, buffer[0:bmppkt.MinLen]) + if err != nil { + return nil, fmt.Errorf("Read failed: %v", err) + } + + l := convert.Uint32b(buffer[1:5]) + if l > defaultBufferLen { + tmp := buffer + buffer = make([]byte, l) + copy(buffer, tmp) + } + + toRead := l + _, err = io.ReadFull(c, buffer[bmppkt.MinLen:toRead]) + if err != nil { + return nil, fmt.Errorf("Read failed: %v", err) + } + + return buffer[0:toRead], nil +} diff --git a/protocols/bgp/server/bmp_server_test.go b/protocols/bgp/server/bmp_server_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d40b672d482827cf09ad4cbbbdd013b591f8b552 --- /dev/null +++ b/protocols/bgp/server/bmp_server_test.go @@ -0,0 +1,295 @@ +package server + +import ( + "net" + "testing" + + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/stretchr/testify/assert" +) + +func TestNewServer(t *testing.T) { + s := NewServer() + assert.Equal(t, &BMPServer{ + routers: map[string]*router{}, + ribClients: map[string]map[afiClient]struct{}{}, + }, s) +} + +func TestSubscribeRIBs(t *testing.T) { + tests := []struct { + name string + srv *BMPServer + expected *BMPServer + }{ + { + name: "Test without routers with clients", + srv: &BMPServer{ + routers: make(map[string]*router), + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.50": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + expected: &BMPServer{ + routers: make(map[string]*router), + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.50": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + "10.20.30.40": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + }, + { + name: "Test with routers no clients", + srv: &BMPServer{ + routers: map[string]*router{ + "10.20.30.40": &router{ + ribClients: make(map[afiClient]struct{}), + }, + }, + ribClients: map[string]map[afiClient]struct{}{}, + }, + expected: &BMPServer{ + routers: map[string]*router{ + "10.20.30.40": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + ribClients: map[string]map[afiClient]struct{}{ + "10.20.30.40": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + }, + } + + for _, test := range tests { + test.srv.SubscribeRIBs(nil, net.IP{10, 20, 30, 40}, packet.IPv4AFI) + + assert.Equalf(t, test.expected, test.srv, "Test %q", test.name) + + } +} + +func TestUnsubscribeRIBs(t *testing.T) { + tests := []struct { + name string + srv *BMPServer + expected *BMPServer + }{ + { + name: "Unsubscribe existing from router", + srv: &BMPServer{ + routers: map[string]*router{ + "10.20.30.40": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + "20.30.40.50": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.50": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + "10.20.30.40": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + expected: &BMPServer{ + routers: map[string]*router{ + "10.20.30.40": &router{ + ribClients: map[afiClient]struct{}{}, + }, + "20.30.40.50": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.50": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + "10.20.30.40": map[afiClient]struct{}{ + }, + }, + }, + }, + { + name: "Unsubscribe existing from non-router", + srv: &BMPServer{ + routers: map[string]*router{ + "10.20.30.60": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + "20.30.40.50": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.50": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + "10.20.30.60": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + expected: &BMPServer{ + routers: map[string]*router{ + "10.20.30.60": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + "20.30.40.50": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.50": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + "10.20.30.60": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + }, + { + name: "Unsubscribe existing from non-existing client", + srv: &BMPServer{ + routers: map[string]*router{ + "10.20.30.40": &router{ + ribClients: map[afiClient]struct{}{}, + }, + "20.30.40.50": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.40": map[afiClient]struct{}{}, + "10.20.30.60": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + expected: &BMPServer{ + routers: map[string]*router{ + "10.20.30.40": &router{ + ribClients: map[afiClient]struct{}{}, + }, + "20.30.40.50": &router{ + ribClients: map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + ribClients: map[string]map[afiClient]struct{}{ + "20.30.40.40": map[afiClient]struct{}{}, + "10.20.30.60": map[afiClient]struct{}{ + afiClient{ + afi: packet.IPv4AFI, + client: nil, + }: struct{}{}, + }, + }, + }, + }, + } + + for _, test := range tests { + test.srv.UnsubscribeRIBs(nil, net.IP{10, 20, 30, 40}, packet.IPv4AFI) + + assert.Equalf(t, test.expected, test.srv, "Test %q", test.name) + + } +} diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index c0697f1afc7686031b151055ba45a7c831761a3b..6e11d6672aed81061d870e79c71183066e35a53e 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -29,6 +29,7 @@ type state interface { // FSM implements the BGP finite state machine (RFC4271) type FSM struct { + isBMP bool peer *peer eventCh chan int con net.Conn @@ -330,6 +331,10 @@ func recvMsg(c net.Conn) (msg []byte, err error) { } func stopTimer(t *time.Timer) { + if t == nil { + return + } + if !t.Stop() { select { case <-t.C: diff --git a/protocols/bgp/server/fsm_address_family.go b/protocols/bgp/server/fsm_address_family.go index 0b241ecca48b4a6169a918843eedc64cf042e5e3..5c3540c9afb4b586d8eb954b8f64b1207a9b3045 100644 --- a/protocols/bgp/server/fsm_address_family.go +++ b/protocols/bgp/server/fsm_address_family.go @@ -53,6 +53,7 @@ func (f *fsmAddressFamily) init(n *routingtable.Neighbor) { f.adjRIBIn = adjRIBIn.New(f.importFilter, contributingASNs, f.fsm.peer.routerID, f.fsm.peer.clusterID, f.addPathRX) contributingASNs.Add(f.fsm.peer.localASN) + f.adjRIBIn.Register(f.rib) f.adjRIBOut = adjRIBOut.New(n, f.exportFilter, !f.addPathTX.BestOnly) @@ -65,6 +66,24 @@ func (f *fsmAddressFamily) init(n *routingtable.Neighbor) { f.rib.RegisterWithOptions(f.adjRIBOut, f.addPathTX) } +func (f *fsmAddressFamily) bmpInit() { + f.adjRIBIn = adjRIBIn.New(filter.NewAcceptAllFilter(), &routingtable.ContributingASNs{}, f.fsm.peer.routerID, f.fsm.peer.clusterID, f.addPathRX) + + if f.rib != nil { + f.adjRIBIn.Register(f.rib) + } +} + +func (f *fsmAddressFamily) bmpDispose() { + f.rib.GetContributingASNs().Remove(f.fsm.peer.localASN) + + f.adjRIBIn.(*adjRIBIn.AdjRIBIn).Flush() + + f.adjRIBIn.Unregister(f.rib) + + f.adjRIBIn = nil +} + func (f *fsmAddressFamily) dispose() { if !f.initialized { return @@ -87,13 +106,11 @@ func (f *fsmAddressFamily) processUpdate(u *packet.BGPUpdate) { return } - if f.multiProtocol { - f.multiProtocolUpdates(u) - return + f.multiProtocolUpdates(u) + if f.afi == packet.IPv4AFI { + f.withdraws(u) + f.updates(u) } - - f.withdraws(u) - f.updates(u) } func (f *fsmAddressFamily) withdraws(u *packet.BGPUpdate) { @@ -184,6 +201,8 @@ func (f *fsmAddressFamily) processAttributes(attrs *packet.PathAttribute, path * path.BGPPath.OriginatorID = pa.Value.(uint32) case packet.ClusterListAttr: path.BGPPath.ClusterList = pa.Value.([]uint32) + case packet.MultiProtocolReachNLRICode: + case packet.MultiProtocolUnreachNLRICode: default: unknownAttr := f.processUnknownAttribute(pa) if unknownAttr != nil { diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index 2758a506f017593d6c9b99f7c16dd110377438f3..05e540b597394424a5fde51461cc2aa55e42888b 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -153,16 +153,19 @@ func (s *establishedState) msgReceived(data []byte, opt *packet.DecodeOptions) ( s.fsm.sendNotification(bgperr.ErrorCode, bgperr.ErrorSubCode) } stopTimer(s.fsm.connectRetryTimer) - s.fsm.con.Close() + if s.fsm.con != nil { + s.fsm.con.Close() + } s.fsm.connectRetryCounter++ - return newIdleState(s.fsm), "Failed to decode BGP message" + return newIdleState(s.fsm), fmt.Sprintf("Failed to decode BGP message: %v", err) } + switch msg.Header.Type { case packet.NotificationMsg: fmt.Println(data) return s.notification() case packet.UpdateMsg: - return s.update(msg) + return s.update(msg.Body.(*packet.BGPUpdate)) case packet.KeepaliveMsg: return s.keepaliveReceived() default: @@ -178,13 +181,11 @@ func (s *establishedState) notification() (state, string) { return newIdleState(s.fsm), "Received NOTIFICATION" } -func (s *establishedState) update(msg *packet.BGPMessage) (state, string) { +func (s *establishedState) update(u *packet.BGPUpdate) (state, string) { if s.fsm.holdTime != 0 { s.fsm.holdTimer.Reset(s.fsm.holdTime) } - u := msg.Body.(*packet.BGPUpdate) - if s.fsm.ipv4Unicast != nil { s.fsm.ipv4Unicast.processUpdate(u) } diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index a3aac48a22add9132441a589994cca7752a689d7..a66898541373f688a06aa9b2283e847095e9e332 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -92,7 +92,7 @@ func (s *openSentState) msgReceived(data []byte, opt *packet.DecodeOptions) (sta case packet.NotificationMsg: return s.notification(msg) case packet.OpenMsg: - return s.openMsgReceived(msg) + return s.openMsgReceived(msg.Body.(*packet.BGPOpen)) default: return s.unexpectedMessage() } @@ -106,11 +106,15 @@ func (s *openSentState) unexpectedMessage() (state, string) { return newIdleState(s.fsm), "FSM Error" } -func (s *openSentState) openMsgReceived(msg *packet.BGPMessage) (state, string) { - openMsg := msg.Body.(*packet.BGPOpen) +func (s *openSentState) openMsgReceived(openMsg *packet.BGPOpen) (state, string) { s.peerASNRcvd = uint32(openMsg.ASN) s.fsm.neighborID = openMsg.BGPIdentifier + + if s.fsm.isBMP { + return s.handleOpenMessage(openMsg) + } + stopTimer(s.fsm.connectRetryTimer) if s.fsm.peer.collisionHandling(s.fsm) { return s.cease() diff --git a/protocols/bgp/server/fsm_open_sent_test.go b/protocols/bgp/server/fsm_open_sent_test.go index 708617b910775a28efcd89ec0866320905912455..395432f31bfe988c0a27ac11c1565a382ee1bea9 100644 --- a/protocols/bgp/server/fsm_open_sent_test.go +++ b/protocols/bgp/server/fsm_open_sent_test.go @@ -1,6 +1,7 @@ package server import ( + "net" "testing" "github.com/bio-routing/bio-rd/protocols/bgp/packet" @@ -69,7 +70,19 @@ func TestOpenMsgReceived(t *testing.T) { fsm := newFSM(&peer{ peerASN: test.asn, }) - fsm.con = &btesting.MockConn{} + + conA, conB := net.Pipe() + fsm.con = conB + + go func() { + for { + buf := make([]byte, 1) + _, err := conA.Read(buf) + if err != nil { + return + } + } + }() s := &openSentState{ fsm: fsm, diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index 9a5b128aacc89a7fa996b75b7e175e8a97dd4d2d..8e0a674ac112f15ce97bf58cf658f7d891a8dd8f 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -20,10 +20,11 @@ type PeerInfo struct { } type peer struct { - server *bgpServer - addr bnet.IP - peerASN uint32 - localASN uint32 + server *bgpServer + addr bnet.IP + localAddr bnet.IP + peerASN uint32 + localASN uint32 // guarded by fsmsMu fsms []*FSM diff --git a/protocols/bmp/packet/decode.go b/protocols/bmp/packet/decode.go index d7989e28bb5955a294b195962d06c980cd0b645b..a76f6b7853e221126b77512039d9fc5296469496 100644 --- a/protocols/bmp/packet/decode.go +++ b/protocols/bmp/packet/decode.go @@ -61,7 +61,7 @@ func Decode(msg []byte) (Msg, error) { return sr, nil case PeerDownNotificationType: - pd, err := decodePeerUpNotification(buf, ch) + pd, err := decodePeerDownNotification(buf, ch) if err != nil { return nil, fmt.Errorf("Unable to decode peer down notification: %v", err) } diff --git a/protocols/bmp/packet/decode_test.go b/protocols/bmp/packet/decode_test.go index b3419725826e141b6225838f30d9e7d91296a2ba..b44a4d0689be0de048c9a7646fd1e51255e69cac 100644 --- a/protocols/bmp/packet/decode_test.go +++ b/protocols/bmp/packet/decode_test.go @@ -26,11 +26,11 @@ func TestDecode(t *testing.T) { { name: "Route monitoring ok", input: []byte{ - 3, 0, 0, 0, 6 + 38 + 4, 0, + 3, 0, 0, 0, 6 + PerPeerHeaderLen + 4, 0, 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -43,7 +43,7 @@ func TestDecode(t *testing.T) { expected: &RouteMonitoringMsg{ CommonHeader: &CommonHeader{ Version: 3, - MsgLength: 6 + 38 + 4, + MsgLength: 6 + PerPeerHeaderLen + 4, MsgType: 0, }, PerPeerHeader: &PerPeerHeader{ @@ -62,11 +62,11 @@ func TestDecode(t *testing.T) { { name: "Route monitoring nok", input: []byte{ - 3, 0, 0, 0, 6 + 38 + 4, 0, + 3, 0, 0, 0, 6 + PerPeerHeaderLen + 4, 0, 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -80,11 +80,11 @@ func TestDecode(t *testing.T) { { name: "Statistic report ok", input: []byte{ - 3, 0, 0, 0, 6 + 9 + 38, 1, + 3, 0, 0, 0, 6 + 9 + PerPeerHeaderLen, 1, 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -98,7 +98,7 @@ func TestDecode(t *testing.T) { expected: &StatsReport{ CommonHeader: &CommonHeader{ Version: 3, - MsgLength: 6 + 9 + 38, + MsgLength: 6 + 9 + PerPeerHeaderLen, MsgType: 1, }, PerPeerHeader: &PerPeerHeader{ @@ -124,11 +124,11 @@ func TestDecode(t *testing.T) { { name: "Statistic report nok", input: []byte{ - 3, 0, 0, 0, 6 + 9 + 38, 1, + 3, 0, 0, 0, 6 + 9 + PerPeerHeaderLen, 1, 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, }, @@ -137,11 +137,11 @@ func TestDecode(t *testing.T) { { name: "peer down ok", input: []byte{ - 3, 0, 0, 0, 6 + 9 + 38, 1, + 3, 0, 0, 0, 6 + 9 + PerPeerHeaderLen, 1, 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -155,7 +155,7 @@ func TestDecode(t *testing.T) { expected: &StatsReport{ CommonHeader: &CommonHeader{ Version: 3, - MsgLength: 6 + 9 + 38, + MsgLength: 6 + 9 + PerPeerHeaderLen, MsgType: 1, }, PerPeerHeader: &PerPeerHeader{ @@ -181,11 +181,11 @@ func TestDecode(t *testing.T) { { name: "peer down nok", input: []byte{ - 3, 0, 0, 0, 6 + 9 + 38, 1, + 3, 0, 0, 0, 6 + 9 + PerPeerHeaderLen, 1, 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -204,7 +204,7 @@ func TestDecode(t *testing.T) { 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -216,6 +216,9 @@ func TestDecode(t *testing.T) { 0, 200, // OPEN Sent + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 34, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -224,6 +227,9 @@ func TestDecode(t *testing.T) { 1, 2, 3, 4, 5, // OPEN Recv + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -253,6 +259,9 @@ func TestDecode(t *testing.T) { LocalPort: 100, RemotePort: 200, SentOpenMsg: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 34, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -261,7 +270,9 @@ func TestDecode(t *testing.T) { 1, 2, 3, 4, 5, }, ReceivedOpenMsg: []byte{ - // OPEN Recv + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -280,7 +291,7 @@ func TestDecode(t *testing.T) { 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -374,7 +385,7 @@ func TestDecode(t *testing.T) { 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -416,7 +427,7 @@ func TestDecode(t *testing.T) { 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, diff --git a/protocols/bmp/packet/peer_down.go b/protocols/bmp/packet/peer_down.go index cdb72ffe33f72dc77e4c7373b6041a467bbbf836..7b0303043f489f93ce4c1ed4cc2610cbb87fa9e2 100644 --- a/protocols/bmp/packet/peer_down.go +++ b/protocols/bmp/packet/peer_down.go @@ -57,7 +57,7 @@ func decodePeerDownNotification(buf *bytes.Buffer, ch *CommonHeader) (*PeerDownN err = decoder.Decode(buf, fields) if err != nil { - return nil, err + return nil, fmt.Errorf("Unable to read Data: %v", err) } return p, nil diff --git a/protocols/bmp/packet/peer_down_test.go b/protocols/bmp/packet/peer_down_test.go index b1df8b55e84d9ae6bf5854e4a2aea51357d73c3a..58cdbed36e9cd6ce8244f960b8cd15cc64004451 100644 --- a/protocols/bmp/packet/peer_down_test.go +++ b/protocols/bmp/packet/peer_down_test.go @@ -32,7 +32,7 @@ func TestDecodePeerDownNotification(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -43,12 +43,12 @@ func TestDecodePeerDownNotification(t *testing.T) { 1, 2, 3, }, ch: &CommonHeader{ - MsgLength: CommonHeaderLen + 4 + 38, + MsgLength: CommonHeaderLen + 4 + PerPeerHeaderLen, }, wantFail: false, expected: &PeerDownNotification{ CommonHeader: &CommonHeader{ - MsgLength: CommonHeaderLen + 4 + 38, + MsgLength: CommonHeaderLen + 4 + PerPeerHeaderLen, }, PerPeerHeader: &PerPeerHeader{ PeerType: 1, @@ -71,7 +71,7 @@ func TestDecodePeerDownNotification(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -106,7 +106,7 @@ func TestDecodePeerDownNotification(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, @@ -121,7 +121,7 @@ func TestDecodePeerDownNotification(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, diff --git a/protocols/bmp/packet/peer_up.go b/protocols/bmp/packet/peer_up.go index 58adc4cc5e6cd294c88758333f6aa26c214f46b0..34d37a7cbc9786ec4459b50a56c917a037c261a4 100644 --- a/protocols/bmp/packet/peer_up.go +++ b/protocols/bmp/packet/peer_up.go @@ -9,7 +9,7 @@ import ( const ( // OpenMsgMinLen is the minimal length of a BGP open message - OpenMsgMinLen = 10 + OpenMsgMinLen = 29 ) // PeerUpNotification represents a peer up notification diff --git a/protocols/bmp/packet/peer_up_test.go b/protocols/bmp/packet/peer_up_test.go index d29ec7550cc757ee8e3332d1de69509e590948bd..0b66d6e8e059ddc6600104d83777791f6b22ae22 100644 --- a/protocols/bmp/packet/peer_up_test.go +++ b/protocols/bmp/packet/peer_up_test.go @@ -18,6 +18,7 @@ func TestPeerUpMsgType(t *testing.T) { t.Errorf("Unexpected result") } } + func TestDecodePeerUp(t *testing.T) { tests := []struct { name string @@ -31,7 +32,7 @@ func TestDecodePeerUp(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -43,6 +44,9 @@ func TestDecodePeerUp(t *testing.T) { 0, 200, // OPEN Sent + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 34, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -51,6 +55,9 @@ func TestDecodePeerUp(t *testing.T) { 1, 2, 3, 4, 5, // OPEN Recv + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -60,12 +67,12 @@ func TestDecodePeerUp(t *testing.T) { 120, 140, 160, // Information }, ch: &CommonHeader{ - MsgLength: 47, + MsgLength: 126, }, wantFail: false, expected: &PeerUpNotification{ CommonHeader: &CommonHeader{ - MsgLength: 47, + MsgLength: 126, }, PerPeerHeader: &PerPeerHeader{ PeerType: 1, @@ -81,6 +88,9 @@ func TestDecodePeerUp(t *testing.T) { LocalPort: 100, RemotePort: 200, SentOpenMsg: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 34, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -89,7 +99,9 @@ func TestDecodePeerUp(t *testing.T) { 1, 2, 3, 4, 5, }, ReceivedOpenMsg: []byte{ - // OPEN Recv + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -106,7 +118,7 @@ func TestDecodePeerUp(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -118,6 +130,9 @@ func TestDecodePeerUp(t *testing.T) { 0, 200, // OPEN Sent + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 34, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -126,6 +141,9 @@ func TestDecodePeerUp(t *testing.T) { 1, 2, 3, 4, 5, // OPEN Recv + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -133,12 +151,12 @@ func TestDecodePeerUp(t *testing.T) { 0, // Opt Parm Len }, ch: &CommonHeader{ - MsgLength: 44, + MsgLength: 82, }, wantFail: false, expected: &PeerUpNotification{ CommonHeader: &CommonHeader{ - MsgLength: 44, + MsgLength: 82, }, PerPeerHeader: &PerPeerHeader{ PeerType: 1, @@ -154,6 +172,9 @@ func TestDecodePeerUp(t *testing.T) { LocalPort: 100, RemotePort: 200, SentOpenMsg: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 34, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -162,7 +183,9 @@ func TestDecodePeerUp(t *testing.T) { 1, 2, 3, 4, 5, }, ReceivedOpenMsg: []byte{ - // OPEN Recv + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -176,13 +199,13 @@ func TestDecodePeerUp(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, }, ch: &CommonHeader{ - MsgLength: 47, + MsgLength: 51, }, wantFail: true, }, @@ -191,7 +214,7 @@ func TestDecodePeerUp(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -202,7 +225,7 @@ func TestDecodePeerUp(t *testing.T) { 0, 100, }, ch: &CommonHeader{ - MsgLength: 47, + MsgLength: 51, }, wantFail: true, }, @@ -211,7 +234,7 @@ func TestDecodePeerUp(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -223,12 +246,15 @@ func TestDecodePeerUp(t *testing.T) { 0, 200, // OPEN Sent + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time }, ch: &CommonHeader{ - MsgLength: 47, + MsgLength: 89, }, wantFail: true, }, @@ -237,7 +263,7 @@ func TestDecodePeerUp(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -248,12 +274,15 @@ func TestDecodePeerUp(t *testing.T) { 0, 200, // OPEN Sent + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time }, ch: &CommonHeader{ - MsgLength: 47, + MsgLength: 88, }, wantFail: true, }, @@ -265,6 +294,9 @@ func TestDecodePeerUp(t *testing.T) { 0, 200, // OPEN Sent + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -273,6 +305,9 @@ func TestDecodePeerUp(t *testing.T) { 1, 2, 3, 4, 5, // OPEN Recv + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 29, + 1, 4, // Version 1, 0, // ASN 2, 0, // Hold Time @@ -280,7 +315,7 @@ func TestDecodePeerUp(t *testing.T) { 3, // Opt Parm Len }, ch: &CommonHeader{ - MsgLength: 47, + MsgLength: 85, }, wantFail: true, }, diff --git a/protocols/bmp/packet/per_peer_header.go b/protocols/bmp/packet/per_peer_header.go index d03f8b43d5d52b6dd705329c0bb10f834d39a15d..f7c60ed97f98068cf2bb573b348407584ef2d3e4 100644 --- a/protocols/bmp/packet/per_peer_header.go +++ b/protocols/bmp/packet/per_peer_header.go @@ -9,14 +9,14 @@ import ( const ( // PerPeerHeaderLen is the length of a per peer header - PerPeerHeaderLen = 38 + PerPeerHeaderLen = 42 ) // PerPeerHeader represents a BMP per peer header type PerPeerHeader struct { PeerType uint8 PeerFlags uint8 - PeerDistinguisher uint32 + PeerDistinguisher uint64 PeerAddress [16]byte PeerAS uint32 PeerBGPID uint32 @@ -28,7 +28,7 @@ type PerPeerHeader struct { func (p *PerPeerHeader) Serialize(buf *bytes.Buffer) { buf.WriteByte(p.PeerType) buf.WriteByte(p.PeerFlags) - buf.Write(convert.Uint32Byte(p.PeerDistinguisher)) + buf.Write(convert.Uint64Byte(p.PeerDistinguisher)) buf.Write(p.PeerAddress[:]) buf.Write(convert.Uint32Byte(p.PeerAS)) buf.Write(convert.Uint32Byte(p.PeerBGPID)) @@ -57,3 +57,11 @@ func decodePerPeerHeader(buf *bytes.Buffer) (*PerPeerHeader, error) { return p, nil } + +// GetIPVersion gets the IP version of the BGP session +func (p *PerPeerHeader) GetIPVersion() uint8 { + if p.PeerFlags>>7 == 1 { + return 6 + } + return 4 +} diff --git a/protocols/bmp/packet/per_peer_header_test.go b/protocols/bmp/packet/per_peer_header_test.go index de503a84edea465106787c5846ce26244c42c84a..8c51ee6c9b286faf927893d20ab093fe792ce0ce 100644 --- a/protocols/bmp/packet/per_peer_header_test.go +++ b/protocols/bmp/packet/per_peer_header_test.go @@ -28,7 +28,7 @@ func TestPerPeerHeaderSerialize(t *testing.T) { expected: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -59,7 +59,7 @@ func TestDecodePerPeerHeader(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -83,7 +83,7 @@ func TestDecodePerPeerHeader(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -113,4 +113,47 @@ func TestDecodePerPeerHeader(t *testing.T) { assert.Equalf(t, test.expected, p, "Test %q", test.name) } + +} + +func TestGetIPVersion(t *testing.T) { + tests := []struct { + name string + p *PerPeerHeader + expected uint8 + }{ + { + name: "IPv4", + p: &PerPeerHeader{ + PeerFlags: 0, + }, + expected: 4, + }, + { + name: "IPv4 #2", + p: &PerPeerHeader{ + PeerFlags: 127, + }, + expected: 4, + }, + { + name: "IPv6", + p: &PerPeerHeader{ + PeerFlags: 128, + }, + expected: 6, + }, + { + name: "IPv6 #2", + p: &PerPeerHeader{ + PeerFlags: 129, + }, + expected: 6, + }, + } + + for _, test := range tests { + v := test.p.GetIPVersion() + assert.Equal(t, test.expected, v) + } } diff --git a/protocols/bmp/packet/route_mirroring_test.go b/protocols/bmp/packet/route_mirroring_test.go index c62799a37a2c1bfa53c01eafb90979aa896716be..87bcf2110ed6c34f230bca7fbb3ceb4eb1c97400 100644 --- a/protocols/bmp/packet/route_mirroring_test.go +++ b/protocols/bmp/packet/route_mirroring_test.go @@ -31,7 +31,7 @@ func TestDecodeRouteMirroringMsg(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -78,7 +78,7 @@ func TestDecodeRouteMirroringMsg(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, }, @@ -92,7 +92,7 @@ func TestDecodeRouteMirroringMsg(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, diff --git a/protocols/bmp/packet/route_monitoring_test.go b/protocols/bmp/packet/route_monitoring_test.go index d28169346d6312f757fd317b879c55e20dd43847..7534e6f31d0deb79159c188ce5184443a63e672a 100644 --- a/protocols/bmp/packet/route_monitoring_test.go +++ b/protocols/bmp/packet/route_monitoring_test.go @@ -31,7 +31,7 @@ func TestDecodeRouteMonitoringMsg(t *testing.T) { input: []byte{ 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, diff --git a/protocols/bmp/packet/stats_report_test.go b/protocols/bmp/packet/stats_report_test.go index 2fc47cbb4a1b69c55834af7db122215dfdf32a2b..0bf372238be74e55b4b4b49ca2c993bf88fbe31c 100644 --- a/protocols/bmp/packet/stats_report_test.go +++ b/protocols/bmp/packet/stats_report_test.go @@ -32,7 +32,7 @@ func TestDecodeStatsReport(t *testing.T) { // Per Peer Header 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -83,7 +83,7 @@ func TestDecodeStatsReport(t *testing.T) { // Per Peer Header 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -96,7 +96,7 @@ func TestDecodeStatsReport(t *testing.T) { // Per Peer Header 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, @@ -111,7 +111,7 @@ func TestDecodeStatsReport(t *testing.T) { // Per Peer Header 1, 2, - 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 200, 124, 0, 0, 0, 123, diff --git a/routingtable/adjRIBIn/adj_rib_in.go b/routingtable/adjRIBIn/adj_rib_in.go index 631f1d197cabc926056f186e21e1b6cd5d719afd..5bf7862334ece9b649baf5c75e42eff4936689f9 100644 --- a/routingtable/adjRIBIn/adj_rib_in.go +++ b/routingtable/adjRIBIn/adj_rib_in.go @@ -37,6 +37,19 @@ func New(exportFilter *filter.Filter, contributingASNs *routingtable.Contributin return a } +// Flush drops all routes from the AdjRIBIn +func (a *AdjRIBIn) Flush() { + a.mu.Lock() + defer a.mu.Unlock() + + routes := a.rt.Dump() + for _, route := range routes { + for _, path := range route.Paths() { + a.removePath(route.Prefix(), path) + } + } +} + // UpdateNewClient sends current state to a new client func (a *AdjRIBIn) UpdateNewClient(client routingtable.RouteTableClient) error { a.mu.RLock() @@ -124,6 +137,11 @@ func (a *AdjRIBIn) RemovePath(pfx net.Prefix, p *route.Path) bool { a.mu.Lock() defer a.mu.Unlock() + return a.removePath(pfx, p) +} + +// removePath removes the path for prefix `pfx` +func (a *AdjRIBIn) removePath(pfx net.Prefix, p *route.Path) bool { r := a.rt.Get(pfx) if r == nil { return false diff --git a/routingtable/locRIB/loc_rib.go b/routingtable/locRIB/loc_rib.go index d9ad2b46f29ee95d8a0d6b42ff513bd7172e067f..aa60b9243aab1846bdb6dd010f9ced6155071268 100644 --- a/routingtable/locRIB/loc_rib.go +++ b/routingtable/locRIB/loc_rib.go @@ -64,7 +64,6 @@ func (a *LocRIB) RouteCount() int64 { func (a *LocRIB) AddPath(pfx net.Prefix, p *route.Path) error { a.mu.Lock() defer a.mu.Unlock() - logrus.WithFields(map[string]interface{}{ "Prefix": pfx, "Route": p, diff --git a/testing/BUILD.bazel b/testing/BUILD.bazel index 02411722b3d58ed2be6fcd2e4c950da21807f8cf..ee2d86559d828f247f0631e2c3d18f3408b550ff 100644 --- a/testing/BUILD.bazel +++ b/testing/BUILD.bazel @@ -2,9 +2,13 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = ["conn_mock.go"], + srcs = [ + "conn_mock.go", + "log.go", + ], importpath = "github.com/bio-routing/bio-rd/testing", visibility = ["//visibility:public"], + deps = ["//vendor/github.com/sirupsen/logrus:go_default_library"], ) go_test( diff --git a/testing/conn_mock.go b/testing/conn_mock.go index 9c89ea64e658ad2cb5c46f91f5ac368fe9d5a436..1be39e05208aa91d67fd841b2de740bfdc15d5da 100644 --- a/testing/conn_mock.go +++ b/testing/conn_mock.go @@ -1,34 +1,32 @@ package testing import ( + "bytes" "net" ) // MockConn mock an connection type MockConn struct { net.Conn - - // Bytes are the bytes written - Bytes []byte + Buf *bytes.Buffer + Closed bool } func NewMockConn() *MockConn { return &MockConn{ - Bytes: make([]byte, 0), + Buf: bytes.NewBuffer(nil), } } func (m *MockConn) Write(b []byte) (int, error) { - m.Bytes = append(m.Bytes, b...) - return len(b), nil + return m.Buf.Write(b) } func (m *MockConn) Read(b []byte) (n int, err error) { - count := len(b) - if count > len(m.Bytes) { - count = len(m.Bytes) - } + return m.Buf.Read(b) +} - copy(b, m.Bytes[0:count]) - return count, nil +func (m *MockConn) Close() error { + m.Closed = true + return nil } diff --git a/testing/conn_mock_test.go b/testing/conn_mock_test.go index 9d964ffa9fecef3f577dd02c498ed9dc110bcf1b..5afe2e35d0e0bbad21910b2600f084bbf088d20b 100644 --- a/testing/conn_mock_test.go +++ b/testing/conn_mock_test.go @@ -1,25 +1,30 @@ package testing import ( + "bytes" "testing" "github.com/stretchr/testify/assert" ) func TestWrite(t *testing.T) { - m := &MockConn{} + m := &MockConn{ + Buf: bytes.NewBuffer(nil), + } payload := []byte{1, 2, 3} m.Write(payload) - assert.Equal(t, payload, m.Bytes) + assert.Equal(t, payload, m.Buf.Bytes()) } func TestRead(t *testing.T) { - m := &MockConn{} + m := &MockConn{ + Buf: bytes.NewBuffer(nil), + } payload := []byte{1, 2, 3} - m.Bytes = payload + m.Buf.Write(payload) buffer := make([]byte, 4) n, _ := m.Read(buffer) diff --git a/testing/log.go b/testing/log.go new file mode 100644 index 0000000000000000000000000000000000000000..538b23ef199506c37fb4c392951cd960d33082cf --- /dev/null +++ b/testing/log.go @@ -0,0 +1,26 @@ +package testing + +import ( + "fmt" + + "github.com/sirupsen/logrus" +) + +// LogFormatter provides a log formatter for unit tests free of timestamps +type LogFormatter struct{} + +// NewLogFormatter creates a new log formatter +func NewLogFormatter() *LogFormatter { + return &LogFormatter{} +} + +// Format formats a log entry +func (l *LogFormatter) Format(e *logrus.Entry) ([]byte, error) { + var res string + if len(e.Data) == 0 { + res = fmt.Sprintf("level=%s msg=%q", e.Level, e.Message) + } else { + res = fmt.Sprintf("level=%s msg=%q fields=%v", e.Level, e.Message, e.Data) + } + return []byte(res), nil +}