github.com/osrg/gobgp@v2.0.0+incompatible/cmd/gobgp/policy.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  	"regexp"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/osrg/gobgp/internal/pkg/apiutil"
    29  
    30  	"github.com/spf13/cobra"
    31  
    32  	api "github.com/osrg/gobgp/api"
    33  	"github.com/osrg/gobgp/internal/pkg/config"
    34  	"github.com/osrg/gobgp/pkg/packet/bgp"
    35  )
    36  
    37  var (
    38  	_regexpCommunity      = regexp.MustCompile(`\^\^(\S+)\$\$`)
    39  	repexpCommunity       = regexp.MustCompile(`(\d+.)*\d+:\d+`)
    40  	regexpLargeCommunity  = regexp.MustCompile(`\d+:\d+:\d+`)
    41  	regexpCommunityString = regexp.MustCompile(`[\^\$]`)
    42  )
    43  
    44  func parseCommunityRegexp(arg string) (*regexp.Regexp, error) {
    45  	i, err := strconv.ParseUint(arg, 10, 32)
    46  	if err == nil {
    47  		return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff))
    48  	}
    49  	if repexpCommunity.MatchString(arg) {
    50  		return regexp.Compile(fmt.Sprintf("^%s$", arg))
    51  	}
    52  	for i, v := range bgp.WellKnownCommunityNameMap {
    53  		if strings.Replace(strings.ToLower(arg), "_", "-", -1) == v {
    54  			return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff))
    55  		}
    56  	}
    57  	exp, err := regexp.Compile(arg)
    58  	if err != nil {
    59  		return nil, fmt.Errorf("invalid community format: %s", arg)
    60  	}
    61  	return exp, nil
    62  }
    63  
    64  func parseExtCommunityRegexp(arg string) (bgp.ExtendedCommunityAttrSubType, *regexp.Regexp, error) {
    65  	var subtype bgp.ExtendedCommunityAttrSubType
    66  	elems := strings.SplitN(arg, ":", 2)
    67  	if len(elems) < 2 {
    68  		return subtype, nil, fmt.Errorf("invalid ext-community format([rt|soo]:<value>)")
    69  	}
    70  	switch strings.ToLower(elems[0]) {
    71  	case "rt":
    72  		subtype = bgp.EC_SUBTYPE_ROUTE_TARGET
    73  	case "soo":
    74  		subtype = bgp.EC_SUBTYPE_ROUTE_ORIGIN
    75  	default:
    76  		return subtype, nil, fmt.Errorf("unknown ext-community subtype. rt, soo is supported")
    77  	}
    78  	exp, err := parseCommunityRegexp(elems[1])
    79  	return subtype, exp, err
    80  }
    81  
    82  func parseLargeCommunityRegexp(arg string) (*regexp.Regexp, error) {
    83  	if regexpLargeCommunity.MatchString(arg) {
    84  		return regexp.Compile(fmt.Sprintf("^%s$", arg))
    85  	}
    86  	exp, err := regexp.Compile(arg)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("invalid large-community format: %s", arg)
    89  	}
    90  	return exp, nil
    91  }
    92  
    93  func routeTypePrettyString(s api.Conditions_RouteType) string {
    94  	switch s {
    95  	case api.Conditions_ROUTE_TYPE_EXTERNAL:
    96  		return "external"
    97  	case api.Conditions_ROUTE_TYPE_INTERNAL:
    98  		return "internal"
    99  	case api.Conditions_ROUTE_TYPE_LOCAL:
   100  		return "local"
   101  	}
   102  	return "unknown"
   103  }
   104  
   105  func prettyString(v interface{}) string {
   106  	switch a := v.(type) {
   107  	case *api.MatchSet:
   108  		var typ string
   109  		switch a.MatchType {
   110  		case api.MatchType_ALL:
   111  			typ = "all"
   112  		case api.MatchType_ANY:
   113  			typ = "any"
   114  		case api.MatchType_INVERT:
   115  			typ = "invert"
   116  		}
   117  		return fmt.Sprintf("%s %s", typ, a.GetName())
   118  	case *api.AsPathLength:
   119  		var typ string
   120  		switch a.LengthType {
   121  		case api.AsPathLengthType_EQ:
   122  			typ = "="
   123  		case api.AsPathLengthType_GE:
   124  			typ = ">="
   125  		case api.AsPathLengthType_LE:
   126  			typ = "<="
   127  		}
   128  		return fmt.Sprintf("%s%d", typ, a.Length)
   129  	case *api.CommunityAction:
   130  		l := regexpCommunityString.ReplaceAllString(strings.Join(a.Communities, ", "), "")
   131  		var typ string
   132  		switch a.ActionType {
   133  		case api.CommunityActionType_COMMUNITY_ADD:
   134  			typ = "add"
   135  		case api.CommunityActionType_COMMUNITY_REMOVE:
   136  			typ = "remove"
   137  		case api.CommunityActionType_COMMUNITY_REPLACE:
   138  			typ = "replace"
   139  		}
   140  		return fmt.Sprintf("%s[%s]", typ, l)
   141  	case *api.MedAction:
   142  		if a.ActionType == api.MedActionType_MED_MOD && a.Value > 0 {
   143  			return fmt.Sprintf("+%d", a.Value)
   144  		}
   145  		return fmt.Sprintf("%d", a.Value)
   146  	case *api.LocalPrefAction:
   147  		return fmt.Sprintf("%d", a.Value)
   148  	case *api.NexthopAction:
   149  		if a.Self {
   150  			return "self"
   151  		}
   152  		return a.Address
   153  	case *api.AsPrependAction:
   154  		return fmt.Sprintf("prepend %d %d times", a.Asn, a.Repeat)
   155  	}
   156  	return "unknown"
   157  }
   158  
   159  func formatDefinedSet(head bool, typ string, indent int, list []*api.DefinedSet) string {
   160  	if len(list) == 0 {
   161  		return "Nothing defined yet\n"
   162  	}
   163  	buff := bytes.NewBuffer(make([]byte, 0, 64))
   164  	sIndent := strings.Repeat(" ", indent)
   165  	maxNameLen := 0
   166  	for _, s := range list {
   167  		if len(s.GetName()) > maxNameLen {
   168  			maxNameLen = len(s.GetName())
   169  		}
   170  	}
   171  	if head {
   172  		if len("NAME") > maxNameLen {
   173  			maxNameLen = len("NAME")
   174  		}
   175  	}
   176  	format := fmt.Sprintf("%%-%ds  %%s\n", maxNameLen)
   177  	if head {
   178  		buff.WriteString(fmt.Sprintf(format, "NAME", typ))
   179  	}
   180  	for _, s := range list {
   181  		l := s.GetList()
   182  		if len(l) == 0 {
   183  			buff.WriteString(fmt.Sprintf(format, s.GetName(), ""))
   184  		}
   185  		for i, x := range l {
   186  			if typ == "COMMUNITY" || typ == "EXT-COMMUNITY" || typ == "LARGE-COMMUNITY" {
   187  				x = _regexpCommunity.ReplaceAllString(x, "$1")
   188  			}
   189  			if i == 0 {
   190  				buff.WriteString(fmt.Sprintf(format, s.GetName(), x))
   191  			} else {
   192  				buff.WriteString(fmt.Sprint(sIndent))
   193  				buff.WriteString(fmt.Sprintf(format, "", x))
   194  			}
   195  		}
   196  	}
   197  	return buff.String()
   198  }
   199  
   200  func showDefinedSet(v string, args []string) error {
   201  	var typ api.DefinedType
   202  	switch v {
   203  	case cmdPrefix:
   204  		typ = api.DefinedType_PREFIX
   205  	case cmdNeighbor:
   206  		typ = api.DefinedType_NEIGHBOR
   207  	case cmdAspath:
   208  		typ = api.DefinedType_AS_PATH
   209  	case cmdCommunity:
   210  		typ = api.DefinedType_COMMUNITY
   211  	case cmdExtcommunity:
   212  		typ = api.DefinedType_EXT_COMMUNITY
   213  	case cmdLargecommunity:
   214  		typ = api.DefinedType_LARGE_COMMUNITY
   215  	default:
   216  		return fmt.Errorf("unknown defined type: %s", v)
   217  	}
   218  	m := make([]*api.DefinedSet, 0)
   219  	var name string
   220  	if len(args) > 0 {
   221  		name = args[0]
   222  	}
   223  	stream, err := client.ListDefinedSet(ctx, &api.ListDefinedSetRequest{
   224  		DefinedType: typ,
   225  		Name:        name,
   226  	})
   227  	if err != nil {
   228  		return err
   229  	}
   230  	for {
   231  		r, err := stream.Recv()
   232  		if err == io.EOF {
   233  			break
   234  		} else if err != nil {
   235  			return err
   236  		}
   237  		m = append(m, r.DefinedSet)
   238  	}
   239  
   240  	if globalOpts.Json {
   241  		j, _ := json.Marshal(m)
   242  		fmt.Println(string(j))
   243  		return nil
   244  	}
   245  	if globalOpts.Quiet {
   246  		if len(args) > 0 {
   247  			fmt.Println(m)
   248  		} else {
   249  			for _, p := range m {
   250  				fmt.Println(p.GetName())
   251  			}
   252  		}
   253  		return nil
   254  	}
   255  	var output string
   256  	switch v {
   257  	case cmdPrefix:
   258  		output = formatDefinedSet(true, "PREFIX", 0, m)
   259  	case cmdNeighbor:
   260  		output = formatDefinedSet(true, "ADDRESS", 0, m)
   261  	case cmdAspath:
   262  		output = formatDefinedSet(true, "AS-PATH", 0, m)
   263  	case cmdCommunity:
   264  		output = formatDefinedSet(true, "COMMUNITY", 0, m)
   265  	case cmdExtcommunity:
   266  		output = formatDefinedSet(true, "EXT-COMMUNITY", 0, m)
   267  	case cmdLargecommunity:
   268  		output = formatDefinedSet(true, "LARGE-COMMUNITY", 0, m)
   269  	}
   270  	fmt.Print(output)
   271  	return nil
   272  }
   273  
   274  func parsePrefixSet(args []string) (*api.DefinedSet, error) {
   275  	if len(args) < 1 {
   276  		return nil, fmt.Errorf("empty neighbor set name")
   277  	}
   278  	name := args[0]
   279  	args = args[1:]
   280  	var list []*api.Prefix
   281  	if len(args) > 0 {
   282  		mask := ""
   283  		if len(args) > 1 {
   284  			mask = args[1]
   285  		}
   286  		min, max, err := config.ParseMaskLength(args[0], mask)
   287  		if err != nil {
   288  			return nil, err
   289  		}
   290  		prefix := &api.Prefix{
   291  			IpPrefix:      args[0],
   292  			MaskLengthMax: uint32(max),
   293  			MaskLengthMin: uint32(min),
   294  		}
   295  		list = []*api.Prefix{prefix}
   296  	}
   297  	return &api.DefinedSet{
   298  		DefinedType: api.DefinedType_PREFIX,
   299  		Name:        name,
   300  		Prefixes:    list,
   301  	}, nil
   302  }
   303  
   304  func parseNeighborSet(args []string) (*api.DefinedSet, error) {
   305  	if len(args) < 1 {
   306  		return nil, fmt.Errorf("empty neighbor set name")
   307  	}
   308  	name := args[0]
   309  	args = args[1:]
   310  	list := make([]string, 0, len(args[1:]))
   311  	for _, arg := range args {
   312  		address := net.ParseIP(arg)
   313  		if address.To4() != nil {
   314  			list = append(list, fmt.Sprintf("%s/32", arg))
   315  		} else if address.To16() != nil {
   316  			list = append(list, fmt.Sprintf("%s/128", arg))
   317  		} else {
   318  			_, _, err := net.ParseCIDR(arg)
   319  			if err != nil {
   320  				return nil, fmt.Errorf("invalid address or prefix: %s\nplease enter ipv4 or ipv6 format", arg)
   321  			}
   322  		}
   323  	}
   324  	return &api.DefinedSet{
   325  		DefinedType: api.DefinedType_NEIGHBOR,
   326  		Name:        name,
   327  		List:        list,
   328  	}, nil
   329  }
   330  
   331  func parseAsPathSet(args []string) (*api.DefinedSet, error) {
   332  	if len(args) < 1 {
   333  		return nil, fmt.Errorf("empty as-path set name")
   334  	}
   335  	name := args[0]
   336  	args = args[1:]
   337  	for _, arg := range args {
   338  		_, err := regexp.Compile(arg)
   339  		if err != nil {
   340  			return nil, err
   341  		}
   342  	}
   343  	return &api.DefinedSet{
   344  		DefinedType: api.DefinedType_AS_PATH,
   345  		Name:        name,
   346  		List:        args,
   347  	}, nil
   348  }
   349  
   350  func parseCommunitySet(args []string) (*api.DefinedSet, error) {
   351  	if len(args) < 1 {
   352  		return nil, fmt.Errorf("empty community set name")
   353  	}
   354  	name := args[0]
   355  	args = args[1:]
   356  	for _, arg := range args {
   357  		if _, err := parseCommunityRegexp(arg); err != nil {
   358  			return nil, err
   359  		}
   360  	}
   361  	return &api.DefinedSet{
   362  		DefinedType: api.DefinedType_COMMUNITY,
   363  		Name:        name,
   364  		List:        args,
   365  	}, nil
   366  }
   367  
   368  func parseExtCommunitySet(args []string) (*api.DefinedSet, error) {
   369  	if len(args) < 1 {
   370  		return nil, fmt.Errorf("empty ext-community set name")
   371  	}
   372  	name := args[0]
   373  	args = args[1:]
   374  	for _, arg := range args {
   375  		if _, _, err := parseExtCommunityRegexp(arg); err != nil {
   376  			return nil, err
   377  		}
   378  	}
   379  	return &api.DefinedSet{
   380  		DefinedType: api.DefinedType_EXT_COMMUNITY,
   381  		Name:        name,
   382  		List:        args,
   383  	}, nil
   384  }
   385  
   386  func parseLargeCommunitySet(args []string) (*api.DefinedSet, error) {
   387  	if len(args) < 1 {
   388  		return nil, fmt.Errorf("empty large-community set name")
   389  	}
   390  	name := args[0]
   391  	args = args[1:]
   392  	for _, arg := range args {
   393  		if _, err := parseLargeCommunityRegexp(arg); err != nil {
   394  			return nil, err
   395  		}
   396  	}
   397  	return &api.DefinedSet{
   398  		DefinedType: api.DefinedType_LARGE_COMMUNITY,
   399  		Name:        name,
   400  		List:        args,
   401  	}, nil
   402  }
   403  
   404  func parseDefinedSet(settype string, args []string) (*api.DefinedSet, error) {
   405  	if len(args) < 1 {
   406  		return nil, fmt.Errorf("empty large-community set name")
   407  	}
   408  
   409  	switch settype {
   410  	case cmdPrefix:
   411  		return parsePrefixSet(args)
   412  	case cmdNeighbor:
   413  		return parseNeighborSet(args)
   414  	case cmdAspath:
   415  		return parseAsPathSet(args)
   416  	case cmdCommunity:
   417  		return parseCommunitySet(args)
   418  	case cmdExtcommunity:
   419  		return parseExtCommunitySet(args)
   420  	case cmdLargecommunity:
   421  		return parseLargeCommunitySet(args)
   422  	default:
   423  		return nil, fmt.Errorf("invalid defined set type: %s", settype)
   424  	}
   425  }
   426  
   427  var modPolicyUsageFormat = map[string]string{
   428  	cmdPrefix:         "usage: policy prefix %s <name> [<prefix> [<mask range>]]",
   429  	cmdNeighbor:       "usage: policy neighbor %s <name> [<neighbor address>...]",
   430  	cmdAspath:         "usage: policy aspath %s <name> [<regexp>...]",
   431  	cmdCommunity:      "usage: policy community %s <name> [<regexp>...]",
   432  	cmdExtcommunity:   "usage: policy extcommunity %s <name> [<regexp>...]",
   433  	cmdLargecommunity: "usage: policy large-community %s <name> [<regexp>...]",
   434  }
   435  
   436  func modDefinedSet(settype string, modtype string, args []string) error {
   437  	var d *api.DefinedSet
   438  	var err error
   439  	if len(args) < 1 {
   440  		return fmt.Errorf(modPolicyUsageFormat[settype], modtype)
   441  	}
   442  	if d, err = parseDefinedSet(settype, args); err != nil {
   443  		return err
   444  	}
   445  	switch modtype {
   446  	case cmdAdd:
   447  		_, err = client.AddDefinedSet(ctx, &api.AddDefinedSetRequest{
   448  			DefinedSet: d,
   449  		})
   450  	case cmdDel:
   451  		all := false
   452  		if len(args) < 2 {
   453  			all = true
   454  		}
   455  		_, err = client.DeleteDefinedSet(ctx, &api.DeleteDefinedSetRequest{
   456  			DefinedSet: d,
   457  			All:        all,
   458  		})
   459  	}
   460  	return err
   461  }
   462  
   463  func printStatement(indent int, s *api.Statement) {
   464  	sIndent := func(indent int) string {
   465  		return strings.Repeat(" ", indent)
   466  	}
   467  	fmt.Printf("%sStatementName %s:\n", sIndent(indent), s.Name)
   468  	fmt.Printf("%sConditions:\n", sIndent(indent+2))
   469  
   470  	ind := sIndent(indent + 4)
   471  
   472  	c := s.Conditions
   473  	if c.PrefixSet != nil {
   474  		fmt.Printf("%sPrefixSet: %s \n", ind, prettyString(c.PrefixSet))
   475  	} else if c.NeighborSet != nil {
   476  		fmt.Printf("%sNeighborSet: %s\n", ind, prettyString(c.NeighborSet))
   477  	} else if c.AsPathSet != nil {
   478  		fmt.Printf("%sAsPathSet: %s \n", ind, prettyString(c.AsPathSet))
   479  	} else if c.CommunitySet != nil {
   480  		fmt.Printf("%sCommunitySet: %s\n", ind, prettyString(c.CommunitySet))
   481  	} else if c.ExtCommunitySet != nil {
   482  		fmt.Printf("%sExtCommunitySet: %s\n", ind, prettyString(c.ExtCommunitySet))
   483  	} else if c.LargeCommunitySet != nil {
   484  		fmt.Printf("%sLargeCommunitySet: %s\n", ind, prettyString(c.LargeCommunitySet))
   485  	} else if c.NextHopInList != nil {
   486  		fmt.Printf("%sNextHopInList: %s\n", ind, "[ "+strings.Join(c.NextHopInList, ", ")+" ]")
   487  	} else if c.AsPathLength != nil {
   488  		fmt.Printf("%sAsPathLength: %s\n", ind, prettyString(c.AsPathLength))
   489  	} else if c.RpkiResult != -1 {
   490  		var result string
   491  		switch c.RpkiResult {
   492  		case 0:
   493  			result = "none"
   494  		case 1:
   495  			result = "valid"
   496  		case 2:
   497  			result = "invalid"
   498  		case 3:
   499  			result = "not-found"
   500  		}
   501  		fmt.Printf("%sRPKI result: %s\n", ind, result)
   502  	} else if c.RouteType != api.Conditions_ROUTE_TYPE_NONE {
   503  		fmt.Printf("%sRoute Type: %s\n", ind, routeTypePrettyString(c.RouteType))
   504  	} else if c.AfiSafiIn != nil {
   505  		fmt.Printf("%sAFI SAFI In: %s\n", ind, c.AfiSafiIn)
   506  	}
   507  
   508  	fmt.Printf("%sActions:\n", sIndent(indent+2))
   509  	a := s.Actions
   510  	if a.Community != nil {
   511  		fmt.Println(ind, "Community: ", prettyString(a.Community))
   512  	} else if a.ExtCommunity != nil {
   513  		fmt.Println(ind, "ExtCommunity: ", prettyString(a.ExtCommunity))
   514  	} else if a.LargeCommunity != nil {
   515  		fmt.Println(ind, "LargeCommunity: ", prettyString(a.LargeCommunity))
   516  	} else if a.Med != nil {
   517  		fmt.Println(ind, "MED: ", prettyString(a.Med))
   518  	} else if a.LocalPref != nil {
   519  		fmt.Println(ind, "LocalPref: ", prettyString(a.LocalPref))
   520  	} else if a.AsPrepend != nil {
   521  		fmt.Println(ind, "ASPathPrepend: ", prettyString(a.AsPrepend))
   522  	} else if a.Nexthop != nil {
   523  		fmt.Println(ind, "Nexthop: ", prettyString(a.Nexthop))
   524  	}
   525  
   526  	if a.RouteAction != api.RouteAction_NONE {
   527  		action := "accept"
   528  		if a.RouteAction == api.RouteAction_REJECT {
   529  			action = "reject"
   530  		}
   531  		fmt.Println(ind, action)
   532  	}
   533  }
   534  
   535  func printPolicy(indent int, pd *api.Policy) {
   536  	for _, s := range pd.Statements {
   537  		printStatement(indent, s)
   538  	}
   539  }
   540  
   541  func showPolicy(args []string) error {
   542  	policies := make([]*api.Policy, 0)
   543  	stream, err := client.ListPolicy(ctx, &api.ListPolicyRequest{})
   544  	if err != nil {
   545  		return err
   546  	}
   547  	for {
   548  		r, err := stream.Recv()
   549  		if err == io.EOF {
   550  			break
   551  		} else if err != nil {
   552  			return nil
   553  		}
   554  		policies = append(policies, r.Policy)
   555  	}
   556  
   557  	var m []*api.Policy
   558  	if len(args) > 0 {
   559  		for _, p := range policies {
   560  			if args[0] == p.Name {
   561  				m = append(m, p)
   562  				break
   563  			}
   564  		}
   565  		if len(m) == 0 {
   566  			return fmt.Errorf("not found %s", args[0])
   567  		}
   568  	} else {
   569  		m = policies
   570  	}
   571  	if globalOpts.Json {
   572  		j, _ := json.Marshal(m)
   573  		fmt.Println(string(j))
   574  		return nil
   575  	}
   576  	if globalOpts.Quiet {
   577  		for _, p := range m {
   578  			fmt.Println(p.Name)
   579  		}
   580  		return nil
   581  	}
   582  
   583  	for _, pd := range m {
   584  		fmt.Printf("Name %s:\n", pd.Name)
   585  		printPolicy(4, pd)
   586  	}
   587  	return nil
   588  }
   589  
   590  func showStatement(args []string) error {
   591  	stmts := make([]*api.Statement, 0)
   592  	stream, err := client.ListStatement(ctx, &api.ListStatementRequest{})
   593  	if err != nil {
   594  		return err
   595  	}
   596  	for {
   597  		r, err := stream.Recv()
   598  		if err == io.EOF {
   599  			break
   600  		} else if err != nil {
   601  			return err
   602  		}
   603  		stmts = append(stmts, r.Statement)
   604  	}
   605  
   606  	var m []*api.Statement
   607  	if len(args) > 0 {
   608  		for _, s := range stmts {
   609  			if args[0] == s.Name {
   610  				m = append(m, s)
   611  				break
   612  			}
   613  		}
   614  		if len(m) == 0 {
   615  			return fmt.Errorf("not found %s", args[0])
   616  		}
   617  	} else {
   618  		m = stmts
   619  	}
   620  	if globalOpts.Json {
   621  		j, _ := json.Marshal(m)
   622  		fmt.Println(string(j))
   623  		return nil
   624  	}
   625  	if globalOpts.Quiet {
   626  		for _, s := range m {
   627  			fmt.Println(s.Name)
   628  		}
   629  		return nil
   630  	}
   631  	for _, s := range m {
   632  		printStatement(0, s)
   633  	}
   634  	return nil
   635  }
   636  
   637  func modStatement(op string, args []string) error {
   638  	if len(args) < 1 {
   639  		return fmt.Errorf("usage: gobgp policy statement %s <name>", op)
   640  	}
   641  	stmt := &api.Statement{
   642  		Name: args[0],
   643  	}
   644  	var err error
   645  	switch op {
   646  	case cmdAdd:
   647  		_, err = client.AddStatement(ctx, &api.AddStatementRequest{
   648  			Statement: stmt,
   649  		})
   650  	case cmdDel:
   651  		_, err = client.DeleteStatement(ctx, &api.DeleteStatementRequest{
   652  			Statement: stmt,
   653  			All:       true,
   654  		})
   655  	default:
   656  		return fmt.Errorf("invalid operation: %s", op)
   657  	}
   658  	return err
   659  }
   660  
   661  func modCondition(name, op string, args []string) error {
   662  	stmt := &api.Statement{
   663  		Name:       name,
   664  		Conditions: &api.Conditions{},
   665  	}
   666  	usage := fmt.Sprintf("usage: gobgp policy statement %s %s condition", name, op)
   667  	if len(args) < 1 {
   668  		return fmt.Errorf("%s { prefix | neighbor | as-path | community | ext-community | large-community | as-path-length | rpki | route-type | next-hop-in-list | afi-safi-in }", usage)
   669  	}
   670  	typ := args[0]
   671  	args = args[1:]
   672  	switch typ {
   673  	case "prefix":
   674  		stmt.Conditions.PrefixSet = &api.MatchSet{}
   675  		if len(args) < 1 {
   676  			return fmt.Errorf("%s prefix <set-name> [{ any | invert }]", usage)
   677  		}
   678  		stmt.Conditions.PrefixSet.Name = args[0]
   679  		if len(args) == 1 {
   680  			break
   681  		}
   682  		switch strings.ToLower(args[1]) {
   683  		case "any":
   684  			stmt.Conditions.PrefixSet.MatchType = api.MatchType_ANY
   685  		case "invert":
   686  			stmt.Conditions.PrefixSet.MatchType = api.MatchType_INVERT
   687  		default:
   688  			return fmt.Errorf("%s prefix <set-name> [{ any | invert }]", usage)
   689  		}
   690  	case "neighbor":
   691  		stmt.Conditions.NeighborSet = &api.MatchSet{}
   692  		if len(args) < 1 {
   693  			return fmt.Errorf("%s neighbor <set-name> [{ any | invert }]", usage)
   694  		}
   695  		stmt.Conditions.NeighborSet.Name = args[0]
   696  		if len(args) == 1 {
   697  			break
   698  		}
   699  		switch strings.ToLower(args[1]) {
   700  		case "any":
   701  			stmt.Conditions.NeighborSet.MatchType = api.MatchType_ANY
   702  		case "invert":
   703  			stmt.Conditions.NeighborSet.MatchType = api.MatchType_INVERT
   704  		default:
   705  			return fmt.Errorf("%s neighbor <set-name> [{ any | invert }]", usage)
   706  		}
   707  	case "as-path":
   708  		stmt.Conditions.AsPathSet = &api.MatchSet{}
   709  		if len(args) < 1 {
   710  			return fmt.Errorf("%s as-path <set-name> [{ any | all | invert }]", usage)
   711  		}
   712  		stmt.Conditions.AsPathSet.Name = args[0]
   713  		if len(args) == 1 {
   714  			break
   715  		}
   716  		switch strings.ToLower(args[1]) {
   717  		case "any":
   718  			stmt.Conditions.AsPathSet.MatchType = api.MatchType_ANY
   719  		case "all":
   720  			stmt.Conditions.AsPathSet.MatchType = api.MatchType_ALL
   721  		case "invert":
   722  			stmt.Conditions.AsPathSet.MatchType = api.MatchType_INVERT
   723  		default:
   724  			return fmt.Errorf("%s as-path <set-name> [{ any | all | invert }]", usage)
   725  		}
   726  	case "community":
   727  		stmt.Conditions.CommunitySet = &api.MatchSet{}
   728  		if len(args) < 1 {
   729  			return fmt.Errorf("%s community <set-name> [{ any | all | invert }]", usage)
   730  		}
   731  		stmt.Conditions.CommunitySet.Name = args[0]
   732  		if len(args) == 1 {
   733  			break
   734  		}
   735  		switch strings.ToLower(args[1]) {
   736  		case "any":
   737  			stmt.Conditions.CommunitySet.MatchType = api.MatchType_ANY
   738  		case "all":
   739  			stmt.Conditions.CommunitySet.MatchType = api.MatchType_ALL
   740  		case "invert":
   741  			stmt.Conditions.CommunitySet.MatchType = api.MatchType_INVERT
   742  		default:
   743  			return fmt.Errorf("%s community <set-name> [{ any | all | invert }]", usage)
   744  		}
   745  	case "ext-community":
   746  		stmt.Conditions.ExtCommunitySet = &api.MatchSet{}
   747  		if len(args) < 1 {
   748  			return fmt.Errorf("%s ext-community <set-name> [{ any | all | invert }]", usage)
   749  		}
   750  		stmt.Conditions.ExtCommunitySet.Name = args[0]
   751  		if len(args) == 1 {
   752  			break
   753  		}
   754  		switch strings.ToLower(args[1]) {
   755  		case "any":
   756  			stmt.Conditions.ExtCommunitySet.MatchType = api.MatchType_ANY
   757  		case "all":
   758  			stmt.Conditions.ExtCommunitySet.MatchType = api.MatchType_ALL
   759  		case "invert":
   760  			stmt.Conditions.ExtCommunitySet.MatchType = api.MatchType_INVERT
   761  		default:
   762  			return fmt.Errorf("%s ext-community <set-name> [{ any | all | invert }]", usage)
   763  		}
   764  	case "large-community":
   765  		stmt.Conditions.LargeCommunitySet = &api.MatchSet{}
   766  		if len(args) < 1 {
   767  			return fmt.Errorf("%s large-community <set-name> [{ any | all | invert }]", usage)
   768  		}
   769  		stmt.Conditions.LargeCommunitySet.Name = args[0]
   770  		if len(args) == 1 {
   771  			break
   772  		}
   773  		switch strings.ToLower(args[1]) {
   774  		case "any":
   775  			stmt.Conditions.LargeCommunitySet.MatchType = api.MatchType_ANY
   776  		case "all":
   777  			stmt.Conditions.LargeCommunitySet.MatchType = api.MatchType_ALL
   778  		case "invert":
   779  			stmt.Conditions.LargeCommunitySet.MatchType = api.MatchType_INVERT
   780  		default:
   781  			return fmt.Errorf("%s large-community <set-name> [{ any | all | invert }]", usage)
   782  		}
   783  	case "as-path-length":
   784  		stmt.Conditions.AsPathLength = &api.AsPathLength{}
   785  		if len(args) < 2 {
   786  			return fmt.Errorf("%s as-path-length <length> { eq | ge | le }", usage)
   787  		}
   788  		length, err := strconv.ParseUint(args[0], 10, 32)
   789  		if err != nil {
   790  			return err
   791  		}
   792  		stmt.Conditions.AsPathLength.Length = uint32(length)
   793  		switch strings.ToLower(args[1]) {
   794  		case "eq":
   795  			stmt.Conditions.AsPathLength.LengthType = api.AsPathLengthType_EQ
   796  		case "ge":
   797  			stmt.Conditions.AsPathLength.LengthType = api.AsPathLengthType_GE
   798  		case "le":
   799  			stmt.Conditions.AsPathLength.LengthType = api.AsPathLengthType_LE
   800  		default:
   801  			return fmt.Errorf("%s as-path-length <length> { eq | ge | le }", usage)
   802  		}
   803  	case "rpki":
   804  		if len(args) < 1 {
   805  			return fmt.Errorf("%s rpki { valid | invalid | not-found }", usage)
   806  		}
   807  		switch strings.ToLower(args[0]) {
   808  		case "valid":
   809  			stmt.Conditions.RpkiResult = int32(config.RpkiValidationResultTypeToIntMap[config.RPKI_VALIDATION_RESULT_TYPE_VALID])
   810  		case "invalid":
   811  			stmt.Conditions.RpkiResult = int32(config.RpkiValidationResultTypeToIntMap[config.RPKI_VALIDATION_RESULT_TYPE_INVALID])
   812  		case "not-found":
   813  			stmt.Conditions.RpkiResult = int32(config.RpkiValidationResultTypeToIntMap[config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND])
   814  		default:
   815  			return fmt.Errorf("%s rpki { valid | invalid | not-found }", usage)
   816  		}
   817  	case "route-type":
   818  		err := fmt.Errorf("%s route-type { internal | external | local }", usage)
   819  		if len(args) < 1 {
   820  			return err
   821  		}
   822  		switch strings.ToLower(args[0]) {
   823  		case "internal":
   824  			stmt.Conditions.RouteType = api.Conditions_ROUTE_TYPE_INTERNAL
   825  		case "external":
   826  			stmt.Conditions.RouteType = api.Conditions_ROUTE_TYPE_EXTERNAL
   827  		case "local":
   828  			stmt.Conditions.RouteType = api.Conditions_ROUTE_TYPE_LOCAL
   829  		default:
   830  			return err
   831  		}
   832  	case "next-hop-in-list":
   833  		stmt.Conditions.NextHopInList = args
   834  	case "afi-safi-in":
   835  		afiSafisInList := make([]*api.Family, 0, len(args))
   836  		for _, arg := range args {
   837  			afi, safi := bgp.RouteFamilyToAfiSafi(bgp.AddressFamilyValueMap[arg])
   838  			afiSafisInList = append(afiSafisInList, apiutil.ToApiFamily(afi, safi))
   839  		}
   840  		stmt.Conditions.AfiSafiIn = afiSafisInList
   841  	default:
   842  		return fmt.Errorf("%s { prefix | neighbor | as-path | community | ext-community | large-community | as-path-length | rpki | route-type | next-hop-in-list | afi-safi-in }", usage)
   843  	}
   844  
   845  	var err error
   846  	switch op {
   847  	case cmdAdd:
   848  		_, err = client.AddStatement(ctx, &api.AddStatementRequest{
   849  			Statement: stmt,
   850  		})
   851  	case cmdDel:
   852  		_, err = client.DeleteStatement(ctx, &api.DeleteStatementRequest{
   853  			Statement: stmt,
   854  		})
   855  	default:
   856  		return fmt.Errorf("invalid operation: %s", op)
   857  	}
   858  	return err
   859  }
   860  
   861  func modAction(name, op string, args []string) error {
   862  	stmt := &api.Statement{
   863  		Name:    name,
   864  		Actions: &api.Actions{},
   865  	}
   866  	usage := fmt.Sprintf("usage: gobgp policy statement %s %s action", name, op)
   867  	if len(args) < 1 {
   868  		return fmt.Errorf("%s { reject | accept | community | ext-community | large-community | med | local-pref | as-prepend | next-hop }", usage)
   869  	}
   870  	typ := args[0]
   871  	args = args[1:]
   872  	switch typ {
   873  	case "reject":
   874  		stmt.Actions.RouteAction = api.RouteAction_REJECT
   875  	case "accept":
   876  		stmt.Actions.RouteAction = api.RouteAction_ACCEPT
   877  	case "community":
   878  		stmt.Actions.Community = &api.CommunityAction{}
   879  		if len(args) < 1 {
   880  			return fmt.Errorf("%s community { add | remove | replace } <value>...", usage)
   881  		}
   882  		stmt.Actions.Community.Communities = args[1:]
   883  		switch strings.ToLower(args[0]) {
   884  		case "add":
   885  			stmt.Actions.Community.ActionType = api.CommunityActionType_COMMUNITY_ADD
   886  		case "remove":
   887  			stmt.Actions.Community.ActionType = api.CommunityActionType_COMMUNITY_REMOVE
   888  		case "replace":
   889  			stmt.Actions.Community.ActionType = api.CommunityActionType_COMMUNITY_REPLACE
   890  		default:
   891  			return fmt.Errorf("%s community { add | remove | replace } <value>...", usage)
   892  		}
   893  	case "ext-community":
   894  		stmt.Actions.ExtCommunity = &api.CommunityAction{}
   895  		if len(args) < 1 {
   896  			return fmt.Errorf("%s ext-community { add | remove | replace } <value>...", usage)
   897  		}
   898  		stmt.Actions.ExtCommunity.Communities = args[1:]
   899  		switch strings.ToLower(args[0]) {
   900  		case "add":
   901  			stmt.Actions.ExtCommunity.ActionType = api.CommunityActionType_COMMUNITY_ADD
   902  		case "remove":
   903  			stmt.Actions.ExtCommunity.ActionType = api.CommunityActionType_COMMUNITY_REMOVE
   904  		case "replace":
   905  			stmt.Actions.ExtCommunity.ActionType = api.CommunityActionType_COMMUNITY_REPLACE
   906  		default:
   907  			return fmt.Errorf("%s ext-community { add | remove | replace } <value>...", usage)
   908  		}
   909  	case "large-community":
   910  		stmt.Actions.LargeCommunity = &api.CommunityAction{}
   911  		if len(args) < 1 {
   912  			return fmt.Errorf("%s large-community { add | remove | replace } <value>...", usage)
   913  		}
   914  		stmt.Actions.LargeCommunity.Communities = args[1:]
   915  		switch strings.ToLower(args[0]) {
   916  		case "add":
   917  			stmt.Actions.LargeCommunity.ActionType = api.CommunityActionType_COMMUNITY_ADD
   918  		case "remove":
   919  			stmt.Actions.LargeCommunity.ActionType = api.CommunityActionType_COMMUNITY_REMOVE
   920  		case "replace":
   921  			stmt.Actions.LargeCommunity.ActionType = api.CommunityActionType_COMMUNITY_REPLACE
   922  		default:
   923  			return fmt.Errorf("%s large-community { add | remove | replace } <value>...", usage)
   924  		}
   925  	case "med":
   926  		stmt.Actions.Med = &api.MedAction{}
   927  		if len(args) < 2 {
   928  			return fmt.Errorf("%s med { add | sub | set } <value>", usage)
   929  		}
   930  		med, err := strconv.ParseInt(args[1], 10, 32)
   931  		if err != nil {
   932  			return err
   933  		}
   934  		stmt.Actions.Med.Value = int64(med)
   935  		switch strings.ToLower(args[0]) {
   936  		case "add":
   937  			stmt.Actions.Med.ActionType = api.MedActionType_MED_MOD
   938  		case "sub":
   939  			stmt.Actions.Med.ActionType = api.MedActionType_MED_MOD
   940  			stmt.Actions.Med.Value = -1 * stmt.Actions.Med.Value
   941  		case "set":
   942  			stmt.Actions.Med.ActionType = api.MedActionType_MED_REPLACE
   943  		default:
   944  			return fmt.Errorf("%s med { add | sub | set } <value>", usage)
   945  		}
   946  	case "local-pref":
   947  		stmt.Actions.LocalPref = &api.LocalPrefAction{}
   948  		if len(args) < 1 {
   949  			return fmt.Errorf("%s local-pref <value>", usage)
   950  		}
   951  		value, err := strconv.ParseUint(args[0], 10, 32)
   952  		if err != nil {
   953  			return err
   954  		}
   955  		stmt.Actions.LocalPref.Value = uint32(value)
   956  	case "as-prepend":
   957  		stmt.Actions.AsPrepend = &api.AsPrependAction{}
   958  		if len(args) < 2 {
   959  			return fmt.Errorf("%s as-prepend { <asn> | last-as } <repeat-value>", usage)
   960  		}
   961  		asn, _ := strconv.ParseUint(args[0], 10, 32)
   962  		stmt.Actions.AsPrepend.Asn = uint32(asn)
   963  		repeat, err := strconv.ParseUint(args[1], 10, 8)
   964  		if err != nil {
   965  			return err
   966  		}
   967  		stmt.Actions.AsPrepend.Repeat = uint32(repeat)
   968  	case "next-hop":
   969  		stmt.Actions.Nexthop = &api.NexthopAction{}
   970  		if len(args) != 1 {
   971  			return fmt.Errorf("%s next-hop { <value> | self }", usage)
   972  		}
   973  		stmt.Actions.Nexthop.Address = args[0]
   974  	}
   975  	var err error
   976  	switch op {
   977  	case cmdAdd:
   978  		_, err = client.AddStatement(ctx, &api.AddStatementRequest{
   979  			Statement: stmt,
   980  		})
   981  	case cmdDel:
   982  		_, err = client.DeleteStatement(ctx, &api.DeleteStatementRequest{
   983  			Statement: stmt,
   984  		})
   985  	default:
   986  		return fmt.Errorf("invalid operation: %s", op)
   987  	}
   988  	return err
   989  }
   990  
   991  func modPolicy(modtype string, args []string) error {
   992  	if len(args) < 1 {
   993  		return fmt.Errorf("usage: gobgp policy %s <name> [<statement name>...]", modtype)
   994  	}
   995  	name := args[0]
   996  	args = args[1:]
   997  	stmts := make([]*api.Statement, 0, len(args))
   998  	for _, n := range args {
   999  		stmts = append(stmts, &api.Statement{Name: n})
  1000  	}
  1001  	policy := &api.Policy{
  1002  		Name:       name,
  1003  		Statements: stmts,
  1004  	}
  1005  
  1006  	var err error
  1007  	switch modtype {
  1008  	case cmdAdd:
  1009  		_, err = client.AddPolicy(ctx, &api.AddPolicyRequest{
  1010  			Policy:                  policy,
  1011  			ReferExistingStatements: true,
  1012  		})
  1013  	case cmdDel:
  1014  		all := false
  1015  		if len(args) < 1 {
  1016  			all = true
  1017  		}
  1018  		_, err = client.DeletePolicy(ctx, &api.DeletePolicyRequest{
  1019  			Policy:             policy,
  1020  			All:                all,
  1021  			PreserveStatements: true,
  1022  		})
  1023  	}
  1024  	return err
  1025  }
  1026  
  1027  func newPolicyCmd() *cobra.Command {
  1028  	policyCmd := &cobra.Command{
  1029  		Use: cmdPolicy,
  1030  		Run: func(cmd *cobra.Command, args []string) {
  1031  			err := showPolicy(args)
  1032  			if err != nil {
  1033  				exitWithError(err)
  1034  			}
  1035  		},
  1036  	}
  1037  
  1038  	for _, v := range []string{cmdPrefix, cmdNeighbor, cmdAspath, cmdCommunity, cmdExtcommunity, cmdLargecommunity} {
  1039  		cmd := &cobra.Command{
  1040  			Use: v,
  1041  			Run: func(cmd *cobra.Command, args []string) {
  1042  				if err := showDefinedSet(cmd.Use, args); err != nil {
  1043  					exitWithError(err)
  1044  				}
  1045  			},
  1046  		}
  1047  		for _, w := range []string{cmdAdd, cmdDel} {
  1048  			subcmd := &cobra.Command{
  1049  				Use: w,
  1050  				Run: func(c *cobra.Command, args []string) {
  1051  					if err := modDefinedSet(cmd.Use, c.Use, args); err != nil {
  1052  						exitWithError(err)
  1053  					}
  1054  				},
  1055  			}
  1056  			cmd.AddCommand(subcmd)
  1057  		}
  1058  		policyCmd.AddCommand(cmd)
  1059  	}
  1060  
  1061  	stmtCmdImpl := &cobra.Command{}
  1062  	for _, v := range []string{cmdAdd, cmdDel} {
  1063  		cmd := &cobra.Command{
  1064  			Use: v,
  1065  		}
  1066  		for _, w := range []string{cmdCondition, cmdAction} {
  1067  			subcmd := &cobra.Command{
  1068  				Use: w,
  1069  				Run: func(c *cobra.Command, args []string) {
  1070  					name := args[len(args)-1]
  1071  					args = args[:len(args)-1]
  1072  					var err error
  1073  					if c.Use == cmdCondition {
  1074  						err = modCondition(name, cmd.Use, args)
  1075  					} else {
  1076  						err = modAction(name, cmd.Use, args)
  1077  					}
  1078  					if err != nil {
  1079  						exitWithError(err)
  1080  					}
  1081  				},
  1082  			}
  1083  			cmd.AddCommand(subcmd)
  1084  		}
  1085  		stmtCmdImpl.AddCommand(cmd)
  1086  	}
  1087  
  1088  	stmtCmd := &cobra.Command{
  1089  		Use: cmdStatement,
  1090  		Run: func(cmd *cobra.Command, args []string) {
  1091  			var err error
  1092  			if len(args) < 2 {
  1093  				err = showStatement(args)
  1094  			} else {
  1095  				args = append(args[1:], args[0])
  1096  				stmtCmdImpl.SetArgs(args)
  1097  				err = stmtCmdImpl.Execute()
  1098  			}
  1099  			if err != nil {
  1100  				exitWithError(err)
  1101  			}
  1102  		},
  1103  	}
  1104  	for _, v := range []string{cmdAdd, cmdDel} {
  1105  		cmd := &cobra.Command{
  1106  			Use: v,
  1107  			Run: func(c *cobra.Command, args []string) {
  1108  				err := modStatement(c.Use, args)
  1109  				if err != nil {
  1110  					exitWithError(err)
  1111  				}
  1112  			},
  1113  		}
  1114  		stmtCmd.AddCommand(cmd)
  1115  	}
  1116  	policyCmd.AddCommand(stmtCmd)
  1117  
  1118  	for _, v := range []string{cmdAdd, cmdDel} {
  1119  		cmd := &cobra.Command{
  1120  			Use: v,
  1121  			Run: func(c *cobra.Command, args []string) {
  1122  				err := modPolicy(c.Use, args)
  1123  				if err != nil {
  1124  					exitWithError(err)
  1125  				}
  1126  			},
  1127  		}
  1128  		policyCmd.AddCommand(cmd)
  1129  	}
  1130  
  1131  	return policyCmd
  1132  }