github.com/osrg/gobgp@v2.0.0+incompatible/cmd/gobgp/global.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  	"encoding/json"
    20  	"fmt"
    21  	"net"
    22  	"regexp"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/spf13/cobra"
    29  
    30  	api "github.com/osrg/gobgp/api"
    31  	"github.com/osrg/gobgp/internal/pkg/apiutil"
    32  	"github.com/osrg/gobgp/internal/pkg/table"
    33  
    34  	"github.com/osrg/gobgp/pkg/packet/bgp"
    35  )
    36  
    37  type extCommType int
    38  
    39  const (
    40  	ctAccept extCommType = iota
    41  	ctDiscard
    42  	ctRate
    43  	ctRedirect
    44  	ctMark
    45  	ctAction
    46  	ctRT
    47  	ctEncap
    48  	ctESILabel
    49  	ctRouterMAC
    50  	ctDefaultGateway
    51  	ctValid
    52  	ctNotFound
    53  	ctInvalid
    54  	ctColor
    55  )
    56  
    57  var extCommNameMap = map[extCommType]string{
    58  	ctAccept:         "accept",
    59  	ctDiscard:        "discard",
    60  	ctRate:           "rate-limit",
    61  	ctRedirect:       "redirect",
    62  	ctMark:           "mark",
    63  	ctAction:         "action",
    64  	ctRT:             "rt",
    65  	ctEncap:          "encap",
    66  	ctESILabel:       "esi-label",
    67  	ctRouterMAC:      "router-mac",
    68  	ctDefaultGateway: "default-gateway",
    69  	ctValid:          "valid",
    70  	ctNotFound:       "not-found",
    71  	ctInvalid:        "invalid",
    72  	ctColor:          "color",
    73  }
    74  
    75  var extCommValueMap = map[string]extCommType{
    76  	extCommNameMap[ctAccept]:         ctAccept,
    77  	extCommNameMap[ctDiscard]:        ctDiscard,
    78  	extCommNameMap[ctRate]:           ctRate,
    79  	extCommNameMap[ctRedirect]:       ctRedirect,
    80  	extCommNameMap[ctMark]:           ctMark,
    81  	extCommNameMap[ctAction]:         ctAction,
    82  	extCommNameMap[ctRT]:             ctRT,
    83  	extCommNameMap[ctEncap]:          ctEncap,
    84  	extCommNameMap[ctESILabel]:       ctESILabel,
    85  	extCommNameMap[ctRouterMAC]:      ctRouterMAC,
    86  	extCommNameMap[ctDefaultGateway]: ctDefaultGateway,
    87  	extCommNameMap[ctValid]:          ctValid,
    88  	extCommNameMap[ctNotFound]:       ctNotFound,
    89  	extCommNameMap[ctInvalid]:        ctInvalid,
    90  	extCommNameMap[ctColor]:          ctColor,
    91  }
    92  
    93  func rateLimitParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
    94  	exp := regexp.MustCompile(fmt.Sprintf("^(%s|(%s) (\\d+)(\\.(\\d+))?)( as (\\d+))?$", extCommNameMap[ctDiscard], extCommNameMap[ctRate]))
    95  	elems := exp.FindStringSubmatch(strings.Join(args, " "))
    96  	if len(elems) != 8 {
    97  		return nil, fmt.Errorf("invalid rate-limit")
    98  	}
    99  	var rate float32
   100  	var as uint64
   101  	if elems[2] == extCommNameMap[ctRate] {
   102  		f, err := strconv.ParseFloat(elems[3]+elems[4], 32)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  		rate = float32(f)
   107  	}
   108  	if elems[7] != "" {
   109  		var err error
   110  		as, err = strconv.ParseUint(elems[7], 10, 16)
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  	}
   115  	return []bgp.ExtendedCommunityInterface{bgp.NewTrafficRateExtended(uint16(as), rate)}, nil
   116  }
   117  
   118  func redirectParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   119  	if len(args) < 2 || args[0] != extCommNameMap[ctRedirect] {
   120  		return nil, fmt.Errorf("invalid redirect")
   121  	}
   122  	rt, err := bgp.ParseRouteTarget(strings.Join(args[1:], " "))
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	switch rt.(type) {
   127  	case *bgp.TwoOctetAsSpecificExtended:
   128  		r := rt.(*bgp.TwoOctetAsSpecificExtended)
   129  		return []bgp.ExtendedCommunityInterface{bgp.NewRedirectTwoOctetAsSpecificExtended(r.AS, r.LocalAdmin)}, nil
   130  	case *bgp.IPv4AddressSpecificExtended:
   131  		r := rt.(*bgp.IPv4AddressSpecificExtended)
   132  		return []bgp.ExtendedCommunityInterface{bgp.NewRedirectIPv4AddressSpecificExtended(r.IPv4.String(), r.LocalAdmin)}, nil
   133  	case *bgp.FourOctetAsSpecificExtended:
   134  		r := rt.(*bgp.FourOctetAsSpecificExtended)
   135  		return []bgp.ExtendedCommunityInterface{bgp.NewRedirectFourOctetAsSpecificExtended(r.AS, r.LocalAdmin)}, nil
   136  	case *bgp.IPv6AddressSpecificExtended:
   137  		r := rt.(*bgp.IPv6AddressSpecificExtended)
   138  		return []bgp.ExtendedCommunityInterface{bgp.NewRedirectIPv6AddressSpecificExtended(r.IPv6.String(), r.LocalAdmin)}, nil
   139  	}
   140  	return nil, fmt.Errorf("invalid redirect")
   141  }
   142  
   143  func markParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   144  	if len(args) < 2 || args[0] != extCommNameMap[ctMark] {
   145  		return nil, fmt.Errorf("invalid mark")
   146  	}
   147  	dscp, err := strconv.ParseUint(args[1], 10, 8)
   148  	if err != nil {
   149  		return nil, fmt.Errorf("invalid mark")
   150  	}
   151  	return []bgp.ExtendedCommunityInterface{bgp.NewTrafficRemarkExtended(uint8(dscp))}, nil
   152  }
   153  
   154  func actionParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   155  	if len(args) < 2 || args[0] != extCommNameMap[ctAction] {
   156  		return nil, fmt.Errorf("invalid action")
   157  	}
   158  	sample := false
   159  	terminal := false
   160  	switch args[1] {
   161  	case "sample":
   162  		sample = true
   163  	case "terminal":
   164  		terminal = true
   165  	case "terminal-sample", "sample-terminal":
   166  		sample = true
   167  		terminal = true
   168  	default:
   169  		return nil, fmt.Errorf("invalid action")
   170  	}
   171  	return []bgp.ExtendedCommunityInterface{bgp.NewTrafficActionExtended(terminal, sample)}, nil
   172  }
   173  
   174  func rtParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   175  	if len(args) < 2 || args[0] != extCommNameMap[ctRT] {
   176  		return nil, fmt.Errorf("invalid rt")
   177  	}
   178  	exts := make([]bgp.ExtendedCommunityInterface, 0, len(args[1:]))
   179  	for _, arg := range args[1:] {
   180  		rt, err := bgp.ParseRouteTarget(arg)
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  		exts = append(exts, rt)
   185  	}
   186  	return exts, nil
   187  }
   188  
   189  func encapParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   190  	if len(args) < 2 || args[0] != extCommNameMap[ctEncap] {
   191  		return nil, fmt.Errorf("invalid encap")
   192  	}
   193  	var typ bgp.TunnelType
   194  	switch args[1] {
   195  	case "l2tpv3":
   196  		typ = bgp.TUNNEL_TYPE_L2TP3
   197  	case "gre":
   198  		typ = bgp.TUNNEL_TYPE_GRE
   199  	case "ip-in-ip":
   200  		typ = bgp.TUNNEL_TYPE_IP_IN_IP
   201  	case "vxlan":
   202  		typ = bgp.TUNNEL_TYPE_VXLAN
   203  	case "nvgre":
   204  		typ = bgp.TUNNEL_TYPE_NVGRE
   205  	case "mpls":
   206  		typ = bgp.TUNNEL_TYPE_MPLS
   207  	case "mpls-in-gre":
   208  		typ = bgp.TUNNEL_TYPE_MPLS_IN_GRE
   209  	case "vxlan-gre":
   210  		typ = bgp.TUNNEL_TYPE_VXLAN_GRE
   211  	default:
   212  		return nil, fmt.Errorf("invalid encap type")
   213  	}
   214  	return []bgp.ExtendedCommunityInterface{bgp.NewEncapExtended(typ)}, nil
   215  }
   216  
   217  func esiLabelParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   218  	if len(args) < 2 || args[0] != extCommNameMap[ctESILabel] {
   219  		return nil, fmt.Errorf("invalid esi-label")
   220  	}
   221  	label, err := strconv.ParseUint(args[1], 10, 32)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	isSingleActive := false
   226  	if len(args) > 2 {
   227  		switch args[2] {
   228  		case "single-active":
   229  			isSingleActive = true
   230  		case "all-active":
   231  			// isSingleActive = false
   232  		default:
   233  			return nil, fmt.Errorf("invalid esi-label")
   234  		}
   235  	}
   236  	o := &bgp.ESILabelExtended{
   237  		Label:          uint32(label),
   238  		IsSingleActive: isSingleActive,
   239  	}
   240  	return []bgp.ExtendedCommunityInterface{o}, nil
   241  }
   242  
   243  func routerMacParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   244  	if len(args) < 2 || args[0] != extCommNameMap[ctRouterMAC] {
   245  		return nil, fmt.Errorf("invalid router's mac")
   246  	}
   247  	hw, err := net.ParseMAC(args[1])
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  	o := &bgp.RouterMacExtended{Mac: hw}
   252  	return []bgp.ExtendedCommunityInterface{o}, nil
   253  }
   254  
   255  func defaultGatewayParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   256  	if len(args) < 1 || args[0] != extCommNameMap[ctDefaultGateway] {
   257  		return nil, fmt.Errorf("invalid default-gateway")
   258  	}
   259  	return []bgp.ExtendedCommunityInterface{bgp.NewDefaultGatewayExtended()}, nil
   260  }
   261  
   262  func validationParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   263  	if len(args) < 1 {
   264  		return nil, fmt.Errorf("invalid validation state")
   265  	}
   266  	var typ bgp.ValidationState
   267  	switch args[0] {
   268  	case "valid":
   269  		typ = bgp.VALIDATION_STATE_VALID
   270  	case "not-found":
   271  		typ = bgp.VALIDATION_STATE_NOT_FOUND
   272  	case "invalid":
   273  		typ = bgp.VALIDATION_STATE_INVALID
   274  	default:
   275  		return nil, fmt.Errorf("invalid validation state")
   276  	}
   277  	return []bgp.ExtendedCommunityInterface{bgp.NewValidationExtended(typ)}, nil
   278  }
   279  
   280  func colorParser(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   281  	if len(args) != 2 || args[0] != extCommNameMap[ctColor] {
   282  		return nil, fmt.Errorf("invalid color")
   283  	}
   284  	color, err := strconv.ParseUint(args[1], 10, 32)
   285  	if err != nil {
   286  		return nil, fmt.Errorf("invalid color")
   287  	}
   288  	return []bgp.ExtendedCommunityInterface{bgp.NewColorExtended(uint32(color))}, nil
   289  }
   290  
   291  var extCommParserMap = map[extCommType]func([]string) ([]bgp.ExtendedCommunityInterface, error){
   292  	ctAccept:         nil,
   293  	ctDiscard:        rateLimitParser,
   294  	ctRate:           rateLimitParser,
   295  	ctRedirect:       redirectParser,
   296  	ctMark:           markParser,
   297  	ctAction:         actionParser,
   298  	ctRT:             rtParser,
   299  	ctEncap:          encapParser,
   300  	ctESILabel:       esiLabelParser,
   301  	ctRouterMAC:      routerMacParser,
   302  	ctDefaultGateway: defaultGatewayParser,
   303  	ctValid:          validationParser,
   304  	ctNotFound:       validationParser,
   305  	ctInvalid:        validationParser,
   306  	ctColor:          colorParser,
   307  }
   308  
   309  func parseExtendedCommunities(args []string) ([]bgp.ExtendedCommunityInterface, error) {
   310  	idxs := make([]struct {
   311  		t extCommType
   312  		i int
   313  	}, 0, len(extCommNameMap))
   314  	for idx, v := range args {
   315  		if t, ok := extCommValueMap[v]; ok {
   316  			idxs = append(idxs, struct {
   317  				t extCommType
   318  				i int
   319  			}{t, idx})
   320  		}
   321  	}
   322  	exts := make([]bgp.ExtendedCommunityInterface, 0, len(idxs))
   323  	for i, idx := range idxs {
   324  		var a []string
   325  		f := extCommParserMap[idx.t]
   326  		if i < len(idxs)-1 {
   327  			a = args[:idxs[i+1].i-idx.i]
   328  			args = args[(idxs[i+1].i - idx.i):]
   329  		} else {
   330  			a = args
   331  			args = nil
   332  		}
   333  		if f == nil {
   334  			continue
   335  		}
   336  		ext, err := f(a)
   337  		if err != nil {
   338  			return nil, err
   339  		}
   340  		exts = append(exts, ext...)
   341  	}
   342  	if len(args) > 0 {
   343  		return nil, fmt.Errorf("failed to parse %v", args)
   344  	}
   345  	return exts, nil
   346  }
   347  
   348  func parseFlowSpecArgs(rf bgp.RouteFamily, args []string) (bgp.AddrPrefixInterface, []string, error) {
   349  	// Format:
   350  	// match <rule>... [then <action>...] [rd <rd>] [rt <rt>...]
   351  	req := 3 // match <key1> <arg1> [<key2> <arg2>...]
   352  	if len(args) < req {
   353  		return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args))
   354  	}
   355  	m, err := extractReserved(args, map[string]int{
   356  		"match": paramList,
   357  		"then":  paramList,
   358  		"rd":    paramSingle,
   359  		"rt":    paramList})
   360  	if err != nil {
   361  		return nil, nil, err
   362  	}
   363  	if len(m["match"]) == 0 {
   364  		return nil, nil, fmt.Errorf("specify filtering rules with keyword 'match'")
   365  	}
   366  
   367  	var rd bgp.RouteDistinguisherInterface
   368  	extcomms := m["then"]
   369  	switch rf {
   370  	case bgp.RF_FS_IPv4_VPN, bgp.RF_FS_IPv6_VPN, bgp.RF_FS_L2_VPN:
   371  		if len(m["rd"]) == 0 {
   372  			return nil, nil, fmt.Errorf("specify rd")
   373  		}
   374  		var err error
   375  		if rd, err = bgp.ParseRouteDistinguisher(m["rd"][0]); err != nil {
   376  			return nil, nil, fmt.Errorf("invalid rd: %s", m["rd"][0])
   377  		}
   378  		if len(m["rt"]) > 0 {
   379  			extcomms = append(extcomms, "rt")
   380  			extcomms = append(extcomms, m["rt"]...)
   381  		}
   382  	default:
   383  		if len(m["rd"]) > 0 {
   384  			return nil, nil, fmt.Errorf("cannot specify rd for %s", rf.String())
   385  		}
   386  		if len(m["rt"]) > 0 {
   387  			return nil, nil, fmt.Errorf("cannot specify rt for %s", rf.String())
   388  		}
   389  	}
   390  
   391  	rules, err := bgp.ParseFlowSpecComponents(rf, strings.Join(m["match"], " "))
   392  	if err != nil {
   393  		return nil, nil, err
   394  	}
   395  
   396  	var nlri bgp.AddrPrefixInterface
   397  	switch rf {
   398  	case bgp.RF_FS_IPv4_UC:
   399  		nlri = bgp.NewFlowSpecIPv4Unicast(rules)
   400  	case bgp.RF_FS_IPv6_UC:
   401  		nlri = bgp.NewFlowSpecIPv6Unicast(rules)
   402  	case bgp.RF_FS_IPv4_VPN:
   403  		nlri = bgp.NewFlowSpecIPv4VPN(rd, rules)
   404  	case bgp.RF_FS_IPv6_VPN:
   405  		nlri = bgp.NewFlowSpecIPv6VPN(rd, rules)
   406  	case bgp.RF_FS_L2_VPN:
   407  		nlri = bgp.NewFlowSpecL2VPN(rd, rules)
   408  	default:
   409  		return nil, nil, fmt.Errorf("invalid route family")
   410  	}
   411  
   412  	return nlri, extcomms, nil
   413  }
   414  
   415  func parseEvpnEthernetAutoDiscoveryArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
   416  	// Format:
   417  	// esi <esi> etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [esi-label <esi-label> [single-active | all-active]]
   418  	req := 8
   419  	if len(args) < req {
   420  		return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args))
   421  	}
   422  	m, err := extractReserved(args, map[string]int{
   423  		"esi":       paramList,
   424  		"etag":      paramSingle,
   425  		"label":     paramSingle,
   426  		"rd":        paramSingle,
   427  		"rt":        paramList,
   428  		"encap":     paramSingle,
   429  		"esi-label": paramSingle})
   430  	if err != nil {
   431  		return nil, nil, err
   432  	}
   433  	for _, f := range []string{"esi", "etag", "label", "rd"} {
   434  		for len(m[f]) == 0 {
   435  			return nil, nil, fmt.Errorf("specify %s", f)
   436  		}
   437  	}
   438  
   439  	esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"])
   440  	if err != nil {
   441  		return nil, nil, err
   442  	}
   443  
   444  	e, err := strconv.ParseUint(m["etag"][0], 10, 32)
   445  	if err != nil {
   446  		return nil, nil, err
   447  	}
   448  	etag := uint32(e)
   449  
   450  	l, err := strconv.ParseUint(m["label"][0], 10, 32)
   451  	if err != nil {
   452  		return nil, nil, err
   453  	}
   454  	label := uint32(l)
   455  
   456  	rd, err := bgp.ParseRouteDistinguisher(m["rd"][0])
   457  	if err != nil {
   458  		return nil, nil, err
   459  	}
   460  
   461  	extcomms := make([]string, 0)
   462  	if len(m["rt"]) > 0 {
   463  		extcomms = append(extcomms, "rt")
   464  		extcomms = append(extcomms, m["rt"]...)
   465  	}
   466  	if len(m["encap"]) > 0 {
   467  		extcomms = append(extcomms, "encap", m["encap"][0])
   468  	}
   469  	if len(m["esi-label"]) > 0 {
   470  		extcomms = append(extcomms, "esi-label")
   471  		extcomms = append(extcomms, m["esi-label"]...)
   472  	}
   473  
   474  	r := &bgp.EVPNEthernetAutoDiscoveryRoute{
   475  		RD:    rd,
   476  		ESI:   esi,
   477  		ETag:  etag,
   478  		Label: label,
   479  	}
   480  	return bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY, r), extcomms, nil
   481  }
   482  
   483  func parseEvpnMacAdvArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
   484  	// Format:
   485  	// <mac address> <ip address> [esi <esi>] etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway]
   486  	// or
   487  	// <mac address> <ip address> <etag> [esi <esi>] label <label> rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway]
   488  	// or
   489  	// <mac address> <ip address> <etag> <label> [esi <esi>] rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway]
   490  	req := 6
   491  	if len(args) < req {
   492  		return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args))
   493  	}
   494  	m, err := extractReserved(args, map[string]int{
   495  		"esi":        paramList,
   496  		"etag":       paramSingle,
   497  		"label":      paramSingle,
   498  		"rd":         paramSingle,
   499  		"rt":         paramList,
   500  		"encap":      paramSingle,
   501  		"router-mac": paramSingle})
   502  	if err != nil {
   503  		return nil, nil, err
   504  	}
   505  	if len(m[""]) < 2 {
   506  		return nil, nil, fmt.Errorf("specify mac and ip address")
   507  	}
   508  	macStr := m[""][0]
   509  	ipStr := m[""][1]
   510  	eTagStr := ""
   511  	labelStr := ""
   512  	if len(m[""]) == 2 {
   513  		if len(m["etag"]) == 0 || len(m["label"]) == 0 {
   514  			return nil, nil, fmt.Errorf("specify etag and label")
   515  		}
   516  		eTagStr = m["etag"][0]
   517  		labelStr = m["label"][0]
   518  	} else if len(m[""]) == 3 {
   519  		if len(m["label"]) == 0 {
   520  			return nil, nil, fmt.Errorf("specify label")
   521  		}
   522  		eTagStr = m[""][2]
   523  		labelStr = m["label"][0]
   524  	} else {
   525  		eTagStr = m[""][2]
   526  		labelStr = m[""][3]
   527  	}
   528  	if len(m["rd"]) == 0 {
   529  		return nil, nil, fmt.Errorf("specify rd")
   530  	}
   531  
   532  	mac, err := net.ParseMAC(macStr)
   533  	if err != nil {
   534  		return nil, nil, fmt.Errorf("invalid mac address: %s", macStr)
   535  	}
   536  
   537  	ip := net.ParseIP(ipStr)
   538  	ipLen := 0
   539  	if ip == nil {
   540  		return nil, nil, fmt.Errorf("invalid ip address: %s", ipStr)
   541  	} else if ip.IsUnspecified() {
   542  		ip = nil
   543  	} else if ip.To4() != nil {
   544  		ipLen = net.IPv4len * 8
   545  	} else {
   546  		ipLen = net.IPv6len * 8
   547  	}
   548  
   549  	esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"])
   550  	if err != nil {
   551  		return nil, nil, err
   552  	}
   553  
   554  	eTag, err := strconv.ParseUint(eTagStr, 10, 32)
   555  	if err != nil {
   556  		return nil, nil, fmt.Errorf("invalid etag: %s: %s", eTagStr, err)
   557  	}
   558  
   559  	var labels []uint32
   560  	for _, l := range strings.SplitN(labelStr, ",", 2) {
   561  		label, err := strconv.ParseUint(l, 10, 32)
   562  		if err != nil {
   563  			return nil, nil, fmt.Errorf("invalid label: %s: %s", labelStr, err)
   564  		}
   565  		labels = append(labels, uint32(label))
   566  	}
   567  
   568  	rd, err := bgp.ParseRouteDistinguisher(m["rd"][0])
   569  	if err != nil {
   570  		return nil, nil, err
   571  	}
   572  
   573  	extcomms := make([]string, 0)
   574  	if len(m["rt"]) > 0 {
   575  		extcomms = append(extcomms, "rt")
   576  		extcomms = append(extcomms, m["rt"]...)
   577  	}
   578  	if len(m["encap"]) > 0 {
   579  		extcomms = append(extcomms, "encap", m["encap"][0])
   580  	}
   581  
   582  	if len(m["router-mac"]) != 0 {
   583  		_, err := net.ParseMAC(m["router-mac"][0])
   584  		if err != nil {
   585  			return nil, nil, fmt.Errorf("invalid router-mac address: %s", m["router-mac"][0])
   586  		}
   587  		extcomms = append(extcomms, "router-mac", m["router-mac"][0])
   588  	}
   589  
   590  	for _, a := range args {
   591  		if a == "default-gateway" {
   592  			extcomms = append(extcomms, "default-gateway")
   593  			break
   594  		}
   595  	}
   596  
   597  	r := &bgp.EVPNMacIPAdvertisementRoute{
   598  		RD:               rd,
   599  		ESI:              esi,
   600  		MacAddressLength: 48,
   601  		MacAddress:       mac,
   602  		IPAddressLength:  uint8(ipLen),
   603  		IPAddress:        ip,
   604  		Labels:           labels,
   605  		ETag:             uint32(eTag),
   606  	}
   607  	return bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, r), extcomms, nil
   608  }
   609  
   610  func parseEvpnMulticastArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
   611  	// Format:
   612  	// <ip address> etag <etag> rd <rd> [rt <rt>...] [encap <encap type>]
   613  	// or
   614  	// <ip address> <etag> rd <rd> [rt <rt>...] [encap <encap type>]
   615  	req := 4
   616  	if len(args) < req {
   617  		return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args))
   618  	}
   619  	m, err := extractReserved(args, map[string]int{
   620  		"etag":  paramSingle,
   621  		"rd":    paramSingle,
   622  		"rt":    paramList,
   623  		"encap": paramSingle})
   624  	if err != nil {
   625  		return nil, nil, err
   626  	}
   627  	if len(m[""]) < 1 {
   628  		return nil, nil, fmt.Errorf("specify ip address")
   629  	}
   630  	ipStr := m[""][0]
   631  	eTagStr := ""
   632  	if len(m[""]) == 1 {
   633  		if len(m["etag"]) == 0 {
   634  			return nil, nil, fmt.Errorf("specify etag")
   635  		}
   636  		eTagStr = m["etag"][0]
   637  	} else {
   638  		eTagStr = m[""][1]
   639  	}
   640  	if len(m["rd"]) == 0 {
   641  		return nil, nil, fmt.Errorf("specify rd")
   642  	}
   643  
   644  	ip := net.ParseIP(ipStr)
   645  	ipLen := 0
   646  	if ip == nil {
   647  		return nil, nil, fmt.Errorf("invalid ip address: %s", ipStr)
   648  	} else if ip.IsUnspecified() {
   649  		ip = nil
   650  	} else if ip.To4() != nil {
   651  		ipLen = net.IPv4len * 8
   652  	} else {
   653  		ipLen = net.IPv6len * 8
   654  	}
   655  
   656  	eTag, err := strconv.ParseUint(eTagStr, 10, 32)
   657  	if err != nil {
   658  		return nil, nil, fmt.Errorf("invalid etag: %s: %s", eTagStr, err)
   659  	}
   660  
   661  	rd, err := bgp.ParseRouteDistinguisher(m["rd"][0])
   662  	if err != nil {
   663  		return nil, nil, err
   664  	}
   665  
   666  	extcomms := make([]string, 0)
   667  	if len(m["rt"]) > 0 {
   668  		extcomms = append(extcomms, "rt")
   669  		extcomms = append(extcomms, m["rt"]...)
   670  	}
   671  	if len(m["encap"]) > 0 {
   672  		extcomms = append(extcomms, "encap", m["encap"][0])
   673  	}
   674  
   675  	r := &bgp.EVPNMulticastEthernetTagRoute{
   676  		RD:              rd,
   677  		IPAddressLength: uint8(ipLen),
   678  		IPAddress:       ip,
   679  		ETag:            uint32(eTag),
   680  	}
   681  	return bgp.NewEVPNNLRI(bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, r), extcomms, nil
   682  }
   683  
   684  func parseEvpnEthernetSegmentArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
   685  	// Format:
   686  	// <ip address> esi <esi> rd <rd> [rt <rt>...] [encap <encap type>]
   687  	req := 5
   688  	if len(args) < req {
   689  		return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args))
   690  	}
   691  	m, err := extractReserved(args, map[string]int{
   692  		"esi":   paramList,
   693  		"rd":    paramSingle,
   694  		"rt":    paramList,
   695  		"encap": paramSingle})
   696  	if err != nil {
   697  		return nil, nil, err
   698  	}
   699  	if len(m[""]) < 1 {
   700  		return nil, nil, fmt.Errorf("specify ip address")
   701  	}
   702  	for _, f := range []string{"esi", "rd"} {
   703  		for len(m[f]) == 0 {
   704  			return nil, nil, fmt.Errorf("specify %s", f)
   705  		}
   706  	}
   707  
   708  	ip := net.ParseIP(m[""][0])
   709  	ipLen := 0
   710  	if ip == nil {
   711  		return nil, nil, fmt.Errorf("invalid ip address: %s", m[""][0])
   712  	} else if ip.IsUnspecified() {
   713  		ip = nil
   714  	} else if ip.To4() != nil {
   715  		ipLen = net.IPv4len * 8
   716  	} else {
   717  		ipLen = net.IPv6len * 8
   718  	}
   719  
   720  	esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"])
   721  	if err != nil {
   722  		return nil, nil, err
   723  	}
   724  
   725  	rd, err := bgp.ParseRouteDistinguisher(m["rd"][0])
   726  	if err != nil {
   727  		return nil, nil, err
   728  	}
   729  
   730  	extcomms := make([]string, 0)
   731  	if len(m["rt"]) > 0 {
   732  		extcomms = append(extcomms, "rt")
   733  		extcomms = append(extcomms, m["rt"]...)
   734  	}
   735  	if len(m["encap"]) > 0 {
   736  		extcomms = append(extcomms, "encap", m["encap"][0])
   737  	}
   738  
   739  	r := &bgp.EVPNEthernetSegmentRoute{
   740  		RD:              rd,
   741  		ESI:             esi,
   742  		IPAddressLength: uint8(ipLen),
   743  		IPAddress:       ip,
   744  	}
   745  	return bgp.NewEVPNNLRI(bgp.EVPN_ETHERNET_SEGMENT_ROUTE, r), extcomms, nil
   746  }
   747  
   748  func parseEvpnIPPrefixArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
   749  	// Format:
   750  	// <ip prefix> [gw <gateway>] [esi <esi>] etag <etag> [label <label>] rd <rd> [rt <rt>...] [encap <encap type>]
   751  	req := 5
   752  	if len(args) < req {
   753  		return nil, nil, fmt.Errorf("%d args required at least, but got %d", req, len(args))
   754  	}
   755  	m, err := extractReserved(args, map[string]int{
   756  		"gw":         paramSingle,
   757  		"esi":        paramList,
   758  		"etag":       paramSingle,
   759  		"label":      paramSingle,
   760  		"rd":         paramSingle,
   761  		"rt":         paramList,
   762  		"encap":      paramSingle,
   763  		"router-mac": paramSingle})
   764  	if err != nil {
   765  		return nil, nil, err
   766  	}
   767  	if len(m[""]) < 1 {
   768  		return nil, nil, fmt.Errorf("specify prefix")
   769  	}
   770  	for _, f := range []string{"etag", "rd"} {
   771  		for len(m[f]) == 0 {
   772  			return nil, nil, fmt.Errorf("specify %s", f)
   773  		}
   774  	}
   775  
   776  	_, nw, err := net.ParseCIDR(m[""][0])
   777  	if err != nil {
   778  		return nil, nil, err
   779  	}
   780  	ones, _ := nw.Mask.Size()
   781  
   782  	var gw net.IP
   783  	if len(m["gw"]) > 0 {
   784  		gw = net.ParseIP(m["gw"][0])
   785  	}
   786  
   787  	rd, err := bgp.ParseRouteDistinguisher(m["rd"][0])
   788  	if err != nil {
   789  		return nil, nil, err
   790  	}
   791  
   792  	esi, err := bgp.ParseEthernetSegmentIdentifier(m["esi"])
   793  	if err != nil {
   794  		return nil, nil, err
   795  	}
   796  
   797  	e, err := strconv.ParseUint(m["etag"][0], 10, 32)
   798  	if err != nil {
   799  		return nil, nil, fmt.Errorf("invalid etag: %s: %s", m["etag"][0], err)
   800  	}
   801  	etag := uint32(e)
   802  
   803  	var label uint32
   804  	if len(m["label"]) > 0 {
   805  		e, err := strconv.ParseUint(m["label"][0], 10, 32)
   806  		if err != nil {
   807  			return nil, nil, fmt.Errorf("invalid label: %s: %s", m["label"][0], err)
   808  		}
   809  		label = uint32(e)
   810  	}
   811  
   812  	extcomms := make([]string, 0)
   813  	if len(m["rt"]) > 0 {
   814  		extcomms = append(extcomms, "rt")
   815  		extcomms = append(extcomms, m["rt"]...)
   816  	}
   817  	if len(m["encap"]) > 0 {
   818  		extcomms = append(extcomms, "encap", m["encap"][0])
   819  	}
   820  	if len(m["router-mac"]) > 0 {
   821  		extcomms = append(extcomms, "router-mac", m["router-mac"][0])
   822  	}
   823  
   824  	r := &bgp.EVPNIPPrefixRoute{
   825  		RD:             rd,
   826  		ESI:            esi,
   827  		ETag:           etag,
   828  		IPPrefixLength: uint8(ones),
   829  		IPPrefix:       nw.IP,
   830  		GWIPAddress:    gw,
   831  		Label:          label,
   832  	}
   833  	return bgp.NewEVPNNLRI(bgp.EVPN_IP_PREFIX, r), extcomms, nil
   834  }
   835  
   836  func parseEvpnArgs(args []string) (bgp.AddrPrefixInterface, []string, error) {
   837  	if len(args) < 1 {
   838  		return nil, nil, fmt.Errorf("lack of args. need 1 but %d", len(args))
   839  	}
   840  	subtype := args[0]
   841  	args = args[1:]
   842  	switch subtype {
   843  	case "a-d":
   844  		return parseEvpnEthernetAutoDiscoveryArgs(args)
   845  	case "macadv":
   846  		return parseEvpnMacAdvArgs(args)
   847  	case "multicast":
   848  		return parseEvpnMulticastArgs(args)
   849  	case "esi":
   850  		return parseEvpnEthernetSegmentArgs(args)
   851  	case "prefix":
   852  		return parseEvpnIPPrefixArgs(args)
   853  	}
   854  	return nil, nil, fmt.Errorf("invalid subtype. expect [macadv|multicast|prefix] but %s", subtype)
   855  }
   856  
   857  func extractOrigin(args []string) ([]string, bgp.PathAttributeInterface, error) {
   858  	typ := bgp.BGP_ORIGIN_ATTR_TYPE_INCOMPLETE
   859  	for idx, arg := range args {
   860  		if arg == "origin" && len(args) > (idx+1) {
   861  			switch args[idx+1] {
   862  			case "igp":
   863  				typ = bgp.BGP_ORIGIN_ATTR_TYPE_IGP
   864  			case "egp":
   865  				typ = bgp.BGP_ORIGIN_ATTR_TYPE_EGP
   866  			case "incomplete":
   867  			default:
   868  				return nil, nil, fmt.Errorf("invalid origin type. expect [igp|egp|incomplete] but %s", args[idx+1])
   869  			}
   870  			args = append(args[:idx], args[idx+2:]...)
   871  			break
   872  		}
   873  	}
   874  	return args, bgp.NewPathAttributeOrigin(uint8(typ)), nil
   875  }
   876  
   877  func toAs4Value(s string) (uint32, error) {
   878  	if strings.Contains(s, ".") {
   879  		v := strings.Split(s, ".")
   880  		upper, err := strconv.ParseUint(v[0], 10, 16)
   881  		if err != nil {
   882  			return 0, nil
   883  		}
   884  		lower, err := strconv.ParseUint(v[1], 10, 16)
   885  		if err != nil {
   886  			return 0, nil
   887  		}
   888  		return uint32(upper<<16 | lower), nil
   889  	}
   890  	i, err := strconv.ParseUint(s, 10, 32)
   891  	if err != nil {
   892  		return 0, err
   893  	}
   894  	return uint32(i), nil
   895  }
   896  
   897  var (
   898  	_regexpASPathGroups  = regexp.MustCompile("[{}]")
   899  	_regexpASPathSegment = regexp.MustCompile(`,|\s+`)
   900  )
   901  
   902  func newAsPath(aspath string) (bgp.PathAttributeInterface, error) {
   903  	// For the first step, parses "aspath" into a list of uint32 list.
   904  	// e.g.) "10 20 {30,40} 50" -> [][]uint32{{10, 20}, {30, 40}, {50}}
   905  	segments := _regexpASPathGroups.Split(aspath, -1)
   906  	asPathPrams := make([]bgp.AsPathParamInterface, 0, len(segments))
   907  	for idx, segment := range segments {
   908  		if segment == "" {
   909  			continue
   910  		}
   911  		nums := _regexpASPathSegment.Split(segment, -1)
   912  		asNums := make([]uint32, 0, len(nums))
   913  		for _, n := range nums {
   914  			if n == "" {
   915  				continue
   916  			}
   917  			if asn, err := toAs4Value(n); err != nil {
   918  				return nil, err
   919  			} else {
   920  				asNums = append(asNums, uint32(asn))
   921  			}
   922  		}
   923  		// Assumes "idx" is even, the given "segment" is of type AS_SEQUENCE,
   924  		// otherwise AS_SET, because the "segment" enclosed in parentheses is
   925  		// of type AS_SET.
   926  		if idx%2 == 0 {
   927  			asPathPrams = append(asPathPrams, bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, asNums))
   928  		} else {
   929  			asPathPrams = append(asPathPrams, bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, asNums))
   930  		}
   931  	}
   932  	return bgp.NewPathAttributeAsPath(asPathPrams), nil
   933  }
   934  
   935  func extractAsPath(args []string) ([]string, bgp.PathAttributeInterface, error) {
   936  	for idx, arg := range args {
   937  		if arg == "aspath" && len(args) > (idx+1) {
   938  			attr, err := newAsPath(args[idx+1])
   939  			if err != nil {
   940  				return nil, nil, err
   941  			}
   942  			args = append(args[:idx], args[idx+2:]...)
   943  			return args, attr, nil
   944  		}
   945  	}
   946  	return args, nil, nil
   947  }
   948  
   949  func extractNexthop(rf bgp.RouteFamily, args []string) ([]string, string, error) {
   950  	afi, _ := bgp.RouteFamilyToAfiSafi(rf)
   951  	nexthop := "0.0.0.0"
   952  	if afi == bgp.AFI_IP6 {
   953  		nexthop = "::"
   954  	}
   955  	for idx, arg := range args {
   956  		if arg == "nexthop" && len(args) > (idx+1) {
   957  			if net.ParseIP(args[idx+1]) == nil {
   958  				return nil, "", fmt.Errorf("invalid nexthop address")
   959  			}
   960  			nexthop = args[idx+1]
   961  			args = append(args[:idx], args[idx+2:]...)
   962  			break
   963  		}
   964  	}
   965  	return args, nexthop, nil
   966  }
   967  
   968  func extractLocalPref(args []string) ([]string, bgp.PathAttributeInterface, error) {
   969  	for idx, arg := range args {
   970  		if arg == "local-pref" && len(args) > (idx+1) {
   971  			metric, err := strconv.ParseUint(args[idx+1], 10, 32)
   972  			if err != nil {
   973  				return nil, nil, err
   974  			}
   975  			args = append(args[:idx], args[idx+2:]...)
   976  			return args, bgp.NewPathAttributeLocalPref(uint32(metric)), nil
   977  		}
   978  	}
   979  	return args, nil, nil
   980  }
   981  
   982  func extractMed(args []string) ([]string, bgp.PathAttributeInterface, error) {
   983  	for idx, arg := range args {
   984  		if arg == "med" && len(args) > (idx+1) {
   985  			med, err := strconv.ParseUint(args[idx+1], 10, 32)
   986  			if err != nil {
   987  				return nil, nil, err
   988  			}
   989  			args = append(args[:idx], args[idx+2:]...)
   990  			return args, bgp.NewPathAttributeMultiExitDisc(uint32(med)), nil
   991  		}
   992  	}
   993  	return args, nil, nil
   994  }
   995  
   996  func extractCommunity(args []string) ([]string, bgp.PathAttributeInterface, error) {
   997  	for idx, arg := range args {
   998  		if arg == "community" && len(args) > (idx+1) {
   999  			elems := strings.Split(args[idx+1], ",")
  1000  			comms := make([]uint32, 0, 1)
  1001  			for _, elem := range elems {
  1002  				c, err := table.ParseCommunity(elem)
  1003  				if err != nil {
  1004  					return nil, nil, err
  1005  				}
  1006  				comms = append(comms, c)
  1007  			}
  1008  			args = append(args[:idx], args[idx+2:]...)
  1009  			return args, bgp.NewPathAttributeCommunities(comms), nil
  1010  		}
  1011  	}
  1012  	return args, nil, nil
  1013  }
  1014  
  1015  func extractLargeCommunity(args []string) ([]string, bgp.PathAttributeInterface, error) {
  1016  	for idx, arg := range args {
  1017  		if arg == "large-community" && len(args) > (idx+1) {
  1018  			elems := strings.Split(args[idx+1], ",")
  1019  			comms := make([]*bgp.LargeCommunity, 0, 1)
  1020  			for _, elem := range elems {
  1021  				c, err := bgp.ParseLargeCommunity(elem)
  1022  				if err != nil {
  1023  					return nil, nil, err
  1024  				}
  1025  				comms = append(comms, c)
  1026  			}
  1027  			args = append(args[:idx], args[idx+2:]...)
  1028  			return args, bgp.NewPathAttributeLargeCommunities(comms), nil
  1029  		}
  1030  	}
  1031  	return args, nil, nil
  1032  }
  1033  
  1034  func extractPmsiTunnel(args []string) ([]string, bgp.PathAttributeInterface, error) {
  1035  	for idx, arg := range args {
  1036  		if arg == "pmsi" {
  1037  			pmsi, err := bgp.ParsePmsiTunnel(args[idx+1:])
  1038  			if err != nil {
  1039  				return nil, nil, err
  1040  			}
  1041  			if pmsi.IsLeafInfoRequired {
  1042  				return append(args[:idx], args[idx+5:]...), pmsi, nil
  1043  			}
  1044  			return append(args[:idx], args[idx+4:]...), pmsi, nil
  1045  		}
  1046  	}
  1047  	return args, nil, nil
  1048  }
  1049  
  1050  func extractAigp(args []string) ([]string, bgp.PathAttributeInterface, error) {
  1051  	for idx, arg := range args {
  1052  		if arg == "aigp" {
  1053  			if len(args) < (idx + 3) {
  1054  				return nil, nil, fmt.Errorf("invalid aigp format")
  1055  			}
  1056  			typ := args[idx+1]
  1057  			switch typ {
  1058  			case "metric":
  1059  				metric, err := strconv.ParseUint(args[idx+2], 10, 64)
  1060  				if err != nil {
  1061  					return nil, nil, err
  1062  				}
  1063  				aigp := bgp.NewPathAttributeAigp([]bgp.AigpTLVInterface{bgp.NewAigpTLVIgpMetric(uint64(metric))})
  1064  				return append(args[:idx], args[idx+3:]...), aigp, nil
  1065  			default:
  1066  				return nil, nil, fmt.Errorf("unknown aigp type: %s", typ)
  1067  			}
  1068  		}
  1069  	}
  1070  	return args, nil, nil
  1071  }
  1072  
  1073  func extractAggregator(args []string) ([]string, bgp.PathAttributeInterface, error) {
  1074  	for idx, arg := range args {
  1075  		if arg == "aggregator" {
  1076  			if len(args) < (idx + 1) {
  1077  				return nil, nil, fmt.Errorf("invalid aggregator format")
  1078  			}
  1079  			v := strings.SplitN(args[idx+1], ":", 2)
  1080  			if len(v) != 2 {
  1081  				return nil, nil, fmt.Errorf("invalid aggregator format")
  1082  			}
  1083  			as, err := strconv.ParseUint(v[0], 10, 32)
  1084  			if err != nil {
  1085  				return nil, nil, fmt.Errorf("invalid aggregator format")
  1086  			}
  1087  			attr := bgp.NewPathAttributeAggregator(uint32(as), net.ParseIP(v[1]).String())
  1088  			return append(args[:idx], args[idx+2:]...), attr, nil
  1089  		}
  1090  	}
  1091  	return args, nil, nil
  1092  }
  1093  
  1094  func parsePath(rf bgp.RouteFamily, args []string) (*api.Path, error) {
  1095  	var nlri bgp.AddrPrefixInterface
  1096  	var extcomms []string
  1097  	var err error
  1098  	attrs := make([]bgp.PathAttributeInterface, 0, 1)
  1099  
  1100  	fns := []func([]string) ([]string, bgp.PathAttributeInterface, error){
  1101  		extractOrigin,         // 1 ORIGIN
  1102  		extractAsPath,         // 2 AS_PATH
  1103  		extractMed,            // 4 MULTI_EXIT_DISC
  1104  		extractLocalPref,      // 5 LOCAL_PREF
  1105  		extractAggregator,     // 7 AGGREGATOR
  1106  		extractCommunity,      // 8 COMMUNITY
  1107  		extractPmsiTunnel,     // 22 PMSI_TUNNEL
  1108  		extractAigp,           // 26 AIGP
  1109  		extractLargeCommunity, // 32 LARGE_COMMUNITY
  1110  	}
  1111  
  1112  	for _, fn := range fns {
  1113  		var a bgp.PathAttributeInterface
  1114  		args, a, err = fn(args)
  1115  		if err != nil {
  1116  			return nil, err
  1117  		}
  1118  		if a != nil {
  1119  			attrs = append(attrs, a)
  1120  		}
  1121  	}
  1122  
  1123  	args, nexthop, err := extractNexthop(rf, args)
  1124  	if err != nil {
  1125  		return nil, err
  1126  	}
  1127  
  1128  	switch rf {
  1129  	case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC:
  1130  		if len(args) < 1 {
  1131  			return nil, fmt.Errorf("invalid format")
  1132  		}
  1133  		ip, nw, err := net.ParseCIDR(args[0])
  1134  		if err != nil {
  1135  			return nil, err
  1136  		}
  1137  		ones, _ := nw.Mask.Size()
  1138  		if rf == bgp.RF_IPv4_UC {
  1139  			if ip.To4() == nil {
  1140  				return nil, fmt.Errorf("invalid ipv4 prefix")
  1141  			}
  1142  			nlri = bgp.NewIPAddrPrefix(uint8(ones), ip.String())
  1143  		} else {
  1144  			if ip.To16() == nil {
  1145  				return nil, fmt.Errorf("invalid ipv6 prefix")
  1146  			}
  1147  			nlri = bgp.NewIPv6AddrPrefix(uint8(ones), ip.String())
  1148  		}
  1149  
  1150  		if len(args) > 2 && args[1] == "identifier" {
  1151  			id, err := strconv.ParseUint(args[2], 10, 32)
  1152  			if err != nil {
  1153  				return nil, fmt.Errorf("invalid format")
  1154  			}
  1155  			nlri.SetPathIdentifier(uint32(id))
  1156  			extcomms = args[3:]
  1157  		} else {
  1158  			extcomms = args[1:]
  1159  		}
  1160  
  1161  	case bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN:
  1162  		if len(args) < 5 || args[1] != "label" || args[3] != "rd" {
  1163  			return nil, fmt.Errorf("invalid format")
  1164  		}
  1165  		ip, nw, err := net.ParseCIDR(args[0])
  1166  		if err != nil {
  1167  			return nil, err
  1168  		}
  1169  		ones, _ := nw.Mask.Size()
  1170  
  1171  		label, err := strconv.ParseUint(args[2], 10, 32)
  1172  		if err != nil {
  1173  			return nil, fmt.Errorf("invalid format")
  1174  		}
  1175  		mpls := bgp.NewMPLSLabelStack(uint32(label))
  1176  
  1177  		rd, err := bgp.ParseRouteDistinguisher(args[4])
  1178  		if err != nil {
  1179  			return nil, err
  1180  		}
  1181  
  1182  		extcomms = args[5:]
  1183  
  1184  		if rf == bgp.RF_IPv4_VPN {
  1185  			if ip.To4() == nil {
  1186  				return nil, fmt.Errorf("invalid ipv4 prefix")
  1187  			}
  1188  			nlri = bgp.NewLabeledVPNIPAddrPrefix(uint8(ones), ip.String(), *mpls, rd)
  1189  		} else {
  1190  			if ip.To16() == nil {
  1191  				return nil, fmt.Errorf("invalid ipv6 prefix")
  1192  			}
  1193  			nlri = bgp.NewLabeledVPNIPv6AddrPrefix(uint8(ones), ip.String(), *mpls, rd)
  1194  		}
  1195  	case bgp.RF_IPv4_MPLS, bgp.RF_IPv6_MPLS:
  1196  		if len(args) < 2 {
  1197  			return nil, fmt.Errorf("invalid format")
  1198  		}
  1199  
  1200  		ip, nw, err := net.ParseCIDR(args[0])
  1201  		if err != nil {
  1202  			return nil, err
  1203  		}
  1204  		ones, _ := nw.Mask.Size()
  1205  
  1206  		mpls, err := bgp.ParseMPLSLabelStack(args[1])
  1207  		if err != nil {
  1208  			return nil, err
  1209  		}
  1210  
  1211  		extcomms = args[2:]
  1212  
  1213  		if rf == bgp.RF_IPv4_MPLS {
  1214  			if ip.To4() == nil {
  1215  				return nil, fmt.Errorf("invalid ipv4 prefix")
  1216  			}
  1217  			nlri = bgp.NewLabeledIPAddrPrefix(uint8(ones), ip.String(), *mpls)
  1218  		} else {
  1219  			if ip.To4() != nil {
  1220  				return nil, fmt.Errorf("invalid ipv6 prefix")
  1221  			}
  1222  			nlri = bgp.NewLabeledIPv6AddrPrefix(uint8(ones), ip.String(), *mpls)
  1223  		}
  1224  	case bgp.RF_EVPN:
  1225  		nlri, extcomms, err = parseEvpnArgs(args)
  1226  	case bgp.RF_FS_IPv4_UC, bgp.RF_FS_IPv4_VPN, bgp.RF_FS_IPv6_UC, bgp.RF_FS_IPv6_VPN, bgp.RF_FS_L2_VPN:
  1227  		nlri, extcomms, err = parseFlowSpecArgs(rf, args)
  1228  	case bgp.RF_OPAQUE:
  1229  		m, err := extractReserved(args, map[string]int{
  1230  			"key":   paramSingle,
  1231  			"value": paramSingle})
  1232  		if err != nil {
  1233  			return nil, err
  1234  		}
  1235  		if len(m["key"]) != 1 {
  1236  			return nil, fmt.Errorf("opaque nlri key missing")
  1237  		}
  1238  		if len(m["value"]) > 0 {
  1239  			nlri = bgp.NewOpaqueNLRI([]byte(m["key"][0]), []byte(m["value"][0]))
  1240  		} else {
  1241  			nlri = bgp.NewOpaqueNLRI([]byte(m["key"][0]), nil)
  1242  		}
  1243  	default:
  1244  		return nil, fmt.Errorf("Unsupported route family: %s", rf)
  1245  	}
  1246  	if err != nil {
  1247  		return nil, err
  1248  	}
  1249  
  1250  	if rf == bgp.RF_IPv4_UC && net.ParseIP(nexthop).To4() != nil {
  1251  		attrs = append(attrs, bgp.NewPathAttributeNextHop(nexthop))
  1252  	} else {
  1253  		mpreach := bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri})
  1254  		attrs = append(attrs, mpreach)
  1255  	}
  1256  
  1257  	if extcomms != nil {
  1258  		extcomms, err := parseExtendedCommunities(extcomms)
  1259  		if err != nil {
  1260  			return nil, err
  1261  		}
  1262  		normalextcomms := make([]bgp.ExtendedCommunityInterface, 0)
  1263  		ipv6extcomms := make([]bgp.ExtendedCommunityInterface, 0)
  1264  		for _, com := range extcomms {
  1265  			switch com.(type) {
  1266  			case *bgp.RedirectIPv6AddressSpecificExtended:
  1267  				ipv6extcomms = append(ipv6extcomms, com)
  1268  			default:
  1269  				normalextcomms = append(normalextcomms, com)
  1270  			}
  1271  		}
  1272  		if len(normalextcomms) != 0 {
  1273  			p := bgp.NewPathAttributeExtendedCommunities(normalextcomms)
  1274  			attrs = append(attrs, p)
  1275  		}
  1276  		if len(ipv6extcomms) != 0 {
  1277  			ip6p := bgp.NewPathAttributeIP6ExtendedCommunities(ipv6extcomms)
  1278  			attrs = append(attrs, ip6p)
  1279  		}
  1280  	}
  1281  	sort.Slice(attrs, func(i, j int) bool { return attrs[i].GetType() < attrs[j].GetType() })
  1282  
  1283  	return apiutil.NewPath(nlri, false, attrs, time.Now()), nil
  1284  }
  1285  
  1286  func showGlobalRib(args []string) error {
  1287  	return showNeighborRib(cmdGlobal, "", args)
  1288  }
  1289  
  1290  func modPath(resource string, name, modtype string, args []string) error {
  1291  	f, err := checkAddressFamily(ipv4UC)
  1292  	if err != nil {
  1293  		return err
  1294  	}
  1295  	rf := apiutil.ToRouteFamily(f)
  1296  	path, err := parsePath(rf, args)
  1297  	if err != nil {
  1298  		cmdstr := "global"
  1299  		if resource == cmdVRF {
  1300  			cmdstr = fmt.Sprintf("vrf %s", name)
  1301  		}
  1302  		rdHelpMsgFmt := `
  1303      <RD> : xxx:yyy, xxx.xxx.xxx.xxx:yyy, xxx.xxx:yyy`
  1304  		ss := make([]string, 0, len(bgp.ProtocolNameMap))
  1305  		for _, v := range bgp.ProtocolNameMap {
  1306  			ss = append(ss, v)
  1307  		}
  1308  		sort.SliceStable(ss, func(i, j int) bool { return ss[i] < ss[j] })
  1309  		ss = append(ss, "<DEC_NUM>")
  1310  		ipProtocols := strings.Join(ss, ", ")
  1311  		ss = make([]string, 0, len(bgp.TCPFlagNameMap))
  1312  		for _, v := range bgp.TCPSortedFlags {
  1313  			ss = append(ss, bgp.TCPFlagNameMap[v])
  1314  		}
  1315  		tcpFlags := strings.Join(ss, ", ")
  1316  		ss = make([]string, 0, len(bgp.EthernetTypeNameMap))
  1317  		for _, v := range bgp.EthernetTypeNameMap {
  1318  			ss = append(ss, v)
  1319  		}
  1320  		sort.SliceStable(ss, func(i, j int) bool { return ss[i] < ss[j] })
  1321  		ss = append(ss, "<DEC_NUM>")
  1322  		etherTypes := strings.Join(ss, ", ")
  1323  		helpErrMap := map[bgp.RouteFamily]error{}
  1324  		baseHelpMsgFmt := fmt.Sprintf(`error: %s
  1325  usage: %s rib -a %%s %s <PREFIX> %%s [origin { igp | egp | incomplete }] [aspath <ASPATH>] [nexthop <ADDRESS>] [med <NUM>] [local-pref <NUM>] [community <COMMUNITY>] [aigp metric <NUM>] [large-community <LARGE_COMMUNITY>] [aggregator <AGGREGATOR>]
  1326  	<ASPATH>: <AS>[,<AS>],
  1327  	<COMMUNITY>: xxx:xxx|internet|planned-shut|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|blackhole|no-export|no-advertise|no-export-subconfed|no-peer,
  1328  	<LARGE_COMMUNITY>: xxx:xxx:xxx[,<LARGE_COMMUNITY>],
  1329  	<AGGREGATOR>: <AS>:<ADDRESS>`,
  1330  			err,
  1331  			cmdstr,
  1332  			// <address family>
  1333  			modtype,
  1334  			// <label, rd>
  1335  		)
  1336  		helpErrMap[bgp.RF_IPv4_UC] = fmt.Errorf(baseHelpMsgFmt, "ipv4", "[identifier <VALUE>]")
  1337  		helpErrMap[bgp.RF_IPv6_UC] = fmt.Errorf(baseHelpMsgFmt, "ipv6", "[identifier <VALUE>]")
  1338  		helpErrMap[bgp.RF_IPv4_VPN] = fmt.Errorf(baseHelpMsgFmt, "vpnv4", "label <LABEL> rd <RD> [rt <RT>]")
  1339  		helpErrMap[bgp.RF_IPv6_VPN] = fmt.Errorf(baseHelpMsgFmt, "vpnv6", "label <LABEL> rd <RD> [rt <RT>]")
  1340  		helpErrMap[bgp.RF_IPv4_MPLS] = fmt.Errorf(baseHelpMsgFmt, "ipv4-mpls", "<LABEL>")
  1341  		helpErrMap[bgp.RF_IPv6_MPLS] = fmt.Errorf(baseHelpMsgFmt, "ipv6-mpls", "<LABEL>")
  1342  
  1343  		fsHelpMsgFmt := fmt.Sprintf(`error: %s
  1344  usage: %s rib -a %%s %s%%s match <MATCH> then <THEN>%%s%%s%%s
  1345      <THEN> : { %s |
  1346                 %s |
  1347                 %s <RATE> [as <AS>] |
  1348                 %s <RT> |
  1349                 %s <DEC_NUM> |
  1350                 %s { sample | terminal | sample-terminal } }...
  1351      <RT> : xxx:yyy, xxx.xxx.xxx.xxx:yyy, xxxx::xxxx:yyy, xxx.xxx:yyy`,
  1352  			err,
  1353  			cmdstr,
  1354  			// <address family>
  1355  			modtype,
  1356  			// "" or " rd <RD>"
  1357  			// "" or " [rt <RT>]"
  1358  			// <help message for RD>
  1359  			// <MATCH>
  1360  			extCommNameMap[ctAccept],
  1361  			extCommNameMap[ctDiscard],
  1362  			extCommNameMap[ctRate],
  1363  			extCommNameMap[ctRedirect],
  1364  			extCommNameMap[ctMark],
  1365  			extCommNameMap[ctAction],
  1366  		)
  1367  		baseFsMatchExpr := fmt.Sprintf(`
  1368      <MATCH> : { %s <PREFIX> [<OFFSET>] |
  1369                  %s <PREFIX> [<OFFSET>] |
  1370                  %s <PROTOCOLS>... |
  1371                  %s <FRAGMENTS>... |
  1372                  %s <TCP_FLAGS>... |
  1373                  %s <ITEM>... |
  1374                  %s <ITEM>... |
  1375                  %s <ITEM>... |
  1376                  %s <ITEM>... |
  1377                  %s <ITEM>... |
  1378                  %s <ITEM>... |
  1379                  %s <ITEM>... %%s}...
  1380      <PROTOCOLS> : [&] [<|<=|>|>=|==|!=] <PROTOCOL>
  1381      <PROTOCOL> : %s
  1382      <FRAGMENTS> : [&] [=|!|!=] <FRAGMENT>
  1383      <FRAGMENT> : dont-fragment, is-fragment, first-fragment, last-fragment, not-a-fragment
  1384      <TCP_FLAGS> : [&] [=|!|!=] <TCP_FLAG>
  1385      <TCP_FLAG> : %s%%s
  1386      <ITEM> : [&] [<|<=|>|>=|==|!=] <DEC_NUM>`,
  1387  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_PREFIX],
  1388  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_PREFIX],
  1389  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_IP_PROTO],
  1390  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_FRAGMENT],
  1391  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_TCP_FLAG],
  1392  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_PORT],
  1393  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_PORT],
  1394  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_PORT],
  1395  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ICMP_TYPE],
  1396  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ICMP_CODE],
  1397  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_PKT_LEN],
  1398  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DSCP],
  1399  			// <additional help messages if exists>
  1400  			ipProtocols,
  1401  			tcpFlags,
  1402  			// <additional help messages if exists>
  1403  		)
  1404  		ipv4FsMatchExpr := fmt.Sprintf(baseFsMatchExpr, "", "")
  1405  		ipv6FsMatchExpr := fmt.Sprintf(baseFsMatchExpr, fmt.Sprintf(`|
  1406                  %s <ITEM>... `,
  1407  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LABEL]), "")
  1408  		l2vpnFsMatchExpr := fmt.Sprintf(baseFsMatchExpr, fmt.Sprintf(`|
  1409                  %s <ITEM>... |
  1410                  %s <MAC_ADDRESS> |
  1411                  %s <MAC_ADDRESS> |
  1412                  %s <ETHER_TYPES>... |
  1413                  %s <ITEM>... |
  1414                  %s <ITEM>... |
  1415                  %s <ITEM>... |
  1416                  %s <ITEM>... |
  1417                  %s <ITEM>... |
  1418                  %s <ITEM>... |
  1419                  %s <ITEM>... |
  1420                  %s <ITEM>... `,
  1421  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LABEL],
  1422  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_MAC],
  1423  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_MAC],
  1424  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ETHERNET_TYPE],
  1425  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_DSAP],
  1426  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_SSAP],
  1427  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_CONTROL],
  1428  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SNAP],
  1429  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_VID],
  1430  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_COS],
  1431  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_INNER_VID],
  1432  			bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_INNER_COS]), fmt.Sprintf(`
  1433      <ETHER_TYPES> : [&] [<|<=|>|>=|==|!=] <ETHER_TYPE>
  1434      <ETHER_TYPE> : %s`,
  1435  			etherTypes))
  1436  		helpErrMap[bgp.RF_FS_IPv4_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv4-flowspec", "", "", "", ipv4FsMatchExpr)
  1437  		helpErrMap[bgp.RF_FS_IPv6_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv6-flowspec", "", "", "", ipv6FsMatchExpr)
  1438  		helpErrMap[bgp.RF_FS_IPv4_VPN] = fmt.Errorf(fsHelpMsgFmt, "ipv4-l3vpn-flowspec", " rd <RD>", " [rt <RT>]", rdHelpMsgFmt, ipv4FsMatchExpr)
  1439  		helpErrMap[bgp.RF_FS_IPv6_VPN] = fmt.Errorf(fsHelpMsgFmt, "ipv6-l3vpn-flowspec", " rd <RD>", " [rt <RT>]", rdHelpMsgFmt, ipv6FsMatchExpr)
  1440  		helpErrMap[bgp.RF_FS_L2_VPN] = fmt.Errorf(fsHelpMsgFmt, "l2vpn-flowspec", " rd <RD>", " [rt <RT>]", rdHelpMsgFmt, l2vpnFsMatchExpr)
  1441  		helpErrMap[bgp.RF_EVPN] = fmt.Errorf(`error: %s
  1442  usage: %s rib %s { a-d <A-D> | macadv <MACADV> | multicast <MULTICAST> | esi <ESI> | prefix <PREFIX> } -a evpn
  1443      <A-D>       : esi <esi> etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [esi-label <esi-label> [single-active | all-active]]
  1444      <MACADV>    : <mac address> <ip address> [esi <esi>] etag <etag> label <label> rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>] [default-gateway]
  1445      <MULTICAST> : <ip address> etag <etag> rd <rd> [rt <rt>...] [encap <encap type>] [pmsi <type> [leaf-info-required] <label> <tunnel-id>]
  1446      <ESI>       : <ip address> esi <esi> rd <rd> [rt <rt>...] [encap <encap type>]
  1447      <PREFIX>    : <ip prefix> [gw <gateway>] [esi <esi>] etag <etag> [label <label>] rd <rd> [rt <rt>...] [encap <encap type>] [router-mac <mac address>]`,
  1448  			err,
  1449  			cmdstr,
  1450  			modtype,
  1451  		)
  1452  		helpErrMap[bgp.RF_OPAQUE] = fmt.Errorf(`error: %s
  1453  usage: %s rib %s key <KEY> [value <VALUE>]`,
  1454  			err,
  1455  			cmdstr,
  1456  			modtype,
  1457  		)
  1458  		if err, ok := helpErrMap[rf]; ok {
  1459  			return err
  1460  		}
  1461  		return err
  1462  	}
  1463  
  1464  	r := api.TableType_GLOBAL
  1465  	if resource == cmdVRF {
  1466  		r = api.TableType_VRF
  1467  	}
  1468  
  1469  	if modtype == cmdAdd {
  1470  		_, err = client.AddPath(ctx, &api.AddPathRequest{
  1471  			TableType: r,
  1472  			VrfId:     name,
  1473  			Path:      path,
  1474  		})
  1475  	} else {
  1476  		_, err = client.DeletePath(ctx, &api.DeletePathRequest{
  1477  			TableType: r,
  1478  			VrfId:     name,
  1479  			Path:      path,
  1480  		})
  1481  	}
  1482  	return err
  1483  }
  1484  
  1485  func showGlobalConfig() error {
  1486  	r, err := client.GetBgp(ctx, &api.GetBgpRequest{})
  1487  	if err != nil {
  1488  		return err
  1489  	}
  1490  	if globalOpts.Json {
  1491  		j, _ := json.Marshal(r.Global)
  1492  		fmt.Println(string(j))
  1493  		return nil
  1494  	}
  1495  	g := r.Global
  1496  	fmt.Println("AS:       ", g.As)
  1497  	fmt.Println("Router-ID:", g.RouterId)
  1498  	if len(g.ListenAddresses) > 0 {
  1499  		fmt.Printf("Listening Port: %d, Addresses: %s\n", g.ListenPort, strings.Join(g.ListenAddresses, ", "))
  1500  	}
  1501  	if g.UseMultiplePaths {
  1502  		fmt.Printf("Multipath: enabled")
  1503  	}
  1504  	return nil
  1505  }
  1506  
  1507  func modGlobalConfig(args []string) error {
  1508  	m, err := extractReserved(args, map[string]int{
  1509  		"as":               paramSingle,
  1510  		"router-id":        paramSingle,
  1511  		"listen-port":      paramSingle,
  1512  		"listen-addresses": paramList,
  1513  		"use-multipath":    paramFlag})
  1514  	if err != nil || len(m["as"]) != 1 || len(m["router-id"]) != 1 {
  1515  		return fmt.Errorf("usage: gobgp global as <VALUE> router-id <VALUE> [use-multipath] [listen-port <VALUE>] [listen-addresses <VALUE>...]")
  1516  	}
  1517  	asn, err := strconv.ParseUint(m["as"][0], 10, 32)
  1518  	if err != nil {
  1519  		return err
  1520  	}
  1521  	id := net.ParseIP(m["router-id"][0])
  1522  	if id.To4() == nil {
  1523  		return fmt.Errorf("invalid router-id format")
  1524  	}
  1525  	var port int64
  1526  	if len(m["listen-port"]) > 0 {
  1527  		// Note: GlobalConfig.Port is uint32 type, but the TCP/UDP port is
  1528  		// 16-bit length.
  1529  		port, err = strconv.ParseInt(m["listen-port"][0], 10, 16)
  1530  		if err != nil {
  1531  			return err
  1532  		}
  1533  	}
  1534  	useMultipath := false
  1535  	if _, ok := m["use-multipath"]; ok {
  1536  		useMultipath = true
  1537  	}
  1538  	_, err = client.StartBgp(ctx, &api.StartBgpRequest{
  1539  		Global: &api.Global{
  1540  			As:               uint32(asn),
  1541  			RouterId:         id.String(),
  1542  			ListenPort:       int32(port),
  1543  			ListenAddresses:  m["listen-addresses"],
  1544  			UseMultiplePaths: useMultipath,
  1545  		},
  1546  	})
  1547  	return err
  1548  }
  1549  
  1550  func newGlobalCmd() *cobra.Command {
  1551  	globalCmd := &cobra.Command{
  1552  		Use: cmdGlobal,
  1553  		Run: func(cmd *cobra.Command, args []string) {
  1554  			var err error
  1555  			if len(args) != 0 {
  1556  				err = modGlobalConfig(args)
  1557  			} else {
  1558  				err = showGlobalConfig()
  1559  			}
  1560  			if err != nil {
  1561  				exitWithError(err)
  1562  			}
  1563  		},
  1564  	}
  1565  
  1566  	ribCmd := &cobra.Command{
  1567  		Use: cmdRib,
  1568  		Run: func(cmd *cobra.Command, args []string) {
  1569  			if err := showGlobalRib(args); err != nil {
  1570  				exitWithError(err)
  1571  			}
  1572  		},
  1573  	}
  1574  
  1575  	ribCmd.PersistentFlags().StringVarP(&subOpts.AddressFamily, "address-family", "a", "", "address family")
  1576  
  1577  	for _, v := range []string{cmdAdd, cmdDel} {
  1578  		cmd := &cobra.Command{
  1579  			Use: v,
  1580  			Run: func(cmd *cobra.Command, args []string) {
  1581  				err := modPath(cmdGlobal, "", cmd.Use, args)
  1582  				if err != nil {
  1583  					exitWithError(err)
  1584  				}
  1585  			},
  1586  		}
  1587  		ribCmd.AddCommand(cmd)
  1588  
  1589  		if v == cmdDel {
  1590  			subcmd := &cobra.Command{
  1591  				Use: cmdAll,
  1592  				Run: func(cmd *cobra.Command, args []string) {
  1593  					family, err := checkAddressFamily(ipv4UC)
  1594  					if err != nil {
  1595  						exitWithError(err)
  1596  					}
  1597  					if _, err = client.DeletePath(ctx, &api.DeletePathRequest{
  1598  						TableType: api.TableType_GLOBAL,
  1599  						Family:    family,
  1600  					}); err != nil {
  1601  						exitWithError(err)
  1602  					}
  1603  				},
  1604  			}
  1605  			cmd.AddCommand(subcmd)
  1606  		}
  1607  	}
  1608  
  1609  	summaryCmd := &cobra.Command{
  1610  		Use: cmdSummary,
  1611  		Run: func(cmd *cobra.Command, args []string) {
  1612  			if err := showRibInfo(cmdGlobal, ""); err != nil {
  1613  				exitWithError(err)
  1614  			}
  1615  		},
  1616  	}
  1617  	ribCmd.AddCommand(summaryCmd)
  1618  
  1619  	policyCmd := &cobra.Command{
  1620  		Use: cmdPolicy,
  1621  		Run: func(cmd *cobra.Command, args []string) {
  1622  			if len(args) > 0 {
  1623  				exitWithError(fmt.Errorf("usage: gobgp global policy [{ import | export }]"))
  1624  			}
  1625  			for _, v := range []string{cmdImport, cmdExport} {
  1626  				if err := showNeighborPolicy("", v, 4); err != nil {
  1627  					exitWithError(err)
  1628  				}
  1629  			}
  1630  		},
  1631  	}
  1632  
  1633  	for _, v := range []string{cmdImport, cmdExport} {
  1634  		cmd := &cobra.Command{
  1635  			Use: v,
  1636  			Run: func(cmd *cobra.Command, args []string) {
  1637  				if err := showNeighborPolicy("", cmd.Use, 0); err != nil {
  1638  					exitWithError(err)
  1639  				}
  1640  			},
  1641  		}
  1642  
  1643  		for _, w := range []string{cmdAdd, cmdDel, cmdSet} {
  1644  			subcmd := &cobra.Command{
  1645  				Use: w,
  1646  				Run: func(subcmd *cobra.Command, args []string) {
  1647  					err := modNeighborPolicy("", cmd.Use, subcmd.Use, args)
  1648  					if err != nil {
  1649  						exitWithError(err)
  1650  					}
  1651  				},
  1652  			}
  1653  			cmd.AddCommand(subcmd)
  1654  		}
  1655  
  1656  		policyCmd.AddCommand(cmd)
  1657  	}
  1658  
  1659  	delCmd := &cobra.Command{
  1660  		Use: cmdDel,
  1661  	}
  1662  
  1663  	allCmd := &cobra.Command{
  1664  		Use: cmdAll,
  1665  		Run: func(cmd *cobra.Command, args []string) {
  1666  			if _, err := client.StopBgp(ctx, &api.StopBgpRequest{}); err != nil {
  1667  				exitWithError(err)
  1668  			}
  1669  		},
  1670  	}
  1671  	delCmd.AddCommand(allCmd)
  1672  
  1673  	globalCmd.AddCommand(ribCmd, policyCmd, delCmd)
  1674  	return globalCmd
  1675  }