github.com/osrg/gobgp@v2.0.0+incompatible/internal/pkg/table/destination.go (about)

     1  // Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package table
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/binary"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net"
    24  	"sort"
    25  
    26  	"github.com/osrg/gobgp/internal/pkg/config"
    27  	"github.com/osrg/gobgp/pkg/packet/bgp"
    28  
    29  	log "github.com/sirupsen/logrus"
    30  )
    31  
    32  var SelectionOptions config.RouteSelectionOptionsConfig
    33  var UseMultiplePaths config.UseMultiplePathsConfig
    34  
    35  type BestPathReason uint8
    36  
    37  const (
    38  	BPR_UNKNOWN BestPathReason = iota
    39  	BPR_DISABLED
    40  	BPR_ONLY_PATH
    41  	BPR_REACHABLE_NEXT_HOP
    42  	BPR_HIGHEST_WEIGHT
    43  	BPR_LOCAL_PREF
    44  	BPR_LOCAL_ORIGIN
    45  	BPR_ASPATH
    46  	BPR_ORIGIN
    47  	BPR_MED
    48  	BPR_ASN
    49  	BPR_IGP_COST
    50  	BPR_ROUTER_ID
    51  	BPR_OLDER
    52  	BPR_NON_LLGR_STALE
    53  )
    54  
    55  var BestPathReasonStringMap = map[BestPathReason]string{
    56  	BPR_UNKNOWN:            "Unknown",
    57  	BPR_DISABLED:           "Bestpath selection disabled",
    58  	BPR_ONLY_PATH:          "Only Path",
    59  	BPR_REACHABLE_NEXT_HOP: "Reachable Next Hop",
    60  	BPR_HIGHEST_WEIGHT:     "Highest Weight",
    61  	BPR_LOCAL_PREF:         "Local Pref",
    62  	BPR_LOCAL_ORIGIN:       "Local Origin",
    63  	BPR_ASPATH:             "AS Path",
    64  	BPR_ORIGIN:             "Origin",
    65  	BPR_MED:                "MED",
    66  	BPR_ASN:                "ASN",
    67  	BPR_IGP_COST:           "IGP Cost",
    68  	BPR_ROUTER_ID:          "Router ID",
    69  	BPR_OLDER:              "Older",
    70  	BPR_NON_LLGR_STALE:     "no LLGR Stale",
    71  }
    72  
    73  func (r *BestPathReason) String() string {
    74  	return BestPathReasonStringMap[*r]
    75  }
    76  
    77  func IpToRadixkey(b []byte, max uint8) string {
    78  	var buffer bytes.Buffer
    79  	for i := 0; i < len(b) && i < int(max); i++ {
    80  		fmt.Fprintf(&buffer, "%08b", b[i])
    81  	}
    82  	return buffer.String()[:max]
    83  }
    84  
    85  func CidrToRadixkey(cidr string) string {
    86  	_, n, _ := net.ParseCIDR(cidr)
    87  	ones, _ := n.Mask.Size()
    88  	return IpToRadixkey(n.IP, uint8(ones))
    89  }
    90  
    91  func AddrToRadixkey(addr bgp.AddrPrefixInterface) string {
    92  	var (
    93  		ip   net.IP
    94  		size uint8
    95  	)
    96  	switch T := addr.(type) {
    97  	case *bgp.IPAddrPrefix:
    98  		mask := net.CIDRMask(int(T.Length), net.IPv4len*8)
    99  		ip, size = T.Prefix.Mask(mask).To4(), uint8(T.Length)
   100  	case *bgp.IPv6AddrPrefix:
   101  		mask := net.CIDRMask(int(T.Length), net.IPv6len*8)
   102  		ip, size = T.Prefix.Mask(mask).To16(), uint8(T.Length)
   103  	default:
   104  		return CidrToRadixkey(addr.String())
   105  	}
   106  	return IpToRadixkey(ip, size)
   107  }
   108  
   109  type PeerInfo struct {
   110  	AS                      uint32
   111  	ID                      net.IP
   112  	LocalAS                 uint32
   113  	LocalID                 net.IP
   114  	Address                 net.IP
   115  	LocalAddress            net.IP
   116  	RouteReflectorClient    bool
   117  	RouteReflectorClusterID net.IP
   118  	MultihopTtl             uint8
   119  	Confederation           bool
   120  }
   121  
   122  func (lhs *PeerInfo) Equal(rhs *PeerInfo) bool {
   123  	if lhs == rhs {
   124  		return true
   125  	}
   126  
   127  	if rhs == nil {
   128  		return false
   129  	}
   130  
   131  	if (lhs.AS == rhs.AS) && lhs.ID.Equal(rhs.ID) && lhs.LocalID.Equal(rhs.LocalID) && lhs.Address.Equal(rhs.Address) {
   132  		return true
   133  	}
   134  	return false
   135  }
   136  
   137  func (i *PeerInfo) String() string {
   138  	if i.Address == nil {
   139  		return "local"
   140  	}
   141  	s := bytes.NewBuffer(make([]byte, 0, 64))
   142  	s.WriteString(fmt.Sprintf("{ %s | ", i.Address))
   143  	s.WriteString(fmt.Sprintf("as: %d", i.AS))
   144  	s.WriteString(fmt.Sprintf(", id: %s", i.ID))
   145  	if i.RouteReflectorClient {
   146  		s.WriteString(fmt.Sprintf(", cluster-id: %s", i.RouteReflectorClusterID))
   147  	}
   148  	s.WriteString(" }")
   149  	return s.String()
   150  }
   151  
   152  func NewPeerInfo(g *config.Global, p *config.Neighbor) *PeerInfo {
   153  	clusterID := net.ParseIP(string(p.RouteReflector.State.RouteReflectorClusterId)).To4()
   154  	// exclude zone info
   155  	naddr, _ := net.ResolveIPAddr("ip", p.State.NeighborAddress)
   156  	return &PeerInfo{
   157  		AS:                      p.Config.PeerAs,
   158  		LocalAS:                 g.Config.As,
   159  		LocalID:                 net.ParseIP(g.Config.RouterId).To4(),
   160  		RouteReflectorClient:    p.RouteReflector.Config.RouteReflectorClient,
   161  		Address:                 naddr.IP,
   162  		RouteReflectorClusterID: clusterID,
   163  		MultihopTtl:             p.EbgpMultihop.Config.MultihopTtl,
   164  		Confederation:           p.IsConfederationMember(g),
   165  	}
   166  }
   167  
   168  type Destination struct {
   169  	routeFamily   bgp.RouteFamily
   170  	nlri          bgp.AddrPrefixInterface
   171  	knownPathList []*Path
   172  	localIdMap    *Bitmap
   173  }
   174  
   175  func NewDestination(nlri bgp.AddrPrefixInterface, mapSize int, known ...*Path) *Destination {
   176  	d := &Destination{
   177  		routeFamily:   bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()),
   178  		nlri:          nlri,
   179  		knownPathList: known,
   180  		localIdMap:    NewBitmap(mapSize),
   181  	}
   182  	// the id zero means id is not allocated yet.
   183  	if mapSize != 0 {
   184  		d.localIdMap.Flag(0)
   185  	}
   186  	return d
   187  }
   188  
   189  func (dd *Destination) Family() bgp.RouteFamily {
   190  	return dd.routeFamily
   191  }
   192  
   193  func (dd *Destination) setRouteFamily(routeFamily bgp.RouteFamily) {
   194  	dd.routeFamily = routeFamily
   195  }
   196  
   197  func (dd *Destination) GetNlri() bgp.AddrPrefixInterface {
   198  	return dd.nlri
   199  }
   200  
   201  func (dd *Destination) setNlri(nlri bgp.AddrPrefixInterface) {
   202  	dd.nlri = nlri
   203  }
   204  
   205  func (dd *Destination) GetAllKnownPathList() []*Path {
   206  	return dd.knownPathList
   207  }
   208  
   209  func rsFilter(id string, as uint32, path *Path) bool {
   210  	isASLoop := func(as uint32, path *Path) bool {
   211  		for _, v := range path.GetAsList() {
   212  			if as == v {
   213  				return true
   214  			}
   215  		}
   216  		return false
   217  	}
   218  
   219  	if id != GLOBAL_RIB_NAME && (path.GetSource().Address.String() == id || isASLoop(as, path)) {
   220  		return true
   221  	}
   222  	return false
   223  }
   224  
   225  func (dd *Destination) GetKnownPathList(id string, as uint32) []*Path {
   226  	list := make([]*Path, 0, len(dd.knownPathList))
   227  	for _, p := range dd.knownPathList {
   228  		if rsFilter(id, as, p) {
   229  			continue
   230  		}
   231  		list = append(list, p)
   232  	}
   233  	return list
   234  }
   235  
   236  func getBestPath(id string, as uint32, pathList []*Path) *Path {
   237  	for _, p := range pathList {
   238  		if rsFilter(id, as, p) {
   239  			continue
   240  		}
   241  		return p
   242  	}
   243  	return nil
   244  }
   245  
   246  func (dd *Destination) GetBestPath(id string, as uint32) *Path {
   247  	p := getBestPath(id, as, dd.knownPathList)
   248  	if p == nil || p.IsNexthopInvalid {
   249  		return nil
   250  	}
   251  	return p
   252  }
   253  
   254  func (dd *Destination) GetMultiBestPath(id string) []*Path {
   255  	return getMultiBestPath(id, dd.knownPathList)
   256  }
   257  
   258  // Calculates best-path among known paths for this destination.
   259  //
   260  // Modifies destination's state related to stored paths. Removes withdrawn
   261  // paths from known paths. Also, adds new paths to known paths.
   262  func (dest *Destination) Calculate(newPath *Path) *Update {
   263  	oldKnownPathList := make([]*Path, len(dest.knownPathList))
   264  	copy(oldKnownPathList, dest.knownPathList)
   265  
   266  	if newPath.IsWithdraw {
   267  		p := dest.explicitWithdraw(newPath)
   268  		if p != nil {
   269  			if id := p.GetNlri().PathLocalIdentifier(); id != 0 {
   270  				dest.localIdMap.Unflag(uint(id))
   271  			}
   272  		}
   273  	} else {
   274  		dest.implicitWithdraw(newPath)
   275  		dest.knownPathList = append(dest.knownPathList, newPath)
   276  	}
   277  
   278  	for _, path := range dest.knownPathList {
   279  		if path.GetNlri().PathLocalIdentifier() == 0 {
   280  			id, err := dest.localIdMap.FindandSetZeroBit()
   281  			if err != nil {
   282  				dest.localIdMap.Expand()
   283  				id, _ = dest.localIdMap.FindandSetZeroBit()
   284  			}
   285  			path.GetNlri().SetPathLocalIdentifier(uint32(id))
   286  		}
   287  	}
   288  	// Compute new best path
   289  	dest.computeKnownBestPath()
   290  
   291  	l := make([]*Path, len(dest.knownPathList))
   292  	copy(l, dest.knownPathList)
   293  	return &Update{
   294  		KnownPathList:    l,
   295  		OldKnownPathList: oldKnownPathList,
   296  	}
   297  }
   298  
   299  // Removes withdrawn paths.
   300  //
   301  // Note:
   302  // We may have disproportionate number of withdraws compared to know paths
   303  // since not all paths get installed into the table due to bgp policy and
   304  // we can receive withdraws for such paths and withdrawals may not be
   305  // stopped by the same policies.
   306  //
   307  func (dest *Destination) explicitWithdraw(withdraw *Path) *Path {
   308  	log.WithFields(log.Fields{
   309  		"Topic": "Table",
   310  		"Key":   dest.GetNlri().String(),
   311  	}).Debug("Removing withdrawals")
   312  
   313  	// If we have some withdrawals and no know-paths, it means it is safe to
   314  	// delete these withdraws.
   315  	if len(dest.knownPathList) == 0 {
   316  		log.WithFields(log.Fields{
   317  			"Topic": "Table",
   318  			"Key":   dest.GetNlri().String(),
   319  		}).Debug("Found withdrawals for path(s) that did not get installed")
   320  		return nil
   321  	}
   322  
   323  	// Match all withdrawals from destination paths.
   324  	isFound := -1
   325  	for i, path := range dest.knownPathList {
   326  		// We have a match if the source and path-id are same.
   327  		if path.GetSource().Equal(withdraw.GetSource()) && path.GetNlri().PathIdentifier() == withdraw.GetNlri().PathIdentifier() {
   328  			isFound = i
   329  			withdraw.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier())
   330  		}
   331  	}
   332  
   333  	// We do no have any match for this withdraw.
   334  	if isFound == -1 {
   335  		log.WithFields(log.Fields{
   336  			"Topic": "Table",
   337  			"Key":   dest.GetNlri().String(),
   338  			"Path":  withdraw,
   339  		}).Warn("No matching path for withdraw found, may be path was not installed into table")
   340  		return nil
   341  	} else {
   342  		p := dest.knownPathList[isFound]
   343  		dest.knownPathList = append(dest.knownPathList[:isFound], dest.knownPathList[isFound+1:]...)
   344  		return p
   345  	}
   346  }
   347  
   348  // Identifies which of known paths are old and removes them.
   349  //
   350  // Known paths will no longer have paths whose new version is present in
   351  // new paths.
   352  func (dest *Destination) implicitWithdraw(newPath *Path) {
   353  	found := -1
   354  	for i, path := range dest.knownPathList {
   355  		if newPath.NoImplicitWithdraw() {
   356  			continue
   357  		}
   358  		// Here we just check if source is same and not check if path
   359  		// version num. as newPaths are implicit withdrawal of old
   360  		// paths and when doing RouteRefresh (not EnhancedRouteRefresh)
   361  		// we get same paths again.
   362  		if newPath.GetSource().Equal(path.GetSource()) && newPath.GetNlri().PathIdentifier() == path.GetNlri().PathIdentifier() {
   363  			log.WithFields(log.Fields{
   364  				"Topic": "Table",
   365  				"Key":   dest.GetNlri().String(),
   366  				"Path":  path,
   367  			}).Debug("Implicit withdrawal of old path, since we have learned new path from the same peer")
   368  
   369  			found = i
   370  			newPath.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier())
   371  			break
   372  		}
   373  	}
   374  	if found != -1 {
   375  		dest.knownPathList = append(dest.knownPathList[:found], dest.knownPathList[found+1:]...)
   376  	}
   377  }
   378  
   379  func (dest *Destination) computeKnownBestPath() (*Path, BestPathReason, error) {
   380  	if SelectionOptions.DisableBestPathSelection {
   381  		log.WithFields(log.Fields{
   382  			"Topic": "Table",
   383  		}).Debug("computeKnownBestPath skipped")
   384  		return nil, BPR_DISABLED, nil
   385  	}
   386  
   387  	// If we do not have any paths to this destination, then we do not have
   388  	// new best path.
   389  	if len(dest.knownPathList) == 0 {
   390  		return nil, BPR_UNKNOWN, nil
   391  	}
   392  
   393  	log.WithFields(log.Fields{
   394  		"Topic": "Table",
   395  	}).Debugf("computeKnownBestPath knownPathList: %d", len(dest.knownPathList))
   396  
   397  	// We pick the first path as current best path. This helps in breaking
   398  	// tie between two new paths learned in one cycle for which best-path
   399  	// calculation steps lead to tie.
   400  	if len(dest.knownPathList) == 1 {
   401  		// If the first path has the invalidated next-hop, which evaluated by
   402  		// IGP, returns no path with the reason of the next-hop reachability.
   403  		if dest.knownPathList[0].IsNexthopInvalid {
   404  			return nil, BPR_REACHABLE_NEXT_HOP, nil
   405  		}
   406  		return dest.knownPathList[0], BPR_ONLY_PATH, nil
   407  	}
   408  	dest.sort()
   409  	newBest := dest.knownPathList[0]
   410  	// If the first path has the invalidated next-hop, which evaluated by IGP,
   411  	// returns no path with the reason of the next-hop reachability.
   412  	if dest.knownPathList[0].IsNexthopInvalid {
   413  		return nil, BPR_REACHABLE_NEXT_HOP, nil
   414  	}
   415  	return newBest, newBest.reason, nil
   416  }
   417  
   418  func (dst *Destination) sort() {
   419  	sort.SliceStable(dst.knownPathList, func(i, j int) bool {
   420  		//Compares given paths and returns best path.
   421  		//
   422  		//Parameters:
   423  		//	-`path1`: first path to compare
   424  		//	-`path2`: second path to compare
   425  		//
   426  		//	Best path processing will involve following steps:
   427  		//	1.  Select a path with a reachable next hop.
   428  		//	2.  Select the path with the highest weight.
   429  		//	3.  If path weights are the same, select the path with the highest
   430  		//	local preference value.
   431  		//	4.  Prefer locally originated routes (network routes, redistributed
   432  		//	routes, or aggregated routes) over received routes.
   433  		//	5.  Select the route with the shortest AS-path length.
   434  		//	6.  If all paths have the same AS-path length, select the path based
   435  		//	on origin: IGP is preferred over EGP; EGP is preferred over
   436  		//	Incomplete.
   437  		//	7.  If the origins are the same, select the path with lowest MED
   438  		//	value.
   439  		//	8.  If the paths have the same MED values, select the path learned
   440  		//	via EBGP over one learned via IBGP.
   441  		//	9.  Select the route with the lowest IGP cost to the next hop.
   442  		//	10. Select the route received from the peer with the lowest BGP
   443  		//	router ID.
   444  		//
   445  		//	Returns None if best-path among given paths cannot be computed else best
   446  		//	path.
   447  		//	Assumes paths from NC has source equal to None.
   448  		//
   449  
   450  		path1 := dst.knownPathList[i]
   451  		path2 := dst.knownPathList[j]
   452  
   453  		var better *Path
   454  		reason := BPR_UNKNOWN
   455  
   456  		// draft-uttaro-idr-bgp-persistence-02
   457  		if better == nil {
   458  			better = compareByLLGRStaleCommunity(path1, path2)
   459  			reason = BPR_NON_LLGR_STALE
   460  		}
   461  		// Follow best path calculation algorithm steps.
   462  		// compare by reachability
   463  		if better == nil {
   464  			better = compareByReachableNexthop(path1, path2)
   465  			reason = BPR_REACHABLE_NEXT_HOP
   466  		}
   467  		if better == nil {
   468  			better = compareByHighestWeight(path1, path2)
   469  			reason = BPR_HIGHEST_WEIGHT
   470  		}
   471  		if better == nil {
   472  			better = compareByLocalPref(path1, path2)
   473  			reason = BPR_LOCAL_PREF
   474  		}
   475  		if better == nil {
   476  			better = compareByLocalOrigin(path1, path2)
   477  			reason = BPR_LOCAL_ORIGIN
   478  		}
   479  		if better == nil {
   480  			better = compareByASPath(path1, path2)
   481  			reason = BPR_ASPATH
   482  		}
   483  		if better == nil {
   484  			better = compareByOrigin(path1, path2)
   485  			reason = BPR_ORIGIN
   486  		}
   487  		if better == nil {
   488  			better = compareByMED(path1, path2)
   489  			reason = BPR_MED
   490  		}
   491  		if better == nil {
   492  			better = compareByASNumber(path1, path2)
   493  			reason = BPR_ASN
   494  		}
   495  		if better == nil {
   496  			better = compareByIGPCost(path1, path2)
   497  			reason = BPR_IGP_COST
   498  		}
   499  		if better == nil {
   500  			better = compareByAge(path1, path2)
   501  			reason = BPR_OLDER
   502  		}
   503  		if better == nil {
   504  			var e error
   505  			better, e = compareByRouterID(path1, path2)
   506  			if e != nil {
   507  				log.WithFields(log.Fields{
   508  					"Topic": "Table",
   509  					"Error": e,
   510  				}).Error("Could not get best path by comparing router ID")
   511  			}
   512  			reason = BPR_ROUTER_ID
   513  		}
   514  		if better == nil {
   515  			reason = BPR_UNKNOWN
   516  			better = path1
   517  		}
   518  
   519  		better.reason = reason
   520  
   521  		return better == path1
   522  	})
   523  }
   524  
   525  type Update struct {
   526  	KnownPathList    []*Path
   527  	OldKnownPathList []*Path
   528  }
   529  
   530  func getMultiBestPath(id string, pathList []*Path) []*Path {
   531  	list := make([]*Path, 0, len(pathList))
   532  	var best *Path
   533  	for _, p := range pathList {
   534  		if !p.IsNexthopInvalid {
   535  			if best == nil {
   536  				best = p
   537  				list = append(list, p)
   538  			} else if best.Compare(p) == 0 {
   539  				list = append(list, p)
   540  			}
   541  		}
   542  	}
   543  	return list
   544  }
   545  
   546  func (u *Update) GetWithdrawnPath() []*Path {
   547  	if len(u.KnownPathList) == len(u.OldKnownPathList) {
   548  		return nil
   549  	}
   550  
   551  	l := make([]*Path, 0, len(u.OldKnownPathList))
   552  
   553  	for _, p := range u.OldKnownPathList {
   554  		y := func() bool {
   555  			for _, old := range u.KnownPathList {
   556  				if p == old {
   557  					return true
   558  				}
   559  			}
   560  			return false
   561  		}()
   562  		if !y {
   563  			l = append(l, p.Clone(true))
   564  		}
   565  	}
   566  	return l
   567  }
   568  
   569  func (u *Update) GetChanges(id string, as uint32, peerDown bool) (*Path, *Path, []*Path) {
   570  	best, old := func(id string) (*Path, *Path) {
   571  		old := getBestPath(id, as, u.OldKnownPathList)
   572  		best := getBestPath(id, as, u.KnownPathList)
   573  		if best != nil && best.Equal(old) {
   574  			// RFC4684 3.2. Intra-AS VPN Route Distribution
   575  			// When processing RT membership NLRIs received from internal iBGP
   576  			// peers, it is necessary to consider all available iBGP paths for a
   577  			// given RT prefix, for building the outbound route filter, and not just
   578  			// the best path.
   579  			if best.GetRouteFamily() == bgp.RF_RTC_UC {
   580  				return best, old
   581  			}
   582  			// For BGP Nexthop Tracking, checks if the nexthop reachability
   583  			// was changed or not.
   584  			if best.IsNexthopInvalid != old.IsNexthopInvalid {
   585  				// If the nexthop of the best path became unreachable, we need
   586  				// to withdraw that path.
   587  				if best.IsNexthopInvalid {
   588  					return best.Clone(true), old
   589  				}
   590  				return best, old
   591  			}
   592  			return nil, old
   593  		}
   594  		if best == nil {
   595  			if old == nil {
   596  				return nil, nil
   597  			}
   598  			if peerDown {
   599  				// withdraws were generated by peer
   600  				// down so paths are not in knowpath
   601  				// or adjin.
   602  				old.IsWithdraw = true
   603  				return old, old
   604  			}
   605  			return old.Clone(true), old
   606  		}
   607  		return best, old
   608  	}(id)
   609  
   610  	var multi []*Path
   611  
   612  	if id == GLOBAL_RIB_NAME && UseMultiplePaths.Enabled {
   613  		diff := func(lhs, rhs []*Path) bool {
   614  			if len(lhs) != len(rhs) {
   615  				return true
   616  			}
   617  			for idx, l := range lhs {
   618  				if !l.Equal(rhs[idx]) {
   619  					return true
   620  				}
   621  			}
   622  			return false
   623  		}
   624  		oldM := getMultiBestPath(id, u.OldKnownPathList)
   625  		newM := getMultiBestPath(id, u.KnownPathList)
   626  		if diff(oldM, newM) {
   627  			multi = newM
   628  			if len(newM) == 0 {
   629  				multi = []*Path{best}
   630  			}
   631  		}
   632  	}
   633  	return best, old, multi
   634  }
   635  
   636  func compareByLLGRStaleCommunity(path1, path2 *Path) *Path {
   637  	p1 := path1.IsLLGRStale()
   638  	p2 := path2.IsLLGRStale()
   639  	if p1 == p2 {
   640  		return nil
   641  	} else if p1 {
   642  		return path2
   643  	}
   644  	return path1
   645  }
   646  
   647  func compareByReachableNexthop(path1, path2 *Path) *Path {
   648  	//	Compares given paths and selects best path based on reachable next-hop.
   649  	//
   650  	//	If no path matches this criteria, return nil.
   651  	//	For BGP Nexthop Tracking, evaluates next-hop is validated by IGP.
   652  	log.WithFields(log.Fields{
   653  		"Topic": "Table",
   654  	}).Debugf("enter compareByReachableNexthop -- path1: %s, path2: %s", path1, path2)
   655  
   656  	if path1.IsNexthopInvalid && !path2.IsNexthopInvalid {
   657  		return path2
   658  	} else if !path1.IsNexthopInvalid && path2.IsNexthopInvalid {
   659  		return path1
   660  	}
   661  
   662  	return nil
   663  }
   664  
   665  func compareByHighestWeight(path1, path2 *Path) *Path {
   666  	//	Selects a path with highest weight.
   667  	//
   668  	//	Weight is BGPS specific parameter. It is local to the router on which it
   669  	//	is configured.
   670  	//	Return:
   671  	//	nil if best path among given paths cannot be decided, else best path.
   672  	log.WithFields(log.Fields{
   673  		"Topic": "Table",
   674  	}).Debugf("enter compareByHighestWeight -- path1: %s, path2: %s", path1, path2)
   675  	return nil
   676  }
   677  
   678  func compareByLocalPref(path1, path2 *Path) *Path {
   679  	//	Selects a path with highest local-preference.
   680  	//
   681  	//	Unlike the weight attribute, which is only relevant to the local
   682  	//	router, local preference is an attribute that routers exchange in the
   683  	//	same AS. Highest local-pref is preferred. If we cannot decide,
   684  	//	we return None.
   685  	//
   686  	//	# Default local-pref values is 100
   687  	log.WithFields(log.Fields{
   688  		"Topic": "Table",
   689  	}).Debug("enter compareByLocalPref")
   690  	localPref1, _ := path1.GetLocalPref()
   691  	localPref2, _ := path2.GetLocalPref()
   692  	// Highest local-preference value is preferred.
   693  	if localPref1 > localPref2 {
   694  		return path1
   695  	} else if localPref1 < localPref2 {
   696  		return path2
   697  	} else {
   698  		return nil
   699  	}
   700  }
   701  
   702  func compareByLocalOrigin(path1, path2 *Path) *Path {
   703  
   704  	// Select locally originating path as best path.
   705  	// Locally originating routes are network routes, redistributed routes,
   706  	// or aggregated routes.
   707  	// Returns None if given paths have same source.
   708  	//
   709  	// If both paths are from same sources we cannot compare them here.
   710  	log.WithFields(log.Fields{
   711  		"Topic": "Table",
   712  	}).Debug("enter compareByLocalOrigin")
   713  	if path1.GetSource().Equal(path2.GetSource()) {
   714  		return nil
   715  	}
   716  
   717  	// Here we consider prefix from NC as locally originating static route.
   718  	// Hence it is preferred.
   719  	if path1.IsLocal() {
   720  		return path1
   721  	}
   722  
   723  	if path2.IsLocal() {
   724  		return path2
   725  	}
   726  	return nil
   727  }
   728  
   729  func compareByASPath(path1, path2 *Path) *Path {
   730  	// Calculated the best-paths by comparing as-path lengths.
   731  	//
   732  	// Shortest as-path length is preferred. If both path have same lengths,
   733  	// we return None.
   734  	if SelectionOptions.IgnoreAsPathLength {
   735  		log.WithFields(log.Fields{
   736  			"Topic": "Table",
   737  		}).Debug("compareByASPath -- skip")
   738  		return nil
   739  	}
   740  	log.WithFields(log.Fields{
   741  		"Topic": "Table",
   742  	}).Debug("enter compareByASPath")
   743  	attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
   744  	attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
   745  
   746  	// With addpath support, we could compare paths from API don't
   747  	// AS_PATH. No need to warn here.
   748  	if !path1.IsLocal() && !path2.IsLocal() && (attribute1 == nil || attribute2 == nil) {
   749  		log.WithFields(log.Fields{
   750  			"Topic":   "Table",
   751  			"Key":     "compareByASPath",
   752  			"ASPath1": attribute1,
   753  			"ASPath2": attribute2,
   754  		}).Warn("can't compare ASPath because it's not present")
   755  	}
   756  
   757  	l1 := path1.GetAsPathLen()
   758  	l2 := path2.GetAsPathLen()
   759  
   760  	log.WithFields(log.Fields{
   761  		"Topic": "Table",
   762  	}).Debugf("compareByASPath -- l1: %d, l2: %d", l1, l2)
   763  	if l1 > l2 {
   764  		return path2
   765  	} else if l1 < l2 {
   766  		return path1
   767  	} else {
   768  		return nil
   769  	}
   770  }
   771  
   772  func compareByOrigin(path1, path2 *Path) *Path {
   773  	//	Select the best path based on origin attribute.
   774  	//
   775  	//	IGP is preferred over EGP; EGP is preferred over Incomplete.
   776  	//	If both paths have same origin, we return None.
   777  	log.WithFields(log.Fields{
   778  		"Topic": "Table",
   779  	}).Debug("enter compareByOrigin")
   780  	attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
   781  	attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
   782  
   783  	if attribute1 == nil || attribute2 == nil {
   784  		log.WithFields(log.Fields{
   785  			"Topic":   "Table",
   786  			"Key":     "compareByOrigin",
   787  			"Origin1": attribute1,
   788  			"Origin2": attribute2,
   789  		}).Error("can't compare origin because it's not present")
   790  		return nil
   791  	}
   792  
   793  	origin1 := attribute1.(*bgp.PathAttributeOrigin).Value
   794  	origin2 := attribute2.(*bgp.PathAttributeOrigin).Value
   795  	log.WithFields(log.Fields{
   796  		"Topic": "Table",
   797  	}).Debugf("compareByOrigin -- origin1: %d, origin2: %d", origin1, origin2)
   798  
   799  	// If both paths have same origins
   800  	if origin1 == origin2 {
   801  		return nil
   802  	} else if origin1 < origin2 {
   803  		return path1
   804  	} else {
   805  		return path2
   806  	}
   807  }
   808  
   809  func compareByMED(path1, path2 *Path) *Path {
   810  	//	Select the path based with lowest MED value.
   811  	//
   812  	//	If both paths have same MED, return None.
   813  	//	By default, a route that arrives with no MED value is treated as if it
   814  	//	had a MED of 0, the most preferred value.
   815  	//	RFC says lower MED is preferred over higher MED value.
   816  	//  compare MED among not only same AS path but also all path,
   817  	//  like bgp always-compare-med
   818  
   819  	isInternal := func() bool { return path1.GetAsPathLen() == 0 && path2.GetAsPathLen() == 0 }()
   820  
   821  	isSameAS := func() bool {
   822  		firstAS := func(path *Path) uint32 {
   823  			if asPath := path.GetAsPath(); asPath != nil {
   824  				for _, v := range asPath.Value {
   825  					segType := v.GetType()
   826  					asList := v.GetAS()
   827  					if len(asList) == 0 {
   828  						continue
   829  					}
   830  					switch segType {
   831  					case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
   832  						continue
   833  					}
   834  					return asList[0]
   835  				}
   836  			}
   837  			return 0
   838  		}
   839  		return firstAS(path1) != 0 && firstAS(path1) == firstAS(path2)
   840  	}()
   841  
   842  	if SelectionOptions.AlwaysCompareMed || isInternal || isSameAS {
   843  		log.WithFields(log.Fields{
   844  			"Topic": "Table",
   845  		}).Debug("enter compareByMED")
   846  		getMed := func(path *Path) uint32 {
   847  			attribute := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
   848  			if attribute == nil {
   849  				return 0
   850  			}
   851  			med := attribute.(*bgp.PathAttributeMultiExitDisc).Value
   852  			return med
   853  		}
   854  
   855  		med1 := getMed(path1)
   856  		med2 := getMed(path2)
   857  		log.WithFields(log.Fields{
   858  			"Topic": "Table",
   859  		}).Debugf("compareByMED -- med1: %d, med2: %d", med1, med2)
   860  		if med1 == med2 {
   861  			return nil
   862  		} else if med1 < med2 {
   863  			return path1
   864  		}
   865  		return path2
   866  	} else {
   867  		log.WithFields(log.Fields{
   868  			"Topic": "Table",
   869  		}).Debugf("skip compareByMED %v %v %v", SelectionOptions.AlwaysCompareMed, isInternal, isSameAS)
   870  		return nil
   871  	}
   872  }
   873  
   874  func compareByASNumber(path1, path2 *Path) *Path {
   875  
   876  	//Select the path based on source (iBGP/eBGP) peer.
   877  	//
   878  	//eBGP path is preferred over iBGP. If both paths are from same kind of
   879  	//peers, return None.
   880  	log.WithFields(log.Fields{
   881  		"Topic": "Table",
   882  	}).Debug("enter compareByASNumber")
   883  
   884  	log.WithFields(log.Fields{
   885  		"Topic": "Table",
   886  	}).Debugf("compareByASNumber -- p1Asn: %d, p2Asn: %d", path1.GetSource().AS, path2.GetSource().AS)
   887  	// Path from confederation member should be treated as internal (IBGP learned) path.
   888  	isIBGP1 := path1.GetSource().Confederation || path1.IsIBGP()
   889  	isIBGP2 := path2.GetSource().Confederation || path2.IsIBGP()
   890  	// If one path is from ibgp peer and another is from ebgp peer, take the ebgp path.
   891  	if isIBGP1 != isIBGP2 {
   892  		if isIBGP1 {
   893  			return path2
   894  		}
   895  		return path1
   896  	}
   897  
   898  	// If both paths are from ebgp or ibpg peers, we cannot decide.
   899  	return nil
   900  }
   901  
   902  func compareByIGPCost(path1, path2 *Path) *Path {
   903  	//	Select the route with the lowest IGP cost to the next hop.
   904  	//
   905  	//	Return None if igp cost is same.
   906  	// Currently BGPS has no concept of IGP and IGP cost.
   907  	log.WithFields(log.Fields{
   908  		"Topic": "Table",
   909  	}).Debugf("enter compareByIGPCost -- path1: %v, path2: %v", path1, path2)
   910  	return nil
   911  }
   912  
   913  func compareByRouterID(path1, path2 *Path) (*Path, error) {
   914  	//	Select the route received from the peer with the lowest BGP router ID.
   915  	//
   916  	//	If both paths are eBGP paths, then we do not do any tie breaking, i.e we do
   917  	//	not pick best-path based on this criteria.
   918  	//	RFC: http://tools.ietf.org/html/rfc5004
   919  	//	We pick best path between two iBGP paths as usual.
   920  	log.WithFields(log.Fields{
   921  		"Topic": "Table",
   922  	}).Debug("enter compareByRouterID")
   923  
   924  	// If both paths are from NC we have same router Id, hence cannot compare.
   925  	if path1.IsLocal() && path2.IsLocal() {
   926  		return nil, nil
   927  	}
   928  
   929  	// If both paths are from eBGP peers, then according to RFC we need
   930  	// not tie break using router id.
   931  	if !SelectionOptions.ExternalCompareRouterId && !path1.IsIBGP() && !path2.IsIBGP() {
   932  		return nil, nil
   933  	}
   934  
   935  	if !SelectionOptions.ExternalCompareRouterId && path1.IsIBGP() != path2.IsIBGP() {
   936  		return nil, fmt.Errorf("This method does not support comparing ebgp with ibgp path")
   937  	}
   938  
   939  	// At least one path is not coming from NC, so we get local bgp id.
   940  	id1 := binary.BigEndian.Uint32(path1.GetSource().ID)
   941  	id2 := binary.BigEndian.Uint32(path2.GetSource().ID)
   942  
   943  	// If both router ids are same/equal we cannot decide.
   944  	// This case is possible since router ids are arbitrary.
   945  	if id1 == id2 {
   946  		return nil, nil
   947  	} else if id1 < id2 {
   948  		return path1, nil
   949  	} else {
   950  		return path2, nil
   951  	}
   952  }
   953  
   954  func compareByAge(path1, path2 *Path) *Path {
   955  	if !path1.IsIBGP() && !path2.IsIBGP() && !SelectionOptions.ExternalCompareRouterId {
   956  		age1 := path1.GetTimestamp().UnixNano()
   957  		age2 := path2.GetTimestamp().UnixNano()
   958  		if age1 == age2 {
   959  			return nil
   960  		} else if age1 < age2 {
   961  			return path1
   962  		}
   963  		return path2
   964  	}
   965  	return nil
   966  }
   967  
   968  func (dest *Destination) String() string {
   969  	return fmt.Sprintf("Destination NLRI: %s", dest.nlri.String())
   970  }
   971  
   972  type DestinationSelectOption struct {
   973  	ID        string
   974  	AS        uint32
   975  	VRF       *Vrf
   976  	adj       bool
   977  	Best      bool
   978  	MultiPath bool
   979  }
   980  
   981  func (d *Destination) MarshalJSON() ([]byte, error) {
   982  	return json.Marshal(d.GetAllKnownPathList())
   983  }
   984  
   985  func (d *Destination) Select(option ...DestinationSelectOption) *Destination {
   986  	id := GLOBAL_RIB_NAME
   987  	var vrf *Vrf
   988  	adj := false
   989  	best := false
   990  	mp := false
   991  	as := uint32(0)
   992  	for _, o := range option {
   993  		if o.ID != "" {
   994  			id = o.ID
   995  		}
   996  		if o.VRF != nil {
   997  			vrf = o.VRF
   998  		}
   999  		adj = o.adj
  1000  		best = o.Best
  1001  		mp = o.MultiPath
  1002  		as = o.AS
  1003  	}
  1004  	var paths []*Path
  1005  	if adj {
  1006  		paths = make([]*Path, len(d.knownPathList))
  1007  		copy(paths, d.knownPathList)
  1008  	} else {
  1009  		paths = d.GetKnownPathList(id, as)
  1010  		if vrf != nil {
  1011  			ps := make([]*Path, 0, len(paths))
  1012  			for _, p := range paths {
  1013  				if CanImportToVrf(vrf, p) {
  1014  					ps = append(ps, p.ToLocal())
  1015  				}
  1016  			}
  1017  			paths = ps
  1018  		}
  1019  		if len(paths) == 0 {
  1020  			return nil
  1021  		}
  1022  		if best {
  1023  			if !mp {
  1024  				paths = []*Path{paths[0]}
  1025  			} else {
  1026  				ps := make([]*Path, 0, len(paths))
  1027  				var best *Path
  1028  				for _, p := range paths {
  1029  					if best == nil {
  1030  						best = p
  1031  						ps = append(ps, p)
  1032  					} else if best.Compare(p) == 0 {
  1033  						ps = append(ps, p)
  1034  					}
  1035  				}
  1036  				paths = ps
  1037  			}
  1038  		}
  1039  	}
  1040  	return NewDestination(d.nlri, 0, paths...)
  1041  }