github.com/osrg/gobgp@v2.0.0+incompatible/cmd/gobgp/neighbor.go (about) 1 // Copyright (C) 2015 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 main 17 18 import ( 19 "bytes" 20 "encoding/json" 21 "fmt" 22 "io" 23 "net" 24 "sort" 25 "strconv" 26 "strings" 27 "time" 28 29 "github.com/golang/protobuf/ptypes" 30 "github.com/spf13/cobra" 31 32 api "github.com/osrg/gobgp/api" 33 "github.com/osrg/gobgp/internal/pkg/apiutil" 34 "github.com/osrg/gobgp/internal/pkg/config" 35 "github.com/osrg/gobgp/pkg/packet/bgp" 36 ) 37 38 // used in showRoute() to determine the width of each column 39 var ( 40 columnWidthPrefix = 20 41 columnWidthNextHop = 20 42 columnWidthAsPath = 20 43 columnWidthLabel = 10 44 ) 45 46 func updateColumnWidth(nlri, nexthop, aspath, label string) { 47 if prefixLen := len(nlri); columnWidthPrefix < prefixLen { 48 columnWidthPrefix = prefixLen 49 } 50 if columnWidthNextHop < len(nexthop) { 51 columnWidthNextHop = len(nexthop) 52 } 53 if columnWidthAsPath < len(aspath) { 54 columnWidthAsPath = len(aspath) 55 } 56 if columnWidthLabel < len(label) { 57 columnWidthLabel = len(label) 58 } 59 } 60 61 func getNeighbors(vrf string) ([]*api.Peer, error) { 62 adv := true 63 if vrf != "" { 64 adv = false 65 } else if t := neighborsOpts.Transport; t != "" { 66 switch t { 67 case "ipv4", "ipv6": 68 adv = false 69 default: 70 return nil, fmt.Errorf("invalid transport: %s", t) 71 } 72 } 73 stream, err := client.ListPeer(ctx, &api.ListPeerRequest{ 74 EnableAdvertised: adv, 75 }) 76 77 l := make([]*api.Peer, 0, 1024) 78 for { 79 r, err := stream.Recv() 80 if err == io.EOF { 81 break 82 } else if err != nil { 83 return nil, err 84 } 85 l = append(l, r.Peer) 86 } 87 return l, err 88 } 89 90 func getASN(p *api.Peer) string { 91 asn := "*" 92 if p.State.PeerAs > 0 { 93 asn = fmt.Sprint(p.State.PeerAs) 94 } 95 return asn 96 } 97 98 func counter(p *api.Peer) (uint64, uint64, uint64, error) { 99 accepted := uint64(0) 100 received := uint64(0) 101 advertised := uint64(0) 102 for _, afisafi := range p.AfiSafis { 103 if subOpts.AddressFamily != "" { 104 f, e := checkAddressFamily(&api.Family{}) 105 if e != nil { 106 return 0, 0, 0, e 107 } 108 if f.Afi != afisafi.State.Family.Afi || f.Safi != afisafi.State.Family.Safi { 109 continue 110 } 111 } 112 accepted += afisafi.State.Accepted 113 received += afisafi.State.Received 114 advertised += afisafi.State.Advertised 115 } 116 return received, accepted, advertised, nil 117 } 118 119 func showNeighbors(vrf string) error { 120 m, err := getNeighbors(vrf) 121 if err != nil { 122 return err 123 } 124 if globalOpts.Json { 125 j, _ := json.Marshal(m) 126 fmt.Println(string(j)) 127 return nil 128 } 129 130 if globalOpts.Quiet { 131 for _, p := range m { 132 fmt.Println(p.State.NeighborAddress) 133 } 134 return nil 135 } 136 maxaddrlen := 0 137 maxaslen := 2 138 maxtimelen := len("Up/Down") 139 timedelta := []string{} 140 141 sort.Slice(m, func(i, j int) bool { 142 p1 := m[i].Conf.NeighborAddress 143 p2 := m[j].Conf.NeighborAddress 144 p1Isv4 := !strings.Contains(p1, ":") 145 p2Isv4 := !strings.Contains(p2, ":") 146 if p1Isv4 != p2Isv4 { 147 return p1Isv4 148 } 149 addrlen := 128 150 if p1Isv4 { 151 addrlen = 32 152 } 153 strings := sort.StringSlice{cidr2prefix(fmt.Sprintf("%s/%d", p1, addrlen)), 154 cidr2prefix(fmt.Sprintf("%s/%d", p2, addrlen))} 155 return strings.Less(0, 1) 156 }) 157 158 for _, n := range m { 159 if i := len(n.Conf.NeighborInterface); i > maxaddrlen { 160 maxaddrlen = i 161 } else if j := len(n.State.NeighborAddress); j > maxaddrlen { 162 maxaddrlen = j 163 } 164 if l := len(getASN(n)); l > maxaslen { 165 maxaslen = l 166 } 167 timeStr := "never" 168 if n.Timers.State.Uptime != nil { 169 t, _ := ptypes.Timestamp(n.Timers.State.Downtime) 170 if n.State.SessionState == api.PeerState_ESTABLISHED { 171 t, _ = ptypes.Timestamp(n.Timers.State.Uptime) 172 } 173 timeStr = formatTimedelta(t) 174 } 175 if len(timeStr) > maxtimelen { 176 maxtimelen = len(timeStr) 177 } 178 timedelta = append(timedelta, timeStr) 179 } 180 181 format := "%-" + fmt.Sprint(maxaddrlen) + "s" + " %" + fmt.Sprint(maxaslen) + "s" + " %" + fmt.Sprint(maxtimelen) + "s" 182 format += " %-11s |%9s %9s\n" 183 fmt.Printf(format, "Peer", "AS", "Up/Down", "State", "#Received", "Accepted") 184 formatFsm := func(admin api.PeerState_AdminState, fsm api.PeerState_SessionState) string { 185 switch admin { 186 case api.PeerState_DOWN: 187 return "Idle(Admin)" 188 case api.PeerState_PFX_CT: 189 return "Idle(PfxCt)" 190 } 191 192 switch fsm { 193 case api.PeerState_UNKNOWN: 194 // should never happen 195 return "Unknown" 196 case api.PeerState_IDLE: 197 return "Idle" 198 case api.PeerState_CONNECT: 199 return "Connect" 200 case api.PeerState_ACTIVE: 201 return "Active" 202 case api.PeerState_OPENSENT: 203 return "Sent" 204 case api.PeerState_OPENCONFIRM: 205 return "Confirm" 206 case api.PeerState_ESTABLISHED: 207 return "Establ" 208 default: 209 return string(fsm) 210 } 211 } 212 213 for i, n := range m { 214 neigh := n.State.NeighborAddress 215 if n.Conf.NeighborInterface != "" { 216 neigh = n.Conf.NeighborInterface 217 } 218 received, accepted, _, _ := counter(n) 219 fmt.Printf(format, neigh, getASN(n), timedelta[i], formatFsm(n.State.AdminState, n.State.SessionState), fmt.Sprint(received), fmt.Sprint(accepted)) 220 } 221 222 return nil 223 } 224 225 func showNeighbor(args []string) error { 226 stream, err := client.ListPeer(ctx, &api.ListPeerRequest{ 227 Address: args[0], 228 EnableAdvertised: true, 229 }) 230 if err != nil { 231 return err 232 } 233 r, err := stream.Recv() 234 if err != nil && err != io.EOF { 235 return err 236 } 237 p := r.Peer 238 239 if globalOpts.Json { 240 j, _ := json.Marshal(p) 241 fmt.Println(string(j)) 242 return nil 243 } 244 245 fmt.Printf("BGP neighbor is %s, remote AS %s", p.State.NeighborAddress, getASN(p)) 246 247 if p.RouteReflector.RouteReflectorClient { 248 fmt.Printf(", route-reflector-client\n") 249 } else if p.RouteServer.RouteServerClient { 250 fmt.Printf(", route-server-client\n") 251 } else { 252 fmt.Printf("\n") 253 } 254 255 id := "unknown" 256 if p.State != nil && p.State.RouterId != "" { 257 id = p.State.RouterId 258 } 259 fmt.Printf(" BGP version 4, remote router ID %s\n", id) 260 fmt.Printf(" BGP state = %s", p.State.SessionState) 261 if p.Timers.State.Uptime != nil { 262 t, _ := ptypes.Timestamp(p.Timers.State.Uptime) 263 fmt.Printf(", up for %s\n", formatTimedelta(t)) 264 } else { 265 fmt.Print("\n") 266 } 267 fmt.Printf(" BGP OutQ = %d, Flops = %d\n", p.State.Queues.Output, p.State.Flops) 268 fmt.Printf(" Hold time is %d, keepalive interval is %d seconds\n", int(p.Timers.State.NegotiatedHoldTime), int(p.Timers.State.KeepaliveInterval)) 269 fmt.Printf(" Configured hold time is %d, keepalive interval is %d seconds\n", int(p.Timers.Config.HoldTime), int(p.Timers.Config.KeepaliveInterval)) 270 271 elems := make([]string, 0, 3) 272 if as := p.Conf.AllowOwnAs; as > 0 { 273 elems = append(elems, fmt.Sprintf("Allow Own AS: %d", as)) 274 } 275 switch p.Conf.RemovePrivateAs { 276 case api.PeerConf_ALL: 277 elems = append(elems, "Remove private AS: all") 278 case api.PeerConf_REPLACE: 279 elems = append(elems, "Remove private AS: replace") 280 } 281 if p.Conf.ReplacePeerAs { 282 elems = append(elems, "Replace peer AS: enabled") 283 } 284 285 fmt.Printf(" %s\n", strings.Join(elems, ", ")) 286 287 fmt.Printf(" Neighbor capabilities:\n") 288 caps := []bgp.ParameterCapabilityInterface{} 289 lookup := func(val bgp.ParameterCapabilityInterface, l []bgp.ParameterCapabilityInterface) bgp.ParameterCapabilityInterface { 290 for _, v := range l { 291 if v.Code() == val.Code() { 292 if v.Code() == bgp.BGP_CAP_MULTIPROTOCOL { 293 lhs := v.(*bgp.CapMultiProtocol).CapValue 294 rhs := val.(*bgp.CapMultiProtocol).CapValue 295 if lhs == rhs { 296 return v 297 } 298 continue 299 } 300 return v 301 } 302 } 303 return nil 304 } 305 lcaps, _ := apiutil.UnmarshalCapabilities(p.State.LocalCap) 306 caps = append(caps, lcaps...) 307 308 rcaps, _ := apiutil.UnmarshalCapabilities(p.State.RemoteCap) 309 for _, c := range rcaps { 310 if lookup(c, caps) == nil { 311 caps = append(caps, c) 312 } 313 } 314 315 sort.Slice(caps, func(i, j int) bool { 316 return caps[i].Code() < caps[j].Code() 317 }) 318 319 firstMp := true 320 321 for _, c := range caps { 322 support := "" 323 if m := lookup(c, lcaps); m != nil { 324 support += "advertised" 325 } 326 if lookup(c, rcaps) != nil { 327 if len(support) != 0 { 328 support += " and " 329 } 330 support += "received" 331 } 332 333 switch c.Code() { 334 case bgp.BGP_CAP_MULTIPROTOCOL: 335 if firstMp { 336 fmt.Printf(" %s:\n", c.Code()) 337 firstMp = false 338 } 339 m := c.(*bgp.CapMultiProtocol).CapValue 340 fmt.Printf(" %s:\t%s\n", m, support) 341 case bgp.BGP_CAP_GRACEFUL_RESTART: 342 fmt.Printf(" %s:\t%s\n", c.Code(), support) 343 grStr := func(g *bgp.CapGracefulRestart) string { 344 str := "" 345 if len(g.Tuples) > 0 { 346 str += fmt.Sprintf("restart time %d sec", g.Time) 347 } 348 if g.Flags&0x08 > 0 { 349 if len(str) > 0 { 350 str += ", " 351 } 352 str += "restart flag set" 353 } 354 if g.Flags&0x04 > 0 { 355 if len(str) > 0 { 356 str += ", " 357 } 358 str += "notification flag set" 359 } 360 361 if len(str) > 0 { 362 str += "\n" 363 } 364 for _, t := range g.Tuples { 365 str += fmt.Sprintf(" %s", bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI)) 366 if t.Flags == 0x80 { 367 str += ", forward flag set" 368 } 369 str += "\n" 370 } 371 return str 372 } 373 if m := lookup(c, lcaps); m != nil { 374 g := m.(*bgp.CapGracefulRestart) 375 if s := grStr(g); len(s) > 0 { 376 fmt.Printf(" Local: %s", s) 377 } 378 } 379 if m := lookup(c, rcaps); m != nil { 380 g := m.(*bgp.CapGracefulRestart) 381 if s := grStr(g); len(s) > 0 { 382 fmt.Printf(" Remote: %s", s) 383 } 384 } 385 case bgp.BGP_CAP_LONG_LIVED_GRACEFUL_RESTART: 386 fmt.Printf(" %s:\t%s\n", c.Code(), support) 387 grStr := func(g *bgp.CapLongLivedGracefulRestart) string { 388 var str string 389 for _, t := range g.Tuples { 390 str += fmt.Sprintf(" %s, restart time %d sec", bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI), t.RestartTime) 391 if t.Flags == 0x80 { 392 str += ", forward flag set" 393 } 394 str += "\n" 395 } 396 return str 397 } 398 if m := lookup(c, lcaps); m != nil { 399 g := m.(*bgp.CapLongLivedGracefulRestart) 400 if s := grStr(g); len(s) > 0 { 401 fmt.Printf(" Local:\n%s", s) 402 } 403 } 404 if m := lookup(c, rcaps); m != nil { 405 g := m.(*bgp.CapLongLivedGracefulRestart) 406 if s := grStr(g); len(s) > 0 { 407 fmt.Printf(" Remote:\n%s", s) 408 } 409 } 410 case bgp.BGP_CAP_EXTENDED_NEXTHOP: 411 fmt.Printf(" %s:\t%s\n", c.Code(), support) 412 exnhStr := func(e *bgp.CapExtendedNexthop) string { 413 lines := make([]string, 0, len(e.Tuples)) 414 for _, t := range e.Tuples { 415 var nhafi string 416 switch int(t.NexthopAFI) { 417 case bgp.AFI_IP: 418 nhafi = "ipv4" 419 case bgp.AFI_IP6: 420 nhafi = "ipv6" 421 default: 422 nhafi = fmt.Sprintf("%d", t.NexthopAFI) 423 } 424 line := fmt.Sprintf("nlri: %s, nexthop: %s", bgp.AfiSafiToRouteFamily(t.NLRIAFI, uint8(t.NLRISAFI)), nhafi) 425 lines = append(lines, line) 426 } 427 return strings.Join(lines, "\n") 428 } 429 if m := lookup(c, lcaps); m != nil { 430 e := m.(*bgp.CapExtendedNexthop) 431 if s := exnhStr(e); len(s) > 0 { 432 fmt.Printf(" Local: %s\n", s) 433 } 434 } 435 if m := lookup(c, rcaps); m != nil { 436 e := m.(*bgp.CapExtendedNexthop) 437 if s := exnhStr(e); len(s) > 0 { 438 fmt.Printf(" Remote: %s\n", s) 439 } 440 } 441 case bgp.BGP_CAP_ADD_PATH: 442 fmt.Printf(" %s:\t%s\n", c.Code(), support) 443 if m := lookup(c, lcaps); m != nil { 444 fmt.Println(" Local:") 445 for _, item := range m.(*bgp.CapAddPath).Tuples { 446 fmt.Printf(" %s:\t%s\n", item.RouteFamily, item.Mode) 447 } 448 } 449 if m := lookup(c, rcaps); m != nil { 450 fmt.Println(" Remote:") 451 for _, item := range m.(*bgp.CapAddPath).Tuples { 452 fmt.Printf(" %s:\t%s\n", item.RouteFamily, item.Mode) 453 } 454 } 455 default: 456 fmt.Printf(" %s:\t%s\n", c.Code(), support) 457 } 458 } 459 received, accepted, advertised, e := counter(p) 460 if e != nil { 461 return e 462 } 463 fmt.Print(" Message statistics:\n") 464 fmt.Print(" Sent Rcvd\n") 465 fmt.Printf(" Opens: %10d %10d\n", p.State.Messages.Sent.Open, p.State.Messages.Received.Open) 466 fmt.Printf(" Notifications: %10d %10d\n", p.State.Messages.Sent.Notification, p.State.Messages.Received.Notification) 467 fmt.Printf(" Updates: %10d %10d\n", p.State.Messages.Sent.Update, p.State.Messages.Received.Update) 468 fmt.Printf(" Keepalives: %10d %10d\n", p.State.Messages.Sent.Keepalive, p.State.Messages.Received.Keepalive) 469 fmt.Printf(" Route Refresh: %10d %10d\n", p.State.Messages.Sent.Refresh, p.State.Messages.Received.Refresh) 470 fmt.Printf(" Discarded: %10d %10d\n", p.State.Messages.Sent.Discarded, p.State.Messages.Received.Discarded) 471 fmt.Printf(" Total: %10d %10d\n", p.State.Messages.Sent.Total, p.State.Messages.Received.Total) 472 fmt.Print(" Route statistics:\n") 473 fmt.Printf(" Advertised: %10d\n", advertised) 474 fmt.Printf(" Received: %10d\n", received) 475 fmt.Printf(" Accepted: %10d\n", accepted) 476 first := true 477 for _, a := range p.AfiSafis { 478 limit := a.PrefixLimits 479 if limit != nil && limit.MaxPrefixes > 0 { 480 if first { 481 fmt.Println(" Prefix Limits:") 482 first = false 483 } 484 rf := apiutil.ToRouteFamily(limit.Family) 485 fmt.Printf(" %s:\tMaximum prefixes allowed %d", bgp.AddressFamilyNameMap[rf], limit.MaxPrefixes) 486 if limit.ShutdownThresholdPct > 0 { 487 fmt.Printf(", Threshold for warning message %d%%\n", limit.ShutdownThresholdPct) 488 } else { 489 fmt.Printf("\n") 490 } 491 } 492 } 493 return nil 494 } 495 496 func getPathSymbolString(p *api.Path, idx int, showBest bool) string { 497 symbols := "" 498 if p.Stale { 499 symbols += "S" 500 } 501 if v := p.GetValidation(); v != nil { 502 switch v.State { 503 case api.Validation_STATE_NOT_FOUND: 504 symbols += "N" 505 case api.Validation_STATE_VALID: 506 symbols += "V" 507 case api.Validation_STATE_INVALID: 508 symbols += "I" 509 } 510 511 } 512 if showBest { 513 if idx == 0 && !p.IsNexthopInvalid { 514 symbols += "*>" 515 } else { 516 symbols += "* " 517 } 518 } 519 return symbols 520 } 521 522 func getPathAttributeString(nlri bgp.AddrPrefixInterface, attrs []bgp.PathAttributeInterface) string { 523 s := make([]string, 0) 524 for _, a := range attrs { 525 switch a.GetType() { 526 case bgp.BGP_ATTR_TYPE_NEXT_HOP, bgp.BGP_ATTR_TYPE_MP_REACH_NLRI, bgp.BGP_ATTR_TYPE_AS_PATH, bgp.BGP_ATTR_TYPE_AS4_PATH: 527 continue 528 default: 529 s = append(s, a.String()) 530 } 531 } 532 switch n := nlri.(type) { 533 case *bgp.EVPNNLRI: 534 // We print non route key fields like path attributes. 535 switch route := n.RouteTypeData.(type) { 536 case *bgp.EVPNMacIPAdvertisementRoute: 537 s = append(s, fmt.Sprintf("[ESI: %s]", route.ESI.String())) 538 case *bgp.EVPNIPPrefixRoute: 539 s = append(s, fmt.Sprintf("[ESI: %s]", route.ESI.String())) 540 if route.GWIPAddress != nil { 541 s = append(s, fmt.Sprintf("[GW: %s]", route.GWIPAddress.String())) 542 } 543 } 544 } 545 return fmt.Sprint(s) 546 } 547 548 func makeShowRouteArgs(p *api.Path, idx int, now time.Time, showAge, showBest, showLabel bool, showIdentifier bgp.BGPAddPathMode) []interface{} { 549 nlri, _ := apiutil.GetNativeNlri(p) 550 551 // Path Symbols (e.g. "*>") 552 args := []interface{}{getPathSymbolString(p, idx, showBest)} 553 554 // Path Identifier 555 switch showIdentifier { 556 case bgp.BGP_ADD_PATH_RECEIVE: 557 args = append(args, fmt.Sprint(p.GetIdentifier())) 558 case bgp.BGP_ADD_PATH_SEND: 559 args = append(args, fmt.Sprint(p.GetLocalIdentifier())) 560 } 561 562 // NLRI 563 args = append(args, nlri) 564 565 // Label 566 label := "" 567 if showLabel { 568 label = bgp.LabelString(nlri) 569 args = append(args, label) 570 } 571 572 attrs, _ := apiutil.GetNativePathAttributes(p) 573 // Next Hop 574 nexthop := "fictitious" 575 if n := getNextHopFromPathAttributes(attrs); n != nil { 576 nexthop = n.String() 577 } 578 args = append(args, nexthop) 579 580 // AS_PATH 581 aspathstr := func() string { 582 for _, attr := range attrs { 583 switch a := attr.(type) { 584 case *bgp.PathAttributeAsPath: 585 return bgp.AsPathString(a) 586 } 587 } 588 return "" 589 }() 590 args = append(args, aspathstr) 591 592 // Age 593 if showAge { 594 t, _ := ptypes.Timestamp(p.Age) 595 args = append(args, formatTimedelta(t)) 596 } 597 598 // Path Attributes 599 pattrstr := getPathAttributeString(nlri, attrs) 600 args = append(args, pattrstr) 601 602 updateColumnWidth(nlri.String(), nexthop, aspathstr, label) 603 604 return args 605 } 606 607 func showRoute(dsts []*api.Destination, showAge, showBest, showLabel bool, showIdentifier bgp.BGPAddPathMode) { 608 pathStrs := make([][]interface{}, 0, len(dsts)) 609 now := time.Now() 610 for _, dst := range dsts { 611 for idx, p := range dst.Paths { 612 pathStrs = append(pathStrs, makeShowRouteArgs(p, idx, now, showAge, showBest, showLabel, showIdentifier)) 613 } 614 } 615 616 headers := make([]interface{}, 0) 617 var format string 618 headers = append(headers, "") // Symbols 619 format = fmt.Sprintf("%%-3s") 620 if showIdentifier != bgp.BGP_ADD_PATH_NONE { 621 headers = append(headers, "ID") 622 format += "%-3s " 623 } 624 headers = append(headers, "Network") 625 format += fmt.Sprintf("%%-%ds ", columnWidthPrefix) 626 if showLabel { 627 headers = append(headers, "Labels") 628 format += fmt.Sprintf("%%-%ds ", columnWidthLabel) 629 } 630 headers = append(headers, "Next Hop", "AS_PATH") 631 format += fmt.Sprintf("%%-%ds %%-%ds ", columnWidthNextHop, columnWidthAsPath) 632 if showAge { 633 headers = append(headers, "Age") 634 format += "%-10s " 635 } 636 headers = append(headers, "Attrs") 637 format += "%-s\n" 638 639 fmt.Printf(format, headers...) 640 for _, pathStr := range pathStrs { 641 fmt.Printf(format, pathStr...) 642 } 643 } 644 645 func checkOriginAsWasNotShown(p *api.Path, asPath []bgp.AsPathParamInterface, shownAs map[uint32]struct{}) bool { 646 // the path was generated in internal 647 if len(asPath) == 0 { 648 return false 649 } 650 asList := asPath[len(asPath)-1].GetAS() 651 origin := asList[len(asList)-1] 652 653 if _, ok := shownAs[origin]; ok { 654 return false 655 } 656 shownAs[origin] = struct{}{} 657 return true 658 } 659 660 func showValidationInfo(p *api.Path, shownAs map[uint32]struct{}) error { 661 var asPath []bgp.AsPathParamInterface 662 attrs, _ := apiutil.GetNativePathAttributes(p) 663 for _, attr := range attrs { 664 if attr.GetType() == bgp.BGP_ATTR_TYPE_AS_PATH { 665 asPath = attr.(*bgp.PathAttributeAsPath).Value 666 } 667 } 668 669 nlri, _ := apiutil.GetNativeNlri(p) 670 if len(asPath) == 0 { 671 return fmt.Errorf("The path to %s was locally generated.\n", nlri.String()) 672 } else if !checkOriginAsWasNotShown(p, asPath, shownAs) { 673 return nil 674 } 675 676 status := p.GetValidation().State 677 reason := p.GetValidation().Reason 678 asList := asPath[len(asPath)-1].GetAS() 679 origin := asList[len(asList)-1] 680 681 fmt.Printf("Target Prefix: %s, AS: %d\n", nlri.String(), origin) 682 fmt.Printf(" This route is %s", status) 683 switch status { 684 case api.Validation_STATE_INVALID: 685 fmt.Printf(" reason: %s\n", reason) 686 switch reason { 687 case api.Validation_REASON_AS: 688 fmt.Println(" No VRP ASN matches the route origin ASN.") 689 case api.Validation_REASON_LENGTH: 690 fmt.Println(" Route Prefix length is greater than the maximum length allowed by VRP(s) matching this route origin ASN.") 691 } 692 case api.Validation_STATE_NOT_FOUND: 693 fmt.Println("\n No VRP Covers the Route Prefix") 694 default: 695 fmt.Print("\n\n") 696 } 697 698 printVRPs := func(l []*api.Roa) { 699 if len(l) == 0 { 700 fmt.Println(" No Entry") 701 } else { 702 var format string 703 if ip, _, _ := net.ParseCIDR(nlri.String()); ip.To4() != nil { 704 format = " %-18s %-6s %-10s\n" 705 } else { 706 format = " %-42s %-6s %-10s\n" 707 } 708 fmt.Printf(format, "Network", "AS", "MaxLen") 709 for _, m := range l { 710 fmt.Printf(format, m.Prefix, fmt.Sprint(m.As), fmt.Sprint(m.Maxlen)) 711 } 712 } 713 } 714 715 fmt.Println(" Matched VRPs: ") 716 printVRPs(p.GetValidation().Matched) 717 fmt.Println(" Unmatched AS VRPs: ") 718 printVRPs(p.GetValidation().UnmatchedAs) 719 fmt.Println(" Unmatched Length VRPs: ") 720 printVRPs(p.GetValidation().UnmatchedLength) 721 722 return nil 723 } 724 725 func showRibInfo(r, name string) error { 726 def := addr2AddressFamily(net.ParseIP(name)) 727 if r == cmdGlobal { 728 def = ipv4UC 729 } 730 family, err := checkAddressFamily(def) 731 if err != nil { 732 return err 733 } 734 735 var t api.TableType 736 switch r { 737 case cmdGlobal: 738 t = api.TableType_GLOBAL 739 case cmdLocal: 740 t = api.TableType_LOCAL 741 case cmdAdjIn: 742 t = api.TableType_ADJ_IN 743 case cmdAdjOut: 744 t = api.TableType_ADJ_OUT 745 default: 746 return fmt.Errorf("invalid resource to show RIB info: %s", r) 747 } 748 rsp, err := client.GetTable(ctx, &api.GetTableRequest{ 749 TableType: t, 750 Family: family, 751 Name: name, 752 }) 753 754 if err != nil { 755 return err 756 } 757 758 if globalOpts.Json { 759 j, _ := json.Marshal(rsp) 760 fmt.Println(string(j)) 761 return nil 762 } 763 fmt.Printf("Table %s\n", family) 764 fmt.Printf("Destination: %d, Path: %d\n", rsp.NumDestination, rsp.NumPath) 765 return nil 766 } 767 768 func parseCIDRorIP(str string) (net.IP, *net.IPNet, error) { 769 ip, n, err := net.ParseCIDR(str) 770 if err == nil { 771 return ip, n, nil 772 } 773 ip = net.ParseIP(str) 774 if ip == nil { 775 return ip, nil, fmt.Errorf("invalid CIDR/IP") 776 } 777 return ip, nil, nil 778 } 779 780 func showNeighborRib(r string, name string, args []string) error { 781 showBest := false 782 showAge := true 783 showLabel := false 784 showIdentifier := bgp.BGP_ADD_PATH_NONE 785 validationTarget := "" 786 787 def := addr2AddressFamily(net.ParseIP(name)) 788 switch r { 789 case cmdGlobal: 790 def = ipv4UC 791 showBest = true 792 case cmdLocal: 793 showBest = true 794 case cmdAdjOut: 795 showAge = false 796 case cmdVRF: 797 def = ipv4UC 798 showBest = true 799 } 800 family, err := checkAddressFamily(def) 801 if err != nil { 802 return err 803 } 804 rf := apiutil.ToRouteFamily(family) 805 switch rf { 806 case bgp.RF_IPv4_MPLS, bgp.RF_IPv6_MPLS, bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN, bgp.RF_EVPN: 807 showLabel = true 808 } 809 810 var filter []*api.TableLookupPrefix 811 if len(args) > 0 { 812 target := args[0] 813 switch rf { 814 case bgp.RF_EVPN: 815 // Uses target as EVPN Route Type string 816 default: 817 if _, _, err = parseCIDRorIP(target); err != nil { 818 return err 819 } 820 } 821 var option api.TableLookupOption 822 args = args[1:] 823 for len(args) != 0 { 824 if args[0] == "longer-prefixes" { 825 option = api.TableLookupOption_LOOKUP_LONGER 826 } else if args[0] == "shorter-prefixes" { 827 option = api.TableLookupOption_LOOKUP_SHORTER 828 } else if args[0] == "validation" { 829 if r != cmdAdjIn { 830 return fmt.Errorf("RPKI information is supported for only adj-in.") 831 } 832 validationTarget = target 833 } else { 834 return fmt.Errorf("invalid format for route filtering") 835 } 836 args = args[1:] 837 } 838 filter = []*api.TableLookupPrefix{&api.TableLookupPrefix{ 839 Prefix: target, 840 LookupOption: option, 841 }, 842 } 843 } 844 845 var t api.TableType 846 switch r { 847 case cmdGlobal: 848 t = api.TableType_GLOBAL 849 case cmdLocal: 850 t = api.TableType_LOCAL 851 case cmdAdjIn, cmdAccepted, cmdRejected: 852 t = api.TableType_ADJ_IN 853 showIdentifier = bgp.BGP_ADD_PATH_RECEIVE 854 case cmdAdjOut: 855 t = api.TableType_ADJ_OUT 856 showIdentifier = bgp.BGP_ADD_PATH_SEND 857 case cmdVRF: 858 t = api.TableType_VRF 859 } 860 861 stream, err := client.ListPath(ctx, &api.ListPathRequest{ 862 TableType: t, 863 Family: family, 864 Name: name, 865 Prefixes: filter, 866 SortType: api.ListPathRequest_PREFIX, 867 }) 868 if err != nil { 869 return err 870 } 871 872 rib := make([]*api.Destination, 0) 873 for { 874 r, err := stream.Recv() 875 if err == io.EOF { 876 break 877 } else if err != nil { 878 return err 879 } 880 rib = append(rib, r.Destination) 881 } 882 883 switch r { 884 case cmdLocal, cmdAdjIn, cmdAccepted, cmdRejected, cmdAdjOut: 885 if len(rib) == 0 { 886 stream, err := client.ListPeer(ctx, &api.ListPeerRequest{ 887 Address: name, 888 }) 889 if err != nil { 890 return err 891 } 892 r, err := stream.Recv() 893 if err != nil && err != io.EOF { 894 return err 895 } 896 if r == nil { 897 return fmt.Errorf("Neighbor %v is not found", name) 898 } 899 if r.Peer.State.SessionState != api.PeerState_ESTABLISHED { 900 return fmt.Errorf("Neighbor %v's BGP session is not established", name) 901 } 902 } 903 } 904 905 if globalOpts.Json { 906 d := make(map[string]*apiutil.Destination) 907 for _, dst := range rib { 908 d[dst.Prefix] = apiutil.NewDestination(dst) 909 } 910 j, _ := json.Marshal(d) 911 fmt.Println(string(j)) 912 return nil 913 } 914 915 if validationTarget != "" { 916 // show RPKI validation info 917 d := func() *api.Destination { 918 for _, dst := range rib { 919 if dst.Prefix == validationTarget { 920 return dst 921 } 922 } 923 return nil 924 }() 925 if d == nil { 926 fmt.Println("Network not in table") 927 return nil 928 } 929 shownAs := make(map[uint32]struct{}) 930 for _, p := range d.GetPaths() { 931 if err := showValidationInfo(p, shownAs); err != nil { 932 return err 933 } 934 } 935 } else { 936 // show RIB 937 var dsts []*api.Destination 938 switch rf { 939 case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC: 940 type d struct { 941 prefix net.IP 942 dst *api.Destination 943 } 944 l := make([]*d, 0, len(rib)) 945 for _, dst := range rib { 946 _, p, _ := net.ParseCIDR(dst.Prefix) 947 l = append(l, &d{prefix: p.IP, dst: dst}) 948 } 949 950 sort.Slice(l, func(i, j int) bool { 951 return bytes.Compare(l[i].prefix, l[j].prefix) < 0 952 }) 953 954 dsts = make([]*api.Destination, 0, len(rib)) 955 for _, s := range l { 956 dsts = append(dsts, s.dst) 957 } 958 default: 959 dsts = append(dsts, rib...) 960 } 961 962 for _, d := range dsts { 963 switch r { 964 case cmdAccepted: 965 l := make([]*api.Path, 0, len(d.Paths)) 966 for _, p := range d.GetPaths() { 967 if !p.Filtered { 968 l = append(l, p) 969 } 970 } 971 d.Paths = l 972 case cmdRejected: 973 // always nothing 974 d.Paths = []*api.Path{} 975 default: 976 } 977 } 978 if len(dsts) > 0 { 979 showRoute(dsts, showAge, showBest, showLabel, showIdentifier) 980 } else { 981 fmt.Println("Network not in table") 982 } 983 } 984 return nil 985 } 986 987 func resetNeighbor(cmd string, remoteIP string, args []string) error { 988 if reasonLen := len(neighborsOpts.Reason); reasonLen > bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX { 989 return fmt.Errorf("Too long reason for shutdown communication (max %d bytes)", bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX) 990 } 991 var comm string 992 soft := true 993 dir := api.ResetPeerRequest_BOTH 994 switch cmd { 995 case cmdReset: 996 soft = false 997 comm = neighborsOpts.Reason 998 case cmdSoftReset: 999 case cmdSoftResetIn: 1000 dir = api.ResetPeerRequest_IN 1001 case cmdSoftResetOut: 1002 dir = api.ResetPeerRequest_OUT 1003 } 1004 _, err := client.ResetPeer(ctx, &api.ResetPeerRequest{ 1005 Address: remoteIP, 1006 Communication: comm, 1007 Soft: soft, 1008 Direction: dir, 1009 }) 1010 return err 1011 } 1012 1013 func stateChangeNeighbor(cmd string, remoteIP string, args []string) error { 1014 if reasonLen := len(neighborsOpts.Reason); reasonLen > bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX { 1015 return fmt.Errorf("Too long reason for shutdown communication (max %d bytes)", bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX) 1016 } 1017 switch cmd { 1018 case cmdShutdown: 1019 fmt.Printf("WARNING: command `%s` is deprecated. use `%s` instead\n", cmdShutdown, cmdDisable) 1020 _, err := client.ShutdownPeer(ctx, &api.ShutdownPeerRequest{ 1021 Address: remoteIP, 1022 Communication: neighborsOpts.Reason, 1023 }) 1024 return err 1025 case cmdEnable: 1026 _, err := client.EnablePeer(ctx, &api.EnablePeerRequest{ 1027 Address: remoteIP, 1028 }) 1029 return err 1030 case cmdDisable: 1031 _, err := client.DisablePeer(ctx, &api.DisablePeerRequest{ 1032 Address: remoteIP, 1033 }) 1034 return err 1035 } 1036 return nil 1037 } 1038 1039 func showNeighborPolicy(remoteIP, policyType string, indent int) error { 1040 var assignment *api.PolicyAssignment 1041 var err error 1042 var dir api.PolicyDirection 1043 1044 switch strings.ToLower(policyType) { 1045 case "import": 1046 dir = api.PolicyDirection_IMPORT 1047 case "export": 1048 dir = api.PolicyDirection_EXPORT 1049 default: 1050 return fmt.Errorf("invalid policy type: choose from (in|import|export)") 1051 } 1052 stream, err := client.ListPolicyAssignment(ctx, &api.ListPolicyAssignmentRequest{ 1053 Name: remoteIP, 1054 Direction: dir, 1055 }) 1056 if err != nil { 1057 return err 1058 } 1059 r, err := stream.Recv() 1060 if err != nil { 1061 return err 1062 } 1063 assignment = r.Assignment 1064 1065 if globalOpts.Json { 1066 j, _ := json.Marshal(assignment) 1067 fmt.Println(string(j)) 1068 return nil 1069 } 1070 1071 fmt.Printf("%s policy:\n", strings.Title(policyType)) 1072 fmt.Printf("%sDefault: %s\n", strings.Repeat(" ", indent), assignment.DefaultAction.String()) 1073 for _, p := range assignment.Policies { 1074 fmt.Printf("%sName %s:\n", strings.Repeat(" ", indent), p.Name) 1075 printPolicy(indent+4, p) 1076 } 1077 return nil 1078 } 1079 1080 func extractDefaultAction(args []string) ([]string, api.RouteAction, error) { 1081 for idx, arg := range args { 1082 if arg == "default" { 1083 if len(args) < (idx + 2) { 1084 return nil, api.RouteAction_NONE, fmt.Errorf("specify default action [accept|reject]") 1085 } 1086 typ := args[idx+1] 1087 switch strings.ToLower(typ) { 1088 case "accept": 1089 return append(args[:idx], args[idx+2:]...), api.RouteAction_ACCEPT, nil 1090 case "reject": 1091 return append(args[:idx], args[idx+2:]...), api.RouteAction_REJECT, nil 1092 default: 1093 return nil, api.RouteAction_NONE, fmt.Errorf("invalid default action") 1094 } 1095 } 1096 } 1097 return args, api.RouteAction_NONE, nil 1098 } 1099 1100 func modNeighborPolicy(remoteIP, policyType, cmdType string, args []string) error { 1101 if remoteIP == "" { 1102 remoteIP = globalRIBName 1103 } 1104 1105 assign := &api.PolicyAssignment{ 1106 Name: remoteIP, 1107 } 1108 1109 switch strings.ToLower(policyType) { 1110 case "import": 1111 assign.Direction = api.PolicyDirection_IMPORT 1112 case "export": 1113 assign.Direction = api.PolicyDirection_EXPORT 1114 } 1115 1116 usage := fmt.Sprintf("usage: gobgp neighbor %s policy %s %s", remoteIP, policyType, cmdType) 1117 if remoteIP == "" { 1118 usage = fmt.Sprintf("usage: gobgp global policy %s %s", policyType, cmdType) 1119 } 1120 1121 var err error 1122 switch cmdType { 1123 case cmdAdd, cmdSet: 1124 if len(args) < 1 { 1125 return fmt.Errorf("%s <policy name>... [default {%s|%s}]", usage, "accept", "reject") 1126 } 1127 var err error 1128 var def api.RouteAction 1129 args, def, err = extractDefaultAction(args) 1130 if err != nil { 1131 return fmt.Errorf("%s\n%s <policy name>... [default {%s|%s}]", err, usage, "accept", "reject") 1132 } 1133 assign.DefaultAction = def 1134 } 1135 ps := make([]*api.Policy, 0, len(args)) 1136 for _, name := range args { 1137 ps = append(ps, &api.Policy{Name: name}) 1138 } 1139 assign.Policies = ps 1140 switch cmdType { 1141 case cmdAdd: 1142 _, err = client.AddPolicyAssignment(ctx, &api.AddPolicyAssignmentRequest{ 1143 Assignment: assign, 1144 }) 1145 case cmdSet: 1146 _, err = client.SetPolicyAssignment(ctx, &api.SetPolicyAssignmentRequest{ 1147 Assignment: assign, 1148 }) 1149 case cmdDel: 1150 all := false 1151 if len(args) == 0 { 1152 all = true 1153 } 1154 _, err = client.DeletePolicyAssignment(ctx, &api.DeletePolicyAssignmentRequest{ 1155 Assignment: assign, 1156 All: all, 1157 }) 1158 } 1159 return err 1160 } 1161 1162 func modNeighbor(cmdType string, args []string) error { 1163 params := map[string]int{ 1164 "interface": paramSingle, 1165 } 1166 usage := fmt.Sprintf("usage: gobgp neighbor %s [ <neighbor-address> | interface <neighbor-interface> ]", cmdType) 1167 if cmdType == cmdAdd { 1168 usage += " as <VALUE>" 1169 } else if cmdType == cmdUpdate { 1170 usage += " [ as <VALUE> ]" 1171 } 1172 if cmdType == cmdAdd || cmdType == cmdUpdate { 1173 params["as"] = paramSingle 1174 params["family"] = paramSingle 1175 params["vrf"] = paramSingle 1176 params["route-reflector-client"] = paramSingle 1177 params["route-server-client"] = paramFlag 1178 params["allow-own-as"] = paramSingle 1179 params["remove-private-as"] = paramSingle 1180 params["replace-peer-as"] = paramFlag 1181 params["ebgp-multihop-ttl"] = paramSingle 1182 usage += " [ family <address-families-list> | vrf <vrf-name> | route-reflector-client [<cluster-id>] | route-server-client | allow-own-as <num> | remove-private-as (all|replace) | replace-peer-as | ebgp-multihop-ttl <ttl>]" 1183 } 1184 1185 m, err := extractReserved(args, params) 1186 if err != nil || (len(m[""]) != 1 && len(m["interface"]) != 1) { 1187 return fmt.Errorf("%s", usage) 1188 } 1189 1190 unnumbered := len(m["interface"]) > 0 1191 if !unnumbered { 1192 if _, err := net.ResolveIPAddr("ip", m[""][0]); err != nil { 1193 return err 1194 } 1195 } 1196 1197 getNeighborAddress := func() (string, error) { 1198 if unnumbered { 1199 return config.GetIPv6LinkLocalNeighborAddress(m["interface"][0]) 1200 } 1201 return m[""][0], nil 1202 } 1203 1204 getNeighborConfig := func() (*api.Peer, error) { 1205 addr, err := getNeighborAddress() 1206 if err != nil { 1207 return nil, err 1208 } 1209 var peer *api.Peer 1210 switch cmdType { 1211 case cmdAdd, cmdDel: 1212 peer = &api.Peer{ 1213 Conf: &api.PeerConf{}, 1214 State: &api.PeerState{}, 1215 } 1216 if unnumbered { 1217 peer.Conf.NeighborInterface = m["interface"][0] 1218 } else { 1219 peer.Conf.NeighborAddress = addr 1220 } 1221 peer.State.NeighborAddress = addr 1222 case cmdUpdate: 1223 stream, err := client.ListPeer(ctx, &api.ListPeerRequest{ 1224 Address: addr, 1225 }) 1226 if err != nil { 1227 return nil, err 1228 } 1229 r, err := stream.Recv() 1230 if err != nil { 1231 return nil, err 1232 } 1233 peer = r.Peer 1234 default: 1235 return nil, fmt.Errorf("invalid command: %s", cmdType) 1236 } 1237 return peer, nil 1238 } 1239 1240 updateNeighborConfig := func(peer *api.Peer) error { 1241 if len(m["as"]) > 0 { 1242 as, err := strconv.ParseUint(m["as"][0], 10, 32) 1243 if err != nil { 1244 return err 1245 } 1246 peer.Conf.PeerAs = uint32(as) 1247 } 1248 if len(m["family"]) == 1 { 1249 peer.AfiSafis = make([]*api.AfiSafi, 0) // for the case of cmdUpdate 1250 for _, f := range strings.Split(m["family"][0], ",") { 1251 rf, err := bgp.GetRouteFamily(f) 1252 if err != nil { 1253 return err 1254 } 1255 afi, safi := bgp.RouteFamilyToAfiSafi(rf) 1256 peer.AfiSafis = append(peer.AfiSafis, &api.AfiSafi{Config: &api.AfiSafiConfig{Family: apiutil.ToApiFamily(afi, safi)}}) 1257 } 1258 } 1259 if len(m["vrf"]) == 1 { 1260 peer.Conf.Vrf = m["vrf"][0] 1261 } 1262 if option, ok := m["route-reflector-client"]; ok { 1263 peer.RouteReflector.RouteReflectorClient = true 1264 if len(option) == 1 { 1265 peer.RouteReflector.RouteReflectorClusterId = option[0] 1266 } 1267 } 1268 if _, ok := m["route-server-client"]; ok { 1269 peer.RouteServer.RouteServerClient = true 1270 } 1271 if option, ok := m["allow-own-as"]; ok { 1272 as, err := strconv.ParseUint(option[0], 10, 8) 1273 if err != nil { 1274 return err 1275 } 1276 peer.Conf.AllowOwnAs = uint32(as) 1277 } 1278 if option, ok := m["remove-private-as"]; ok { 1279 switch option[0] { 1280 case "all": 1281 peer.Conf.RemovePrivateAs = api.PeerConf_ALL 1282 case "replace": 1283 peer.Conf.RemovePrivateAs = api.PeerConf_REPLACE 1284 default: 1285 return fmt.Errorf("invalid remove-private-as value: all or replace") 1286 } 1287 } 1288 if _, ok := m["replace-peer-as"]; ok { 1289 peer.Conf.ReplacePeerAs = true 1290 } 1291 if len(m["ebgp-multihop-ttl"]) == 1 { 1292 ttl, err := strconv.ParseUint(m["ebgp-multihop-ttl"][0], 10, 32) 1293 if err != nil { 1294 return err 1295 } 1296 peer.EbgpMultihop = &api.EbgpMultihop{ 1297 Enabled: true, 1298 MultihopTtl: uint32(ttl), 1299 } 1300 } 1301 return nil 1302 } 1303 1304 n, err := getNeighborConfig() 1305 if err != nil { 1306 return err 1307 } 1308 1309 switch cmdType { 1310 case cmdAdd: 1311 if err = updateNeighborConfig(n); err != nil { 1312 return err 1313 } 1314 _, err = client.AddPeer(ctx, &api.AddPeerRequest{ 1315 Peer: n, 1316 }) 1317 case cmdDel: 1318 _, err = client.DeletePeer(ctx, &api.DeletePeerRequest{ 1319 Address: n.Conf.NeighborAddress, 1320 Interface: n.Conf.NeighborInterface, 1321 }) 1322 case cmdUpdate: 1323 if err = updateNeighborConfig(n); err != nil { 1324 return err 1325 } 1326 _, err = client.UpdatePeer(ctx, &api.UpdatePeerRequest{ 1327 Peer: n, 1328 DoSoftResetIn: true, 1329 }) 1330 } 1331 return err 1332 } 1333 1334 func newNeighborCmd() *cobra.Command { 1335 1336 neighborCmdImpl := &cobra.Command{} 1337 1338 type cmds struct { 1339 names []string 1340 f func(string, string, []string) error 1341 } 1342 1343 c := make([]cmds, 0, 3) 1344 c = append(c, cmds{[]string{cmdLocal, cmdAdjIn, cmdAdjOut, cmdAccepted, cmdRejected}, showNeighborRib}) 1345 c = append(c, cmds{[]string{cmdReset, cmdSoftReset, cmdSoftResetIn, cmdSoftResetOut}, resetNeighbor}) 1346 c = append(c, cmds{[]string{cmdShutdown, cmdEnable, cmdDisable}, stateChangeNeighbor}) 1347 1348 getPeer := func(addr string) (*api.Peer, error) { 1349 var r *api.ListPeerResponse 1350 stream, err := client.ListPeer(ctx, &api.ListPeerRequest{ 1351 Address: addr, 1352 }) 1353 if err == nil { 1354 r, err = stream.Recv() 1355 } 1356 if err != nil && err != io.EOF { 1357 return nil, err 1358 } 1359 return r.Peer, nil 1360 } 1361 1362 for _, v := range c { 1363 f := v.f 1364 for _, name := range v.names { 1365 c := &cobra.Command{ 1366 Use: name, 1367 Run: func(cmd *cobra.Command, args []string) { 1368 addr := "" 1369 switch name { 1370 case cmdReset, cmdSoftReset, cmdSoftResetIn, cmdSoftResetOut, cmdShutdown: 1371 if args[len(args)-1] == "all" { 1372 addr = "all" 1373 } 1374 } 1375 if addr == "" { 1376 p, err := getPeer(args[len(args)-1]) 1377 if err != nil { 1378 exitWithError(err) 1379 } 1380 addr = p.State.NeighborAddress 1381 } 1382 err := f(cmd.Use, addr, args[:len(args)-1]) 1383 if err != nil { 1384 exitWithError(err) 1385 } 1386 }, 1387 } 1388 neighborCmdImpl.AddCommand(c) 1389 switch name { 1390 case cmdLocal, cmdAdjIn, cmdAdjOut: 1391 n := name 1392 c.AddCommand(&cobra.Command{ 1393 Use: cmdSummary, 1394 Run: func(cmd *cobra.Command, args []string) { 1395 if err := showRibInfo(n, args[len(args)-1]); err != nil { 1396 exitWithError(err) 1397 } 1398 }, 1399 }) 1400 } 1401 } 1402 } 1403 1404 policyCmd := &cobra.Command{ 1405 Use: cmdPolicy, 1406 Run: func(cmd *cobra.Command, args []string) { 1407 peer, err := getPeer(args[0]) 1408 if err != nil { 1409 exitWithError(err) 1410 } 1411 remoteIP := peer.State.NeighborAddress 1412 for _, v := range []string{cmdIn, cmdImport, cmdExport} { 1413 if err := showNeighborPolicy(remoteIP, v, 4); err != nil { 1414 exitWithError(err) 1415 } 1416 } 1417 }, 1418 } 1419 1420 for _, v := range []string{cmdIn, cmdImport, cmdExport} { 1421 cmd := &cobra.Command{ 1422 Use: v, 1423 Run: func(cmd *cobra.Command, args []string) { 1424 peer, err := getPeer(args[0]) 1425 if err != nil { 1426 exitWithError(err) 1427 } 1428 remoteIP := peer.State.NeighborAddress 1429 err = showNeighborPolicy(remoteIP, cmd.Use, 0) 1430 if err != nil { 1431 exitWithError(err) 1432 } 1433 }, 1434 } 1435 1436 for _, w := range []string{cmdAdd, cmdDel, cmdSet} { 1437 subcmd := &cobra.Command{ 1438 Use: w, 1439 Run: func(subcmd *cobra.Command, args []string) { 1440 peer, err := getPeer(args[len(args)-1]) 1441 if err != nil { 1442 exitWithError(err) 1443 } 1444 remoteIP := peer.State.NeighborAddress 1445 args = args[:len(args)-1] 1446 if err = modNeighborPolicy(remoteIP, cmd.Use, subcmd.Use, args); err != nil { 1447 exitWithError(err) 1448 } 1449 }, 1450 } 1451 cmd.AddCommand(subcmd) 1452 } 1453 1454 policyCmd.AddCommand(cmd) 1455 1456 } 1457 1458 neighborCmdImpl.AddCommand(policyCmd) 1459 1460 neighborCmd := &cobra.Command{ 1461 Use: cmdNeighbor, 1462 Run: func(cmd *cobra.Command, args []string) { 1463 var err error 1464 if len(args) == 0 { 1465 err = showNeighbors("") 1466 } else if len(args) == 1 { 1467 err = showNeighbor(args) 1468 } else { 1469 args = append(args[1:], args[0]) 1470 neighborCmdImpl.SetArgs(args) 1471 err = neighborCmdImpl.Execute() 1472 } 1473 if err != nil { 1474 exitWithError(err) 1475 } 1476 }, 1477 } 1478 1479 for _, v := range []string{cmdAdd, cmdDel, cmdUpdate} { 1480 cmd := &cobra.Command{ 1481 Use: v, 1482 Run: func(c *cobra.Command, args []string) { 1483 if err := modNeighbor(c.Use, args); err != nil { 1484 exitWithError(err) 1485 } 1486 }, 1487 } 1488 neighborCmd.AddCommand(cmd) 1489 } 1490 1491 neighborCmd.PersistentFlags().StringVarP(&subOpts.AddressFamily, "address-family", "a", "", "address family") 1492 neighborCmd.PersistentFlags().StringVarP(&neighborsOpts.Reason, "reason", "", "", "specifying communication field on Cease NOTIFICATION message with Administrative Shutdown subcode") 1493 neighborCmd.PersistentFlags().StringVarP(&neighborsOpts.Transport, "transport", "t", "", "specifying a transport protocol") 1494 return neighborCmd 1495 }