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