Skip to content

Commit

Permalink
evpn: fix quadratic evpn mac-mobility handling
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Tuetuopay committed Dec 17, 2023
1 parent 99d96ac commit c393f43
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 15 deletions.
61 changes: 61 additions & 0 deletions internal/pkg/table/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,18 @@ 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 {
t := &Table{
routeFamily: rf,
destinations: make(map[string]*Destination),
logger: logger,
macIndex: make(map[string]map[string]struct{}),
}
for _, dst := range dsts {
t.setDestination(dst)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down
40 changes: 25 additions & 15 deletions internal/pkg/table/table_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,24 +239,26 @@ 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}) {

f := func(p *Path) (bgp.EthernetSegmentIdentifier, uint32, net.HardwareAddr, int, net.IP) {
nlri := p.GetNlri().(*bgp.EVPNNLRI)
d := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
ecs := p.GetExtCommunities()
seq := -1
for _, ec := range ecs {
if t, st := ec.GetTypes(); t == bgp.EC_TYPE_EVPN && st == bgp.EC_SUBTYPE_MAC_MOBILITY {
seq = int(ec.(*bgp.MacMobilityExtended).Sequence)
break
}
}
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
}
f := func(p *Path) (bgp.EthernetSegmentIdentifier, uint32, net.HardwareAddr, int, net.IP) {
nlri := p.GetNlri().(*bgp.EVPNNLRI)
d := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
ecs := p.GetExtCommunities()
seq := -1
for _, ec := range ecs {
if t, st := ec.GetTypes(); t == bgp.EC_TYPE_EVPN && st == bgp.EC_SUBTYPE_MAC_MOBILITY {
seq = int(ec.(*bgp.MacMobilityExtended).Sequence)
break
}
}
return d.ESI, d.ETag, d.MacAddress, seq, p.GetSource().Address
}
e1, et1, m1, s1, i1 := f(path)
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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit c393f43

Please sign in to comment.