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 }