diff options
-rw-r--r-- | policy/policy.go | 56 | ||||
-rw-r--r-- | policy/policy_test.go | 88 |
2 files changed, 135 insertions, 9 deletions
diff --git a/policy/policy.go b/policy/policy.go index 62a667af..06921eab 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -1009,9 +1009,10 @@ func (r *RoutingAction) apply(path *table.Path) *table.Path { } type CommunityAction struct { - Values []uint32 - ext []byte - action config.BgpSetCommunityOptionType + Values []uint32 + ext []byte + action config.BgpSetCommunityOptionType + RegexpValues []*regexp.Regexp } const ( @@ -1032,20 +1033,39 @@ func NewCommunityAction(action config.SetCommunity) *CommunityAction { return nil } - values := make([]uint32, len(communities)) - for i, com := range communities { + values := make([]uint32, 0, len(communities)) + regexpValues := make([]*regexp.Regexp, 0, len(communities)) + + for _, com := range communities { matched, value := getCommunityValue(com) if matched { - values[i] = value - } else { + values = append(values, value) + continue + } + + exp, err := regexp.Compile(com) + if err != nil { log.WithFields(log.Fields{ "Topic": "Policy", "Type": "Community Action", - }).Error("community string invalid.") + }).Errorf("community string invalid") return nil } + regexpValues = append(regexpValues, exp) + } + if len(values) > 0 { + m.Values = values + } + if len(regexpValues) > 0 { + if action.Options != COMMUNITY_ACTION_REMOVE { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Community Action", + }).Error("regexp values can only be used for remove action") + return nil + } + m.RegexpValues = regexpValues } - m.Values = values switch action.Options { case COMMUNITY_ACTION_ADD: @@ -1064,6 +1084,21 @@ func NewCommunityAction(action config.SetCommunity) *CommunityAction { return m } +func RegexpRemoveCommunities(path *table.Path, exps []*regexp.Regexp) { + comms := path.GetCommunities() + newComms := make([]uint32, 0, len(comms)) + for _, comm := range comms { + c := fmt.Sprintf("%d:%d", comm>>16, comm&0x0000ffff) + for _, exp := range exps { + if !exp.MatchString(c) { + newComms = append(newComms, comm) + break + } + } + } + path.SetCommunities(newComms, true) +} + func (a *CommunityAction) apply(path *table.Path) *table.Path { if len(a.ext) > 0 { return a.extApply(path) @@ -1074,6 +1109,9 @@ func (a *CommunityAction) apply(path *table.Path) *table.Path { path.SetCommunities(list, false) case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: path.RemoveCommunities(list) + if len(a.RegexpValues) > 0 { + RegexpRemoveCommunities(path, a.RegexpValues) + } case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: path.SetCommunities(list, true) } diff --git a/policy/policy_test.go b/policy/policy_test.go index 246c77b3..de8d4691 100644 --- a/policy/policy_test.go +++ b/policy/policy_test.go @@ -1244,6 +1244,94 @@ func TestPolicyMatchAndRemoveCommunities(t *testing.T) { assert.Equal(t, []uint32{stringToCommunityValue(community2)}, newPath.GetCommunities()) } +func TestPolicyMatchAndRemoveCommunitiesRegexp(t *testing.T) { + + // create path + community1 := "65000:100" + community2 := "65000:200" + community3 := "65100:100" + peer := &table.PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")} + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("10.0.0.1") + med := bgp.NewPathAttributeMultiExitDisc(0) + communities := bgp.NewPathAttributeCommunities([]uint32{ + stringToCommunityValue(community1), + stringToCommunityValue(community2), + stringToCommunityValue(community3), + }) + pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities} + nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")} + updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri) + path := table.ProcessMessage(updateMsg, peer)[0] + // create policy + ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24") + ns := createNeighborSet("ns1", "10.0.0.1") + + ds := config.DefinedSets{} + ds.PrefixSets.PrefixSetList = []config.PrefixSet{ps} + ds.NeighborSets.NeighborSetList = []config.NeighborSet{ns} + + s := createStatement("statement1", "ps1", "ns1", true) + s.Actions.BgpActions.SetCommunity = createSetCommunity("REMOVE", ".*:100") + + pd := createPolicyDefinition("pd1", s) + pl := createRoutingPolicy(ds, pd) + + //test + df := pl.DefinedSets + p := NewPolicy(pl.PolicyDefinitions.PolicyDefinitionList[0], df) + pType, newPath := p.Apply(path) + assert.Equal(t, ROUTE_TYPE_ACCEPT, pType) + assert.NotEqual(t, nil, newPath) + assert.Equal(t, []uint32{stringToCommunityValue(community2)}, newPath.GetCommunities()) +} + +func TestPolicyMatchAndRemoveCommunitiesRegexp2(t *testing.T) { + + // create path + community1 := "0:1" + community2 := "10:1" + community3 := "45686:2" + peer := &table.PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")} + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("10.0.0.1") + med := bgp.NewPathAttributeMultiExitDisc(0) + communities := bgp.NewPathAttributeCommunities([]uint32{ + stringToCommunityValue(community1), + stringToCommunityValue(community2), + stringToCommunityValue(community3), + }) + pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities} + nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")} + updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri) + path := table.ProcessMessage(updateMsg, peer)[0] + // create policy + ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24") + ns := createNeighborSet("ns1", "10.0.0.1") + + ds := config.DefinedSets{} + ds.PrefixSets.PrefixSetList = []config.PrefixSet{ps} + ds.NeighborSets.NeighborSetList = []config.NeighborSet{ns} + + s := createStatement("statement1", "ps1", "ns1", true) + s.Actions.BgpActions.SetCommunity = createSetCommunity("REMOVE", "^(0|45686):[0-9]+") + + pd := createPolicyDefinition("pd1", s) + pl := createRoutingPolicy(ds, pd) + + //test + df := pl.DefinedSets + p := NewPolicy(pl.PolicyDefinitions.PolicyDefinitionList[0], df) + pType, newPath := p.Apply(path) + assert.Equal(t, ROUTE_TYPE_ACCEPT, pType) + assert.NotEqual(t, nil, newPath) + assert.Equal(t, []uint32{stringToCommunityValue(community2)}, newPath.GetCommunities()) +} + func TestPolicyMatchAndClearCommunities(t *testing.T) { // create path |