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 }