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  }