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 }