diff --git a/config/BUILD.bazel b/config/BUILD.bazel index 0597dcfbcb0107809739ca60ba91eb748ba56d5c..59561fc18788143092394d865b43cd731773674e 100644 --- a/config/BUILD.bazel +++ b/config/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "//net:go_default_library", "//routingtable:go_default_library", "//routingtable/filter:go_default_library", + "//routingtable/locRIB:go_default_library", "//vendor/github.com/taktv6/tflow2/convert:go_default_library", ], ) diff --git a/config/peer.go b/config/peer.go index 12124c7c6d3f44928dfaa5a1b9a05fa3bbf645a7..82a9f61f56fea089384b18ada019d4192834d6ea 100644 --- a/config/peer.go +++ b/config/peer.go @@ -6,6 +6,7 @@ import ( bnet "github.com/bio-routing/bio-rd/net" "github.com/bio-routing/bio-rd/routingtable" "github.com/bio-routing/bio-rd/routingtable/filter" + "github.com/bio-routing/bio-rd/routingtable/locRIB" ) // Peer defines the configuration for a BGP session @@ -22,10 +23,16 @@ type Peer struct { RouterID uint32 AddPathSend routingtable.ClientOptions AddPathRecv bool - ImportFilter *filter.Filter - ExportFilter *filter.Filter RouteServerClient bool RouteReflectorClient bool RouteReflectorClusterID uint32 - IPv6 bool + IPv4 *AddressFamilyConfig + IPv6 *AddressFamilyConfig +} + +// AddressFamilyConfig represents all configuration parameters specific for an address family +type AddressFamilyConfig struct { + RIB *locRIB.LocRIB + ImportFilter *filter.Filter + ExportFilter *filter.Filter } diff --git a/main_ipv4.go b/main_ipv4.go index 65293f07853d5819abdb96a6ab5def218f0c2320..0d3c5a7cd4b0ce93fbe645136bd588cfbc872c2b 100644 --- a/main_ipv4.go +++ b/main_ipv4.go @@ -43,10 +43,13 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) { AddPathSend: routingtable.ClientOptions{ MaxPaths: 10, }, - ImportFilter: filter.NewAcceptAllFilter(), - ExportFilter: filter.NewAcceptAllFilter(), + IPv4: &config.AddressFamilyConfig{ + RIB: rib, + ImportFilter: filter.NewAcceptAllFilter(), + ExportFilter: filter.NewAcceptAllFilter(), + }, RouteServerClient: true, - }, rib) + }) b.AddPeer(config.Peer{ AdminEnabled: true, @@ -63,8 +66,11 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) { MaxPaths: 10, }, AddPathRecv: true, - ImportFilter: filter.NewAcceptAllFilter(), - ExportFilter: filter.NewAcceptAllFilter(), RouteServerClient: true, - }, rib) + IPv4: &config.AddressFamilyConfig{ + RIB: rib, + ImportFilter: filter.NewAcceptAllFilter(), + ExportFilter: filter.NewAcceptAllFilter(), + }, + }) } diff --git a/main_ipv6.go b/main_ipv6.go index 160007cc76481b968cfc98de0c459f1422b3b157..2d4a24100e2e1eea680fa63368235495846456ac 100644 --- a/main_ipv6.go +++ b/main_ipv6.go @@ -41,10 +41,12 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) { AddPathSend: routingtable.ClientOptions{ BestOnly: true, }, - ImportFilter: filter.NewAcceptAllFilter(), - ExportFilter: filter.NewDrainFilter(), - IPv6: true, - }, rib) + IPv6: &config.AddressFamilyConfig{ + RIB: rib, + ImportFilter: filter.NewAcceptAllFilter(), + ExportFilter: filter.NewDrainFilter(), + }, + }) b.AddPeer(config.Peer{ AdminEnabled: true, @@ -60,8 +62,10 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) { AddPathSend: routingtable.ClientOptions{ BestOnly: true, }, - ImportFilter: filter.NewDrainFilter(), - ExportFilter: filter.NewAcceptAllFilter(), - IPv6: true, - }, rib) + IPv6: &config.AddressFamilyConfig{ + RIB: rib, + ImportFilter: filter.NewDrainFilter(), + ExportFilter: filter.NewAcceptAllFilter(), + }, + }) } diff --git a/protocols/bgp/server/BUILD.bazel b/protocols/bgp/server/BUILD.bazel index 631671e09122407f96bf8bf012840bc1e7d99391..78bdeb1f3635cabb3ead2855ae4431ee20a3c04c 100644 --- a/protocols/bgp/server/BUILD.bazel +++ b/protocols/bgp/server/BUILD.bazel @@ -6,6 +6,7 @@ go_library( "fake_conn.go", "fsm.go", "fsm_active.go", + "fsm_address_family.go", "fsm_cease.go", "fsm_connect.go", "fsm_established.go", @@ -42,7 +43,7 @@ go_library( go_test( name = "go_default_test", srcs = [ - "fsm_established_test.go", + "fsm_address_family_test.go", "fsm_open_sent_test.go", "fsm_test.go", "server_test.go", diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index a5b48ef195df148ad69139c638601e0dae935586..c8d98157ebf1d447630202179bbf08963d01af56 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -10,8 +10,6 @@ import ( "github.com/bio-routing/bio-rd/protocols/bgp/packet" "github.com/bio-routing/bio-rd/protocols/bgp/types" - "github.com/bio-routing/bio-rd/routingtable" - "github.com/bio-routing/bio-rd/routingtable/locRIB" log "github.com/sirupsen/logrus" ) @@ -62,10 +60,8 @@ type FSM struct { local net.IP ribsInitialized bool - adjRIBIn routingtable.RouteTableClient - adjRIBOut routingtable.RouteTableClient - rib *locRIB.LocRIB - updateSender *UpdateSender + ipv4Unicast *fsmAddressFamily + ipv6Unicast *fsmAddressFamily neighborID uint32 state state @@ -93,7 +89,7 @@ func NewActiveFSM2(peer *peer) *FSM { } func newFSM2(peer *peer) *FSM { - return &FSM{ + f := &FSM{ connectRetryTime: time.Minute, peer: peer, eventCh: make(chan int), @@ -103,9 +99,18 @@ func newFSM2(peer *peer) *FSM { msgRecvCh: make(chan []byte), msgRecvFailCh: make(chan error), stopMsgRecvCh: make(chan struct{}), - rib: peer.rib, options: &types.Options{}, } + + if peer.ipv4 != nil { + f.ipv4Unicast = newFSMAddressFamily(packet.IPv4AFI, packet.UnicastSAFI, peer.ipv4, f) + } + + if peer.ipv6 != nil { + f.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, peer.ipv6, f) + } + + return f } func (fsm *FSM) start() { diff --git a/protocols/bgp/server/fsm_address_family.go b/protocols/bgp/server/fsm_address_family.go new file mode 100644 index 0000000000000000000000000000000000000000..daa5465e16f2bb90e0da43316b4bb475021ffd1b --- /dev/null +++ b/protocols/bgp/server/fsm_address_family.go @@ -0,0 +1,206 @@ +package server + +import ( + "time" + + bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" + "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/adjRIBOut" + "github.com/bio-routing/bio-rd/routingtable/filter" + "github.com/bio-routing/bio-rd/routingtable/locRIB" +) + +// fsmAddressFamily holds RIBs and the UpdateSender of an peer for an AFI/SAFI combination +type fsmAddressFamily struct { + afi uint16 + safi uint8 + fsm *FSM + + adjRIBIn routingtable.RouteTableClient + adjRIBOut routingtable.RouteTableClient + rib *locRIB.LocRIB + + importFilter *filter.Filter + exportFilter *filter.Filter + + updateSender *UpdateSender + + initialized bool +} + +func newFSMAddressFamily(afi uint16, safi uint8, params *familyParameters, fsm *FSM) *fsmAddressFamily { + return &fsmAddressFamily{ + afi: afi, + safi: safi, + fsm: fsm, + rib: params.rib, + importFilter: params.importFilter, + exportFilter: params.exportFilter, + } +} + +func (f *fsmAddressFamily) init(n *routingtable.Neighbor) { + contributingASNs := f.rib.GetContributingASNs() + + f.adjRIBIn = adjRIBIn.New(f.importFilter, contributingASNs, f.fsm.peer.routerID, f.fsm.peer.clusterID) + contributingASNs.Add(f.fsm.peer.localASN) + f.adjRIBIn.Register(f.rib) + + f.adjRIBOut = adjRIBOut.New(n, f.exportFilter) + clientOptions := routingtable.ClientOptions{ + BestOnly: true, + } + if f.fsm.options.AddPathRX { + clientOptions = f.fsm.peer.addPathSend + } + + f.updateSender = newUpdateSender(f.fsm, f.afi, f.safi) + f.updateSender.Start(time.Millisecond * 5) + + f.adjRIBOut.Register(f.updateSender) + f.rib.RegisterWithOptions(f.adjRIBOut, clientOptions) +} + +func (f *fsmAddressFamily) dispose() { + if !f.initialized { + return + } + + f.rib.GetContributingASNs().Remove(f.fsm.peer.localASN) + f.adjRIBIn.Unregister(f.rib) + f.rib.Unregister(f.adjRIBOut) + f.adjRIBOut.Unregister(f.updateSender) + f.updateSender.Destroy() + + f.adjRIBIn = nil + f.adjRIBOut = nil + + f.initialized = false +} + +func (f *fsmAddressFamily) processUpdate(u *packet.BGPUpdate) { + if f.afi == packet.IPv4AFI && f.safi == packet.UnicastSAFI { + f.withdraws(u) + f.updates(u) + } + + if f.fsm.options.SupportsMultiProtocol { + f.multiProtocolUpdates(u) + } +} + +func (f *fsmAddressFamily) withdraws(u *packet.BGPUpdate) { + for r := u.WithdrawnRoutes; r != nil; r = r.Next { + pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen) + f.adjRIBIn.RemovePath(pfx, nil) + } +} + +func (f *fsmAddressFamily) updates(u *packet.BGPUpdate) { + for r := u.NLRI; r != nil; r = r.Next { + pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen) + + path := f.newRoutePath() + f.processAttributes(u.PathAttributes, path) + + f.adjRIBIn.AddPath(pfx, path) + } +} + +func (f *fsmAddressFamily) multiProtocolUpdates(u *packet.BGPUpdate) { + if !f.fsm.options.SupportsMultiProtocol { + return + } + + path := f.newRoutePath() + f.processAttributes(u.PathAttributes, path) + + for pa := u.PathAttributes; pa != nil; pa = pa.Next { + switch pa.TypeCode { + case packet.MultiProtocolReachNLRICode: + f.multiProtocolUpdate(path, pa.Value.(packet.MultiProtocolReachNLRI)) + case packet.MultiProtocolUnreachNLRICode: + f.multiProtocolWithdraw(path, pa.Value.(packet.MultiProtocolUnreachNLRI)) + } + } +} + +func (f *fsmAddressFamily) newRoutePath() *route.Path { + return &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Source: f.fsm.peer.addr, + EBGP: f.fsm.peer.localASN != f.fsm.peer.peerASN, + }, + } +} + +func (f *fsmAddressFamily) multiProtocolUpdate(path *route.Path, nlri packet.MultiProtocolReachNLRI) { + path.BGPPath.NextHop = nlri.NextHop + + for _, pfx := range nlri.Prefixes { + f.adjRIBIn.AddPath(pfx, path) + } +} + +func (f *fsmAddressFamily) multiProtocolWithdraw(path *route.Path, nlri packet.MultiProtocolUnreachNLRI) { + for _, pfx := range nlri.Prefixes { + f.adjRIBIn.RemovePath(pfx, path) + } +} + +func (f *fsmAddressFamily) processAttributes(attrs *packet.PathAttribute, path *route.Path) { + for pa := attrs; pa != nil; pa = pa.Next { + switch pa.TypeCode { + case packet.OriginAttr: + path.BGPPath.Origin = pa.Value.(uint8) + case packet.LocalPrefAttr: + path.BGPPath.LocalPref = pa.Value.(uint32) + case packet.MEDAttr: + path.BGPPath.MED = pa.Value.(uint32) + case packet.NextHopAttr: + path.BGPPath.NextHop = pa.Value.(bnet.IP) + case packet.ASPathAttr: + path.BGPPath.ASPath = pa.Value.(types.ASPath) + path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length() + case packet.AggregatorAttr: + aggr := pa.Value.(types.Aggregator) + path.BGPPath.Aggregator = &aggr + case packet.AtomicAggrAttr: + path.BGPPath.AtomicAggregate = true + case packet.CommunitiesAttr: + path.BGPPath.Communities = pa.Value.([]uint32) + case packet.LargeCommunitiesAttr: + path.BGPPath.LargeCommunities = pa.Value.([]types.LargeCommunity) + case packet.OriginatorIDAttr: + path.BGPPath.OriginatorID = pa.Value.(uint32) + case packet.ClusterListAttr: + path.BGPPath.ClusterList = pa.Value.([]uint32) + default: + unknownAttr := f.processUnknownAttribute(pa) + if unknownAttr != nil { + path.BGPPath.UnknownAttributes = append(path.BGPPath.UnknownAttributes, *unknownAttr) + } + } + } +} + +func (f *fsmAddressFamily) processUnknownAttribute(attr *packet.PathAttribute) *types.UnknownPathAttribute { + if !attr.Transitive { + return nil + } + + u := &types.UnknownPathAttribute{ + Transitive: true, + Optional: attr.Optional, + Partial: attr.Partial, + TypeCode: attr.TypeCode, + Value: attr.Value.([]byte), + } + + return u +} diff --git a/protocols/bgp/server/fsm_established_test.go b/protocols/bgp/server/fsm_address_family_test.go similarity index 95% rename from protocols/bgp/server/fsm_established_test.go rename to protocols/bgp/server/fsm_address_family_test.go index 61103d99c133e01e47ab923e81d121f5acc48018..54bd4b4ac9a7d40718f882d9408bf630d981b15a 100644 --- a/protocols/bgp/server/fsm_established_test.go +++ b/protocols/bgp/server/fsm_address_family_test.go @@ -43,12 +43,12 @@ func TestProcessAttributes(t *testing.T) { Next: unknown1, } - e := &establishedState{} + f := &fsmAddressFamily{} p := &route.Path{ BGPPath: &route.BGPPath{}, } - e.processAttributes(asPath, p) + f.processAttributes(asPath, p) expectedCodes := []uint8{200, 100} expectedValues := [][]byte{[]byte{5, 6}, []byte{1, 2, 3, 4}} diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index 2baf46ab430f9d000c67b65091e014f785945157..2ffa19415e0d1233b5718b9127b8dbbfaaf9874b 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -4,15 +4,12 @@ import ( "bytes" "fmt" "net" - "time" bnet "github.com/bio-routing/bio-rd/net" "github.com/bio-routing/bio-rd/protocols/bgp/packet" - "github.com/bio-routing/bio-rd/protocols/bgp/types" "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/adjRIBOut" + log "github.com/sirupsen/logrus" ) type establishedState struct { @@ -57,12 +54,6 @@ func (s establishedState) run() (state, string) { } func (s *establishedState) init() error { - contributingASNs := s.fsm.rib.GetContributingASNs() - - s.fsm.adjRIBIn = adjRIBIn.New(s.fsm.peer.importFilter, contributingASNs, s.fsm.peer.routerID, s.fsm.peer.clusterID) - contributingASNs.Add(s.fsm.peer.localASN) - s.fsm.adjRIBIn.Register(s.fsm.rib) - host, _, err := net.SplitHostPort(s.fsm.con.LocalAddr().String()) if err != nil { return fmt.Errorf("Unable to get local address: %v", err) @@ -88,35 +79,26 @@ func (s *establishedState) init() error { ClusterID: s.fsm.peer.clusterID, } - s.fsm.adjRIBOut = adjRIBOut.New(n, s.fsm.peer.exportFilter) - clientOptions := routingtable.ClientOptions{ - BestOnly: true, - } - if s.fsm.options.AddPathRX { - clientOptions = s.fsm.peer.addPathSend + if s.fsm.ipv4Unicast != nil { + s.fsm.ipv4Unicast.init(n) } - s.fsm.updateSender = newUpdateSender(s.fsm) - s.fsm.updateSender.Start(time.Millisecond * 5) - - s.fsm.adjRIBOut.Register(s.fsm.updateSender) - s.fsm.rib.RegisterWithOptions(s.fsm.adjRIBOut, clientOptions) + if s.fsm.ipv6Unicast != nil { + s.fsm.ipv6Unicast.init(n) + } s.fsm.ribsInitialized = true return nil } func (s *establishedState) uninit() { - s.fsm.rib.GetContributingASNs().Remove(s.fsm.peer.localASN) - s.fsm.adjRIBIn.Unregister(s.fsm.rib) - s.fsm.rib.Unregister(s.fsm.adjRIBOut) - s.fsm.adjRIBOut.Unregister(s.fsm.updateSender) - s.fsm.updateSender.Destroy() - - s.fsm.adjRIBIn = nil - s.fsm.adjRIBOut = nil + if s.fsm.ipv4Unicast != nil { + s.fsm.ipv4Unicast.dispose() + } - s.fsm.ribsInitialized = false + if s.fsm.ipv6Unicast != nil { + s.fsm.ipv6Unicast.dispose() + } } func (s *establishedState) manualStop() (state, string) { @@ -205,123 +187,47 @@ func (s *establishedState) update(msg *packet.BGPMessage) (state, string) { } u := msg.Body.(*packet.BGPUpdate) - s.withdraws(u) - s.updates(u) - s.multiProtocolUpdates(u) - - return newEstablishedState(s.fsm), s.fsm.reason -} + afi, safi := s.addressFamilyForUpdate(u) -func (s *establishedState) withdraws(u *packet.BGPUpdate) { - for r := u.WithdrawnRoutes; r != nil; r = r.Next { - pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen) - s.fsm.adjRIBIn.RemovePath(pfx, nil) + if safi != packet.UnicastSAFI { + // only unicast support, so other SAFIs are ignored + return newEstablishedState(s.fsm), s.fsm.reason } -} - -func (s *establishedState) updates(u *packet.BGPUpdate) { - for r := u.NLRI; r != nil; r = r.Next { - pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen) - - path := s.newRoutePath() - s.processAttributes(u.PathAttributes, path) - s.fsm.adjRIBIn.AddPath(pfx, path) - } -} - -func (s *establishedState) multiProtocolUpdates(u *packet.BGPUpdate) { - if !s.fsm.options.SupportsMultiProtocol { - return - } - - path := s.newRoutePath() - s.processAttributes(u.PathAttributes, path) - - for pa := u.PathAttributes; pa != nil; pa = pa.Next { - switch pa.TypeCode { - case packet.MultiProtocolReachNLRICode: - s.multiProtocolUpdate(path, pa.Value.(packet.MultiProtocolReachNLRI)) - case packet.MultiProtocolUnreachNLRICode: - s.multiProtocolWithdraw(path, pa.Value.(packet.MultiProtocolUnreachNLRI)) + switch afi { + case packet.IPv4AFI: + 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) } -} - -func (s *establishedState) newRoutePath() *route.Path { - return &route.Path{ - Type: route.BGPPathType, - BGPPath: &route.BGPPath{ - Source: s.fsm.peer.addr, - EBGP: s.fsm.peer.localASN != s.fsm.peer.peerASN, - }, - } -} -func (s *establishedState) multiProtocolUpdate(path *route.Path, nlri packet.MultiProtocolReachNLRI) { - path.BGPPath.NextHop = nlri.NextHop - - for _, pfx := range nlri.Prefixes { - s.fsm.adjRIBIn.AddPath(pfx, path) - } + return newEstablishedState(s.fsm), s.fsm.reason } -func (s *establishedState) multiProtocolWithdraw(path *route.Path, nlri packet.MultiProtocolUnreachNLRI) { - for _, pfx := range nlri.Prefixes { - s.fsm.adjRIBIn.RemovePath(pfx, path) +func (s *establishedState) addressFamilyForUpdate(u *packet.BGPUpdate) (afi uint16, safi uint8) { + if !s.fsm.options.SupportsMultiProtocol || u.NLRI != nil || u.WithdrawnRoutes != nil { + return packet.IPv4AFI, packet.UnicastSAFI } -} -func (s *establishedState) processAttributes(attrs *packet.PathAttribute, path *route.Path) { - for pa := attrs; pa != nil; pa = pa.Next { - switch pa.TypeCode { - case packet.OriginAttr: - path.BGPPath.Origin = pa.Value.(uint8) - case packet.LocalPrefAttr: - path.BGPPath.LocalPref = pa.Value.(uint32) - case packet.MEDAttr: - path.BGPPath.MED = pa.Value.(uint32) - case packet.NextHopAttr: - path.BGPPath.NextHop = pa.Value.(bnet.IP) - case packet.ASPathAttr: - path.BGPPath.ASPath = pa.Value.(types.ASPath) - path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length() - case packet.AggregatorAttr: - aggr := pa.Value.(types.Aggregator) - path.BGPPath.Aggregator = &aggr - case packet.AtomicAggrAttr: - path.BGPPath.AtomicAggregate = true - case packet.CommunitiesAttr: - path.BGPPath.Communities = pa.Value.([]uint32) - case packet.LargeCommunitiesAttr: - path.BGPPath.LargeCommunities = pa.Value.([]types.LargeCommunity) - case packet.OriginatorIDAttr: - path.BGPPath.OriginatorID = pa.Value.(uint32) - case packet.ClusterListAttr: - path.BGPPath.ClusterList = pa.Value.([]uint32) - default: - unknownAttr := s.processUnknownAttribute(pa) - if unknownAttr != nil { - path.BGPPath.UnknownAttributes = append(path.BGPPath.UnknownAttributes, *unknownAttr) - } + for cur := u.PathAttributes; cur != nil; cur = cur.Next { + if cur.TypeCode == packet.MultiProtocolReachNLRICode { + a := cur.Value.(packet.MultiProtocolReachNLRI) + return a.AFI, a.SAFI } - } -} - -func (s *establishedState) processUnknownAttribute(attr *packet.PathAttribute) *types.UnknownPathAttribute { - if !attr.Transitive { - return nil - } - u := &types.UnknownPathAttribute{ - Transitive: true, - Optional: attr.Optional, - Partial: attr.Partial, - TypeCode: attr.TypeCode, - Value: attr.Value.([]byte), + if cur.TypeCode == packet.MultiProtocolUnreachNLRICode { + a := cur.Value.(packet.MultiProtocolUnreachNLRI) + return a.AFI, a.SAFI + } } - return u + return } func (s *establishedState) keepaliveReceived() (state, string) { diff --git a/protocols/bgp/server/fsm_test.go b/protocols/bgp/server/fsm_test.go index 5cca8d85ead35eeca7d49d04ab64e0cf8d6dc7ec..50181d8e3aa66ec34dbc21e7c18827e1b6b3d4c8 100644 --- a/protocols/bgp/server/fsm_test.go +++ b/protocols/bgp/server/fsm_test.go @@ -13,14 +13,16 @@ import ( bnet "github.com/bio-routing/bio-rd/net" ) -// TestFSM100Updates emulates receiving 100 BGP updates and withdraws. Checks route counts. -func TestFSM100Updates(t *testing.T) { +// TestFSM255UpdatesIPv4 emulates receiving 255 BGP updates and withdraws. Checks route counts. +func TestFSM100UpdatesIPv4(t *testing.T) { fsmA := newFSM2(&peer{ - addr: bnet.IPv4FromOctets(169, 254, 100, 100), - rib: locRIB.New(), - importFilter: filter.NewAcceptAllFilter(), - exportFilter: filter.NewAcceptAllFilter(), - routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(), + addr: bnet.IPv4FromOctets(169, 254, 100, 100), + routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(), + ipv4: &familyParameters{ + rib: locRIB.New(), + importFilter: filter.NewAcceptAllFilter(), + exportFilter: filter.NewAcceptAllFilter(), + }, }) fsmA.holdTimer = time.NewTimer(time.Second * 90) @@ -94,7 +96,7 @@ func TestFSM100Updates(t *testing.T) { } time.Sleep(time.Second) - ribRouteCount := fsmA.rib.RouteCount() + ribRouteCount := fsmA.ipv4Unicast.rib.RouteCount() if ribRouteCount != 255 { t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount) } @@ -112,11 +114,126 @@ func TestFSM100Updates(t *testing.T) { 0, 0, } fsmA.msgRecvCh <- update - ribRouteCount = fsmA.rib.RouteCount() + ribRouteCount = fsmA.ipv4Unicast.rib.RouteCount() + } + time.Sleep(time.Second * 1) + + ribRouteCount = fsmA.ipv4Unicast.rib.RouteCount() + if ribRouteCount != 0 { + t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount) + } + + fsmA.eventCh <- ManualStop + wg.Wait() +} + +// TestFSM255UpdatesIPv6 emulates receiving 255 BGP updates and withdraws. Checks route counts. +func TestFSM255UpdatesIPv6(t *testing.T) { + fsmA := newFSM2(&peer{ + addr: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xffff, 0, 0, 0, 1), + routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(), + ipv6: &familyParameters{ + rib: locRIB.New(), + importFilter: filter.NewAcceptAllFilter(), + exportFilter: filter.NewAcceptAllFilter(), + }, + }) + fsmA.options.SupportsMultiProtocol = true + + fsmA.holdTimer = time.NewTimer(time.Second * 90) + fsmA.keepaliveTimer = time.NewTimer(time.Second * 30) + fsmA.connectRetryTimer = time.NewTimer(time.Second * 120) + fsmA.state = newEstablishedState(fsmA) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + fsmA.con = fakeConn{} + for { + nextState, reason := fsmA.state.run() + fsmA.state = nextState + stateName := stateName(nextState) + switch stateName { + case "idle": + wg.Done() + return + case "cease": + t.Errorf("Unexpected cease state: %s", reason) + wg.Done() + return + case "established": + continue + default: + t.Errorf("Unexpected new state: %s", reason) + wg.Done() + return + } + } + + }() + + for i := uint8(0); i < 255; i++ { + update := []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 76, + 2, + 0, 0, + 0, 53, + 64, // Attribute flags + 1, // Attribute Type code (ORIGIN) + 1, // Length + 2, // INCOMPLETE + + 64, // Attribute flags + 2, // Attribute Type code (AS Path) + 12, // Length + 2, // Type = AS_SEQUENCE + 2, // Path Segement Length + 59, 65, // AS15169 + 12, 248, // AS3320 + 1, // Type = AS_SET + 2, // Path Segement Length + 59, 65, // AS15169 + 12, 248, // AS3320 + + 0x90, // Attribute flags + 0x0e, // MP_REACH_NLRI + 0x00, 30, // Length + 0x00, 0x02, // AFI + 0x01, // SAFI + 0x10, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // Nexthop + 0x00, + 64, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x0, i, + } + + fsmA.msgRecvCh <- update + } + + time.Sleep(time.Second) + ribRouteCount := fsmA.ipv6Unicast.rib.RouteCount() + if ribRouteCount != 255 { + t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount) + } + + for i := uint8(0); i < 255; i++ { + update := []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 35, // Length + 0x02, // UPDATE + 0x00, 0x00, // withdrawn routes + 0x00, 0x0c, + 0x90, 0x0f, + 0x00, 12, // Length + 0x00, 0x02, // AFI + 0x01, // SAFI + 64, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x0, i, + } + fsmA.msgRecvCh <- update + ribRouteCount = fsmA.ipv6Unicast.rib.RouteCount() } time.Sleep(time.Second * 1) - ribRouteCount = fsmA.rib.RouteCount() + ribRouteCount = fsmA.ipv6Unicast.rib.RouteCount() if ribRouteCount != 0 { t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount) } diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index 02cf053bf000c5d1dd232c2243b7b617062a8fcf..4f101d815a7789a26564b6d37527026746404e5e 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -28,7 +28,6 @@ type peer struct { fsms []*FSM fsmsMu sync.Mutex - rib *locRIB.LocRIB routerID uint32 addPathSend routingtable.ClientOptions addPathRecv bool @@ -36,11 +35,18 @@ type peer struct { keepaliveTime time.Duration holdTime time.Duration optOpenParams []packet.OptParam - importFilter *filter.Filter - exportFilter *filter.Filter routeServerClient bool routeReflectorClient bool clusterID uint32 + + ipv4 *familyParameters + ipv6 *familyParameters +} + +type familyParameters struct { + rib *locRIB.LocRIB + importFilter *filter.Filter + exportFilter *filter.Filter } func (p *peer) snapshot() PeerInfo { @@ -103,37 +109,41 @@ func isEstablishedState(s state) bool { // NewPeer creates a new peer with the given config. If an connection is established, the adjRIBIN of the peer is connected // to the given rib. To actually connect the peer, call Start() on the returned peer. -func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error) { +func newPeer(c config.Peer, server *bgpServer) (*peer, error) { if c.LocalAS == 0 { c.LocalAS = server.localASN } + p := &peer{ server: server, addr: c.PeerAddress, peerASN: c.PeerAS, localASN: c.LocalAS, fsms: make([]*FSM, 0), - rib: rib, addPathSend: c.AddPathSend, addPathRecv: c.AddPathRecv, reconnectInterval: c.ReconnectInterval, keepaliveTime: c.KeepAlive, holdTime: c.HoldTime, optOpenParams: make([]packet.OptParam, 0), - importFilter: filterOrDefault(c.ImportFilter), - exportFilter: filterOrDefault(c.ExportFilter), routeServerClient: c.RouteServerClient, routeReflectorClient: c.RouteReflectorClient, clusterID: c.RouteReflectorClusterID, } + if c.IPv4 != nil { + p.ipv4 = &familyParameters{ + rib: c.IPv4.RIB, + importFilter: filterOrDefault(c.IPv4.ImportFilter), + exportFilter: filterOrDefault(c.IPv4.ExportFilter), + } + } + // If we are a route reflector and no ClusterID was set, use our RouterID if p.routeReflectorClient && p.clusterID == 0 { p.clusterID = c.RouterID } - p.fsms = append(p.fsms, NewActiveFSM2(p)) - caps := make(packet.Capabilities, 0) addPathEnabled, addPathCap := handleAddPathCapability(c) @@ -143,7 +153,12 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error caps = append(caps, asn4Capability(c)) - if c.IPv6 { + if c.IPv6 != nil { + p.ipv6 = &familyParameters{ + rib: c.IPv6.RIB, + importFilter: filterOrDefault(c.IPv6.ImportFilter), + exportFilter: filterOrDefault(c.IPv6.ExportFilter), + } caps = append(caps, multiProtocolCapability(packet.IPv6AFI)) } @@ -152,6 +167,8 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error Value: caps, }) + p.fsms = append(p.fsms, NewActiveFSM2(p)) + return p, nil } diff --git a/protocols/bgp/server/server.go b/protocols/bgp/server/server.go index a27e522276e3fd61791c7f8926db87b2041d2d3f..df63a4a92ef954a6f5be941b0433b2264324135e 100644 --- a/protocols/bgp/server/server.go +++ b/protocols/bgp/server/server.go @@ -26,7 +26,7 @@ type bgpServer struct { type BGPServer interface { RouterID() uint32 Start(*config.Global) error - AddPeer(config.Peer, *locRIB.LocRIB) error + AddPeer(config.Peer) error GetPeerInfoAll() map[string]PeerInfo } @@ -110,8 +110,8 @@ func (b *bgpServer) incomingConnectionWorker() { } } -func (b *bgpServer) AddPeer(c config.Peer, rib *locRIB.LocRIB) error { - peer, err := newPeer(c, rib, b) +func (b *bgpServer) AddPeer(c config.Peer) error { + peer, err := newPeer(c, b) if err != nil { return err } diff --git a/protocols/bgp/server/server_test.go b/protocols/bgp/server/server_test.go index fffe879ce078deaa77a6a795d357f64ef74e3796..896c3201b01799c3317a5f1e4e1ad1eed194f2bd 100644 --- a/protocols/bgp/server/server_test.go +++ b/protocols/bgp/server/server_test.go @@ -41,10 +41,13 @@ func TestBgpServerPeerSnapshot(t *testing.T) { AddPathSend: routingtable.ClientOptions{ MaxPaths: 10, }, - ImportFilter: filter.NewDrainFilter(), - ExportFilter: filter.NewAcceptAllFilter(), + IPv4: &config.AddressFamilyConfig{ + RIB: rib, + ImportFilter: filter.NewDrainFilter(), + ExportFilter: filter.NewAcceptAllFilter(), + }, } - s.AddPeer(pc, rib) + s.AddPeer(pc) info = s.GetPeerInfoAll() if want, got := 1, len(info); want != got { diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 478e9a08b137bf306ddbab0453b42ad869636411..dd1c799985a6b6fb66b26381f26ebbfd4ab1c920 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -16,6 +16,8 @@ import ( type UpdateSender struct { routingtable.ClientManager fsm *FSM + afi uint16 + safi uint8 iBGP bool rrClient bool toSendMu sync.Mutex @@ -28,9 +30,11 @@ type pathPfxs struct { pfxs []bnet.Prefix } -func newUpdateSender(fsm *FSM) *UpdateSender { +func newUpdateSender(fsm *FSM, afi uint16, safi uint8) *UpdateSender { return &UpdateSender{ fsm: fsm, + afi: afi, + safi: safi, iBGP: fsm.peer.localASN == fsm.peer.peerASN, rrClient: fsm.peer.routeReflectorClient, destroyCh: make(chan struct{}), @@ -125,19 +129,27 @@ func (u *UpdateSender) sender(aggrTime time.Duration) { } func (u *UpdateSender) updateOverhead() int { - // TODO: for multi RIB support we need the AFI/SAFI combination to determine overhead. For now: MultiProtocol = IPv6 - if u.fsm.options.SupportsMultiProtocol { - // since we are replacing the next hop attribute IPv4Len has to be subtracted, we also add another byte for extended length - return packet.AFILen + packet.SAFILen + 1 + packet.IPv6Len - packet.IPv4Len + 1 + if !u.fsm.options.SupportsMultiProtocol { + return 0 } - return 0 + addrLen := packet.IPv4AFI + if u.afi == packet.IPv6AFI { + addrLen = packet.IPv6Len + } + + // since we are replacing the next hop attribute IPv4Len has to be subtracted, we also add another byte for extended length + return packet.AFILen + packet.SAFILen + 1 + addrLen - packet.IPv4Len + 1 } func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) { var err error for _, prefixes := range updatePrefixes { update := u.updateMessageForPrefixes(prefixes, pathAttrs, pathID) + if update == nil { + log.Errorf("Failed to create update: Neighbor does not support multi protocol.") + return + } err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) if err != nil { @@ -147,11 +159,15 @@ 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 { + return u.bgpUpdate(pfxs, pa, pathID) + } + if u.fsm.options.SupportsMultiProtocol { return u.bgpUpdateMultiProtocol(pfxs, pa, pathID) } - return u.bgpUpdate(pfxs, pa, pathID) + return nil } func (u *UpdateSender) bgpUpdate(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate { @@ -179,8 +195,8 @@ func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.Pat attrs := &packet.PathAttribute{ TypeCode: packet.MultiProtocolReachNLRICode, Value: packet.MultiProtocolReachNLRI{ - AFI: packet.IPv6AFI, - SAFI: packet.UnicastSAFI, + AFI: u.afi, + SAFI: u.safi, NextHop: nextHop, Prefixes: pfxs, }, @@ -224,7 +240,7 @@ 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) + return withDrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx, u.afi, u.safi) } return withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) diff --git a/protocols/bgp/server/update_sender_test.go b/protocols/bgp/server/update_sender_test.go index 5c934fd2afd55d193e17df762faa8e53a0de9a2a..40719d6972a7b3f551d23823c4635666e5c59eac 100644 --- a/protocols/bgp/server/update_sender_test.go +++ b/protocols/bgp/server/update_sender_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/stretchr/testify/assert" bnet "github.com/bio-routing/bio-rd/net" @@ -22,10 +24,11 @@ func TestSender(t *testing.T) { generateNLRIs uint64 expectedUpdates [][]byte addPath bool - ipv6 bool + afi uint16 }{ { name: "Two paths with 3 NLRIs each", + afi: packet.IPv4AFI, paths: []pathPfxs{ { path: &route.Path{ @@ -87,6 +90,7 @@ func TestSender(t *testing.T) { }, { name: "Two paths with 3 NLRIs each with BGP Add Path", + afi: packet.IPv4AFI, addPath: true, paths: []pathPfxs{ { @@ -165,6 +169,7 @@ func TestSender(t *testing.T) { }, { name: "Overflow. Too many NLRIs.", + afi: packet.IPv4AFI, paths: []pathPfxs{ { path: &route.Path{ @@ -332,6 +337,7 @@ func TestSender(t *testing.T) { }, { name: "Overflow with IPv6. Too many NLRIs.", + afi: packet.IPv6AFI, paths: []pathPfxs{ { path: &route.Path{ @@ -857,18 +863,30 @@ func TestSender(t *testing.T) { 0x0, 0x40, 0x1, 0x1, 0x0, 0x40, 0x5, 0x4, 0x0, 0x0, 0x0, 0x64, }, }, - ipv6: true, }, } for _, test := range tests { fsmA := newFSM2(&peer{ - addr: bnet.IPv4FromOctets(169, 254, 100, 100), - rib: locRIB.New(), - importFilter: filter.NewAcceptAllFilter(), - exportFilter: filter.NewAcceptAllFilter(), + addr: bnet.IPv4FromOctets(169, 254, 100, 100), }) + rib := locRIB.New() + if test.afi == packet.IPv6AFI { + fsmA.options.SupportsMultiProtocol = true + fsmA.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, &familyParameters{ + rib: rib, + importFilter: filter.NewAcceptAllFilter(), + exportFilter: filter.NewAcceptAllFilter(), + }, fsmA) + } else { + fsmA.ipv4Unicast = newFSMAddressFamily(packet.IPv4AFI, packet.UnicastSAFI, &familyParameters{ + rib: rib, + importFilter: filter.NewAcceptAllFilter(), + exportFilter: filter.NewAcceptAllFilter(), + }, fsmA) + } + fsmA.holdTimer = time.NewTimer(time.Second * 90) fsmA.keepaliveTimer = time.NewTimer(time.Second * 30) fsmA.connectRetryTimer = time.NewTimer(time.Second * 120) @@ -881,11 +899,7 @@ func TestSender(t *testing.T) { } } - if test.ipv6 { - fsmA.options.SupportsMultiProtocol = true - } - - updateSender := newUpdateSender(fsmA) + updateSender := newUpdateSender(fsmA, test.afi, packet.UnicastSAFI) for _, pathPfx := range test.paths { for _, pfx := range pathPfx.pfxs { @@ -897,7 +911,7 @@ func TestSender(t *testing.T) { y := i - x var pfx bnet.Prefix - if test.ipv6 { + if test.afi == packet.IPv6AFI { pfx = bnet.NewPfx(bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0), 48) } else { pfx = bnet.NewPfx(bnet.IPv4FromOctets(10, 0, uint8(x), uint8(y)), 32) diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 013f6ce33c5b247d6021292c3b611f3c325f24f5..2ab554b682691c57d0164d95eadef6afbcd14936 100644 --- a/protocols/bgp/server/withdraw.go +++ b/protocols/bgp/server/withdraw.go @@ -59,16 +59,17 @@ 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) 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, Value: packet.MultiProtocolUnreachNLRI{ - AFI: packet.IPv6AFI, - SAFI: packet.UnicastSAFI, + AFI: afi, + SAFI: safi, Prefixes: []net.Prefix{pfx}, }, }, } + return serializeAndSendUpdate(out, update, opt) } diff --git a/protocols/bgp/server/withdraw_test.go b/protocols/bgp/server/withdraw_test.go index 393be032dcd178b2cef772f576b24e1c5b2d29e4..17cb8751b8673dab38920a55db79699f6c643fb4 100644 --- a/protocols/bgp/server/withdraw_test.go +++ b/protocols/bgp/server/withdraw_test.go @@ -1,16 +1,15 @@ package server import ( - "testing" - - "github.com/bio-routing/bio-rd/protocols/bgp/types" - - "errors" - "bytes" + "errors" + "testing" "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" ) @@ -91,7 +90,7 @@ func TestWithDrawPrefixesMultiProtocol(t *testing.T) { opt := &types.Options{ AddPathRX: false, } - err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix) + err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix, packet.IPv6AFI, packet.UnicastSAFI) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/routingtable/filter/BUILD.bazel b/routingtable/filter/BUILD.bazel index 4ac13572ad6e4ec9364449e209fa641a2f7c8125..17bee85a8312740e989ae1f88fcf0150458437fa 100644 --- a/routingtable/filter/BUILD.bazel +++ b/routingtable/filter/BUILD.bazel @@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ + "action.go", "community_filter.go", "filter.go", "helper.go", diff --git a/routingtable/filter/action.go b/routingtable/filter/action.go new file mode 100644 index 0000000000000000000000000000000000000000..888ad3f5397f51337cfb04b487883956da107f80 --- /dev/null +++ b/routingtable/filter/action.go @@ -0,0 +1,12 @@ +package filter + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/routingtable/filter/actions" +) + +// Action performs actions on a `route.Path` +type Action interface { + Do(p net.Prefix, pa *route.Path) actions.Result +} diff --git a/routingtable/filter/actions/BUILD.bazel b/routingtable/filter/actions/BUILD.bazel index b4195842fcc40a6d484111cb9aed065d02e1b604..d974ffd1c11bf232f50458e3a5c3a96ed8a08fbd 100644 --- a/routingtable/filter/actions/BUILD.bazel +++ b/routingtable/filter/actions/BUILD.bazel @@ -4,10 +4,10 @@ go_library( name = "go_default_library", srcs = [ "accept_action.go", + "action.go", "add_community_action.go", "add_large_community_action.go", "as_path_prepend_action.go", - "filter_action.go", "reject_action.go", "set_local_pref_action.go", "set_nexthop_action.go", diff --git a/routingtable/filter/actions/accept_action.go b/routingtable/filter/actions/accept_action.go index 6c0cc2acbfa3d75603ff33cc10025bdad09b3a23..6e340d5394a178066b0b7dfa45c60e46590de3ff 100644 --- a/routingtable/filter/actions/accept_action.go +++ b/routingtable/filter/actions/accept_action.go @@ -8,6 +8,9 @@ import ( type AcceptAction struct { } -func (*AcceptAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { - return pa, false +func (*AcceptAction) Do(p net.Prefix, pa *route.Path) Result { + return Result{ + Path: pa, + Terminate: true, + } } diff --git a/routingtable/filter/actions/action.go b/routingtable/filter/actions/action.go new file mode 100644 index 0000000000000000000000000000000000000000..3cd6f0ac746ae168eb5c6fa8c6eb7f7d2fea9a75 --- /dev/null +++ b/routingtable/filter/actions/action.go @@ -0,0 +1,11 @@ +package actions + +import ( + "github.com/bio-routing/bio-rd/route" +) + +type Result struct { + Path *route.Path + Reject bool + Terminate bool +} diff --git a/routingtable/filter/actions/add_community_action.go b/routingtable/filter/actions/add_community_action.go index c24107e8b94be6657174fbd3dffb06ae978ff767..4931056afc9bc901bef92b502e67775ee4e61d5c 100644 --- a/routingtable/filter/actions/add_community_action.go +++ b/routingtable/filter/actions/add_community_action.go @@ -15,9 +15,9 @@ func NewAddCommunityAction(coms []uint32) *AddCommunityAction { } } -func (a *AddCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { +func (a *AddCommunityAction) Do(p net.Prefix, pa *route.Path) Result { if pa.BGPPath == nil || len(a.communities) == 0 { - return pa, false + return Result{Path: pa} } modified := pa.Copy() @@ -26,5 +26,5 @@ func (a *AddCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Pa modified.BGPPath.Communities = append(modified.BGPPath.Communities, com) } - return modified, false + return Result{Path: modified} } diff --git a/routingtable/filter/actions/add_community_action_test.go b/routingtable/filter/actions/add_community_action_test.go index 9ec3d1401b87da808c324a984e1d5ef7620658cd..3527437b9efab61df4bd5203848318bbbf87849d 100644 --- a/routingtable/filter/actions/add_community_action_test.go +++ b/routingtable/filter/actions/add_community_action_test.go @@ -53,9 +53,9 @@ func TestAddingCommunities(t *testing.T) { } a := NewAddCommunityAction(test.communities) - modPath, _ := a.Do(net.Prefix{}, p) + res := a.Do(net.Prefix{}, p) - assert.Equal(t, test.expected, modPath.BGPPath.CommunitiesString()) + assert.Equal(t, test.expected, res.Path.BGPPath.CommunitiesString()) }) } } diff --git a/routingtable/filter/actions/add_large_community_action.go b/routingtable/filter/actions/add_large_community_action.go index cb5f1a020ea6bfdde33948cc8535077df0fec26d..9e0a9da44955a1779ce8655551d09a3a31f077d4 100644 --- a/routingtable/filter/actions/add_large_community_action.go +++ b/routingtable/filter/actions/add_large_community_action.go @@ -16,13 +16,13 @@ func NewAddLargeCommunityAction(coms []types.LargeCommunity) *AddLargeCommunityA } } -func (a *AddLargeCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { +func (a *AddLargeCommunityAction) Do(p net.Prefix, pa *route.Path) Result { if pa.BGPPath == nil || len(a.communities) == 0 { - return pa, false + return Result{Path: pa} } modified := pa.Copy() modified.BGPPath.LargeCommunities = append(modified.BGPPath.LargeCommunities, a.communities...) - return modified, false + return Result{Path: modified} } diff --git a/routingtable/filter/actions/add_large_community_action_test.go b/routingtable/filter/actions/add_large_community_action_test.go index 6b569d2ebd79e7225791b0172c2ad32fab764d88..fe077817e5098a9966efc11a7d7d03e905212460 100644 --- a/routingtable/filter/actions/add_large_community_action_test.go +++ b/routingtable/filter/actions/add_large_community_action_test.go @@ -71,7 +71,7 @@ func TestAddingLargeCommunities(t *testing.T) { } for _, test := range tests { - t.Run(test.name, func(te *testing.T) { + t.Run(test.name, func(t *testing.T) { p := &route.Path{ BGPPath: &route.BGPPath{ LargeCommunities: test.current, @@ -79,9 +79,9 @@ func TestAddingLargeCommunities(t *testing.T) { } a := NewAddLargeCommunityAction(test.communities) - modPath, _ := a.Do(net.Prefix{}, p) + res := a.Do(net.Prefix{}, p) - assert.Equal(te, test.expected, modPath.BGPPath.LargeCommunitiesString()) + assert.Equal(t, test.expected, res.Path.BGPPath.LargeCommunitiesString()) }) } } diff --git a/routingtable/filter/actions/as_path_prepend_action.go b/routingtable/filter/actions/as_path_prepend_action.go index 30d63ca783fd91a9530f336f3311106c57ac5968..2e53b1e84526aec2e092b3fea3bb1056dc360f2a 100644 --- a/routingtable/filter/actions/as_path_prepend_action.go +++ b/routingtable/filter/actions/as_path_prepend_action.go @@ -17,13 +17,13 @@ func NewASPathPrependAction(asn uint32, times uint16) *ASPathPrependAction { } } -func (a *ASPathPrependAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { +func (a *ASPathPrependAction) Do(p net.Prefix, pa *route.Path) Result { if pa.BGPPath == nil { - return pa, false + return Result{Path: pa} } modified := pa.Copy() modified.BGPPath.Prepend(a.asn, a.times) - return modified, false + return Result{Path: modified} } diff --git a/routingtable/filter/actions/as_path_prepend_action_test.go b/routingtable/filter/actions/as_path_prepend_action_test.go index f7c7af59394670a51c6062d43df19ae45ec69eb2..12cc0267f22b9a2bbe32f334c0aeb4f3f48bcf30 100644 --- a/routingtable/filter/actions/as_path_prepend_action_test.go +++ b/routingtable/filter/actions/as_path_prepend_action_test.go @@ -54,9 +54,9 @@ func TestAppendPath(t *testing.T) { } for _, test := range tests { - t.Run(test.name, func(te *testing.T) { + t.Run(test.name, func(t *testing.T) { a := NewASPathPrependAction(12345, test.times) - p, _ := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + res := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ BGPPath: test.bgpPath, }) @@ -64,8 +64,8 @@ func TestAppendPath(t *testing.T) { return } - assert.Equal(te, test.expectedPath, p.BGPPath.ASPath.String(), "ASPath") - assert.Equal(te, test.expectedLength, p.BGPPath.ASPathLen, "ASPathLen") + assert.Equal(t, test.expectedPath, res.Path.BGPPath.ASPath.String(), "ASPath") + assert.Equal(t, test.expectedLength, res.Path.BGPPath.ASPathLen, "ASPathLen") }) } } diff --git a/routingtable/filter/actions/filter_action.go b/routingtable/filter/actions/filter_action.go deleted file mode 100644 index 7a2665146922719766b880f9093eb1049c6ae018..0000000000000000000000000000000000000000 --- a/routingtable/filter/actions/filter_action.go +++ /dev/null @@ -1,10 +0,0 @@ -package actions - -import ( - "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/route" -) - -type FilterAction interface { - Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) -} diff --git a/routingtable/filter/actions/reject_action.go b/routingtable/filter/actions/reject_action.go index d740139626b99395db71a3b2e7c2cea1d04ebacf..4693bbfaad45aa8f0b83e914733834a6a52a99f8 100644 --- a/routingtable/filter/actions/reject_action.go +++ b/routingtable/filter/actions/reject_action.go @@ -8,6 +8,10 @@ import ( type RejectAction struct { } -func (*RejectAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { - return pa, true +func (*RejectAction) Do(p net.Prefix, pa *route.Path) Result { + return Result{ + Path: pa, + Reject: true, + Terminate: true, + } } diff --git a/routingtable/filter/actions/set_local_pref_action.go b/routingtable/filter/actions/set_local_pref_action.go index e60b6faa0ab5e4e986f785d7b8322db2c6492e34..26d038ec0ad0f0f4d110bb1fdb9bfc3e08c67458 100644 --- a/routingtable/filter/actions/set_local_pref_action.go +++ b/routingtable/filter/actions/set_local_pref_action.go @@ -15,13 +15,13 @@ func NewSetLocalPrefAction(pref uint32) *SetLocalPrefAction { } } -func (a *SetLocalPrefAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { +func (a *SetLocalPrefAction) Do(p net.Prefix, pa *route.Path) Result { if pa.BGPPath == nil { - return pa, false + return Result{Path: pa} } modified := *pa modified.BGPPath.LocalPref = a.pref - return &modified, false + return Result{Path: &modified} } diff --git a/routingtable/filter/actions/set_local_pref_action_test.go b/routingtable/filter/actions/set_local_pref_action_test.go index 7b104d3949d127e794dcd34a2a05a7b7aa3f2b02..40ef0f6b3e649c6dc2cf45d3ba6b31fd6b8862b0 100644 --- a/routingtable/filter/actions/set_local_pref_action_test.go +++ b/routingtable/filter/actions/set_local_pref_action_test.go @@ -27,14 +27,14 @@ func TestSetLocalPref(t *testing.T) { } for _, test := range tests { - t.Run(test.name, func(te *testing.T) { + t.Run(test.name, func(t *testing.T) { a := NewSetLocalPrefAction(150) - p, _ := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + res := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ BGPPath: test.bgpPath, }) if test.expectedLocalPref > 0 { - assert.Equal(te, test.expectedLocalPref, p.BGPPath.LocalPref) + assert.Equal(t, test.expectedLocalPref, res.Path.BGPPath.LocalPref) } }) } diff --git a/routingtable/filter/actions/set_nexthop_action.go b/routingtable/filter/actions/set_nexthop_action.go index de1e9bc959b8f9d827c48c844699aa2aed92fda9..8b00a8717744611d2bd17661b201973b727026bb 100644 --- a/routingtable/filter/actions/set_nexthop_action.go +++ b/routingtable/filter/actions/set_nexthop_action.go @@ -16,13 +16,13 @@ func NewSetNextHopAction(ip bnet.IP) *SetNextHopAction { } } -func (a *SetNextHopAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { +func (a *SetNextHopAction) Do(p net.Prefix, pa *route.Path) Result { if pa.BGPPath == nil { - return pa, false + return Result{Path: pa} } modified := pa.Copy() modified.BGPPath.NextHop = a.ip - return modified, false + return Result{Path: modified} } diff --git a/routingtable/filter/actions/set_nexthop_action_test.go b/routingtable/filter/actions/set_nexthop_action_test.go index 7f691d235f288e1155980ea997b4e6fddaa6a4de..bebbafebbd6fec029e7a74dad4b4fda9e2a50f29 100644 --- a/routingtable/filter/actions/set_nexthop_action_test.go +++ b/routingtable/filter/actions/set_nexthop_action_test.go @@ -31,12 +31,12 @@ func TestSetNextHopTest(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { a := NewSetNextHopAction(bnet.IPv4FromOctets(100, 64, 2, 1)) - p, _ := a.Do(net.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ + res := a.Do(net.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{ BGPPath: test.bgpPath, }) if test.bgpPath != nil { - assert.Equal(t, test.expected, p.BGPPath.NextHop) + assert.Equal(t, test.expected, res.Path.BGPPath.NextHop) } }) } diff --git a/routingtable/filter/filter.go b/routingtable/filter/filter.go index cb88087983c31ebb2a5b58bf7a002117c829efb7..27f3b1f4fc69b4c879a8268408f4539add3183cd 100644 --- a/routingtable/filter/filter.go +++ b/routingtable/filter/filter.go @@ -21,10 +21,11 @@ func (f *Filter) ProcessTerms(p net.Prefix, pa *route.Path) (modPath *route.Path modPath = pa for _, t := range f.terms { - modPath, reject = t.Process(p, modPath) - if reject { - return modPath, true + res := t.Process(p, modPath) + if res.Terminate { + return res.Path, res.Reject } + modPath = res.Path } return modPath, false diff --git a/routingtable/filter/filter_test.go b/routingtable/filter/filter_test.go index 52ebc8b9180aab9429b22489d75db95e210d2f65..0eeb4942df0938d3445a4df6598ac523fa2d577f 100644 --- a/routingtable/filter/filter_test.go +++ b/routingtable/filter/filter_test.go @@ -23,7 +23,7 @@ func TestProcessTerms(t *testing.T) { prefix: net.NewPfx(net.IPv4(0), 0), path: &route.Path{}, term: &Term{ - then: []FilterAction{ + then: []Action{ &actions.AcceptAction{}, }, }, @@ -35,19 +35,32 @@ func TestProcessTerms(t *testing.T) { prefix: net.NewPfx(net.IPv4(0), 0), path: &route.Path{}, term: &Term{ - then: []FilterAction{ + then: []Action{ &actions.RejectAction{}, }, }, exptectAccept: false, expectModified: false, }, + { + name: "accept before reject", + prefix: net.NewPfx(net.IPv4(0), 0), + path: &route.Path{}, + term: &Term{ + then: []Action{ + &actions.AcceptAction{}, + &actions.RejectAction{}, + }, + }, + exptectAccept: true, + expectModified: false, + }, { name: "modified", prefix: net.NewPfx(net.IPv4(0), 0), path: &route.Path{}, term: &Term{ - then: []FilterAction{ + then: []Action{ &mockAction{}, &actions.AcceptAction{}, }, @@ -58,7 +71,7 @@ func TestProcessTerms(t *testing.T) { } for _, test := range tests { - t.Run(test.name, func(te *testing.T) { + t.Run(test.name, func(t *testing.T) { f := NewFilter([]*Term{test.term}) p, reject := f.ProcessTerms(test.prefix, test.path) diff --git a/routingtable/filter/helper.go b/routingtable/filter/helper.go index 17a7d73789d5be569bec1725592a35f1b3841386..1cfb6e5c8be7ea13e3e76a8609b8307b48e665e9 100644 --- a/routingtable/filter/helper.go +++ b/routingtable/filter/helper.go @@ -10,7 +10,7 @@ func NewAcceptAllFilter() *Filter { []*Term{ NewTerm( []*TermCondition{}, - []FilterAction{ + []Action{ &actions.AcceptAction{}, }), }) @@ -22,7 +22,7 @@ func NewDrainFilter() *Filter { []*Term{ NewTerm( []*TermCondition{}, - []FilterAction{ + []Action{ &actions.RejectAction{}, }), }) diff --git a/routingtable/filter/term.go b/routingtable/filter/term.go index d644acba4a977e67e757fc5c7f71863531427e12..cdf56c7fa90eff0a0f844377a27985900235231d 100644 --- a/routingtable/filter/term.go +++ b/routingtable/filter/term.go @@ -5,18 +5,20 @@ import ( "github.com/bio-routing/bio-rd/route" ) -type FilterAction interface { - Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) -} - // Term matches a path against a list of conditions and performs actions if it matches type Term struct { from []*TermCondition - then []FilterAction + then []Action +} + +type TermResult struct { + Path *route.Path + Terminate bool + Reject bool } // NewTerm creates a new term -func NewTerm(from []*TermCondition, then []FilterAction) *Term { +func NewTerm(from []*TermCondition, then []Action) *Term { t := &Term{ from: from, then: then, @@ -26,7 +28,7 @@ func NewTerm(from []*TermCondition, then []FilterAction) *Term { } // Process processes a path returning if the path should be rejected and returns a possible modified version of the path -func (t *Term) Process(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { +func (t *Term) Process(p net.Prefix, pa *route.Path) TermResult { orig := pa if len(t.from) == 0 { @@ -39,18 +41,23 @@ func (t *Term) Process(p net.Prefix, pa *route.Path) (modPath *route.Path, rejec } } - return orig, false + return TermResult{Path: orig} } -func (t *Term) processActions(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) { - modPath = pa +func (t *Term) processActions(p net.Prefix, pa *route.Path) TermResult { + modPath := pa for _, action := range t.then { - modPath, reject = action.Do(p, modPath) - if reject { - continue + res := action.Do(p, modPath) + if res.Terminate { + return TermResult{ + Path: modPath, + Terminate: true, + Reject: res.Reject, + } } + modPath = res.Path } - return modPath, reject + return TermResult{Path: modPath} } diff --git a/routingtable/filter/term_test.go b/routingtable/filter/term_test.go index 876eb6691ec33a0a95697267f171ace3307d8a83..591fd234befde8d15538dd7cf293a9922479560d 100644 --- a/routingtable/filter/term_test.go +++ b/routingtable/filter/term_test.go @@ -12,11 +12,11 @@ import ( type mockAction struct { } -func (*mockAction) Do(p net.Prefix, pa *route.Path) (*route.Path, bool) { +func (*mockAction) Do(p net.Prefix, pa *route.Path) actions.Result { cp := *pa cp.Type = route.OSPFPathType - return &cp, false + return actions.Result{Path: &cp} } func TestProcess(t *testing.T) { @@ -25,7 +25,7 @@ func TestProcess(t *testing.T) { prefix net.Prefix path *route.Path from []*TermCondition - then []FilterAction + then []Action expectReject bool expectModified bool }{ @@ -34,7 +34,7 @@ func TestProcess(t *testing.T) { prefix: net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8), path: &route.Path{}, from: []*TermCondition{}, - then: []FilterAction{ + then: []Action{ &actions.AcceptAction{}, }, expectReject: false, @@ -48,7 +48,7 @@ func TestProcess(t *testing.T) { NewTermConditionWithPrefixLists( NewPrefixList(net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8))), }, - then: []FilterAction{ + then: []Action{ &actions.AcceptAction{}, }, expectReject: false, @@ -62,7 +62,7 @@ func TestProcess(t *testing.T) { NewTermConditionWithPrefixLists( NewPrefixList(net.NewPfx(net.IPv4(0), 32))), }, - then: []FilterAction{ + then: []Action{ &actions.AcceptAction{}, }, expectReject: false, @@ -76,7 +76,7 @@ func TestProcess(t *testing.T) { NewTermConditionWithPrefixLists( NewPrefixList(net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8))), }, - then: []FilterAction{ + then: []Action{ &mockAction{}, }, expectReject: false, @@ -90,7 +90,7 @@ func TestProcess(t *testing.T) { NewTermConditionWithRouteFilters( NewRouteFilter(net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8), Exact())), }, - then: []FilterAction{ + then: []Action{ &mockAction{}, &actions.AcceptAction{}, }, @@ -109,7 +109,7 @@ func TestProcess(t *testing.T) { }, }, }, - then: []FilterAction{ + then: []Action{ &actions.AcceptAction{}, }, expectReject: false, @@ -118,18 +118,18 @@ func TestProcess(t *testing.T) { } for _, test := range tests { - t.Run(test.name, func(te *testing.T) { + t.Run(test.name, func(t *testing.T) { term := NewTerm(test.from, test.then) - pa, reject := term.Process(test.prefix, test.path) - assert.Equal(te, test.expectReject, reject, "reject") + res := term.Process(test.prefix, test.path) + assert.Equal(t, test.expectReject, res.Reject, "reject") - if pa != test.path && !test.expectModified { - te.Fatal("expected path to be not modified but was") + if res.Path != test.path && !test.expectModified { + t.Fatal("expected path to be not modified but was") } - if pa == test.path && test.expectModified { - te.Fatal("expected path to be modified but was same reference") + if res.Path == test.path && test.expectModified { + t.Fatal("expected path to be modified but was same reference") } }) }