diff --git a/internal/pkg/table/table.go b/internal/pkg/table/table.go index 8952d56ba..83cab0ef8 100644 --- a/internal/pkg/table/table.go +++ b/internal/pkg/table/table.go @@ -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 { diff --git a/internal/pkg/table/table_manager.go b/internal/pkg/table/table_manager.go index f7a2a0128..3b4127a5e 100644 --- a/internal/pkg/table/table_manager.go +++ b/internal/pkg/table/table_manager.go @@ -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 { @@ -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 {