diff options
author | ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> | 2015-10-18 13:14:41 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2015-10-20 10:33:10 +0900 |
commit | 16234ccbb3bbd88759012c95efb690aa2eaba542 (patch) | |
tree | 1c6cc5caa7a645e60e2049916857c43933aa1854 | |
parent | f61709a2a668f093a44badd127e412be8ed14b92 (diff) |
cli: support defined-set configuration
prefix-set modification
gobgp policy prefix add p0 10.0.0.0/24 10..15
neighbor-set modification
gobgp policy neighbor add n0 10.0.0.1
as-path-set modification
gobgp policy aspath add a0 '^100_200_300$'
community-set modification
gobgp policy community add c0 100:100
ext-community-set modification
gobgp policy community add e0 rt:100:100
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
-rw-r--r-- | gobgp/cmd/common.go | 1 | ||||
-rw-r--r-- | gobgp/cmd/policy.go | 841 | ||||
-rw-r--r-- | server/server.go | 12 | ||||
-rw-r--r-- | table/policy.go | 82 |
4 files changed, 301 insertions, 635 deletions
diff --git a/gobgp/cmd/common.go b/gobgp/cmd/common.go index 12f54008..0b6ca6ff 100644 --- a/gobgp/cmd/common.go +++ b/gobgp/cmd/common.go @@ -35,6 +35,7 @@ const ( CMD_ADD = "add" CMD_DEL = "del" CMD_ALL = "all" + CMD_SET = "set" CMD_LOCAL = "local" CMD_ADJ_IN = "adj-in" CMD_ADJ_OUT = "adj-out" diff --git a/gobgp/cmd/policy.go b/gobgp/cmd/policy.go index f56b300e..2b4ecbc6 100644 --- a/gobgp/cmd/policy.go +++ b/gobgp/cmd/policy.go @@ -33,6 +33,9 @@ import ( ) func formatPolicyPrefix(head bool, indent int, psl []*api.DefinedSet) string { + if len(psl) == 0 { + return "Nothing defined yet\n" + } buff := bytes.NewBuffer(make([]byte, 0, 64)) sIndent := strings.Repeat(" ", indent) maxNameLen := 0 @@ -49,20 +52,24 @@ func formatPolicyPrefix(head bool, indent int, psl []*api.DefinedSet) string { } if head { - if len("Name") > maxNameLen { - maxNameLen = len("Name") + if len("NAME") > maxNameLen { + maxNameLen = len("NAME") } - if len("Prefix") > maxPrefixLen { - maxPrefixLen = len("Prefix") + if len("PREFIX") > maxPrefixLen { + maxPrefixLen = len("PREFIX") } } format := fmt.Sprintf("%%-%ds %%-%ds ", maxNameLen, maxPrefixLen) if head { - buff.WriteString(fmt.Sprintf(format, "Name", "Prefix")) + buff.WriteString(fmt.Sprintf(format, "NAME", "PREFIX")) buff.WriteString("MaskLengthRange\n") } for _, ps := range psl { + if len(ps.Prefixes) == 0 { + buff.WriteString(fmt.Sprintf(format, ps.Name, "")) + buff.WriteString("\n") + } for i, p := range ps.Prefixes { if i == 0 { buff.WriteString(fmt.Sprintf(format, ps.Name, p.IpPrefix)) @@ -77,168 +84,46 @@ func formatPolicyPrefix(head bool, indent int, psl []*api.DefinedSet) string { return buff.String() } -func formatPolicyNeighbor(head bool, indent int, nsl []*api.DefinedSet) string { - buff := bytes.NewBuffer(make([]byte, 0, 64)) - sIndent := strings.Repeat(" ", indent) - maxNameLen := 0 - maxAddressLen := 0 - for _, ns := range nsl { - if len(ns.Name) > maxNameLen { - maxNameLen = len(ns.Name) - } - for _, n := range ns.List { - if len(n) > maxAddressLen { - maxAddressLen = len(n) - } - } - } - - if head { - if len("Name") > maxNameLen { - maxNameLen = len("Name") - } - if len("Address") > maxAddressLen { - maxAddressLen = len("Address") - } - } - - format := "%-" + fmt.Sprint(maxNameLen) + "s %-" + fmt.Sprint(maxAddressLen) + "s\n" - if head { - buff.WriteString(fmt.Sprintf(format, "Name", "Address")) - } - for _, ns := range nsl { - for i, n := range ns.List { - if i == 0 { - buff.WriteString(fmt.Sprintf(format, ns.Name, n)) - } else { - buff.WriteString(fmt.Sprintf(sIndent)) - buff.WriteString(fmt.Sprintf(format, "", n)) - } - } +func formatDefinedSet(head bool, typ string, indent int, list []*api.DefinedSet) string { + if len(list) == 0 { + return "Nothing defined yet\n" } - return buff.String() -} - -func formatPolicyAsPath(haed bool, indent int, apsl []*api.DefinedSet) string { buff := bytes.NewBuffer(make([]byte, 0, 64)) sIndent := strings.Repeat(" ", indent) maxNameLen := 0 - maxPathLen := 0 - for _, aps := range apsl { - if len(aps.Name) > maxNameLen { - maxNameLen = len(aps.Name) - } - for _, m := range aps.List { - if len(m) > maxPathLen { - maxPathLen = len(m) - } - } - } - - if haed { - if len("Name") > maxNameLen { - maxNameLen = len("Name") - } - if len("AsPath") > maxPathLen { - maxPathLen = len("AsPath") - } - } - - format := "%-" + fmt.Sprint(maxNameLen) + "s %-" + fmt.Sprint(maxPathLen) + "s\n" - if haed { - buff.WriteString(fmt.Sprintf(format, "Name", "AsPath")) - } - for _, aps := range apsl { - for i, a := range aps.List { - if i == 0 { - buff.WriteString(fmt.Sprintf(format, aps.Name, a)) - } else { - buff.WriteString(fmt.Sprintf(sIndent)) - buff.WriteString(fmt.Sprintf(format, "", a)) - } + maxValueLen := 0 + for _, s := range list { + if len(s.Name) > maxNameLen { + maxNameLen = len(s.Name) } - } - return buff.String() -} - -func formatPolicyCommunity(head bool, indent int, csl []*api.DefinedSet) string { - buff := bytes.NewBuffer(make([]byte, 0, 64)) - sIndent := strings.Repeat(" ", indent) - maxNameLen := 0 - maxCommunityLen := 0 - for _, cs := range csl { - if len(cs.Name) > maxNameLen { - maxNameLen = len(cs.Name) - } - for _, m := range cs.List { - if len(m) > maxCommunityLen { - maxCommunityLen = len(m) + for _, x := range s.List { + if len(x) > maxValueLen { + maxValueLen = len(x) } } } - if head { - if len("Name") > maxNameLen { - maxNameLen = len("Name") + if len("NAME") > maxNameLen { + maxNameLen = len("NAME") } - if len("Community") > maxCommunityLen { - maxCommunityLen = len("Community") + if len(typ) > maxValueLen { + maxValueLen = len(typ) } } - - format := "%-" + fmt.Sprint(maxNameLen) + "s %-" + fmt.Sprint(maxCommunityLen) + "s\n" + format := fmt.Sprintf("%%-%ds %%-%ds\n", maxNameLen, maxValueLen) if head { - buff.WriteString(fmt.Sprintf(format, "Name", "Community")) + buff.WriteString(fmt.Sprintf(format, "NAME", typ)) } - for _, cs := range csl { - for i, c := range cs.List { - if i == 0 { - buff.WriteString(fmt.Sprintf(format, cs.Name, c)) - } else { - buff.WriteString(fmt.Sprintf(sIndent)) - buff.WriteString(fmt.Sprintf(format, "", c)) - } - } - } - return buff.String() -} - -func formatPolicyExtCommunity(head bool, indent int, ecsl []*api.DefinedSet) string { - buff := bytes.NewBuffer(make([]byte, 0, 64)) - sIndent := strings.Repeat(" ", indent) - maxNameLen := 0 - maxCommunityLen := 0 - for _, es := range ecsl { - if len(es.Name) > maxNameLen { - maxNameLen = len(es.Name) - } - for _, m := range es.List { - if len(m) > maxCommunityLen { - maxCommunityLen = len(m) - } - } - } - - if head { - if len("Name") > maxNameLen { - maxNameLen = len("Name") + for _, s := range list { + if len(s.List) == 0 { + buff.WriteString(fmt.Sprintf(format, s.Name, "")) } - if len("ExtCommunity") > maxCommunityLen { - maxCommunityLen = len("ExtCommunity") - } - } - - format := "%-" + fmt.Sprint(maxNameLen) + "s %-" + fmt.Sprint(maxCommunityLen) + "s\n" - if head { - buff.WriteString(fmt.Sprintf(format, "Name", "ExtCommunity")) - } - for _, ecs := range ecsl { - for i, ec := range ecs.List { + for i, x := range s.List { if i == 0 { - buff.WriteString(fmt.Sprintf(format, ecs.Name, ec)) + buff.WriteString(fmt.Sprintf(format, s.Name, x)) } else { buff.WriteString(fmt.Sprintf(sIndent)) - buff.WriteString(fmt.Sprintf(format, "", ec)) + buff.WriteString(fmt.Sprintf(format, "", x)) } } } @@ -273,6 +158,9 @@ func show(v string, args []string) error { } m = append(m, p) } else { + arg := &api.DefinedSet{ + Type: int32(typ), + } stream, e := client.GetDefinedSets(context.Background(), arg) if e != nil { return e @@ -313,458 +201,202 @@ func show(v string, args []string) error { case CMD_PREFIX: output = formatPolicyPrefix(true, 0, m) case CMD_NEIGHBOR: - output = formatPolicyNeighbor(true, 0, m) + output = formatDefinedSet(true, "ADDRESS", 0, m) case CMD_ASPATH: - output = formatPolicyAsPath(true, 0, m) + output = formatDefinedSet(true, "AS-PATH", 0, m) case CMD_COMMUNITY: - output = formatPolicyCommunity(true, 0, m) + output = formatDefinedSet(true, "COMMUNITY", 0, m) case CMD_EXTCOMMUNITY: - output = formatPolicyExtCommunity(true, 0, m) + output = formatDefinedSet(true, "EXT-COMMUNITY", 0, m) } fmt.Print(output) return nil } -func parsePrefixSet(eArgs []string) (*api.DefinedSet, error) { - _, ipNet, e := net.ParseCIDR(eArgs[1]) - if e != nil { - return nil, fmt.Errorf("invalid prefix: %s\nplease enter ipv4 or ipv6 format", eArgs[1]) +func parsePrefixSet(args []string) (*api.DefinedSet, error) { + if len(args) < 1 { + return nil, fmt.Errorf("empty neighbor set name") } - prefix := &api.Prefix{ - IpPrefix: eArgs[1], - } - - if len(eArgs) == 3 { - maskRange := eArgs[2] - idx := strings.Index(maskRange, "..") - if idx == -1 { - return nil, fmt.Errorf("invalid mask length range: %s", maskRange) - } - var min, max int - var e error - if idx != 0 { - if min, e = strconv.Atoi(maskRange[:idx]); e != nil { + name := args[0] + args = args[1:] + var list []*api.Prefix + if len(args) > 0 { + _, ipNet, err := net.ParseCIDR(args[0]) + if err != nil { + return nil, fmt.Errorf("invalid prefix: %s\nplease enter ipv4 or ipv6 format", args[1]) + } + l, _ := ipNet.Mask.Size() + prefix := &api.Prefix{ + IpPrefix: args[0], + MaskLengthMin: uint32(l), + MaskLengthMax: uint32(l), + } + if len(args) > 1 { + maskRange := args[1] + exp := regexp.MustCompile("(\\d+)\\.\\.(\\d+)") + elems := exp.FindStringSubmatch(maskRange) + if len(elems) != 3 { return nil, fmt.Errorf("invalid mask length range: %s", maskRange) } - } - if idx != len(maskRange)-1 { - if max, e = strconv.Atoi(maskRange[idx+2:]); e != nil { + // we've already checked the range is sane by regexp + min, _ := strconv.Atoi(elems[1]) + max, _ := strconv.Atoi(elems[2]) + if min > max { return nil, fmt.Errorf("invalid mask length range: %s", maskRange) } - } - if ipv4 := ipNet.IP.To4(); ipv4 != nil { - if min < 0 || 32 < max { - return nil, fmt.Errorf("ipv4 mask length range outside scope :%s", maskRange) - } - } else { - if min < 0 || 128 < max { - return nil, fmt.Errorf("ipv6 mask length range outside scope :%s", maskRange) + if ipv4 := ipNet.IP.To4(); ipv4 != nil { + f := func(i int) bool { + return i >= 0 && i <= 32 + } + if !f(min) || !f(max) { + return nil, fmt.Errorf("ipv4 mask length range outside scope :%s", maskRange) + } + } else { + f := func(i int) bool { + return i >= 0 && i <= 128 + } + if !f(min) || !f(max) { + return nil, fmt.Errorf("ipv6 mask length range outside scope :%s", maskRange) + } } + prefix.MaskLengthMin = uint32(min) + prefix.MaskLengthMax = uint32(max) } - if min >= max { - return nil, fmt.Errorf("invalid mask length range: %s\nlarge value to the right from the left", maskRange) - } - prefix.MaskLengthMin = uint32(min) - prefix.MaskLengthMax = uint32(max) + list = []*api.Prefix{prefix} } - prefixList := []*api.Prefix{prefix} return &api.DefinedSet{ Type: int32(table.DEFINED_TYPE_PREFIX), - Name: eArgs[0], - Prefixes: prefixList, + Name: name, + Prefixes: list, }, nil } -func modPolicy(resource api.Resource, op api.Operation, data interface{}) error { - pd := &api.PolicyDefinition{} - if resource != api.Resource_POLICY_ROUTEPOLICY { - co := &api.Conditions{} - switch resource { - case api.Resource_POLICY_PREFIX: - co.PrefixSet = data.(*api.PrefixSet) - case api.Resource_POLICY_NEIGHBOR: - co.NeighborSet = data.(*api.MatchSet) - case api.Resource_POLICY_ASPATH: - co.AsPathSet = data.(*api.MatchSet) - case api.Resource_POLICY_COMMUNITY: - co.CommunitySet = data.(*api.MatchSet) - case api.Resource_POLICY_EXTCOMMUNITY: - co.ExtCommunitySet = data.(*api.MatchSet) - } - pd.Statements = []*api.Statement{{Conditions: co}} - } else { - pd = data.(*api.PolicyDefinition) - } - arg := &api.PolicyArguments{ - Resource: resource, - Operation: op, - PolicyDefinition: pd, - } - stream, err := client.ModPolicyRoutePolicy(context.Background()) - if err != nil { - return err - } - err = stream.Send(arg) - if err != nil { - return err - } - stream.CloseSend() - - res, e := stream.Recv() - if e != nil { - return e - } - if res.Code != api.Error_SUCCESS { - return fmt.Errorf("error: code: %d, msg: %s", res.Code, res.Msg) - } - return nil -} - -func modPolicyPrefix(modtype string, eArgs []string) error { - prefixSet := &api.DefinedSet{} - var e error - var operation api.Operation - switch modtype { - case CMD_ADD: - if len(eArgs) < 2 { - return fmt.Errorf("usage: policy prefix add <prefix set name> <prefix> [<mask length renge>]") - } - if prefixSet, e = parsePrefixSet(eArgs); e != nil { - return e - } - operation = api.Operation_ADD - case CMD_DEL: - if len(eArgs) == 0 { - return fmt.Errorf("usage: policy prefix del <prefix set name> [<prefix> [<mask length renge>]]") - } else if len(eArgs) == 1 { - prefixSet = &api.DefinedSet{ - Name: eArgs[0], - } - } else { - if prefixSet, e = parsePrefixSet(eArgs); e != nil { - return e - } - } - operation = api.Operation_DEL - case CMD_ALL: - if len(eArgs) > 0 { - return fmt.Errorf("Argument can not be entered: %s", eArgs[0:]) - } - operation = api.Operation_DEL_ALL - default: - return fmt.Errorf("invalid modType %s", modtype) - } - if e = modPolicy(api.Resource_POLICY_PREFIX, operation, prefixSet); e != nil { - return e - } - return nil -} - -func showPolicyNeighbor(args []string) error { - arg := &api.DefinedSet{ - Type: int32(table.DEFINED_TYPE_NEIGHBOR), - Name: args[0], - } - ns, e := client.GetDefinedSet(context.Background(), arg) - if e != nil { - return e - } - if globalOpts.Json { - j, _ := json.Marshal(ns) - fmt.Println(string(j)) - return nil +func parseNeighborSet(args []string) (*api.DefinedSet, error) { + if len(args) < 1 { + return nil, fmt.Errorf("empty neighbor set name") } - if globalOpts.Quiet { - for _, n := range ns.List { - fmt.Println(n) - } - return nil - } - output := formatPolicyNeighbor(true, 0, []*api.DefinedSet{ns}) - fmt.Print(output) - return nil -} - -func parseNeighborSet(eArgs []string) (*api.DefinedSet, error) { - address := net.ParseIP(eArgs[1]) - if address.To4() == nil { - if address.To16() == nil { - return nil, fmt.Errorf("invalid address: %s\nplease enter ipv4 or ipv6 format", eArgs[1]) + name := args[0] + args = args[1:] + for _, arg := range args { + address := net.ParseIP(arg) + if address.To4() == nil && address.To16() == nil { + return nil, fmt.Errorf("invalid address: %s\nplease enter ipv4 or ipv6 format", arg) } } - return &api.DefinedSet{ Type: int32(table.DEFINED_TYPE_NEIGHBOR), - Name: eArgs[0], - List: []string{address.String()}, + Name: name, + List: args, }, nil } -func modPolicyNeighbor(modtype string, eArgs []string) error { - neighborSet := &api.DefinedSet{} - var e error - var operation api.Operation - - switch modtype { - case CMD_ADD: - if len(eArgs) < 2 { - return fmt.Errorf("usage: policy neighbor add <neighbor set name> <address>") - } - if neighborSet, e = parseNeighborSet(eArgs); e != nil { - return e - } - operation = api.Operation_ADD - case CMD_DEL: - if len(eArgs) == 0 { - return fmt.Errorf("usage: policy neighbor del <neighbor set name> [<address>]") - } else if len(eArgs) == 1 { - neighborSet = &api.DefinedSet{ - Name: eArgs[0], - } - } else { - if neighborSet, e = parseNeighborSet(eArgs); e != nil { - return e - } - } - operation = api.Operation_DEL - case CMD_ALL: - if len(eArgs) > 0 { - return fmt.Errorf("Argument can not be entered: %s", eArgs[0:]) - } - operation = api.Operation_DEL_ALL - default: - return fmt.Errorf("invalid modType %s", modtype) +func parseAsPathSet(args []string) (*api.DefinedSet, error) { + if len(args) < 1 { + return nil, fmt.Errorf("empty as-path set name") } - if e = modPolicy(api.Resource_POLICY_NEIGHBOR, operation, neighborSet); e != nil { - return e - } - return nil -} - -func parseAsPathSet(eArgs []string) (*api.DefinedSet, error) { - as := eArgs[1] - isTop := as[:1] == "^" - if isTop { - as = as[1:] - } - isEnd := as[len(as)-1:] == "$" - if isEnd { - as = as[:len(as)-1] - } - elems := strings.Split(as, "_") - for _, el := range elems { - if len(el) == 0 { - return nil, fmt.Errorf("invalid aspath element: %s \ndo not enter a blank", eArgs[1]) - } - _, err := regexp.Compile(el) + name := args[0] + args = args[1:] + for _, arg := range args { + _, err := regexp.Compile(arg) if err != nil { - return nil, fmt.Errorf("invalid aspath element: %s \n"+ - "can not comple aspath values to regular expressions.", eArgs[1]) + return nil, err } } return &api.DefinedSet{ - Name: eArgs[0], - List: []string{eArgs[1]}, + Type: int32(table.DEFINED_TYPE_AS_PATH), + Name: name, + List: args, }, nil } -func modPolicyAsPath(modtype string, eArgs []string) error { - asPathSet := &api.DefinedSet{} - var e error - var operation api.Operation - - switch modtype { - case CMD_ADD: - if len(eArgs) < 2 { - return fmt.Errorf("usage: policy aspath add <aspath set name> <aspath>") - } - if asPathSet, e = parseAsPathSet(eArgs); e != nil { - return e - } - operation = api.Operation_ADD - case CMD_DEL: - if len(eArgs) == 0 { - return fmt.Errorf("usage: policy aspath del <aspath set name> [<aspath>]") - } else if len(eArgs) == 1 { - asPathSet = &api.DefinedSet{ - Name: eArgs[0], - } - } else { - if asPathSet, e = parseAsPathSet(eArgs); e != nil { - return e - } - } - operation = api.Operation_DEL - case CMD_ALL: - if len(eArgs) > 0 { - return fmt.Errorf("Argument can not be entered: %s", eArgs[0:]) - } - operation = api.Operation_DEL_ALL - default: - return fmt.Errorf("invalid modType %s", modtype) +func parseCommunitySet(args []string) (*api.DefinedSet, error) { + if len(args) < 1 { + return nil, fmt.Errorf("empty community set name") } - if e = modPolicy(api.Resource_POLICY_ASPATH, operation, asPathSet); e != nil { - return e + name := args[0] + args = args[1:] + for _, arg := range args { + if _, err := table.ParseCommunityRegexp(arg); err != nil { + return nil, err + } } - return nil + return &api.DefinedSet{ + Type: int32(table.DEFINED_TYPE_COMMUNITY), + Name: name, + List: args, + }, nil } -func checkCommunityFormat(comStr string) bool { - _, e := table.ParseCommunity(comStr) - if e == nil { - return true +func parseExtCommunitySet(args []string) (*api.DefinedSet, error) { + if len(args) < 1 { + return nil, fmt.Errorf("empty ext-community set name") } - return false -} - -func parseCommunitySet(eArgs []string) (*api.DefinedSet, error) { - if !checkCommunityFormat(eArgs[1]) { - if _, err := regexp.Compile(eArgs[1]); err != nil { - return nil, fmt.Errorf("invalid community: %s\nplease enter community format", eArgs[1]) + name := args[0] + args = args[1:] + for _, arg := range args { + if _, _, err := table.ParseExtCommunityRegexp(arg); err != nil { + return nil, err } } - communitySet := &api.DefinedSet{ - Name: eArgs[0], - List: []string{eArgs[1]}, - } - return communitySet, nil + return &api.DefinedSet{ + Type: int32(table.DEFINED_TYPE_EXT_COMMUNITY), + Name: name, + List: args, + }, nil } -func modPolicyCommunity(modtype string, eArgs []string) error { - communitySet := &api.DefinedSet{} - var e error - var operation api.Operation - - switch modtype { - case CMD_ADD: - if len(eArgs) < 2 { - return fmt.Errorf("usage: policy community add <community set name> <community>") - } - if communitySet, e = parseCommunitySet(eArgs); e != nil { - return e - } - operation = api.Operation_ADD - case CMD_DEL: - if len(eArgs) == 0 { - return fmt.Errorf("usage: policy community add <community set name> [<community>]") - } else if len(eArgs) == 1 { - communitySet = &api.DefinedSet{ - Name: eArgs[0], - } - } else { - if communitySet, e = parseCommunitySet(eArgs); e != nil { - return e - } - } - operation = api.Operation_DEL - case CMD_ALL: - if len(eArgs) > 0 { - return fmt.Errorf("Argument can not be entered: %s", eArgs[0:]) - } - operation = api.Operation_DEL_ALL +func parseDefinedSet(settype string, args []string) (*api.DefinedSet, error) { + switch settype { + case CMD_PREFIX: + return parsePrefixSet(args) + case CMD_NEIGHBOR: + return parseNeighborSet(args) + case CMD_ASPATH: + return parseAsPathSet(args) + case CMD_COMMUNITY: + return parseCommunitySet(args) + case CMD_EXTCOMMUNITY: + return parseExtCommunitySet(args) default: - return fmt.Errorf("invalid modType %s", modtype) - } - if e = modPolicy(api.Resource_POLICY_COMMUNITY, operation, communitySet); e != nil { - return e + return nil, fmt.Errorf("invalid setype: %s", settype) } - return nil } -func checkExtCommunityFormat(eComStr string) bool { - // extended community regexp - checkSubType := func(eComStr string) (bool, string) { - regSubType, _ := regexp.Compile("^(RT|SoO):(.*)$") - if regSubType.MatchString(eComStr) { - regResult := regSubType.FindStringSubmatch(eComStr) - return true, regResult[2] - } - return false, "" - } - checkValue := func(eComVal string) (bool, string) { - regVal, _ := regexp.Compile("^([0-9\\.]+):([0-9]+)$") - if regVal.MatchString(eComVal) { - regResult := regVal.FindStringSubmatch(eComVal) - return true, regResult[1] - } - return false, "" - } - checkElem := func(gAdmin string) bool { - addr := net.ParseIP(gAdmin) - if addr.To4() != nil { - return true - } - regAs, _ := regexp.Compile("^([0-9]+)$") - regAs4, _ := regexp.Compile("^([0-9]+).([0-9]+)$") - if regAs.MatchString(gAdmin) || regAs4.MatchString(gAdmin) { - return true - } - return false - } - - if subTypeOk, eComVal := checkSubType(eComStr); subTypeOk { - if valOk, gAdmin := checkValue(eComVal); valOk { - if checkElem(gAdmin) { - return true - } - } - _, err := regexp.Compile(eComVal) - if err == nil { - return true - } - } - return false +var modPolicyUsageFormat = map[string]string{ + CMD_PREFIX: "usage: policy prefix %s <name> [<prefix> [<mask range>]]", + CMD_NEIGHBOR: "usage: policy neighbor %s <name> [<neighbor address>...]", + CMD_ASPATH: "usage: policy aspath %s <name> [<regexp>...]", + CMD_COMMUNITY: "usage: policy community %s <name> [<regexp>...]", + CMD_EXTCOMMUNITY: "usage: policy extcommunity %s <name> [<regexp>...]", } -func parseExtCommunitySet(eArgs []string) (*api.DefinedSet, error) { - if !checkExtCommunityFormat(eArgs[1]) { - return nil, fmt.Errorf("invalid extended community: %s\nplease enter extended community format", eArgs[1]) +func mod(settype string, modtype string, args []string) error { + var d *api.DefinedSet + var err error + if len(args) < 1 { + return fmt.Errorf(modPolicyUsageFormat[settype], modtype) } - return &api.DefinedSet{ - Name: eArgs[0], - List: []string{eArgs[1]}, - }, nil -} - -func modPolicyExtCommunity(modtype string, eArgs []string) error { - extCommunitySet := &api.DefinedSet{} - var e error - var operation api.Operation - + if d, err = parseDefinedSet(settype, args); err != nil { + return err + } + var op api.Operation switch modtype { case CMD_ADD: - if len(eArgs) < 2 { - return fmt.Errorf("usage: policy extcommunity add <community set name> <community>") - } - if extCommunitySet, e = parseExtCommunitySet(eArgs); e != nil { - return e - } - operation = api.Operation_ADD + op = api.Operation_ADD case CMD_DEL: - if len(eArgs) == 0 { - return fmt.Errorf("usage: policy extcommunity add <community set name> [<community>]") - } else if len(eArgs) == 1 { - extCommunitySet = &api.DefinedSet{ - Name: eArgs[0], - } + if len(args) < 2 { + op = api.Operation_DEL_ALL } else { - if extCommunitySet, e = parseExtCommunitySet(eArgs); e != nil { - return e - } - } - operation = api.Operation_DEL - case CMD_ALL: - if len(eArgs) > 0 { - return fmt.Errorf("Argument can not be entered: %s", eArgs[0:]) + op = api.Operation_DEL } - operation = api.Operation_DEL_ALL - default: - return fmt.Errorf("invalid modType %s", modtype) + case CMD_SET: + op = api.Operation_REPLACE } - if e = modPolicy(api.Resource_POLICY_EXTCOMMUNITY, operation, extCommunitySet); e != nil { - return e - } - return nil + _, err = client.ModDefinedSet(context.Background(), &api.ModDefinedSetArguments{ + Operation: op, + Set: d, + }) + return err } func showPolicyStatement(indent int, pd *api.PolicyDefinition) { @@ -1140,6 +772,51 @@ func parseActions() (*api.Actions, error) { return actions, nil } +func modPolicy(resource api.Resource, op api.Operation, data interface{}) error { + pd := &api.PolicyDefinition{} + if resource != api.Resource_POLICY_ROUTEPOLICY { + co := &api.Conditions{} + switch resource { + case api.Resource_POLICY_PREFIX: + co.PrefixSet = data.(*api.PrefixSet) + case api.Resource_POLICY_NEIGHBOR: + co.NeighborSet = data.(*api.MatchSet) + case api.Resource_POLICY_ASPATH: + co.AsPathSet = data.(*api.MatchSet) + case api.Resource_POLICY_COMMUNITY: + co.CommunitySet = data.(*api.MatchSet) + case api.Resource_POLICY_EXTCOMMUNITY: + co.ExtCommunitySet = data.(*api.MatchSet) + } + pd.Statements = []*api.Statement{{Conditions: co}} + } else { + pd = data.(*api.PolicyDefinition) + } + arg := &api.PolicyArguments{ + Resource: resource, + Operation: op, + PolicyDefinition: pd, + } + stream, err := client.ModPolicyRoutePolicy(context.Background()) + if err != nil { + return err + } + err = stream.Send(arg) + if err != nil { + return err + } + stream.CloseSend() + + res, e := stream.Recv() + if e != nil { + return e + } + if res.Code != api.Error_SUCCESS { + return fmt.Errorf("error: code: %d, msg: %s", res.Code, res.Msg) + } + return nil +} + func modPolicyRoutePolicy(modtype string, eArgs []string) error { var operation api.Operation pd := &api.PolicyDefinition{} @@ -1195,56 +872,6 @@ func modPolicyRoutePolicy(modtype string, eArgs []string) error { return nil } -func NewPolicyAddCmd(v string, mod func(string, []string) error) *cobra.Command { - policyAddCmd := &cobra.Command{ - Use: CMD_ADD, - Run: func(cmd *cobra.Command, args []string) { - err := mod(cmd.Use, args) - if err != nil { - fmt.Println(err) - } - }, - } - if v == CMD_ROUTEPOLICY { - policyAddCmd.Flags().StringVarP(&conditionOpts.Prefix, "c-prefix", "", "", "a prefix set name of policy condition") - policyAddCmd.Flags().StringVarP(&conditionOpts.Neighbor, "c-neighbor", "", "", "a neighbor set name of policy condition") - policyAddCmd.Flags().StringVarP(&conditionOpts.AsPath, "c-aspath", "", "", "an as path set name of policy condition") - policyAddCmd.Flags().StringVarP(&conditionOpts.Community, "c-community", "", "", "a community set name of policy condition") - policyAddCmd.Flags().StringVarP(&conditionOpts.ExtCommunity, "c-extcommunity", "", "", "a extended community set name of policy condition") - policyAddCmd.Flags().StringVarP(&conditionOpts.AsPathLength, "c-aslen", "", "", "an as path length of policy condition (<operator>,<numeric>)") - policyAddCmd.Flags().StringVarP(&actionOpts.RouteAction, "a-route", "", "", "a route action of policy action (accept | reject)") - policyAddCmd.Flags().StringVarP(&actionOpts.CommunityAction, "a-community", "", "", "a community of policy action") - policyAddCmd.Flags().StringVarP(&actionOpts.MedAction, "a-med", "", "", "a med of policy action") - policyAddCmd.Flags().StringVarP(&actionOpts.AsPathPrependAction, "a-asprepend", "", "", "aspath prepend for policy action") - } - - return policyAddCmd -} - -func NewPolicyDelCmd(mod func(string, []string) error) *cobra.Command { - policyDelCmd := &cobra.Command{ - Use: CMD_DEL, - Run: func(cmd *cobra.Command, args []string) { - err := mod(cmd.Use, args) - if err != nil { - fmt.Println(err) - } - }, - } - - subcmd := &cobra.Command{ - Use: CMD_ALL, - Run: func(cmd *cobra.Command, args []string) { - err := mod(cmd.Use, args) - if err != nil { - fmt.Println(err) - } - }, - } - policyDelCmd.AddCommand(subcmd) - return policyDelCmd -} - func NewPolicyCmd() *cobra.Command { policyCmd := &cobra.Command{ Use: CMD_POLICY, @@ -1262,21 +889,6 @@ func NewPolicyCmd() *cobra.Command { } for _, v := range []string{CMD_PREFIX, CMD_NEIGHBOR, CMD_ASPATH, CMD_COMMUNITY, CMD_EXTCOMMUNITY, CMD_ROUTEPOLICY} { - var mod func(string, []string) error - switch v { - case CMD_PREFIX: - mod = modPolicyPrefix - case CMD_NEIGHBOR: - mod = modPolicyNeighbor - case CMD_ASPATH: - mod = modPolicyAsPath - case CMD_COMMUNITY: - mod = modPolicyCommunity - case CMD_EXTCOMMUNITY: - mod = modPolicyExtCommunity - case CMD_ROUTEPOLICY: - mod = modPolicyRoutePolicy - } cmd := &cobra.Command{ Use: v, Run: func(cmd *cobra.Command, args []string) { @@ -1285,10 +897,17 @@ func NewPolicyCmd() *cobra.Command { } }, } - policyAddCmd := NewPolicyAddCmd(v, mod) - cmd.AddCommand(policyAddCmd) - policyDelCmd := NewPolicyDelCmd(mod) - cmd.AddCommand(policyDelCmd) + for _, w := range []string{CMD_ADD, CMD_DEL, CMD_SET} { + subcmd := &cobra.Command{ + Use: w, + Run: func(c *cobra.Command, args []string) { + if err := mod(cmd.Use, c.Use, args); err != nil { + fmt.Println(err) + } + }, + } + cmd.AddCommand(subcmd) + } policyCmd.AddCommand(cmd) } return policyCmd diff --git a/server/server.go b/server/server.go index 8867186c..18c2693e 100644 --- a/server/server.go +++ b/server/server.go @@ -1638,10 +1638,9 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { } close(grpcReq.ResponseCh) case REQ_MOD_DEFINED_SET: - if err := server.handleGrpcModDefinedSet(grpcReq); err != nil { - grpcReq.ResponseCh <- &GrpcResponse{ - ResponseErr: err, - } + err := server.handleGrpcModDefinedSet(grpcReq) + grpcReq.ResponseCh <- &GrpcResponse{ + ResponseErr: err, } close(grpcReq.ResponseCh) case REQ_POLICY_ROUTEPOLICY, REQ_POLICY_ROUTEPOLICIES: @@ -1698,6 +1697,7 @@ func (server *BgpServer) handleGrpcGetDefinedSet(grpcReq *GrpcRequest) error { if !ok { return fmt.Errorf("invalid defined-set type: %d", typ) } + found := false for _, s := range set { if name != "" && name != s.Name() { continue @@ -1705,10 +1705,14 @@ func (server *BgpServer) handleGrpcGetDefinedSet(grpcReq *GrpcRequest) error { grpcReq.ResponseCh <- &GrpcResponse{ Data: s.ToApiStruct(), } + found = true if name != "" { break } } + if !found { + return fmt.Errorf("not found %s", name) + } return nil } diff --git a/table/policy.go b/table/policy.go index 5fd406f4..97277f6f 100644 --- a/table/policy.go +++ b/table/policy.go @@ -469,23 +469,43 @@ func (s *regExpSet) Type() DefinedType { } func (lhs *regExpSet) Append(arg DefinedSet) error { - rhs, ok := arg.(*regExpSet) - if !ok { - return fmt.Errorf("type cast failed") + if lhs.Type() != arg.Type() { + return fmt.Errorf("can't append to different type of defined-set") } - lhs.list = append(lhs.list, rhs.list...) + var list []*regexp.Regexp + switch lhs.Type() { + case DEFINED_TYPE_AS_PATH: + list = arg.(*AsPathSet).list + case DEFINED_TYPE_COMMUNITY: + list = arg.(*CommunitySet).list + case DEFINED_TYPE_EXT_COMMUNITY: + list = arg.(*ExtCommunitySet).list + default: + return fmt.Errorf("invalid defined-set type: %d", lhs.Type()) + } + lhs.list = append(lhs.list, list...) return nil } func (lhs *regExpSet) Remove(arg DefinedSet) error { - rhs, ok := arg.(*regExpSet) - if !ok { - return fmt.Errorf("type cast failed") + if lhs.Type() != arg.Type() { + return fmt.Errorf("can't append to different type of defined-set") + } + var list []*regexp.Regexp + switch lhs.Type() { + case DEFINED_TYPE_AS_PATH: + list = arg.(*AsPathSet).list + case DEFINED_TYPE_COMMUNITY: + list = arg.(*CommunitySet).list + case DEFINED_TYPE_EXT_COMMUNITY: + list = arg.(*ExtCommunitySet).list + default: + return fmt.Errorf("invalid defined-set type: %d", lhs.Type()) } ps := make([]*regexp.Regexp, 0, len(lhs.list)) for _, x := range lhs.list { found := false - for _, y := range rhs.list { + for _, y := range list { if x.String() == y.String() { found = true break @@ -584,7 +604,7 @@ func ParseCommunity(arg string) (uint32, error) { return 0, fmt.Errorf("failed to parse %s as community", arg) } -func parseExtCommunity(arg string) (bgp.ExtendedCommunityInterface, error) { +func ParseExtCommunity(arg string) (bgp.ExtendedCommunityInterface, error) { var subtype bgp.ExtendedCommunityAttrSubType elems := strings.SplitN(arg, ":", 2) if len(elems) < 2 { @@ -601,7 +621,7 @@ func parseExtCommunity(arg string) (bgp.ExtendedCommunityInterface, error) { return bgp.ParseExtendedCommunity(subtype, elems[1]) } -func parseCommunityRegexp(arg string) (*regexp.Regexp, error) { +func ParseCommunityRegexp(arg string) (*regexp.Regexp, error) { i, err := strconv.Atoi(arg) if err == nil { return regexp.MustCompile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff)), nil @@ -621,7 +641,7 @@ func parseCommunityRegexp(arg string) (*regexp.Regexp, error) { return exp, nil } -func parseExtCommunityRegexp(arg string) (bgp.ExtendedCommunityAttrSubType, *regexp.Regexp, error) { +func ParseExtCommunityRegexp(arg string) (bgp.ExtendedCommunityAttrSubType, *regexp.Regexp, error) { var subtype bgp.ExtendedCommunityAttrSubType elems := strings.SplitN(arg, ":", 2) if len(elems) < 2 { @@ -635,7 +655,7 @@ func parseExtCommunityRegexp(arg string) (bgp.ExtendedCommunityAttrSubType, *reg default: return subtype, nil, fmt.Errorf("unknown ext-community subtype. rt, soo is supported") } - exp, err := parseCommunityRegexp(elems[1]) + exp, err := ParseCommunityRegexp(elems[1]) return subtype, exp, err } @@ -660,7 +680,7 @@ func NewCommunitySet(c config.CommunitySet) (*CommunitySet, error) { } list := make([]*regexp.Regexp, 0, len(c.CommunityList)) for _, x := range c.CommunityList { - exp, err := parseCommunityRegexp(x.Community) + exp, err := ParseCommunityRegexp(x.Community) if err != nil { return nil, err } @@ -680,6 +700,28 @@ type ExtCommunitySet struct { subtypeList []bgp.ExtendedCommunityAttrSubType } +func (s *ExtCommunitySet) ToApiStruct() *api.DefinedSet { + list := make([]string, 0, len(s.list)) + f := func(idx int, arg string) string { + switch s.subtypeList[idx] { + case bgp.EC_SUBTYPE_ROUTE_TARGET: + return fmt.Sprintf("rt:%s", arg) + case bgp.EC_SUBTYPE_ROUTE_ORIGIN: + return fmt.Sprintf("soo:%s", arg) + default: + return fmt.Sprintf("%d:%s", s.subtypeList[idx]) + } + } + for idx, exp := range s.list { + list = append(list, f(idx, exp.String())) + } + return &api.DefinedSet{ + Type: int32(s.typ), + Name: s.name, + List: list, + } +} + func NewExtCommunitySetFromApiStruct(a *api.DefinedSet) (*ExtCommunitySet, error) { c := config.ExtCommunitySet{ ExtCommunitySetName: a.Name, @@ -702,7 +744,7 @@ func NewExtCommunitySet(c config.ExtCommunitySet) (*ExtCommunitySet, error) { list := make([]*regexp.Regexp, 0, len(c.ExtCommunityList)) subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(c.ExtCommunityList)) for _, x := range c.ExtCommunityList { - subtype, exp, err := parseExtCommunityRegexp(x.ExtCommunity) + subtype, exp, err := ParseExtCommunityRegexp(x.ExtCommunity) if err != nil { return nil, err } @@ -1386,7 +1428,7 @@ func NewCommunityActionFromApiStruct(a *api.CommunityAction) (*CommunityAction, } for _, x := range a.Communities { if op == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { - exp, err := parseCommunityRegexp(x) + exp, err := ParseCommunityRegexp(x) if err != nil { return nil, err } @@ -1423,7 +1465,7 @@ func NewCommunityAction(c config.SetCommunity) (*CommunityAction, error) { } for _, x := range c.SetCommunityMethod.Communities { if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { - exp, err := parseCommunityRegexp(x) + exp, err := ParseCommunityRegexp(x) if err != nil { return nil, err } @@ -1498,14 +1540,14 @@ func NewExtCommunityActionFromApiStruct(a *api.CommunityAction) (*ExtCommunityAc } for _, x := range a.Communities { if op == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { - subtype, exp, err := parseExtCommunityRegexp(x) + subtype, exp, err := ParseExtCommunityRegexp(x) if err != nil { return nil, err } removeList = append(removeList, exp) subtypeList = append(subtypeList, subtype) } else { - comm, err := parseExtCommunity(x) + comm, err := ParseExtCommunity(x) if err != nil { return nil, err } @@ -1540,14 +1582,14 @@ func NewExtCommunityAction(c config.SetExtCommunity) (*ExtCommunityAction, error } for _, x := range c.SetExtCommunityMethod.Communities { if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { - subtype, exp, err := parseExtCommunityRegexp(x) + subtype, exp, err := ParseExtCommunityRegexp(x) if err != nil { return nil, err } removeList = append(removeList, exp) subtypeList = append(subtypeList, subtype) } else { - comm, err := parseExtCommunity(x) + comm, err := ParseExtCommunity(x) if err != nil { return nil, err } |