diff options
Diffstat (limited to 'table')
-rw-r--r-- | table/path.go | 22 | ||||
-rw-r--r-- | table/policy.go | 259 | ||||
-rw-r--r-- | table/policy_test.go | 93 |
3 files changed, 355 insertions, 19 deletions
diff --git a/table/path.go b/table/path.go index f8058730..81d76ddf 100644 --- a/table/path.go +++ b/table/path.go @@ -762,6 +762,28 @@ func (path *Path) SetExtCommunities(exts []bgp.ExtendedCommunityInterface, doRep } } +func (path *Path) GetLargeCommunities() []*bgp.LargeCommunity { + if a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY); a != nil { + v := a.(*bgp.PathAttributeLargeCommunities).Values + ret := make([]*bgp.LargeCommunity, 0, len(v)) + for _, c := range v { + ret = append(ret, c) + } + return ret + } + return nil +} + +func (path *Path) SetLargeCommunities(cs []*bgp.LargeCommunity, doReplace bool) { + a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY) + if a == nil || doReplace { + path.setPathAttr(bgp.NewPathAttributeLargeCommunities(cs)) + } else { + l := a.(*bgp.PathAttributeLargeCommunities).Values + path.setPathAttr(bgp.NewPathAttributeLargeCommunities(append(l, cs...))) + } +} + func (path *Path) GetMed() (uint32, error) { attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) if attr == nil { diff --git a/table/policy.go b/table/policy.go index 2122e508..a90d7fe3 100644 --- a/table/policy.go +++ b/table/policy.go @@ -44,6 +44,7 @@ const ( DEFINED_TYPE_AS_PATH DEFINED_TYPE_COMMUNITY DEFINED_TYPE_EXT_COMMUNITY + DEFINED_TYPE_LARGE_COMMUNITY ) type RouteType int @@ -126,6 +127,7 @@ const ( CONDITION_AS_PATH_LENGTH CONDITION_RPKI CONDITION_ROUTE_TYPE + CONDITION_LARGE_COMMUNITY ) type ActionType int @@ -138,6 +140,7 @@ const ( ACTION_AS_PATH_PREPEND ACTION_NEXTHOP ACTION_LOCAL_PREF + ACTION_LARGE_COMMUNITY ) func NewMatchOption(c interface{}) (MatchOption, error) { @@ -733,6 +736,8 @@ func (lhs *regExpSet) Append(arg DefinedSet) error { list = arg.(*CommunitySet).list case DEFINED_TYPE_EXT_COMMUNITY: list = arg.(*ExtCommunitySet).list + case DEFINED_TYPE_LARGE_COMMUNITY: + list = arg.(*LargeCommunitySet).list default: return fmt.Errorf("invalid defined-set type: %d", lhs.Type()) } @@ -752,6 +757,8 @@ func (lhs *regExpSet) Remove(arg DefinedSet) error { list = arg.(*CommunitySet).list case DEFINED_TYPE_EXT_COMMUNITY: list = arg.(*ExtCommunitySet).list + case DEFINED_TYPE_LARGE_COMMUNITY: + list = arg.(*LargeCommunitySet).list default: return fmt.Errorf("invalid defined-set type: %d", lhs.Type()) } @@ -773,11 +780,16 @@ func (lhs *regExpSet) Remove(arg DefinedSet) error { } func (lhs *regExpSet) Replace(arg DefinedSet) error { - rhs, ok := arg.(*regExpSet) - if !ok { + switch c := arg.(type) { + case *CommunitySet: + lhs.list = c.list + case *ExtCommunitySet: + lhs.list = c.list + case *LargeCommunitySet: + lhs.list = c.list + default: return fmt.Errorf("type cast failed") } - lhs.list = rhs.list return nil } @@ -966,6 +978,57 @@ func NewExtCommunitySet(c config.ExtCommunitySet) (*ExtCommunitySet, error) { }, nil } +type LargeCommunitySet struct { + regExpSet +} + +func (s *LargeCommunitySet) ToConfig() *config.LargeCommunitySet { + list := make([]string, 0, len(s.list)) + for _, exp := range s.list { + list = append(list, exp.String()) + } + return &config.LargeCommunitySet{ + LargeCommunitySetName: s.name, + LargeCommunityList: list, + } +} + +func ParseLargeCommunityRegexp(arg string) (*regexp.Regexp, error) { + if regexp.MustCompile("\\d+:\\d+:\\d+").MatchString(arg) { + return regexp.MustCompile(fmt.Sprintf("^%s$", arg)), nil + } + exp, err := regexp.Compile(arg) + if err != nil { + return nil, fmt.Errorf("invalid large-community format: %s", arg) + } + return exp, nil +} + +func NewLargeCommunitySet(c config.LargeCommunitySet) (*LargeCommunitySet, error) { + name := c.LargeCommunitySetName + if name == "" { + if len(c.LargeCommunityList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("empty large community set name") + } + list := make([]*regexp.Regexp, 0, len(c.LargeCommunityList)) + for _, x := range c.LargeCommunityList { + exp, err := ParseLargeCommunityRegexp(x) + if err != nil { + return nil, err + } + list = append(list, exp) + } + return &LargeCommunitySet{ + regExpSet: regExpSet{ + typ: DEFINED_TYPE_LARGE_COMMUNITY, + name: name, + list: list, + }, + }, nil +} + type Condition interface { Name() string Type() ConditionType @@ -1310,6 +1373,64 @@ func NewExtCommunityCondition(c config.MatchExtCommunitySet) (*ExtCommunityCondi }, nil } +type LargeCommunityCondition struct { + name string + set *LargeCommunitySet + option MatchOption +} + +func (c *LargeCommunityCondition) Type() ConditionType { + return CONDITION_LARGE_COMMUNITY +} + +func (c *LargeCommunityCondition) Set() DefinedSet { + return c.set +} + +func (c *LargeCommunityCondition) Option() MatchOption { + return c.option +} + +func (c *LargeCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool { + result := false + cs := path.GetLargeCommunities() + for _, x := range c.set.list { + result = false + for _, y := range cs { + if x.MatchString(y.String()) { + result = true + break + } + } + if c.option == MATCH_OPTION_ALL && !result { + break + } + if (c.option == MATCH_OPTION_ANY || c.option == MATCH_OPTION_INVERT) && result { + break + } + } + if c.option == MATCH_OPTION_INVERT { + result = !result + } + return result +} + +func (c *LargeCommunityCondition) Name() string { return c.name } + +func NewLargeCommunityCondition(c config.MatchLargeCommunitySet) (*LargeCommunityCondition, error) { + if c.LargeCommunitySet == "" { + return nil, nil + } + o, err := NewMatchOption(c.MatchSetOptions) + if err != nil { + return nil, err + } + return &LargeCommunityCondition{ + name: c.LargeCommunitySet, + option: o, + }, nil +} + type AsPathLengthCondition struct { length uint32 operator AttributeComparison @@ -1507,6 +1628,25 @@ func RegexpRemoveExtCommunities(path *Path, exps []*regexp.Regexp, subtypes []bg path.SetExtCommunities(newComms, true) } +func RegexpRemoveLargeCommunities(path *Path, exps []*regexp.Regexp) { + comms := path.GetLargeCommunities() + newComms := make([]*bgp.LargeCommunity, 0, len(comms)) + for _, comm := range comms { + c := comm.String() + match := false + for _, exp := range exps { + if exp.MatchString(c) { + match = true + break + } + } + if match == false { + newComms = append(newComms, comm) + } + } + path.SetLargeCommunities(newComms, true) +} + func (a *CommunityAction) Type() ActionType { return ACTION_COMMUNITY } @@ -1668,6 +1808,80 @@ func NewExtCommunityAction(c config.SetExtCommunity) (*ExtCommunityAction, error }, nil } +type LargeCommunityAction struct { + action config.BgpSetCommunityOptionType + list []*bgp.LargeCommunity + removeList []*regexp.Regexp +} + +func (a *LargeCommunityAction) Type() ActionType { + return ACTION_LARGE_COMMUNITY +} + +func (a *LargeCommunityAction) Apply(path *Path, _ *PolicyOptions) *Path { + switch a.action { + case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: + path.SetLargeCommunities(a.list, false) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: + RegexpRemoveLargeCommunities(path, a.removeList) + case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: + path.SetLargeCommunities(a.list, true) + } + return path +} + +func (a *LargeCommunityAction) ToConfig() *config.SetLargeCommunity { + cs := make([]string, 0, len(a.list)+len(a.removeList)) + for _, comm := range a.list { + cs = append(cs, comm.String()) + } + for _, exp := range a.removeList { + cs = append(cs, exp.String()) + } + return &config.SetLargeCommunity{ + SetLargeCommunityMethod: config.SetLargeCommunityMethod{CommunitiesList: cs}, + Options: config.BgpSetCommunityOptionType(a.action), + } +} + +func NewLargeCommunityAction(c config.SetLargeCommunity) (*LargeCommunityAction, error) { + a, ok := CommunityOptionValueMap[strings.ToLower(string(c.Options))] + if !ok { + if len(c.SetLargeCommunityMethod.CommunitiesList) == 0 { + return nil, nil + } + return nil, fmt.Errorf("invalid option name: %s", c.Options) + } + var list []*bgp.LargeCommunity + var removeList []*regexp.Regexp + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + removeList = make([]*regexp.Regexp, 0, len(c.SetLargeCommunityMethod.CommunitiesList)) + } else { + list = make([]*bgp.LargeCommunity, 0, len(c.SetLargeCommunityMethod.CommunitiesList)) + } + for _, x := range c.SetLargeCommunityMethod.CommunitiesList { + if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE { + exp, err := ParseLargeCommunityRegexp(x) + if err != nil { + return nil, err + } + removeList = append(removeList, exp) + } else { + comm, err := bgp.ParseLargeCommunity(x) + if err != nil { + return nil, err + } + list = append(list, comm) + } + } + return &LargeCommunityAction{ + action: a, + list: list, + removeList: removeList, + }, nil + +} + type MedAction struct { value int64 action MedActionType @@ -1943,6 +2157,9 @@ func (s *Statement) ToConfig() *config.Statement { case *ExtCommunityCondition: v := c.(*ExtCommunityCondition) cond.BgpConditions.MatchExtCommunitySet = config.MatchExtCommunitySet{ExtCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]} + case *LargeCommunityCondition: + v := c.(*LargeCommunityCondition) + cond.BgpConditions.MatchLargeCommunitySet = config.MatchLargeCommunitySet{LargeCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]} case *RpkiValidationCondition: v := c.(*RpkiValidationCondition) cond.BgpConditions.RpkiValidationResult = v.result @@ -1967,6 +2184,8 @@ func (s *Statement) ToConfig() *config.Statement { act.BgpActions.SetCommunity = *a.(*CommunityAction).ToConfig() case *ExtCommunityAction: act.BgpActions.SetExtCommunity = *a.(*ExtCommunityAction).ToConfig() + case *LargeCommunityAction: + act.BgpActions.SetLargeCommunity = *a.(*LargeCommunityAction).ToConfig() case *MedAction: act.BgpActions.SetMed = a.(*MedAction).ToConfig() case *LocalPrefAction: @@ -2132,6 +2351,9 @@ func NewStatement(c config.Statement) (*Statement, error) { func() (Condition, error) { return NewExtCommunityCondition(c.Conditions.BgpConditions.MatchExtCommunitySet) }, + func() (Condition, error) { + return NewLargeCommunityCondition(c.Conditions.BgpConditions.MatchLargeCommunitySet) + }, } cs = make([]Condition, 0, len(cfs)) for _, f := range cfs { @@ -2155,6 +2377,9 @@ func NewStatement(c config.Statement) (*Statement, error) { return NewExtCommunityAction(c.Actions.BgpActions.SetExtCommunity) }, func() (Action, error) { + return NewLargeCommunityAction(c.Actions.BgpActions.SetLargeCommunity) + }, + func() (Action, error) { return NewMedAction(c.Actions.BgpActions.SetMed) }, func() (Action, error) { @@ -2478,6 +2703,14 @@ func (r *RoutingPolicy) validateCondition(v Condition) (err error) { c := v.(*ExtCommunityCondition) c.set = i.(*ExtCommunitySet) } + case CONDITION_LARGE_COMMUNITY: + m := r.definedSetMap[DEFINED_TYPE_LARGE_COMMUNITY] + if i, ok := m[v.Name()]; !ok { + return fmt.Errorf("not found large-community set %s", v.Name()) + } else { + c := v.(*LargeCommunityCondition) + c.set = i.(*LargeCommunitySet) + } case CONDITION_AS_PATH_LENGTH: case CONDITION_RPKI: } @@ -2576,6 +2809,17 @@ func (r *RoutingPolicy) reload(c config.RoutingPolicy) error { } dmap[DEFINED_TYPE_EXT_COMMUNITY][y.Name()] = y } + dmap[DEFINED_TYPE_LARGE_COMMUNITY] = make(map[string]DefinedSet) + for _, x := range bd.LargeCommunitySets { + y, err := NewLargeCommunitySet(x) + if err != nil { + return err + } + if y == nil { + return fmt.Errorf("empty large-community set") + } + dmap[DEFINED_TYPE_LARGE_COMMUNITY][y.Name()] = y + } pmap := make(map[string]*Policy) smap := make(map[string]*Statement) for _, x := range c.PolicyDefinitions { @@ -2632,9 +2876,10 @@ func (r *RoutingPolicy) GetDefinedSet(typ DefinedType) (*config.DefinedSets, err PrefixSets: make([]config.PrefixSet, 0), NeighborSets: make([]config.NeighborSet, 0), BgpDefinedSets: config.BgpDefinedSets{ - CommunitySets: make([]config.CommunitySet, 0), - ExtCommunitySets: make([]config.ExtCommunitySet, 0), - AsPathSets: make([]config.AsPathSet, 0), + CommunitySets: make([]config.CommunitySet, 0), + ExtCommunitySets: make([]config.ExtCommunitySet, 0), + LargeCommunitySets: make([]config.LargeCommunitySet, 0), + AsPathSets: make([]config.AsPathSet, 0), }, } for _, s := range set { @@ -2647,6 +2892,8 @@ func (r *RoutingPolicy) GetDefinedSet(typ DefinedType) (*config.DefinedSets, err sets.BgpDefinedSets.CommunitySets = append(sets.BgpDefinedSets.CommunitySets, *s.(*CommunitySet).ToConfig()) case *ExtCommunitySet: sets.BgpDefinedSets.ExtCommunitySets = append(sets.BgpDefinedSets.ExtCommunitySets, *s.(*ExtCommunitySet).ToConfig()) + case *LargeCommunitySet: + sets.BgpDefinedSets.LargeCommunitySets = append(sets.BgpDefinedSets.LargeCommunitySets, *s.(*LargeCommunitySet).ToConfig()) case *AsPathSet: sets.BgpDefinedSets.AsPathSets = append(sets.BgpDefinedSets.AsPathSets, *s.(*AsPathSet).ToConfig()) } diff --git a/table/policy_test.go b/table/policy_test.go index cfafb1ed..58b944e2 100644 --- a/table/policy_test.go +++ b/table/policy_test.go @@ -29,12 +29,7 @@ import ( "time" ) -func init() { - log.SetLevel(log.DebugLevel) -} - func TestPrefixCalcurateNoRange(t *testing.T) { - log.SetLevel(log.DebugLevel) // create path peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")} origin := bgp.NewPathAttributeOrigin(0) @@ -125,7 +120,6 @@ func TestPrefixCalcurateLengthRange(t *testing.T) { } func TestPrefixCalcurateNoRangeIPv6(t *testing.T) { - log.SetLevel(log.DebugLevel) // create path peer := &PeerInfo{AS: 65001, Address: net.ParseIP("2001::192:168:50:1")} origin := bgp.NewPathAttributeOrigin(0) @@ -1415,8 +1409,6 @@ func TestAs4PathConditionEvaluateMixedWith2byteAS(t *testing.T) { func TestCommunityConditionEvaluate(t *testing.T) { - log.SetLevel(log.DebugLevel) - // setup // create path peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")} @@ -1681,7 +1673,6 @@ func TestPolicyMatchAndAddCommunities(t *testing.T) { pType, newPath := p.Apply(path, nil) assert.Equal(t, ROUTE_TYPE_ACCEPT, pType) assert.NotEqual(t, nil, newPath) - log.Debug(newPath) assert.Equal(t, []uint32{stringToCommunityValue(community)}, newPath.GetCommunities()) } @@ -1914,8 +1905,6 @@ func TestPolicyMatchAndClearCommunities(t *testing.T) { func TestExtCommunityConditionEvaluate(t *testing.T) { - log.SetLevel(log.DebugLevel) - // setup // create path peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")} @@ -2097,8 +2086,6 @@ func TestExtCommunityConditionEvaluate(t *testing.T) { func TestExtCommunityConditionEvaluateWithOtherCondition(t *testing.T) { - log.SetLevel(log.DebugLevel) - // setup // create path peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.2.1.1")} @@ -2871,5 +2858,85 @@ func TestPrefixSetMatch(t *testing.T) { path = NewPath(nil, bgp.NewIPAddrPrefix(6, "0.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false) assert.False(t, m.Evaluate(path, nil)) +} + +func TestLargeCommunityMatchAction(t *testing.T) { + coms := []*bgp.LargeCommunity{ + &bgp.LargeCommunity{100, 100, 100}, + &bgp.LargeCommunity{100, 200, 200}, + } + p := NewPath(nil, nil, false, []bgp.PathAttributeInterface{bgp.NewPathAttributeLargeCommunities(coms)}, time.Time{}, false) + + c := config.LargeCommunitySet{ + LargeCommunitySetName: "l0", + LargeCommunityList: []string{ + "100:100:100", + "100:300:100", + }, + } + + set, err := NewLargeCommunitySet(c) + assert.Equal(t, err, nil) + + m, err := NewLargeCommunityCondition(config.MatchLargeCommunitySet{ + LargeCommunitySet: "l0", + }) + assert.Equal(t, err, nil) + m.set = set + + assert.Equal(t, m.Evaluate(p, nil), true) + + a, err := NewLargeCommunityAction(config.SetLargeCommunity{ + config.SetLargeCommunityMethod{ + []string{"100:100:100"}, + }, + config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE, + }) + assert.Equal(t, err, nil) + p = a.Apply(p, nil) + + assert.Equal(t, m.Evaluate(p, nil), false) + + a, err = NewLargeCommunityAction(config.SetLargeCommunity{ + config.SetLargeCommunityMethod{ + []string{ + "100:300:100", + "200:100:100", + }, + }, + config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD, + }) + assert.Equal(t, err, nil) + p = a.Apply(p, nil) + + assert.Equal(t, m.Evaluate(p, nil), true) + + a, err = NewLargeCommunityAction(config.SetLargeCommunity{ + config.SetLargeCommunityMethod{ + []string{"^100:"}, + }, + config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE, + }) + assert.Equal(t, err, nil) + p = a.Apply(p, nil) + + assert.Equal(t, m.Evaluate(p, nil), false) + + c = config.LargeCommunitySet{ + LargeCommunitySetName: "l1", + LargeCommunityList: []string{ + "200:", + }, + } + + set, err = NewLargeCommunitySet(c) + assert.Equal(t, err, nil) + + m, err = NewLargeCommunityCondition(config.MatchLargeCommunitySet{ + LargeCommunitySet: "l1", + }) + assert.Equal(t, err, nil) + m.set = set + assert.Equal(t, m.Evaluate(p, nil), true) } |