diff --git a/protocols/bgp/packet/fuzzing.go b/protocols/bgp/packet/fuzzing.go index 0adaded071d6eb2f23ecd4eb92055daa266cd8bb..9f829cc89c3dcfb7566287fa9c6c3ffbab11ce5b 100644 --- a/protocols/bgp/packet/fuzzing.go +++ b/protocols/bgp/packet/fuzzing.go @@ -35,13 +35,16 @@ func getAllOptions() []types.Options { parameters := []bool{true, false} var ret []types.Options for _, octet := range parameters { - for _, multi := range parameters { - for _, addPathX := range parameters { - ret = append(ret, types.Options{ - Supports4OctetASN: octet, - SupportsMultiProtocol: multi, - AddPathRX: addPathX, - }) + for _, mpbgp4 := range parameters { + for _, mpbgp6 := range parameters { + for _, addPathX := range parameters { + ret = append(ret, types.Options{ + Supports4OctetASN: octet, + MultiProtocolIPv4: mpbgp4, + MultiProtocolIPv6: mpbgp6, + AddPathRX: addPathX, + }) + } } } } diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index 1e614ab1a45315f7ba13df63a18531c5937a92e8..60eb417202135a54f1488f0fd10af620c997b2ec 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -72,23 +72,23 @@ type FSM struct { connectionCancelFunc context.CancelFunc } -// NewPassiveFSM2 initiates a new passive FSM -func NewPassiveFSM2(peer *peer, con *net.TCPConn) *FSM { - fsm := newFSM2(peer) +// NewPassiveFSM initiates a new passive FSM +func NewPassiveFSM(peer *peer, con *net.TCPConn) *FSM { + fsm := newFSM(peer) fsm.con = con fsm.state = newIdleState(fsm) return fsm } -// NewActiveFSM2 initiates a new passive FSM -func NewActiveFSM2(peer *peer) *FSM { - fsm := newFSM2(peer) +// NewActiveFSM initiates a new passive FSM +func NewActiveFSM(peer *peer) *FSM { + fsm := newFSM(peer) fsm.active = true fsm.state = newIdleState(fsm) return fsm } -func newFSM2(peer *peer) *FSM { +func newFSM(peer *peer) *FSM { f := &FSM{ connectRetryTime: time.Minute, peer: peer, diff --git a/protocols/bgp/server/fsm_address_family.go b/protocols/bgp/server/fsm_address_family.go index daa5465e16f2bb90e0da43316b4bb475021ffd1b..f81f4d82905f3aeb9f60157cb2cad611859b18ec 100644 --- a/protocols/bgp/server/fsm_address_family.go +++ b/protocols/bgp/server/fsm_address_family.go @@ -83,14 +83,29 @@ func (f *fsmAddressFamily) dispose() { } func (f *fsmAddressFamily) processUpdate(u *packet.BGPUpdate) { - if f.afi == packet.IPv4AFI && f.safi == packet.UnicastSAFI { - f.withdraws(u) - f.updates(u) + if f.safi != packet.UnicastSAFI { + return + } + + mp := false + switch f.afi { + case packet.IPv4AFI: + if f.fsm.options.MultiProtocolIPv4 { + mp = true + } + case packet.IPv6AFI: + if f.fsm.options.MultiProtocolIPv6 { + mp = true + } } - if f.fsm.options.SupportsMultiProtocol { + if mp { f.multiProtocolUpdates(u) + return } + + f.withdraws(u) + f.updates(u) } func (f *fsmAddressFamily) withdraws(u *packet.BGPUpdate) { @@ -112,10 +127,6 @@ func (f *fsmAddressFamily) updates(u *packet.BGPUpdate) { } func (f *fsmAddressFamily) multiProtocolUpdates(u *packet.BGPUpdate) { - if !f.fsm.options.SupportsMultiProtocol { - return - } - path := f.newRoutePath() f.processAttributes(u.PathAttributes, path) @@ -140,6 +151,10 @@ func (f *fsmAddressFamily) newRoutePath() *route.Path { } func (f *fsmAddressFamily) multiProtocolUpdate(path *route.Path, nlri packet.MultiProtocolReachNLRI) { + if f.afi != nlri.AFI || f.safi != nlri.SAFI { + return + } + path.BGPPath.NextHop = nlri.NextHop for _, pfx := range nlri.Prefixes { @@ -148,6 +163,10 @@ func (f *fsmAddressFamily) multiProtocolUpdate(path *route.Path, nlri packet.Mul } func (f *fsmAddressFamily) multiProtocolWithdraw(path *route.Path, nlri packet.MultiProtocolUnreachNLRI) { + if f.afi != nlri.AFI || f.safi != nlri.SAFI { + return + } + for _, pfx := range nlri.Prefixes { f.adjRIBIn.RemovePath(pfx, path) } diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index 2ffa19415e0d1233b5718b9127b8dbbfaaf9874b..246c7cbf6a9df6018c33fe135b2eae2fcf2e2ae2 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -187,7 +187,16 @@ func (s *establishedState) update(msg *packet.BGPMessage) (state, string) { } u := msg.Body.(*packet.BGPUpdate) - afi, safi := s.addressFamilyForUpdate(u) + + if s.fsm.ipv4Unicast != nil { + s.fsm.ipv4Unicast.processUpdate(u) + } + + if s.fsm.ipv6Unicast != nil { + s.fsm.ipv6Unicast.processUpdate(u) + } + + afi, safi := s.updateAddressFamily(u) if safi != packet.UnicastSAFI { // only unicast support, so other SAFIs are ignored @@ -199,19 +208,19 @@ func (s *establishedState) update(msg *packet.BGPMessage) (state, string) { if s.fsm.ipv4Unicast == nil { log.Warnf("Received update for family IPv4 unicast, but this family is not configured.") } - s.fsm.ipv4Unicast.processUpdate(u) + case packet.IPv6AFI: if s.fsm.ipv6Unicast == nil { log.Warnf("Received update for family IPv6 unicast, but this family is not configured.") } - s.fsm.ipv6Unicast.processUpdate(u) + } return newEstablishedState(s.fsm), s.fsm.reason } -func (s *establishedState) addressFamilyForUpdate(u *packet.BGPUpdate) (afi uint16, safi uint8) { - if !s.fsm.options.SupportsMultiProtocol || u.NLRI != nil || u.WithdrawnRoutes != nil { +func (s *establishedState) updateAddressFamily(u *packet.BGPUpdate) (afi uint16, safi uint8) { + if u.WithdrawnRoutes != nil || u.NLRI != nil { return packet.IPv4AFI, packet.UnicastSAFI } diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index 3ece04475f645340a60e814e3452110ce56ab5ff..be9be952ff27b615f0d24f1ec8920198a1b813be 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -175,7 +175,16 @@ func (s *openSentState) processCapability(cap packet.Capability) { } func (s *openSentState) processMultiProtocolCapability(cap packet.MultiProtocolCapability) { - s.fsm.options.SupportsMultiProtocol = true + if cap.SAFI != 0 { + return + } + + switch cap.AFI { + case packet.IPv4AFI: + s.fsm.options.MultiProtocolIPv4 = true + case packet.IPv6AFI: + s.fsm.options.MultiProtocolIPv6 = true + } } func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapability) { diff --git a/protocols/bgp/server/fsm_open_sent_test.go b/protocols/bgp/server/fsm_open_sent_test.go index fe34eede357c762de2674701132ddfa91bf170b9..03133879f5569c623a13f9f1fee49a3dcf11da59 100644 --- a/protocols/bgp/server/fsm_open_sent_test.go +++ b/protocols/bgp/server/fsm_open_sent_test.go @@ -66,7 +66,7 @@ func TestOpenMsgReceived(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - fsm := newFSM2(&peer{ + fsm := newFSM(&peer{ peerASN: test.asn, }) fsm.con = &btesting.MockConn{} diff --git a/protocols/bgp/server/fsm_test.go b/protocols/bgp/server/fsm_test.go index 50181d8e3aa66ec34dbc21e7c18827e1b6b3d4c8..7d040713aa74d362caae10888c5c2c2b6de3bd63 100644 --- a/protocols/bgp/server/fsm_test.go +++ b/protocols/bgp/server/fsm_test.go @@ -13,9 +13,9 @@ import ( bnet "github.com/bio-routing/bio-rd/net" ) -// TestFSM255UpdatesIPv4 emulates receiving 255 BGP updates and withdraws. Checks route counts. +// TestFSM55UpdatesIPv4 emulates receiving 255 BGP updates and withdraws. Checks route counts. func TestFSM100UpdatesIPv4(t *testing.T) { - fsmA := newFSM2(&peer{ + fsmA := newFSM(&peer{ addr: bnet.IPv4FromOctets(169, 254, 100, 100), routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(), ipv4: &familyParameters{ @@ -129,7 +129,7 @@ func TestFSM100UpdatesIPv4(t *testing.T) { // TestFSM255UpdatesIPv6 emulates receiving 255 BGP updates and withdraws. Checks route counts. func TestFSM255UpdatesIPv6(t *testing.T) { - fsmA := newFSM2(&peer{ + fsmA := newFSM(&peer{ addr: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xffff, 0, 0, 0, 1), routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(), ipv6: &familyParameters{ @@ -138,8 +138,8 @@ func TestFSM255UpdatesIPv6(t *testing.T) { exportFilter: filter.NewAcceptAllFilter(), }, }) - fsmA.options.SupportsMultiProtocol = true + fsmA.options.MultiProtocolIPv6 = true fsmA.holdTimer = time.NewTimer(time.Second * 90) fsmA.keepaliveTimer = time.NewTimer(time.Second * 30) fsmA.connectRetryTimer = time.NewTimer(time.Second * 120) @@ -323,7 +323,7 @@ func TestOpenMessage(t *testing.T) { }, } - fsm := newFSM2(&p) + fsm := newFSM(&p) msg := fsm.openMessage() assert.Equal(t, &test.expected, msg) diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index bde263b504a590b411dc02291f64fe578d282263..b70534b0898a2c3b3a68d4509e47e4ae17039a97 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -177,7 +177,7 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) { Value: caps, }) - p.fsms = append(p.fsms, NewActiveFSM2(p)) + p.fsms = append(p.fsms, NewActiveFSM(p)) return p, nil } diff --git a/protocols/bgp/server/server.go b/protocols/bgp/server/server.go index 170ca0a2e327ea0c49045c88d1070d4e5f6ca005..d6d50f5c3941556c46a6ad702c0713f6f2640d61 100644 --- a/protocols/bgp/server/server.go +++ b/protocols/bgp/server/server.go @@ -96,7 +96,7 @@ func (b *bgpServer) incomingConnectionWorker() { }).Info("Incoming TCP connection") log.WithField("Peer", peerAddr).Debug("Sending incoming TCP connection to fsm for peer") - fsm := NewActiveFSM2(peer) + fsm := NewActiveFSM(peer) fsm.state = newActiveState(fsm) fsm.startConnectRetryTimer() diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index dd1c799985a6b6fb66b26381f26ebbfd4ab1c920..400c8470eff31ed29df849c2df7b0063bcfad54c 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -1,6 +1,7 @@ package server import ( + "fmt" "sync" "time" @@ -129,7 +130,7 @@ func (u *UpdateSender) sender(aggrTime time.Duration) { } func (u *UpdateSender) updateOverhead() int { - if !u.fsm.options.SupportsMultiProtocol { + if u.afi == packet.IPv4AFI && !u.fsm.options.MultiProtocolIPv4 { return 0 } @@ -159,14 +160,18 @@ func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefix } func (u *UpdateSender) updateMessageForPrefixes(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate { - if u.afi == packet.IPv4AFI && u.safi == packet.UnicastSAFI { + switch u.afi { + case packet.IPv4AFI: + if u.fsm.options.MultiProtocolIPv4 { + return u.bgpUpdateMultiProtocol(pfxs, pa, pathID) + } return u.bgpUpdate(pfxs, pa, pathID) + case packet.IPv6AFI: + if u.fsm.options.MultiProtocolIPv6 { + return u.bgpUpdateMultiProtocol(pfxs, pa, pathID) + } + return nil } - - if u.fsm.options.SupportsMultiProtocol { - return u.bgpUpdateMultiProtocol(pfxs, pa, pathID) - } - return nil } @@ -239,11 +244,31 @@ func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool { } func (u *UpdateSender) withdrawPrefix(pfx bnet.Prefix, p *route.Path) error { - if u.fsm.options.SupportsMultiProtocol { - return withDrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx, u.afi, u.safi) + if u.afi == packet.IPv4AFI { + return u.withdrawPrefixIPv4(pfx, p) + } + + if u.afi == packet.IPv6AFI { + return u.withdrawPrefixIPv4(pfx, p) + } + + return fmt.Errorf("Unsupported AFI: %v", u.afi) +} + +func (u *UpdateSender) withdrawPrefixIPv4(pfx bnet.Prefix, p *route.Path) error { + if u.fsm.options.MultiProtocolIPv4 { + return withdrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx, u.afi, u.safi) + } + + return withdrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) +} + +func (u *UpdateSender) withdrawPrefixIPv6(pfx bnet.Prefix, p *route.Path) error { + if !u.fsm.options.MultiProtocolIPv6 { + return fmt.Errorf("IPv6 was not negotiated") } - return withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) + return withdrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) } // UpdateNewClient does nothing diff --git a/protocols/bgp/server/update_sender_test.go b/protocols/bgp/server/update_sender_test.go index 40719d6972a7b3f551d23823c4635666e5c59eac..6085dd8f4441f05ee7ebe7b803700a28ac9261fc 100644 --- a/protocols/bgp/server/update_sender_test.go +++ b/protocols/bgp/server/update_sender_test.go @@ -867,13 +867,13 @@ func TestSender(t *testing.T) { } for _, test := range tests { - fsmA := newFSM2(&peer{ + fsmA := newFSM(&peer{ addr: bnet.IPv4FromOctets(169, 254, 100, 100), }) rib := locRIB.New() if test.afi == packet.IPv6AFI { - fsmA.options.SupportsMultiProtocol = true + fsmA.options.MultiProtocolIPv6 = true fsmA.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, &familyParameters{ rib: rib, importFilter: filter.NewAcceptAllFilter(), diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 2ab554b682691c57d0164d95eadef6afbcd14936..842d50f5497a31ff6562342f0f31eed79b5c32aa 100644 --- a/protocols/bgp/server/withdraw.go +++ b/protocols/bgp/server/withdraw.go @@ -10,9 +10,9 @@ import ( "github.com/bio-routing/bio-rd/route" ) -// withDrawPrefixes generates a BGPUpdate message and write it to the given +// withdrawPrefixes generates a BGPUpdate message and write it to the given // io.Writer. -func withDrawPrefixes(out io.Writer, opt *types.Options, prefixes ...net.Prefix) error { +func withdrawPrefixes(out io.Writer, opt *types.Options, prefixes ...net.Prefix) error { if len(prefixes) < 1 { return nil } @@ -40,9 +40,9 @@ func withDrawPrefixes(out io.Writer, opt *types.Options, prefixes ...net.Prefix) } -// withDrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given +// withdrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given // io.Writer. -func withDrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix, p *route.Path) error { +func withdrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix, p *route.Path) error { if p.Type != route.BGPPathType { return errors.New("wrong path type, expected BGPPathType") } @@ -59,7 +59,7 @@ func withDrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix, return serializeAndSendUpdate(out, update, opt) } -func withDrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Prefix, afi uint16, safi uint8) error { +func withdrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Prefix, afi uint16, safi uint8) error { update := &packet.BGPUpdate{ PathAttributes: &packet.PathAttribute{ TypeCode: packet.MultiProtocolUnreachNLRICode, diff --git a/protocols/bgp/server/withdraw_test.go b/protocols/bgp/server/withdraw_test.go index 17cb8751b8673dab38920a55db79699f6c643fb4..b379e2a00bf106fe8ff86c4866a351a2ddae8968 100644 --- a/protocols/bgp/server/withdraw_test.go +++ b/protocols/bgp/server/withdraw_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestWithDrawPrefixes(t *testing.T) { +func TestWithdrawPrefixes(t *testing.T) { testcases := []struct { Name string Prefix []net.Prefix @@ -54,7 +54,7 @@ func TestWithDrawPrefixes(t *testing.T) { for _, tc := range testcases { buf := bytes.NewBuffer([]byte{}) opt := &types.Options{} - err := withDrawPrefixes(buf, opt, tc.Prefix...) + err := withdrawPrefixes(buf, opt, tc.Prefix...) assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) } @@ -90,7 +90,7 @@ func TestWithDrawPrefixesMultiProtocol(t *testing.T) { opt := &types.Options{ AddPathRX: false, } - err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix, packet.IPv6AFI, packet.UnicastSAFI) + err := withdrawPrefixesMultiProtocol(buf, opt, test.Prefix, packet.IPv6AFI, packet.UnicastSAFI) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -153,7 +153,7 @@ func TestWithDrawPrefixesAddPath(t *testing.T) { opt := &types.Options{ AddPathRX: true, } - err := withDrawPrefixesAddPath(buf, opt, tc.Prefix, tc.Path) + err := withdrawPrefixesAddPath(buf, opt, tc.Prefix, tc.Path) assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) } diff --git a/protocols/bgp/types/options.go b/protocols/bgp/types/options.go index 8b96d6697b7262b53954a12663dff872fd3492b2..5bc87827dc6faff3948b0ec817d8151bdbc34d03 100644 --- a/protocols/bgp/types/options.go +++ b/protocols/bgp/types/options.go @@ -2,7 +2,8 @@ package types // Options represents options to the update sender, decoder and encoder type Options struct { - Supports4OctetASN bool - SupportsMultiProtocol bool - AddPathRX bool + Supports4OctetASN bool + AddPathRX bool + MultiProtocolIPv4 bool + MultiProtocolIPv6 bool }