diff options
-rw-r--r-- | api/gobgp.pb.go | 14 | ||||
-rw-r--r-- | api/gobgp.proto | 7 | ||||
-rw-r--r-- | gobgp/cmd/common.go | 9 | ||||
-rw-r--r-- | gobgp/cmd/neighbor.go | 1 | ||||
-rw-r--r-- | gobgp/cmd/policy.go | 371 | ||||
-rw-r--r-- | table/policy.go | 75 |
6 files changed, 427 insertions, 50 deletions
diff --git a/api/gobgp.pb.go b/api/gobgp.pb.go index e3d4e8b3..5dc0f6fc 100644 --- a/api/gobgp.pb.go +++ b/api/gobgp.pb.go @@ -24,7 +24,6 @@ It has these top-level messages: Peer Prefix DefinedSet - PrefixSet MatchSet AsPathLength Conditions @@ -452,15 +451,6 @@ func (m *DefinedSet) GetPrefixes() []*Prefix { return nil } -type PrefixSet struct { - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Option int32 `protobuf:"varint,2,opt,name=option" json:"option,omitempty"` -} - -func (m *PrefixSet) Reset() { *m = PrefixSet{} } -func (m *PrefixSet) String() string { return proto.CompactTextString(m) } -func (*PrefixSet) ProtoMessage() {} - type MatchSet struct { Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` Option int32 `protobuf:"varint,2,opt,name=option" json:"option,omitempty"` @@ -480,7 +470,7 @@ func (m *AsPathLength) String() string { return proto.CompactTextString(m) } func (*AsPathLength) ProtoMessage() {} type Conditions struct { - PrefixSet *PrefixSet `protobuf:"bytes,1,opt,name=prefix_set" json:"prefix_set,omitempty"` + PrefixSet *MatchSet `protobuf:"bytes,1,opt,name=prefix_set" json:"prefix_set,omitempty"` NeighborSet *MatchSet `protobuf:"bytes,2,opt,name=neighbor_set" json:"neighbor_set,omitempty"` AsPathLength *AsPathLength `protobuf:"bytes,3,opt,name=as_path_length" json:"as_path_length,omitempty"` AsPathSet *MatchSet `protobuf:"bytes,4,opt,name=as_path_set" json:"as_path_set,omitempty"` @@ -493,7 +483,7 @@ func (m *Conditions) Reset() { *m = Conditions{} } func (m *Conditions) String() string { return proto.CompactTextString(m) } func (*Conditions) ProtoMessage() {} -func (m *Conditions) GetPrefixSet() *PrefixSet { +func (m *Conditions) GetPrefixSet() *MatchSet { if m != nil { return m.PrefixSet } diff --git a/api/gobgp.proto b/api/gobgp.proto index 84eb8c74..212f4073 100644 --- a/api/gobgp.proto +++ b/api/gobgp.proto @@ -201,11 +201,6 @@ message DefinedSet { repeated Prefix prefixes = 4; } -message PrefixSet { - string name = 1; - int32 option = 2; -} - message MatchSet { string name = 1; int32 option = 2; @@ -217,7 +212,7 @@ message AsPathLength { } message Conditions { - PrefixSet prefix_set = 1; + MatchSet prefix_set = 1; MatchSet neighbor_set = 2; AsPathLength as_path_length = 3; MatchSet as_path_set = 4; diff --git a/gobgp/cmd/common.go b/gobgp/cmd/common.go index 54bf07ea..d3f9a8cd 100644 --- a/gobgp/cmd/common.go +++ b/gobgp/cmd/common.go @@ -47,12 +47,9 @@ const ( CMD_ENABLE = "enable" CMD_DISABLE = "disable" CMD_PREFIX = "prefix" - CMD_ASPATH = "aspath" + CMD_ASPATH = "as-path" CMD_COMMUNITY = "community" - CMD_EXTCOMMUNITY = "extcommunity" - CMD_ROUTEPOLICY = "routepolicy" - CMD_CONDITIONS = "conditions" - CMD_ACTIONS = "actions" + CMD_EXTCOMMUNITY = "ext-community" CMD_IMPORT = "import" CMD_EXPORT = "export" CMD_IN = "in" @@ -67,6 +64,8 @@ const ( CMD_ACCEPTED = "accepted" CMD_REJECTED = "rejected" CMD_STATEMENT = "statement" + CMD_CONDITION = "condition" + CMD_ACTION = "action" ) var subOpts struct { diff --git a/gobgp/cmd/neighbor.go b/gobgp/cmd/neighbor.go index 12dbabf0..7627e943 100644 --- a/gobgp/cmd/neighbor.go +++ b/gobgp/cmd/neighbor.go @@ -789,6 +789,7 @@ func NewNeighborCmd() *cobra.Command { Use: w, Run: func(subcmd *cobra.Command, args []string) { remoteIP := net.ParseIP(args[len(args)-1]) + args = args[:len(args)-1] if remoteIP == nil { fmt.Println("invalid ip address:", args[len(args)-1]) os.Exit(1) diff --git a/gobgp/cmd/policy.go b/gobgp/cmd/policy.go index 441c1ff9..239676e8 100644 --- a/gobgp/cmd/policy.go +++ b/gobgp/cmd/policy.go @@ -26,6 +26,7 @@ import ( "golang.org/x/net/context" "io" "net" + "os" "regexp" "sort" "strconv" @@ -130,7 +131,7 @@ func formatDefinedSet(head bool, typ string, indent int, list []*api.DefinedSet) return buff.String() } -func show(v string, args []string) error { +func showDefinedSet(v string, args []string) error { var typ table.DefinedType switch v { case CMD_PREFIX: @@ -370,7 +371,7 @@ var modPolicyUsageFormat = map[string]string{ CMD_EXTCOMMUNITY: "usage: policy extcommunity %s <name> [<regexp>...]", } -func mod(settype string, modtype string, args []string) error { +func modDefinedSet(settype string, modtype string, args []string) error { var d *api.DefinedSet var err error if len(args) < 1 { @@ -573,7 +574,7 @@ func parseConditions() (*api.Conditions, error) { if err != nil { return nil, fmt.Errorf("invalid prefix option format\n%s", err) } - conditions.PrefixSet = &api.PrefixSet{ + conditions.PrefixSet = &api.MatchSet{ Name: name, Option: op, } @@ -781,7 +782,7 @@ func modPolicy(resource api.Resource, op api.Operation, data interface{}) error co := &api.Conditions{} switch resource { case api.Resource_POLICY_PREFIX: - co.PrefixSet = data.(*api.PrefixSet) + co.PrefixSet = data.(*api.MatchSet) case api.Resource_POLICY_NEIGHBOR: co.NeighborSet = data.(*api.MatchSet) case api.Resource_POLICY_ASPATH: @@ -908,6 +909,306 @@ func showStatement(args []string) error { return nil } +func modStatement(op string, args []string) error { + if len(args) < 1 { + return fmt.Errorf("usage: gobgp policy statement %s <name>", op) + } + name := args[0] + var o api.Operation + switch op { + case CMD_ADD: + o = api.Operation_ADD + case CMD_DEL: + o = api.Operation_DEL + default: + return fmt.Errorf("invalid operation: %s", op) + } + stmt := &api.Statement{ + Name: name, + } + arg := &api.ModStatementArguments{ + Operation: o, + Statement: stmt, + } + _, err := client.ModStatement(context.Background(), arg) + return err +} + +func modCondition(name, op string, args []string) error { + var o api.Operation + switch op { + case CMD_ADD: + o = api.Operation_ADD + case CMD_DEL: + o = api.Operation_DEL + case CMD_SET: + o = api.Operation_REPLACE + default: + return fmt.Errorf("invalid operation: %s", op) + } + stmt := &api.Statement{ + Name: name, + Conditions: &api.Conditions{}, + } + arg := &api.ModStatementArguments{ + Operation: o, + Statement: stmt, + } + usage := fmt.Sprintf("usage: gobgp policy statement %s %s condition", name, op) + if len(args) < 1 { + return fmt.Errorf("%s { prefix | neighbor | as-path | community | ext-community | as-path-length | rpki }", usage) + } + typ := args[0] + args = args[1:] + switch typ { + case "prefix": + if len(args) < 1 { + return fmt.Errorf("%s prefix <set-name> [{ any | invert }]", usage) + } + stmt.Conditions.PrefixSet = &api.MatchSet{ + Name: args[0], + } + if len(args) == 1 { + break + } + switch strings.ToLower(args[1]) { + case "any": + stmt.Conditions.PrefixSet.Option = int32(config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY) + case "invert": + stmt.Conditions.PrefixSet.Option = int32(config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT) + default: + return fmt.Errorf("%s prefix <set-name> [{ any | invert }]", usage) + } + case "neighbor": + if len(args) < 1 { + return fmt.Errorf("%s neighbor <set-name> [{ any | invert }]", usage) + } + stmt.Conditions.NeighborSet = &api.MatchSet{ + Name: args[0], + } + if len(args) == 1 { + break + } + switch strings.ToLower(args[1]) { + case "any": + stmt.Conditions.NeighborSet.Option = int32(config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY) + case "invert": + stmt.Conditions.NeighborSet.Option = int32(config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT) + default: + return fmt.Errorf("%s neighbor <set-name> [{ any | invert }]", usage) + } + case "as-path": + if len(args) < 1 { + return fmt.Errorf("%s as-path <set-name> [{ any | all | invert }]", usage) + } + stmt.Conditions.AsPathSet = &api.MatchSet{ + Name: args[0], + } + if len(args) == 1 { + break + } + switch strings.ToLower(args[1]) { + case "any": + stmt.Conditions.AsPathSet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_ANY) + case "all": + stmt.Conditions.AsPathSet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_ALL) + case "invert": + stmt.Conditions.AsPathSet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_INVERT) + default: + return fmt.Errorf("%s as-path <set-name> [{ any | all | invert }]", usage) + } + case "community": + if len(args) < 1 { + return fmt.Errorf("%s community <set-name> [{ any | all | invert }]", usage) + } + stmt.Conditions.CommunitySet = &api.MatchSet{ + Name: args[0], + } + if len(args) == 1 { + break + } + switch strings.ToLower(args[1]) { + case "any": + stmt.Conditions.CommunitySet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_ANY) + case "all": + stmt.Conditions.CommunitySet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_ALL) + case "invert": + stmt.Conditions.CommunitySet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_INVERT) + default: + return fmt.Errorf("%s community <set-name> [{ any | all | invert }]", usage) + } + case "ext-community": + if len(args) < 1 { + return fmt.Errorf("%s ext-community <set-name> [{ any | all | invert }]", usage) + } + stmt.Conditions.ExtCommunitySet = &api.MatchSet{ + Name: args[0], + } + if len(args) == 1 { + break + } + switch strings.ToLower(args[1]) { + case "any": + stmt.Conditions.ExtCommunitySet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_ANY) + case "all": + stmt.Conditions.ExtCommunitySet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_ALL) + case "invert": + stmt.Conditions.ExtCommunitySet.Option = int32(config.MATCH_SET_OPTIONS_TYPE_INVERT) + default: + return fmt.Errorf("%s ext-community <set-name> [{ any | all | invert }]", usage) + } + case "as-path-length": + if len(args) < 2 { + return fmt.Errorf("%s as-path-length <length> { eq | ge | le }", usage) + } + length, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + stmt.Conditions.AsPathLength = &api.AsPathLength{ + Length: uint32(length), + } + switch strings.ToLower(args[1]) { + case "eq": + stmt.Conditions.AsPathLength.Type = int32(table.ATTRIBUTE_EQ) + case "ge": + stmt.Conditions.AsPathLength.Type = int32(table.ATTRIBUTE_GE) + case "le": + stmt.Conditions.AsPathLength.Type = int32(table.ATTRIBUTE_LE) + default: + return fmt.Errorf("%s as-path-length <length> { eq | ge | le }", usage) + } + case "rpki": + if len(args) < 1 { + return fmt.Errorf("%s rpki { valid | invalid | not-found }") + } + switch strings.ToLower(args[0]) { + case "valid": + stmt.Conditions.RpkiResult = int32(config.RPKI_VALIDATION_RESULT_TYPE_VALID) + case "invalid": + stmt.Conditions.RpkiResult = int32(config.RPKI_VALIDATION_RESULT_TYPE_INVALID) + case "not-found": + stmt.Conditions.RpkiResult = int32(config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND) + default: + return fmt.Errorf("%s rpki { valid | invalid | not-found }") + } + } + _, err := client.ModStatement(context.Background(), arg) + return err +} + +func modAction(name, op string, args []string) error { + var o api.Operation + switch op { + case CMD_ADD: + o = api.Operation_ADD + case CMD_DEL: + o = api.Operation_DEL + case CMD_SET: + o = api.Operation_REPLACE + default: + return fmt.Errorf("invalid operation: %s", op) + } + stmt := &api.Statement{ + Name: name, + Actions: &api.Actions{}, + } + arg := &api.ModStatementArguments{ + Operation: o, + Statement: stmt, + } + usage := fmt.Sprintf("usage: gobgp policy statement %s %s action", name, op) + if len(args) < 1 { + return fmt.Errorf("%s { reject | accept | community | ext-community | med | as-prepend }", usage) + } + typ := args[0] + args = args[1:] + switch typ { + case "reject": + stmt.Actions.RouteAction = api.RouteAction_REJECT + case "accept": + stmt.Actions.RouteAction = api.RouteAction_ACCEPT + case "community": + if len(args) < 1 { + return fmt.Errorf("%s community { add | remove | replace } <value>...", usage) + } + stmt.Actions.Community = &api.CommunityAction{ + Communities: args[1:], + } + switch strings.ToLower(args[0]) { + case "add": + stmt.Actions.Community.Option = int32(config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD) + case "remove": + stmt.Actions.Community.Option = int32(config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE) + case "replace": + stmt.Actions.Community.Option = int32(config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE) + default: + return fmt.Errorf("%s community { add | remove | replace } <value>...", usage) + } + case "ext-community": + if len(args) < 1 { + return fmt.Errorf("%s ext-community { add | remove | replace } <value>...", usage) + } + stmt.Actions.ExtCommunity = &api.CommunityAction{ + Communities: args[1:], + } + switch strings.ToLower(args[0]) { + case "add": + stmt.Actions.ExtCommunity.Option = int32(config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD) + case "remove": + stmt.Actions.ExtCommunity.Option = int32(config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE) + case "replace": + stmt.Actions.ExtCommunity.Option = int32(config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE) + default: + return fmt.Errorf("%s ext-community { add | remove | replace } <value>...", usage) + } + case "med": + if len(args) < 2 { + return fmt.Errorf("%s med { add | sub | set } <value>") + } + med, err := strconv.Atoi(args[1]) + if err != nil { + return err + } + stmt.Actions.Med = &api.MedAction{ + Value: int64(med), + } + switch strings.ToLower(args[0]) { + case "add": + stmt.Actions.Med.Type = int32(table.MED_ACTION_MOD) + case "sub": + stmt.Actions.Med.Type = int32(table.MED_ACTION_MOD) + stmt.Actions.Med.Value *= -1 + case "set": + stmt.Actions.Med.Type = int32(table.MED_ACTION_REPLACE) + default: + return fmt.Errorf("%s med { add | sub | set } <value>") + } + case "as-prepend": + if len(args) < 2 { + return fmt.Errorf("%s as-prepend { <asn> | last-as } <repeat-value>", usage) + } + asn, err := strconv.Atoi(args[0]) + last := false + if args[0] == "last-as" { + last = true + } else if err != nil { + return err + } + repeat, err := strconv.Atoi(args[1]) + if err != nil { + return err + } + stmt.Actions.AsPrepend = &api.AsPrependAction{ + Asn: uint32(asn), + Repeat: uint32(repeat), + UseLeftMost: last, + } + } + _, err := client.ModStatement(context.Background(), arg) + return err +} + func NewPolicyCmd() *cobra.Command { policyCmd := &cobra.Command{ Use: CMD_POLICY, @@ -924,11 +1225,11 @@ func NewPolicyCmd() *cobra.Command { }, } - for _, v := range []string{CMD_PREFIX, CMD_NEIGHBOR, CMD_ASPATH, CMD_COMMUNITY, CMD_EXTCOMMUNITY, CMD_ROUTEPOLICY} { + for _, v := range []string{CMD_PREFIX, CMD_NEIGHBOR, CMD_ASPATH, CMD_COMMUNITY, CMD_EXTCOMMUNITY} { cmd := &cobra.Command{ Use: v, Run: func(cmd *cobra.Command, args []string) { - if err := show(cmd.Use, args); err != nil { + if err := showDefinedSet(cmd.Use, args); err != nil { fmt.Println(err) } }, @@ -937,7 +1238,7 @@ func NewPolicyCmd() *cobra.Command { subcmd := &cobra.Command{ Use: w, Run: func(c *cobra.Command, args []string) { - if err := mod(cmd.Use, c.Use, args); err != nil { + if err := modDefinedSet(cmd.Use, c.Use, args); err != nil { fmt.Println(err) } }, @@ -947,15 +1248,65 @@ func NewPolicyCmd() *cobra.Command { policyCmd.AddCommand(cmd) } - cmd := &cobra.Command{ + stmtCmdImpl := &cobra.Command{} + for _, v := range []string{CMD_ADD, CMD_DEL, CMD_SET} { + cmd := &cobra.Command{ + Use: v, + } + for _, w := range []string{CMD_CONDITION, CMD_ACTION} { + subcmd := &cobra.Command{ + Use: w, + Run: func(c *cobra.Command, args []string) { + name := args[len(args)-1] + args = args[:len(args)-1] + var err error + if c.Use == CMD_CONDITION { + err = modCondition(name, cmd.Use, args) + } else { + err = modAction(name, cmd.Use, args) + } + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }, + } + cmd.AddCommand(subcmd) + } + stmtCmdImpl.AddCommand(cmd) + } + + stmtCmd := &cobra.Command{ Use: CMD_STATEMENT, Run: func(cmd *cobra.Command, args []string) { - if err := showStatement(args); err != nil { + var err error + if len(args) < 2 { + err = showStatement(args) + } else { + args = append(args[1:], args[0]) + stmtCmdImpl.SetArgs(args) + err = stmtCmdImpl.Execute() + } + if err != nil { fmt.Println(err) + os.Exit(1) } }, } - policyCmd.AddCommand(cmd) + for _, v := range []string{CMD_ADD, CMD_DEL} { + cmd := &cobra.Command{ + Use: v, + Run: func(c *cobra.Command, args []string) { + err := modStatement(c.Use, args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }, + } + stmtCmd.AddCommand(cmd) + } + policyCmd.AddCommand(stmtCmd) return policyCmd } diff --git a/table/policy.go b/table/policy.go index 1d8cb161..4b146a0f 100644 --- a/table/policy.go +++ b/table/policy.go @@ -854,14 +854,17 @@ func (c *PrefixCondition) Evaluate(path *Path) bool { return result } -func (c *PrefixCondition) ToApiStruct() *api.PrefixSet { - return &api.PrefixSet{ +func (c *PrefixCondition) ToApiStruct() *api.MatchSet { + return &api.MatchSet{ Name: c.set.Name(), Option: int32(c.option), } } -func NewPrefixConditionFromApiStruct(a *api.PrefixSet, m map[string]DefinedSet) (*PrefixCondition, error) { +func NewPrefixConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*PrefixCondition, error) { + if a == nil { + return nil, nil + } c := config.MatchPrefixSet{ PrefixSet: a.Name, MatchSetOptions: config.MatchSetOptionsRestrictedType(a.Option), @@ -951,6 +954,9 @@ func (c *NeighborCondition) ToApiStruct() *api.MatchSet { } func NewNeighborConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*NeighborCondition, error) { + if a == nil { + return nil, nil + } c := config.MatchNeighborSet{ NeighborSet: a.Name, MatchSetOptions: config.MatchSetOptionsRestrictedType(a.Option), @@ -1032,6 +1038,9 @@ func (c *AsPathCondition) Evaluate(path *Path) bool { } func NewAsPathConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*AsPathCondition, error) { + if a == nil { + return nil, nil + } c := config.MatchAsPathSet{ AsPathSet: a.Name, MatchSetOptions: config.MatchSetOptionsType(a.Option), @@ -1116,6 +1125,9 @@ func (c *CommunityCondition) Evaluate(path *Path) bool { } func NewCommunityConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*CommunityCondition, error) { + if a == nil { + return nil, nil + } c := config.MatchCommunitySet{ CommunitySet: a.Name, MatchSetOptions: config.MatchSetOptionsType(a.Option), @@ -1206,6 +1218,9 @@ func (c *ExtCommunityCondition) Evaluate(path *Path) bool { } func NewExtCommunityConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*ExtCommunityCondition, error) { + if a == nil { + return nil, nil + } c := config.MatchExtCommunitySet{ ExtCommunitySet: a.Name, MatchSetOptions: config.MatchSetOptionsType(a.Option), @@ -1281,6 +1296,9 @@ func (c *AsPathLengthCondition) ToApiStruct() *api.AsPathLength { } func NewAsPathLengthConditionFromApiStruct(a *api.AsPathLength) (*AsPathLengthCondition, error) { + if a == nil { + return nil, nil + } return &AsPathLengthCondition{ length: a.Length, operator: AttributeComparison(a.Type), @@ -1325,6 +1343,9 @@ func (c *RpkiValidationCondition) Set() DefinedSet { } func NewRpkiValidationConditionFromApiStruct(a int32) (*RpkiValidationCondition, error) { + if a == 0 { + return nil, nil + } typ := config.RpkiValidationResultType(a) return NewRpkiValidationCondition(typ) } @@ -1478,6 +1499,9 @@ func (a *CommunityAction) ToApiStruct() *api.CommunityAction { } func NewCommunityActionFromApiStruct(a *api.CommunityAction) (*CommunityAction, error) { + if a == nil { + return nil, nil + } var list []uint32 var removeList []*regexp.Regexp op := config.BgpSetCommunityOptionType(a.Option) @@ -1593,6 +1617,9 @@ func (a *ExtCommunityAction) ToApiStruct() *api.CommunityAction { } func NewExtCommunityActionFromApiStruct(a *api.CommunityAction) (*ExtCommunityAction, error) { + if a == nil { + return nil, nil + } var list []bgp.ExtendedCommunityInterface var removeList []*regexp.Regexp subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(a.Communities)) @@ -1713,6 +1740,9 @@ func (a *MedAction) ToApiStruct() *api.MedAction { } func NewMedActionFromApiStruct(a *api.MedAction) (*MedAction, error) { + if a == nil { + return nil, nil + } return &MedAction{ action: MedActionType(a.Type), value: int(a.Value), @@ -1793,6 +1823,9 @@ func (a *AsPathPrependAction) ToApiStruct() *api.AsPrependAction { } func NewAsPathPrependActionFromApiStruct(a *api.AsPrependAction) (*AsPathPrependAction, error) { + if a == nil { + return nil, nil + } return &AsPathPrependAction{ asn: a.Asn, useLeftMost: a.UseLeftMost, @@ -1897,7 +1930,9 @@ func (s *Statement) ToApiStruct() *api.Statement { } } as := &api.Actions{} - as.RouteAction = s.RouteAction.(*RoutingAction).ToApiStruct() + if s.RouteAction != nil { + as.RouteAction = s.RouteAction.(*RoutingAction).ToApiStruct() + } for _, a := range s.ModActions { switch a.(type) { case *CommunityAction: @@ -1944,38 +1979,41 @@ func (lhs *Statement) mod(op opType, rhs *Statement) error { switch op { case ADD: if c != nil { - return fmt.Errorf("condition %d is already set", c.Type()) + return fmt.Errorf("condition %d is already set", x.Type()) } if cs == nil { - cs = make([]Condition, len(rhs.Conditions)) + cs = make([]Condition, 0, len(rhs.Conditions)) } cs = append(cs, x) case REMOVE: if c == nil { - return fmt.Errorf("condition %d is not set", c.Type()) + return fmt.Errorf("condition %d is not set", x.Type()) } cs = append(cs[:i], cs[i+1:]...) + if len(cs) == 0 { + cs = nil + } case REPLACE: if c == nil { - return fmt.Errorf("condition %d is not set", c.Type()) + return fmt.Errorf("condition %d is not set", x.Type()) } cs[i] = x } } - if rhs.RouteAction != nil { + if rhs.RouteAction != nil && !reflect.ValueOf(rhs.RouteAction).IsNil() { switch op { case ADD: - if lhs.RouteAction != nil { + if lhs.RouteAction != nil && !reflect.ValueOf(lhs.RouteAction).IsNil() { return fmt.Errorf("route action is already set") } ra = rhs.RouteAction case REMOVE: - if lhs.RouteAction == nil { + if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() { return fmt.Errorf("route action is not set") } ra = nil case REPLACE: - if lhs.RouteAction == nil { + if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() { return fmt.Errorf("route action is not set") } ra = rhs.RouteAction @@ -1994,20 +2032,23 @@ func (lhs *Statement) mod(op opType, rhs *Statement) error { switch op { case ADD: if a != nil { - return fmt.Errorf("action %d is already set", a.Type()) + return fmt.Errorf("action %d is already set", x.Type()) } if as == nil { - as = make([]Action, len(rhs.ModActions)) + as = make([]Action, 0, len(rhs.ModActions)) } - as = append(as, a) + as = append(as, x) case REMOVE: if a == nil { - return fmt.Errorf("action %d is not set", a.Type()) + return fmt.Errorf("action %d is not set", x.Type()) } as = append(as[:i], as[i+1:]...) + if len(as) == 0 { + as = nil + } case REPLACE: if a == nil { - return fmt.Errorf("action %d is not set", a.Type()) + return fmt.Errorf("action %d is not set", x.Type()) } as[i] = x } |