diff options
Diffstat (limited to 'table/destination.go')
-rw-r--r-- | table/destination.go | 1041 |
1 files changed, 0 insertions, 1041 deletions
diff --git a/table/destination.go b/table/destination.go deleted file mode 100644 index 49dceaa7..00000000 --- a/table/destination.go +++ /dev/null @@ -1,1041 +0,0 @@ -// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -// implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package table - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "fmt" - "net" - "sort" - - log "github.com/sirupsen/logrus" - - "github.com/osrg/gobgp/config" - "github.com/osrg/gobgp/packet/bgp" -) - -var SelectionOptions config.RouteSelectionOptionsConfig -var UseMultiplePaths config.UseMultiplePathsConfig - -type BestPathReason uint8 - -const ( - BPR_UNKNOWN BestPathReason = iota - BPR_DISABLED - BPR_ONLY_PATH - BPR_REACHABLE_NEXT_HOP - BPR_HIGHEST_WEIGHT - BPR_LOCAL_PREF - BPR_LOCAL_ORIGIN - BPR_ASPATH - BPR_ORIGIN - BPR_MED - BPR_ASN - BPR_IGP_COST - BPR_ROUTER_ID - BPR_OLDER - BPR_NON_LLGR_STALE -) - -var BestPathReasonStringMap = map[BestPathReason]string{ - BPR_UNKNOWN: "Unknown", - BPR_DISABLED: "Bestpath selection disabled", - BPR_ONLY_PATH: "Only Path", - BPR_REACHABLE_NEXT_HOP: "Reachable Next Hop", - BPR_HIGHEST_WEIGHT: "Highest Weight", - BPR_LOCAL_PREF: "Local Pref", - BPR_LOCAL_ORIGIN: "Local Origin", - BPR_ASPATH: "AS Path", - BPR_ORIGIN: "Origin", - BPR_MED: "MED", - BPR_ASN: "ASN", - BPR_IGP_COST: "IGP Cost", - BPR_ROUTER_ID: "Router ID", - BPR_OLDER: "Older", - BPR_NON_LLGR_STALE: "no LLGR Stale", -} - -func (r *BestPathReason) String() string { - return BestPathReasonStringMap[*r] -} - -func IpToRadixkey(b []byte, max uint8) string { - var buffer bytes.Buffer - for i := 0; i < len(b) && i < int(max); i++ { - fmt.Fprintf(&buffer, "%08b", b[i]) - } - return buffer.String()[:max] -} - -func CidrToRadixkey(cidr string) string { - _, n, _ := net.ParseCIDR(cidr) - ones, _ := n.Mask.Size() - return IpToRadixkey(n.IP, uint8(ones)) -} - -func AddrToRadixkey(addr bgp.AddrPrefixInterface) string { - var ( - ip net.IP - size uint8 - ) - switch T := addr.(type) { - case *bgp.IPAddrPrefix: - mask := net.CIDRMask(int(T.Length), net.IPv4len*8) - ip, size = T.Prefix.Mask(mask).To4(), uint8(T.Length) - case *bgp.IPv6AddrPrefix: - mask := net.CIDRMask(int(T.Length), net.IPv6len*8) - ip, size = T.Prefix.Mask(mask).To16(), uint8(T.Length) - default: - return CidrToRadixkey(addr.String()) - } - return IpToRadixkey(ip, size) -} - -type PeerInfo struct { - AS uint32 - ID net.IP - LocalAS uint32 - LocalID net.IP - Address net.IP - LocalAddress net.IP - RouteReflectorClient bool - RouteReflectorClusterID net.IP - MultihopTtl uint8 - Confederation bool -} - -func (lhs *PeerInfo) Equal(rhs *PeerInfo) bool { - if lhs == rhs { - return true - } - - if rhs == nil { - return false - } - - if (lhs.AS == rhs.AS) && lhs.ID.Equal(rhs.ID) && lhs.LocalID.Equal(rhs.LocalID) && lhs.Address.Equal(rhs.Address) { - return true - } - return false -} - -func (i *PeerInfo) String() string { - if i.Address == nil { - return "local" - } - s := bytes.NewBuffer(make([]byte, 0, 64)) - s.WriteString(fmt.Sprintf("{ %s | ", i.Address)) - s.WriteString(fmt.Sprintf("as: %d", i.AS)) - s.WriteString(fmt.Sprintf(", id: %s", i.ID)) - if i.RouteReflectorClient { - s.WriteString(fmt.Sprintf(", cluster-id: %s", i.RouteReflectorClusterID)) - } - s.WriteString(" }") - return s.String() -} - -func NewPeerInfo(g *config.Global, p *config.Neighbor) *PeerInfo { - clusterID := net.ParseIP(string(p.RouteReflector.State.RouteReflectorClusterId)).To4() - // exclude zone info - naddr, _ := net.ResolveIPAddr("ip", p.State.NeighborAddress) - return &PeerInfo{ - AS: p.Config.PeerAs, - LocalAS: g.Config.As, - LocalID: net.ParseIP(g.Config.RouterId).To4(), - RouteReflectorClient: p.RouteReflector.Config.RouteReflectorClient, - Address: naddr.IP, - RouteReflectorClusterID: clusterID, - MultihopTtl: p.EbgpMultihop.Config.MultihopTtl, - Confederation: p.IsConfederationMember(g), - } -} - -type Destination struct { - routeFamily bgp.RouteFamily - nlri bgp.AddrPrefixInterface - knownPathList []*Path - localIdMap *Bitmap -} - -func NewDestination(nlri bgp.AddrPrefixInterface, mapSize int, known ...*Path) *Destination { - d := &Destination{ - routeFamily: bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()), - nlri: nlri, - knownPathList: known, - localIdMap: NewBitmap(mapSize), - } - // the id zero means id is not allocated yet. - if mapSize != 0 { - d.localIdMap.Flag(0) - } - return d -} - -func (dd *Destination) Family() bgp.RouteFamily { - return dd.routeFamily -} - -func (dd *Destination) setRouteFamily(routeFamily bgp.RouteFamily) { - dd.routeFamily = routeFamily -} - -func (dd *Destination) GetNlri() bgp.AddrPrefixInterface { - return dd.nlri -} - -func (dd *Destination) setNlri(nlri bgp.AddrPrefixInterface) { - dd.nlri = nlri -} - -func (dd *Destination) GetAllKnownPathList() []*Path { - return dd.knownPathList -} - -func rsFilter(id string, as uint32, path *Path) bool { - isASLoop := func(as uint32, path *Path) bool { - for _, v := range path.GetAsList() { - if as == v { - return true - } - } - return false - } - - if id != GLOBAL_RIB_NAME && (path.GetSource().Address.String() == id || isASLoop(as, path)) { - return true - } - return false -} - -func (dd *Destination) GetKnownPathList(id string, as uint32) []*Path { - list := make([]*Path, 0, len(dd.knownPathList)) - for _, p := range dd.knownPathList { - if rsFilter(id, as, p) { - continue - } - list = append(list, p) - } - return list -} - -func getBestPath(id string, as uint32, pathList []*Path) *Path { - for _, p := range pathList { - if rsFilter(id, as, p) { - continue - } - return p - } - return nil -} - -func (dd *Destination) GetBestPath(id string, as uint32) *Path { - p := getBestPath(id, as, dd.knownPathList) - if p == nil || p.IsNexthopInvalid { - return nil - } - return p -} - -func (dd *Destination) GetMultiBestPath(id string) []*Path { - return getMultiBestPath(id, dd.knownPathList) -} - -// Calculates best-path among known paths for this destination. -// -// Modifies destination's state related to stored paths. Removes withdrawn -// paths from known paths. Also, adds new paths to known paths. -func (dest *Destination) Calculate(newPath *Path) *Update { - oldKnownPathList := make([]*Path, len(dest.knownPathList)) - copy(oldKnownPathList, dest.knownPathList) - - if newPath.IsWithdraw { - p := dest.explicitWithdraw(newPath) - if p != nil { - if id := p.GetNlri().PathLocalIdentifier(); id != 0 { - dest.localIdMap.Unflag(uint(id)) - } - } - } else { - dest.implicitWithdraw(newPath) - dest.knownPathList = append(dest.knownPathList, newPath) - } - - for _, path := range dest.knownPathList { - if path.GetNlri().PathLocalIdentifier() == 0 { - id, err := dest.localIdMap.FindandSetZeroBit() - if err != nil { - dest.localIdMap.Expand() - id, _ = dest.localIdMap.FindandSetZeroBit() - } - path.GetNlri().SetPathLocalIdentifier(uint32(id)) - } - } - // Compute new best path - dest.computeKnownBestPath() - - l := make([]*Path, len(dest.knownPathList)) - copy(l, dest.knownPathList) - return &Update{ - KnownPathList: l, - OldKnownPathList: oldKnownPathList, - } -} - -// Removes withdrawn paths. -// -// Note: -// We may have disproportionate number of withdraws compared to know paths -// since not all paths get installed into the table due to bgp policy and -// we can receive withdraws for such paths and withdrawals may not be -// stopped by the same policies. -// -func (dest *Destination) explicitWithdraw(withdraw *Path) *Path { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": dest.GetNlri().String(), - }).Debug("Removing withdrawals") - - // If we have some withdrawals and no know-paths, it means it is safe to - // delete these withdraws. - if len(dest.knownPathList) == 0 { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": dest.GetNlri().String(), - }).Debug("Found withdrawals for path(s) that did not get installed") - return nil - } - - // Match all withdrawals from destination paths. - isFound := -1 - for i, path := range dest.knownPathList { - // We have a match if the source and path-id are same. - if path.GetSource().Equal(withdraw.GetSource()) && path.GetNlri().PathIdentifier() == withdraw.GetNlri().PathIdentifier() { - isFound = i - withdraw.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier()) - } - } - - // We do no have any match for this withdraw. - if isFound == -1 { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": dest.GetNlri().String(), - "Path": withdraw, - }).Warn("No matching path for withdraw found, may be path was not installed into table") - return nil - } else { - p := dest.knownPathList[isFound] - dest.knownPathList = append(dest.knownPathList[:isFound], dest.knownPathList[isFound+1:]...) - return p - } -} - -// Identifies which of known paths are old and removes them. -// -// Known paths will no longer have paths whose new version is present in -// new paths. -func (dest *Destination) implicitWithdraw(newPath *Path) { - found := -1 - for i, path := range dest.knownPathList { - if newPath.NoImplicitWithdraw() { - continue - } - // Here we just check if source is same and not check if path - // version num. as newPaths are implicit withdrawal of old - // paths and when doing RouteRefresh (not EnhancedRouteRefresh) - // we get same paths again. - if newPath.GetSource().Equal(path.GetSource()) && newPath.GetNlri().PathIdentifier() == path.GetNlri().PathIdentifier() { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": dest.GetNlri().String(), - "Path": path, - }).Debug("Implicit withdrawal of old path, since we have learned new path from the same peer") - - found = i - newPath.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier()) - break - } - } - if found != -1 { - dest.knownPathList = append(dest.knownPathList[:found], dest.knownPathList[found+1:]...) - } -} - -func (dest *Destination) computeKnownBestPath() (*Path, BestPathReason, error) { - if SelectionOptions.DisableBestPathSelection { - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("computeKnownBestPath skipped") - return nil, BPR_DISABLED, nil - } - - // If we do not have any paths to this destination, then we do not have - // new best path. - if len(dest.knownPathList) == 0 { - return nil, BPR_UNKNOWN, nil - } - - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("computeKnownBestPath knownPathList: %d", len(dest.knownPathList)) - - // We pick the first path as current best path. This helps in breaking - // tie between two new paths learned in one cycle for which best-path - // calculation steps lead to tie. - if len(dest.knownPathList) == 1 { - // If the first path has the invalidated next-hop, which evaluated by - // IGP, returns no path with the reason of the next-hop reachability. - if dest.knownPathList[0].IsNexthopInvalid { - return nil, BPR_REACHABLE_NEXT_HOP, nil - } - return dest.knownPathList[0], BPR_ONLY_PATH, nil - } - dest.sort() - newBest := dest.knownPathList[0] - // If the first path has the invalidated next-hop, which evaluated by IGP, - // returns no path with the reason of the next-hop reachability. - if dest.knownPathList[0].IsNexthopInvalid { - return nil, BPR_REACHABLE_NEXT_HOP, nil - } - return newBest, newBest.reason, nil -} - -func (dst *Destination) sort() { - sort.SliceStable(dst.knownPathList, func(i, j int) bool { - //Compares given paths and returns best path. - // - //Parameters: - // -`path1`: first path to compare - // -`path2`: second path to compare - // - // Best path processing will involve following steps: - // 1. Select a path with a reachable next hop. - // 2. Select the path with the highest weight. - // 3. If path weights are the same, select the path with the highest - // local preference value. - // 4. Prefer locally originated routes (network routes, redistributed - // routes, or aggregated routes) over received routes. - // 5. Select the route with the shortest AS-path length. - // 6. If all paths have the same AS-path length, select the path based - // on origin: IGP is preferred over EGP; EGP is preferred over - // Incomplete. - // 7. If the origins are the same, select the path with lowest MED - // value. - // 8. If the paths have the same MED values, select the path learned - // via EBGP over one learned via IBGP. - // 9. Select the route with the lowest IGP cost to the next hop. - // 10. Select the route received from the peer with the lowest BGP - // router ID. - // - // Returns None if best-path among given paths cannot be computed else best - // path. - // Assumes paths from NC has source equal to None. - // - - path1 := dst.knownPathList[i] - path2 := dst.knownPathList[j] - - var better *Path - reason := BPR_UNKNOWN - - // draft-uttaro-idr-bgp-persistence-02 - if better == nil { - better = compareByLLGRStaleCommunity(path1, path2) - reason = BPR_NON_LLGR_STALE - } - // Follow best path calculation algorithm steps. - // compare by reachability - if better == nil { - better = compareByReachableNexthop(path1, path2) - reason = BPR_REACHABLE_NEXT_HOP - } - if better == nil { - better = compareByHighestWeight(path1, path2) - reason = BPR_HIGHEST_WEIGHT - } - if better == nil { - better = compareByLocalPref(path1, path2) - reason = BPR_LOCAL_PREF - } - if better == nil { - better = compareByLocalOrigin(path1, path2) - reason = BPR_LOCAL_ORIGIN - } - if better == nil { - better = compareByASPath(path1, path2) - reason = BPR_ASPATH - } - if better == nil { - better = compareByOrigin(path1, path2) - reason = BPR_ORIGIN - } - if better == nil { - better = compareByMED(path1, path2) - reason = BPR_MED - } - if better == nil { - better = compareByASNumber(path1, path2) - reason = BPR_ASN - } - if better == nil { - better = compareByIGPCost(path1, path2) - reason = BPR_IGP_COST - } - if better == nil { - better = compareByAge(path1, path2) - reason = BPR_OLDER - } - if better == nil { - var e error = nil - better, e = compareByRouterID(path1, path2) - if e != nil { - log.WithFields(log.Fields{ - "Topic": "Table", - "Error": e, - }).Error("Could not get best path by comparing router ID") - } - reason = BPR_ROUTER_ID - } - if better == nil { - reason = BPR_UNKNOWN - better = path1 - } - - better.reason = reason - - return better == path1 - }) -} - -type Update struct { - KnownPathList []*Path - OldKnownPathList []*Path -} - -func getMultiBestPath(id string, pathList []*Path) []*Path { - list := make([]*Path, 0, len(pathList)) - var best *Path - for _, p := range pathList { - if !p.IsNexthopInvalid { - if best == nil { - best = p - list = append(list, p) - } else if best.Compare(p) == 0 { - list = append(list, p) - } - } - } - return list -} - -func (u *Update) GetWithdrawnPath() []*Path { - if len(u.KnownPathList) == len(u.OldKnownPathList) { - return nil - } - - l := make([]*Path, 0, len(u.OldKnownPathList)) - - for _, p := range u.OldKnownPathList { - y := func() bool { - for _, old := range u.KnownPathList { - if p == old { - return true - } - } - return false - }() - if !y { - l = append(l, p.Clone(true)) - } - } - return l -} - -func (u *Update) GetChanges(id string, as uint32, peerDown bool) (*Path, *Path, []*Path) { - best, old := func(id string) (*Path, *Path) { - old := getBestPath(id, as, u.OldKnownPathList) - best := getBestPath(id, as, u.KnownPathList) - if best != nil && best.Equal(old) { - // RFC4684 3.2. Intra-AS VPN Route Distribution - // When processing RT membership NLRIs received from internal iBGP - // peers, it is necessary to consider all available iBGP paths for a - // given RT prefix, for building the outbound route filter, and not just - // the best path. - if best.GetRouteFamily() == bgp.RF_RTC_UC { - return best, old - } - // For BGP Nexthop Tracking, checks if the nexthop reachability - // was changed or not. - if best.IsNexthopInvalid != old.IsNexthopInvalid { - // If the nexthop of the best path became unreachable, we need - // to withdraw that path. - if best.IsNexthopInvalid { - return best.Clone(true), old - } - return best, old - } - return nil, old - } - if best == nil { - if old == nil { - return nil, nil - } - if peerDown { - // withdraws were generated by peer - // down so paths are not in knowpath - // or adjin. - old.IsWithdraw = true - return old, old - } - return old.Clone(true), old - } - return best, old - }(id) - - var multi []*Path - - if id == GLOBAL_RIB_NAME && UseMultiplePaths.Enabled { - diff := func(lhs, rhs []*Path) bool { - if len(lhs) != len(rhs) { - return true - } - for idx, l := range lhs { - if !l.Equal(rhs[idx]) { - return true - } - } - return false - } - oldM := getMultiBestPath(id, u.OldKnownPathList) - newM := getMultiBestPath(id, u.KnownPathList) - if diff(oldM, newM) { - multi = newM - if len(newM) == 0 { - multi = []*Path{best} - } - } - } - return best, old, multi -} - -func compareByLLGRStaleCommunity(path1, path2 *Path) *Path { - p1 := path1.IsLLGRStale() - p2 := path2.IsLLGRStale() - if p1 == p2 { - return nil - } else if p1 { - return path2 - } - return path1 -} - -func compareByReachableNexthop(path1, path2 *Path) *Path { - // Compares given paths and selects best path based on reachable next-hop. - // - // If no path matches this criteria, return nil. - // For BGP Nexthop Tracking, evaluates next-hop is validated by IGP. - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("enter compareByReachableNexthop -- path1: %s, path2: %s", path1, path2) - - if path1.IsNexthopInvalid && !path2.IsNexthopInvalid { - return path2 - } else if !path1.IsNexthopInvalid && path2.IsNexthopInvalid { - return path1 - } - - return nil -} - -func compareByHighestWeight(path1, path2 *Path) *Path { - // Selects a path with highest weight. - // - // Weight is BGPS specific parameter. It is local to the router on which it - // is configured. - // Return: - // nil if best path among given paths cannot be decided, else best path. - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("enter compareByHighestWeight -- path1: %s, path2: %s", path1, path2) - return nil -} - -func compareByLocalPref(path1, path2 *Path) *Path { - // Selects a path with highest local-preference. - // - // Unlike the weight attribute, which is only relevant to the local - // router, local preference is an attribute that routers exchange in the - // same AS. Highest local-pref is preferred. If we cannot decide, - // we return None. - // - // # Default local-pref values is 100 - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("enter compareByLocalPref") - localPref1, _ := path1.GetLocalPref() - localPref2, _ := path2.GetLocalPref() - // Highest local-preference value is preferred. - if localPref1 > localPref2 { - return path1 - } else if localPref1 < localPref2 { - return path2 - } else { - return nil - } -} - -func compareByLocalOrigin(path1, path2 *Path) *Path { - - // Select locally originating path as best path. - // Locally originating routes are network routes, redistributed routes, - // or aggregated routes. - // Returns None if given paths have same source. - // - // If both paths are from same sources we cannot compare them here. - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("enter compareByLocalOrigin") - if path1.GetSource().Equal(path2.GetSource()) { - return nil - } - - // Here we consider prefix from NC as locally originating static route. - // Hence it is preferred. - if path1.IsLocal() { - return path1 - } - - if path2.IsLocal() { - return path2 - } - return nil -} - -func compareByASPath(path1, path2 *Path) *Path { - // Calculated the best-paths by comparing as-path lengths. - // - // Shortest as-path length is preferred. If both path have same lengths, - // we return None. - if SelectionOptions.IgnoreAsPathLength { - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("compareByASPath -- skip") - return nil - } - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("enter compareByASPath") - attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) - attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) - - // With addpath support, we could compare paths from API don't - // AS_PATH. No need to warn here. - if !path1.IsLocal() && !path2.IsLocal() && (attribute1 == nil || attribute2 == nil) { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": "compareByASPath", - "ASPath1": attribute1, - "ASPath2": attribute2, - }).Warn("can't compare ASPath because it's not present") - } - - l1 := path1.GetAsPathLen() - l2 := path2.GetAsPathLen() - - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("compareByASPath -- l1: %d, l2: %d", l1, l2) - if l1 > l2 { - return path2 - } else if l1 < l2 { - return path1 - } else { - return nil - } -} - -func compareByOrigin(path1, path2 *Path) *Path { - // Select the best path based on origin attribute. - // - // IGP is preferred over EGP; EGP is preferred over Incomplete. - // If both paths have same origin, we return None. - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("enter compareByOrigin") - attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN) - attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN) - - if attribute1 == nil || attribute2 == nil { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": "compareByOrigin", - "Origin1": attribute1, - "Origin2": attribute2, - }).Error("can't compare origin because it's not present") - return nil - } - - origin1 := attribute1.(*bgp.PathAttributeOrigin).Value - origin2 := attribute2.(*bgp.PathAttributeOrigin).Value - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("compareByOrigin -- origin1: %d, origin2: %d", origin1, origin2) - - // If both paths have same origins - if origin1 == origin2 { - return nil - } else if origin1 < origin2 { - return path1 - } else { - return path2 - } -} - -func compareByMED(path1, path2 *Path) *Path { - // Select the path based with lowest MED value. - // - // If both paths have same MED, return None. - // By default, a route that arrives with no MED value is treated as if it - // had a MED of 0, the most preferred value. - // RFC says lower MED is preferred over higher MED value. - // compare MED among not only same AS path but also all path, - // like bgp always-compare-med - - isInternal := func() bool { return path1.GetAsPathLen() == 0 && path2.GetAsPathLen() == 0 }() - - isSameAS := func() bool { - firstAS := func(path *Path) uint32 { - if asPath := path.GetAsPath(); asPath != nil { - for _, v := range asPath.Value { - segType := v.GetType() - asList := v.GetAS() - if len(asList) == 0 { - continue - } - switch segType { - case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: - continue - } - return asList[0] - } - } - return 0 - } - return firstAS(path1) != 0 && firstAS(path1) == firstAS(path2) - }() - - if SelectionOptions.AlwaysCompareMed || isInternal || isSameAS { - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("enter compareByMED") - getMed := func(path *Path) uint32 { - attribute := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) - if attribute == nil { - return 0 - } - med := attribute.(*bgp.PathAttributeMultiExitDisc).Value - return med - } - - med1 := getMed(path1) - med2 := getMed(path2) - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("compareByMED -- med1: %d, med2: %d", med1, med2) - if med1 == med2 { - return nil - } else if med1 < med2 { - return path1 - } - return path2 - } else { - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("skip compareByMED %v %v %v", SelectionOptions.AlwaysCompareMed, isInternal, isSameAS) - return nil - } -} - -func compareByASNumber(path1, path2 *Path) *Path { - - //Select the path based on source (iBGP/eBGP) peer. - // - //eBGP path is preferred over iBGP. If both paths are from same kind of - //peers, return None. - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("enter compareByASNumber") - - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("compareByASNumber -- p1Asn: %d, p2Asn: %d", path1.GetSource().AS, path2.GetSource().AS) - // Path from confederation member should be treated as internal (IBGP learned) path. - isIBGP1 := path1.GetSource().Confederation || path1.IsIBGP() - isIBGP2 := path2.GetSource().Confederation || path2.IsIBGP() - // If one path is from ibgp peer and another is from ebgp peer, take the ebgp path. - if isIBGP1 != isIBGP2 { - if isIBGP1 { - return path2 - } - return path1 - } - - // If both paths are from ebgp or ibpg peers, we cannot decide. - return nil -} - -func compareByIGPCost(path1, path2 *Path) *Path { - // Select the route with the lowest IGP cost to the next hop. - // - // Return None if igp cost is same. - // Currently BGPS has no concept of IGP and IGP cost. - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debugf("enter compareByIGPCost -- path1: %v, path2: %v", path1, path2) - return nil -} - -func compareByRouterID(path1, path2 *Path) (*Path, error) { - // Select the route received from the peer with the lowest BGP router ID. - // - // If both paths are eBGP paths, then we do not do any tie breaking, i.e we do - // not pick best-path based on this criteria. - // RFC: http://tools.ietf.org/html/rfc5004 - // We pick best path between two iBGP paths as usual. - log.WithFields(log.Fields{ - "Topic": "Table", - }).Debug("enter compareByRouterID") - - // If both paths are from NC we have same router Id, hence cannot compare. - if path1.IsLocal() && path2.IsLocal() { - return nil, nil - } - - // If both paths are from eBGP peers, then according to RFC we need - // not tie break using router id. - if !SelectionOptions.ExternalCompareRouterId && !path1.IsIBGP() && !path2.IsIBGP() { - return nil, nil - } - - if !SelectionOptions.ExternalCompareRouterId && path1.IsIBGP() != path2.IsIBGP() { - return nil, fmt.Errorf("This method does not support comparing ebgp with ibgp path") - } - - // At least one path is not coming from NC, so we get local bgp id. - id1 := binary.BigEndian.Uint32(path1.GetSource().ID) - id2 := binary.BigEndian.Uint32(path2.GetSource().ID) - - // If both router ids are same/equal we cannot decide. - // This case is possible since router ids are arbitrary. - if id1 == id2 { - return nil, nil - } else if id1 < id2 { - return path1, nil - } else { - return path2, nil - } -} - -func compareByAge(path1, path2 *Path) *Path { - if !path1.IsIBGP() && !path2.IsIBGP() && !SelectionOptions.ExternalCompareRouterId { - age1 := path1.GetTimestamp().UnixNano() - age2 := path2.GetTimestamp().UnixNano() - if age1 == age2 { - return nil - } else if age1 < age2 { - return path1 - } - return path2 - } - return nil -} - -func (dest *Destination) String() string { - return fmt.Sprintf("Destination NLRI: %s", dest.nlri.String()) -} - -type DestinationSelectOption struct { - ID string - AS uint32 - VRF *Vrf - adj bool - Best bool - MultiPath bool -} - -func (d *Destination) MarshalJSON() ([]byte, error) { - return json.Marshal(d.GetAllKnownPathList()) -} - -func (d *Destination) Select(option ...DestinationSelectOption) *Destination { - id := GLOBAL_RIB_NAME - var vrf *Vrf - adj := false - best := false - mp := false - as := uint32(0) - for _, o := range option { - if o.ID != "" { - id = o.ID - } - if o.VRF != nil { - vrf = o.VRF - } - adj = o.adj - best = o.Best - mp = o.MultiPath - as = o.AS - } - var paths []*Path - if adj { - paths = make([]*Path, len(d.knownPathList)) - copy(paths, d.knownPathList) - } else { - paths = d.GetKnownPathList(id, as) - if vrf != nil { - ps := make([]*Path, 0, len(paths)) - for _, p := range paths { - if CanImportToVrf(vrf, p) { - ps = append(ps, p.ToLocal()) - } - } - paths = ps - } - if len(paths) == 0 { - return nil - } - if best { - if !mp { - paths = []*Path{paths[0]} - } else { - ps := make([]*Path, 0, len(paths)) - var best *Path - for _, p := range paths { - if best == nil { - best = p - ps = append(ps, p) - } else if best.Compare(p) == 0 { - ps = append(ps, p) - } - } - paths = ps - } - } - } - return NewDestination(d.nlri, 0, paths...) -} |