diff --git a/protocols/bgp/packet/community.go b/protocols/bgp/packet/community.go index 5fd0f29a3bb95c685de17e5bab067151dc03e13c..6f4e2a67ab3b074117211e28a84d6da2188da3d9 100644 --- a/protocols/bgp/packet/community.go +++ b/protocols/bgp/packet/community.go @@ -6,6 +6,11 @@ import ( "strings" ) +const ( + WellKnownCommunityNoExport = 0xFFFFFF01 + WellKnownCommunityNoAdvertise = 0xFFFFFF02 +) + func CommunityStringForUint32(v uint32) string { e1 := v >> 16 e2 := v - e1<<16 diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index b5b554dd60f5302b09a68bc5f37744137013f281..20be5160eff16fb132ffe45ed1a2875b0d333fdb 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -723,6 +723,7 @@ func (fsm *FSM) established() int { n := &routingtable.Neighbor{ Type: route.BGPPathType, Address: tnet.IPv4ToUint32(fsm.remote), + IBGP: fsm.remoteASN == fsm.localASN, } clientOptions := routingtable.ClientOptions{ diff --git a/routingtable/adjRIBOut/adj_rib_out.go b/routingtable/adjRIBOut/adj_rib_out.go index 51347119a6b360fd2f81283edc18d9225548fdf2..fe728f0e64981276e38322ec3985b467180ee05f 100644 --- a/routingtable/adjRIBOut/adj_rib_out.go +++ b/routingtable/adjRIBOut/adj_rib_out.go @@ -35,7 +35,7 @@ func (a *AdjRIBOut) UpdateNewClient(client routingtable.RouteTableClient) error // AddPath replaces the path for prefix `pfx`. If the prefix doesn't exist it is added. func (a *AdjRIBOut) AddPath(pfx net.Prefix, p *route.Path) error { - if a.isOwnPath(p) { + if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) { return nil } @@ -56,7 +56,7 @@ func (a *AdjRIBOut) AddPath(pfx net.Prefix, p *route.Path) error { // RemovePath removes the path for prefix `pfx` func (a *AdjRIBOut) RemovePath(pfx net.Prefix, p *route.Path) bool { - if a.isOwnPath(p) { + if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) { return false } @@ -77,19 +77,6 @@ func (a *AdjRIBOut) RemovePath(pfx net.Prefix, p *route.Path) bool { return true } -func (a *AdjRIBOut) isOwnPath(p *route.Path) bool { - if p.Type != a.neighbor.Type { - return false - } - - switch p.Type { - case route.BGPPathType: - return p.BGPPath.Source == a.neighbor.Address - } - - return false -} - func (a *AdjRIBOut) removePathsFromClients(pfx net.Prefix, paths []*route.Path) { for _, path := range paths { for _, client := range a.ClientManager.Clients() { diff --git a/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go b/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go index 90f76fcc873643a6d94086b0b84f5bf940b6fa9f..f0c75c0b74ac84d43a6b2cb257ed18504ccc51ef 100644 --- a/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go +++ b/routingtable/adjRIBOutAddPath/adj_rib_out_add_path.go @@ -37,7 +37,7 @@ func (a *AdjRIBOutAddPath) UpdateNewClient(client routingtable.RouteTableClient) // AddPath adds path p to prefix `pfx` func (a *AdjRIBOutAddPath) AddPath(pfx net.Prefix, p *route.Path) error { - if a.isOwnPath(p) { + if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) { return nil } @@ -63,7 +63,7 @@ func (a *AdjRIBOutAddPath) AddPath(pfx net.Prefix, p *route.Path) error { // RemovePath removes the path for prefix `pfx` func (a *AdjRIBOutAddPath) RemovePath(pfx net.Prefix, p *route.Path) bool { - if a.isOwnPath(p) { + if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) { return false } diff --git a/routingtable/neighbor.go b/routingtable/neighbor.go index c7702ea49cb8f52fd43fd61d3bbf9f50c05782d7..7ae0f7395637c3a2f7bcf9de4a890edf804c982b 100644 --- a/routingtable/neighbor.go +++ b/routingtable/neighbor.go @@ -7,4 +7,7 @@ type Neighbor struct { // Type is the type / protocol used for routing inforation communitation Type uint8 + + // IBGP returns if local ASN is equal to remote ASN + IBGP bool } diff --git a/routingtable/update_helper.go b/routingtable/update_helper.go new file mode 100644 index 0000000000000000000000000000000000000000..c18936f3a21055678401a9ec4697ab00363fc4f0 --- /dev/null +++ b/routingtable/update_helper.go @@ -0,0 +1,52 @@ +package routingtable + +import ( + "strings" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/route" + log "github.com/sirupsen/logrus" +) + +// ShouldPropagateUpdate performs some default checks and returns if an route update should be propagated to a neighbor +func ShouldPropagateUpdate(pfx net.Prefix, p *route.Path, n *Neighbor) bool { + return !isOwnPath(p, n) && !isDisallowedByCommunity(p, n) +} + +func isOwnPath(p *route.Path, n *Neighbor) bool { + if p.Type != n.Type { + return false + } + + switch p.Type { + case route.BGPPathType: + return p.BGPPath.Source == n.Address + } + + return false +} + +func isDisallowedByCommunity(p *route.Path, n *Neighbor) bool { + if p.BGPPath == nil || len(p.BGPPath.Communities) == 0 { + return false + } + + strs := strings.Split(p.BGPPath.Communities, " ") + for _, str := range strs { + com, err := packet.ParseCommunityString(str) + if err != nil { + log.WithField("Sender", "routingtable.ShouldAnnounce()"). + WithField("community", str). + WithError(err). + Error("Could not parse community") + continue + } + + if (com == packet.WellKnownCommunityNoExport && !n.IBGP) || com == packet.WellKnownCommunityNoAdvertise { + return true + } + } + + return false +} diff --git a/routingtable/update_helper_test.go b/routingtable/update_helper_test.go new file mode 100644 index 0000000000000000000000000000000000000000..29342f376ead000006039e6aa95caf3399937dc1 --- /dev/null +++ b/routingtable/update_helper_test.go @@ -0,0 +1,76 @@ +package routingtable + +import ( + "net" + "testing" + + bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" +) + +func TestShouldPropagateUpdate(t *testing.T) { + tests := []struct { + name string + communities string + neighbor Neighbor + expected bool + }{ + { + name: "arbitrary path", + expected: true, + }, + { + name: "path was received from this peer before", + communities: "(1,2)", + neighbor: Neighbor{ + Type: route.BGPPathType, + Address: bnet.IPv4ToUint32(net.ParseIP("192.168.1.1")), + }, + expected: false, + }, + { + name: "path with no-export community", + communities: "(1,2) (65535,65281)", + expected: false, + }, + { + name: "path with no-export community (iBGP)", + communities: "(1,2) (65535,65281)", + neighbor: Neighbor{ + IBGP: true, + }, + expected: true, + }, + { + name: "path with no-advertise community", + communities: "(1,2) (65535,65282)", + expected: false, + }, + { + name: "path with no-advertise community (iBGP)", + communities: "(1,2) (65535,65282)", + neighbor: Neighbor{ + IBGP: true, + }, + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + pfx := bnet.NewPfx(0, 32) + + pa := &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + Communities: test.communities, + Source: bnet.IPv4ToUint32(net.ParseIP("192.168.1.1")), + }, + } + + res := ShouldPropagateUpdate(pfx, pa, &test.neighbor) + assert.Equal(te, test.expected, res) + }) + } +}