1
0
mirror of https://github.com/osrg/gobgp.git synced 2024-05-11 05:55:10 +00:00

evpn: fix quadratic evpn mac-mobility handling

This patch adds a special case in the destination hashmap for EVPN
Type-2 routes, to index them by MAC address. This allows for direct
access to the destination struct, instead of iterating over all
destination and all paths.

In effect, this replaces an iteration over all known paths by a quick
lookup to the MAC, leaving only an iteration to multiple paths to the
same MAC (e.g. multihoming or through multiple VNIs).

The practical effect is a reasonable convergence time for large EVPN
instances.

- before: 6m 7s
- after: 11s

The comparison was performed on a Xeon Silver 4209T, and an EVPN
instance comprising of 13k EVPN type-2 routes. The time is measured
by comparing the timestamp of the first and the last routes logged by
the cli's monitor mode.

Given the extreme difference, no further work was done for a more
accurate measurment.
This commit is contained in:
Tuetuopay
2023-11-28 17:23:31 +01:00
parent 99d96acddf
commit c393f4382c
2 changed files with 86 additions and 15 deletions

View File

@ -57,6 +57,10 @@ type Table struct {
routeFamily bgp.RouteFamily
destinations map[string]*Destination
logger log.Logger
// index of route distinguishers with paths to a specific MAC
// this is a map[MAC address]map[RD]struct{}
// this holds a map for a set of RD.
macIndex map[string]map[string]struct{}
}
func NewTable(logger log.Logger, rf bgp.RouteFamily, dsts ...*Destination) *Table {
@ -64,6 +68,7 @@ func NewTable(logger log.Logger, rf bgp.RouteFamily, dsts ...*Destination) *Tabl
routeFamily: rf,
destinations: make(map[string]*Destination),
logger: logger,
macIndex: make(map[string]map[string]struct{}),
}
for _, dst := range dsts {
t.setDestination(dst)
@ -138,6 +143,20 @@ func (t *Table) deleteDest(dest *Destination) {
if len(destinations) == 0 {
t.destinations = make(map[string]*Destination)
}
if nlri, ok := dest.nlri.(*bgp.EVPNNLRI); ok {
if macadv, ok := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute); ok {
mac := *(*string)(unsafe.Pointer(&macadv.MacAddress))
serializedRD, _ := macadv.RD.Serialize()
rd := *(*string)(unsafe.Pointer(&serializedRD))
if rds, ok := t.macIndex[mac]; ok {
delete(rds, rd)
if len(rds) == 0 {
delete(t.macIndex, mac)
}
}
}
}
}
func (t *Table) validatePath(path *Path) {
@ -375,6 +394,19 @@ func (t *Table) GetMUPDestinationsWithRouteType(p string) ([]*Destination, error
func (t *Table) setDestination(dst *Destination) {
t.destinations[t.tableKey(dst.nlri)] = dst
if nlri, ok := dst.nlri.(*bgp.EVPNNLRI); ok {
if macadv, ok := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute); ok {
mac := *(*string)(unsafe.Pointer(&macadv.MacAddress))
serializedRD, _ := macadv.RD.Serialize()
rd := *(*string)(unsafe.Pointer(&serializedRD))
if rds, ok := t.macIndex[mac]; ok {
rds[rd] = struct{}{}
} else {
t.macIndex[mac] = map[string]struct{}{rd: {}}
}
}
}
}
func (t *Table) tableKey(nlri bgp.AddrPrefixInterface) string {
@ -403,6 +435,17 @@ func (t *Table) tableKey(nlri bgp.AddrPrefixInterface) string {
copy(b[8:24], T.Prefix.To16())
b[24] = T.Length
return *(*string)(unsafe.Pointer(&b))
// we need fast lookup to routes for a specific mac address for evpn mac mobility
case *bgp.EVPNNLRI:
switch U := T.RouteTypeData.(type) {
case *bgp.EVPNMacIPAdvertisementRoute:
b := make([]byte, 15)
serializedRD, _ := U.RD.Serialize()
copy(b, serializedRD)
b[8] = bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT
copy(b[9:15], U.MacAddress)
return *(*string)(unsafe.Pointer(&b))
}
}
return nlri.String()
}
@ -437,6 +480,23 @@ func (t *Table) GetKnownPathList(id string, as uint32) []*Path {
return paths
}
func (t *Table) GetKnownPathListWithMac(id string, as uint32, mac net.HardwareAddr) []*Path {
var paths []*Path
if rds, ok := t.macIndex[*(*string)(unsafe.Pointer(&mac))]; ok {
for rd := range rds {
b := make([]byte, 15)
copy(b, rd)
b[8] = bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT
copy(b[9:15], mac)
key := *(*string)(unsafe.Pointer(&b))
if dst, ok := t.destinations[key]; ok {
paths = append(paths, dst.GetKnownPathList(id, as)...)
}
}
}
return paths
}
func (t *Table) Select(option ...TableSelectOption) (*Table, error) {
id := GLOBAL_RIB_NAME
var vrf *Vrf
@ -462,6 +522,7 @@ func (t *Table) Select(option ...TableSelectOption) (*Table, error) {
r := &Table{
routeFamily: t.routeFamily,
destinations: make(map[string]*Destination),
macIndex: make(map[string]map[string]struct{}),
}
if len(prefixes) != 0 {

View File

@ -239,10 +239,7 @@ func (manager *TableManager) handleMacMobility(path *Path) []*Path {
if path.IsWithdraw || path.IsLocal() || nlri.RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
return nil
}
for _, path2 := range manager.GetPathList(GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN}) {
if !path2.IsLocal() || path2.GetNlri().(*bgp.EVPNNLRI).RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
continue
}
f := func(p *Path) (bgp.EthernetSegmentIdentifier, uint32, net.HardwareAddr, int, net.IP) {
nlri := p.GetNlri().(*bgp.EVPNNLRI)
d := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
@ -257,6 +254,11 @@ func (manager *TableManager) handleMacMobility(path *Path) []*Path {
return d.ESI, d.ETag, d.MacAddress, seq, p.GetSource().Address
}
e1, et1, m1, s1, i1 := f(path)
for _, path2 := range manager.GetPathListWithMac(GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN}, m1) {
if !path2.IsLocal() || path2.GetNlri().(*bgp.EVPNNLRI).RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
continue
}
e2, et2, m2, s2, i2 := f(path2)
if et1 == et2 && bytes.Equal(m1, m2) && !bytes.Equal(e1.Value, e2.Value) {
if s1 > s2 || s1 == s2 && bytes.Compare(i1, i2) < 0 {
@ -324,6 +326,14 @@ func (manager *TableManager) GetPathList(id string, as uint32, rfList []bgp.Rout
return paths
}
func (manager *TableManager) GetPathListWithMac(id string, as uint32, rfList []bgp.RouteFamily, mac net.HardwareAddr) []*Path {
var paths []*Path
for _, t := range manager.tables(rfList...) {
paths = append(paths, t.GetKnownPathListWithMac(id, as, mac)...)
}
return paths
}
func (manager *TableManager) GetPathListWithNexthop(id string, rfList []bgp.RouteFamily, nexthop net.IP) []*Path {
paths := make([]*Path, 0, manager.getDestinationCount(rfList))
for _, rf := range rfList {