summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorHiroshi Yokoi <yokoi.hiroshi@po.ntts.co.jp>2015-05-20 23:09:52 +0900
committerHiroshi Yokoi <yokoi.hiroshi@po.ntts.co.jp>2015-05-21 16:46:24 +0900
commitdffb57e43dcdbd419b49fcab089ad4c68342c76a (patch)
treef359c16e62209e9744ef9d63ad8b32d9214cd76c
parent34e43ed4fbabd9960a45a8a384ac8180aae39814 (diff)
policy: support community action
-rw-r--r--policy/policy.go261
-rw-r--r--policy/policy_test.go322
-rw-r--r--table/path.go87
3 files changed, 560 insertions, 110 deletions
diff --git a/policy/policy.go b/policy/policy.go
index 977116fd..a13aeebc 100644
--- a/policy/policy.go
+++ b/policy/policy.go
@@ -23,8 +23,8 @@ import (
"github.com/osrg/gobgp/packet"
"github.com/osrg/gobgp/table"
"net"
- "regexp"
"reflect"
+ "regexp"
"strconv"
"strings"
)
@@ -102,19 +102,22 @@ func NewPolicy(pd config.PolicyDefinition, ds config.DefinedSets) *Policy {
conditions = append(conditions, cc)
}
- action := &RoutingActions{
- AcceptRoute: false,
- }
+ // routeing action
+ ra := NewRoutingAction(statement.Actions)
- if statement.Actions.AcceptRoute {
- action.AcceptRoute = true
+ // modification action
+ mda := make([]Action, 0)
+ com := NewCommunityAction(statement.Actions.BgpActions.SetCommunity)
+ if com != nil {
+ mda = append(mda, com)
}
s := &Statement{
- Name: statement.Name,
- Conditions: conditions,
- Actions: action,
- MatchSetOptions: statement.Conditions.MatchSetOptions,
+ Name: statement.Name,
+ Conditions: conditions,
+ routingAction: ra,
+ modificationActions: mda,
+ MatchSetOptions: statement.Conditions.MatchSetOptions,
}
st = append(st, s)
@@ -124,10 +127,11 @@ func NewPolicy(pd config.PolicyDefinition, ds config.DefinedSets) *Policy {
}
type Statement struct {
- Name string
- Conditions []Condition
- Actions Actions
- MatchSetOptions config.MatchSetOptionsType
+ Name string
+ Conditions []Condition
+ routingAction *RoutingAction
+ modificationActions []Action
+ MatchSetOptions config.MatchSetOptionsType
}
// evaluate each condition in the statement according to MatchSetOptions
@@ -489,75 +493,24 @@ type CommunityElement struct {
// CommunityCondition supports uint and string like 65000:100
// and also supports regular expressions that are available in golang.
// if GoBGP can't parse the regular expression, it return nil and an error message is logged.
-func NewCommunityCondition(communitySetName string, defAsPathSetList []config.CommunitySet) *CommunityCondition {
-
- // check format
- regUint, _ := regexp.Compile("^([0-9]+)$")
- regString, _ := regexp.Compile("([0-9]+):([0-9]+)")
- regWellKnown, _ := regexp.Compile("^(" +
- COMMUNITY_INTERNET + "|" +
- COMMUNITY_NO_EXPORT + "|" +
- COMMUNITY_NO_ADVERTISE + "|" +
- COMMUNITY_NO_EXPORT_SUBCONFED + ")$")
+func NewCommunityCondition(communitySetName string, defCommunitySetList []config.CommunitySet) *CommunityCondition {
communityList := make([]*CommunityElement, 0)
- for _, asPathSet := range defAsPathSetList {
- if asPathSet.CommunitySetName == communitySetName {
- for _, as := range asPathSet.CommunityMembers {
+ for _, communitySet := range defCommunitySetList {
+ if communitySet.CommunitySetName == communitySetName {
+ for _, c := range communitySet.CommunityMembers {
e := &CommunityElement{
- isRegExp: false,
+ isRegExp: false,
+ communityStr: c,
}
- if regUint.MatchString(as) {
- // specified by Uint
- community, err := strconv.ParseUint(as, 10, 32)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Policy",
- "Type": "Community Condition",
- }).Error("failed to parse the community value.")
- return nil
- }
-
- e.community = uint32(community)
- e.communityStr = as
-
- } else if regString.MatchString(as) {
- // specified by string containing ":"
- group := regString.FindStringSubmatch(as)
- asn, errAsn := strconv.ParseUint(group[1], 10, 16)
- val, errVal := strconv.ParseUint(group[2], 10, 16)
-
- if errAsn != nil || errVal != nil {
- log.WithFields(log.Fields{
- "Topic": "Policy",
- "Type": "Community Condition",
- }).Error("failed to parser as number or community value.")
- return nil
- }
- e.community = uint32(asn<<16 | val)
- e.communityStr = as
-
- } else if regWellKnown.MatchString(as) {
- // specified by well known community name
- e.communityStr = as
- switch as {
- case COMMUNITY_INTERNET:
- e.community = COMMUNITY_INTERNET_VAL
- case COMMUNITY_NO_EXPORT:
- e.community = COMMUNITY_NO_EXPORT_VAL
- case COMMUNITY_NO_ADVERTISE:
- e.community = COMMUNITY_NO_ADVERTISE_VAL
- case COMMUNITY_NO_EXPORT_SUBCONFED:
- e.community = COMMUNITY_NO_EXPORT_SUBCONFED_VAL
- }
-
+ if matched, v := getCommunityValue(c); matched {
+ e.community = v
} else {
// specified by regular expression
e.isRegExp = true
- e.communityStr = as
- reg, err := regexp.Compile(as)
+ reg, err := regexp.Compile(c)
if err != nil {
log.WithFields(log.Fields{
"Topic": "Policy",
@@ -579,6 +532,63 @@ func NewCommunityCondition(communitySetName string, defAsPathSetList []config.Co
return nil
}
+// getCommunityValue returns uint32 community value converted from the string.
+// if the string doesn't match a number or string like "65000:1000" or well known
+// community name, it returns false and 0, otherwise returns true and its uint32 value.
+func getCommunityValue(comStr string) (bool, uint32) {
+ // community regexp
+ regUint, _ := regexp.Compile("^([0-9]+)$")
+ regString, _ := regexp.Compile("([0-9]+):([0-9]+)")
+ regWellKnown, _ := regexp.Compile("^(" +
+ COMMUNITY_INTERNET + "|" +
+ COMMUNITY_NO_EXPORT + "|" +
+ COMMUNITY_NO_ADVERTISE + "|" +
+ COMMUNITY_NO_EXPORT_SUBCONFED + ")$")
+
+ if regUint.MatchString(comStr) {
+ // specified by Uint
+ community, err := strconv.ParseUint(comStr, 10, 32)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Community Condition",
+ }).Error("failed to parse the community value.")
+ }
+ return true, uint32(community)
+
+ } else if regString.MatchString(comStr) {
+ // specified by string containing ":"
+ group := regString.FindStringSubmatch(comStr)
+ asn, errAsn := strconv.ParseUint(group[1], 10, 16)
+ val, errVal := strconv.ParseUint(group[2], 10, 16)
+
+ if errAsn != nil || errVal != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Community Condition",
+ }).Error("failed to parser as number or community value.")
+ }
+ community := uint32(asn<<16 | val)
+ return true, community
+
+ } else if regWellKnown.MatchString(comStr) {
+ // specified by well known community name
+ var community uint32
+ switch comStr {
+ case COMMUNITY_INTERNET:
+ community = COMMUNITY_INTERNET_VAL
+ case COMMUNITY_NO_EXPORT:
+ community = COMMUNITY_NO_EXPORT_VAL
+ case COMMUNITY_NO_ADVERTISE:
+ community = COMMUNITY_NO_ADVERTISE_VAL
+ case COMMUNITY_NO_EXPORT_SUBCONFED:
+ community = COMMUNITY_NO_EXPORT_SUBCONFED_VAL
+ }
+ return true, community
+ }
+ return false, 0
+}
+
// compare community in the message's attribute with
// the one in the condition.
func (c *CommunityCondition) evaluate(path table.Path) bool {
@@ -626,23 +636,30 @@ func (c *CommunityCondition) evaluate(path table.Path) bool {
return false
}
-type Actions interface {
+type Action interface {
apply(table.Path) table.Path
}
-type DefaultActions struct {
+type DefaultAction struct {
}
-func (a *DefaultActions) apply(path table.Path) table.Path {
+func (a *DefaultAction) apply(path table.Path) table.Path {
return path
}
-type RoutingActions struct {
- DefaultActions
+type RoutingAction struct {
+ DefaultAction
AcceptRoute bool
}
-func (r *RoutingActions) apply(path table.Path) table.Path {
+func NewRoutingAction(action config.Actions) *RoutingAction {
+ r := &RoutingAction{
+ AcceptRoute: action.AcceptRoute,
+ }
+ return r
+}
+
+func (r *RoutingAction) apply(path table.Path) table.Path {
if r.AcceptRoute {
return path
} else {
@@ -650,10 +667,76 @@ func (r *RoutingActions) apply(path table.Path) table.Path {
}
}
-type ModificationActions struct {
- DefaultActions
- AttrType bgp.BGPAttrType
- Value string
+type ActionType int
+
+type CommunityAction struct {
+ DefaultAction
+ Values []uint32
+ action ActionType
+}
+
+const (
+ COMMUNITY_ACTION_ADD string = "ADD"
+ COMMUNITY_ACTION_REPLACE = "REPLACE"
+ COMMUNITY_ACTION_REMOVE = "REMOVE"
+ COMMUNITY_ACTION_NULL = "NULL"
+)
+
+// NewCommunityAction creates CommunityAction object.
+// If it cannot parse community string, then return nil.
+// Similarly, if option string is invalid, return nil.
+func NewCommunityAction(action config.SetCommunity) *CommunityAction {
+
+ m := &CommunityAction{}
+
+ values := make([]uint32, len(action.Communities))
+ for i, com := range action.Communities {
+ matched, value := getCommunityValue(com)
+ if matched {
+ values[i] = value
+ } else {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Community Action",
+ }).Error("community string invalid.")
+ return nil
+ }
+ }
+ m.Values = values
+
+ switch action.Options {
+ case COMMUNITY_ACTION_ADD:
+ m.action = config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD
+ case COMMUNITY_ACTION_REMOVE:
+ m.action = config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE
+ case COMMUNITY_ACTION_REPLACE:
+ m.action = config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE
+ case COMMUNITY_ACTION_NULL:
+ m.action = config.BGP_SET_COMMUNITY_OPTION_TYPE_NULL
+ default:
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Community Action",
+ }).Error("action string should be ADD or REMOVE or REPLACE or NULL.")
+ return nil
+ }
+ return m
+}
+
+func (a *CommunityAction) apply(path table.Path) table.Path {
+
+ list := a.Values
+ switch a.action {
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD:
+ path.SetCommunities(list, false)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE:
+ path.RemoveCommunities(list)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE:
+ path.SetCommunities(list, true)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_NULL:
+ path.ClearCommunities()
+ }
+ return path
}
type Prefix struct {
@@ -724,8 +807,13 @@ func (p *Policy) Apply(path table.Path) (bool, RouteType, table.Path) {
var p table.Path
if result {
- p = statement.Actions.apply(path)
+ //Routing action
+ p = statement.routingAction.apply(path)
if p != nil {
+ // apply all modification actions
+ for _, action := range statement.modificationActions {
+ p = action.apply(p)
+ }
return true, ROUTE_TYPE_ACCEPT, p
} else {
return true, ROUTE_TYPE_REJECT, nil
@@ -851,7 +939,8 @@ func (p *Policy) ToApiStruct() *api.PolicyDefinition {
AcceptRoute: false,
RejectRoute: true,
}
- if st.Actions.(*RoutingActions).AcceptRoute {
+
+ if st.routingAction.AcceptRoute {
resAction.AcceptRoute = true
resAction.RejectRoute = false
}
diff --git a/policy/policy_test.go b/policy/policy_test.go
index 8a0467bb..054d4d9f 100644
--- a/policy/policy_test.go
+++ b/policy/policy_test.go
@@ -926,13 +926,6 @@ func TestConditionConditionEvaluate(t *testing.T) {
log.SetLevel(log.DebugLevel)
- strToCom := func(s string) uint32 {
- elem := strings.Split(s, ":")
- asn, _ := strconv.ParseUint(elem[0], 10, 16)
- val, _ := strconv.ParseUint(elem[1], 10, 16)
- return uint32(asn<<16 | val)
- }
-
// setup
// create path
peer := &table.PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
@@ -945,10 +938,10 @@ func TestConditionConditionEvaluate(t *testing.T) {
nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
med := bgp.NewPathAttributeMultiExitDisc(0)
communities := bgp.NewPathAttributeCommunities([]uint32{
- strToCom("65001:100"),
- strToCom("65001:200"),
- strToCom("65001:300"),
- strToCom("65001:400"),
+ stringToCommunityValue("65001:100"),
+ stringToCommunityValue("65001:200"),
+ stringToCommunityValue("65001:300"),
+ stringToCommunityValue("65001:400"),
0x00000000,
0xFFFFFF01,
0xFFFFFF02,
@@ -1029,13 +1022,6 @@ func TestConditionConditionEvaluateWithOtherCondition(t *testing.T) {
log.SetLevel(log.DebugLevel)
- strToCom := func(s string) uint32 {
- elem := strings.Split(s, ":")
- asn, _ := strconv.ParseUint(elem[0], 10, 16)
- val, _ := strconv.ParseUint(elem[1], 10, 16)
- return uint32(asn<<16 | val)
- }
-
// setup
// create path
peer := &table.PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
@@ -1048,10 +1034,10 @@ func TestConditionConditionEvaluateWithOtherCondition(t *testing.T) {
nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
med := bgp.NewPathAttributeMultiExitDisc(0)
communities := bgp.NewPathAttributeCommunities([]uint32{
- strToCom("65001:100"),
- strToCom("65001:200"),
- strToCom("65001:300"),
- strToCom("65001:400"),
+ stringToCommunityValue("65001:100"),
+ stringToCommunityValue("65001:200"),
+ stringToCommunityValue("65001:300"),
+ stringToCommunityValue("65001:400"),
0x00000000,
0xFFFFFF01,
0xFFFFFF02,
@@ -1162,4 +1148,296 @@ func TestConditionConditionEvaluateWithOtherCondition(t *testing.T) {
assert.Equal(t, ROUTE_TYPE_NONE, pType)
assert.Equal(t, nil, newPath)
+}
+
+
+func TestPolicyMatchAndAddCommunities(t *testing.T) {
+
+ // create path
+ 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)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.0.101")}
+ withdrawnRoutes := []bgp.WithdrawnRoute{}
+ updateMsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+ path := table.ProcessMessage(updateMsg, peer)[0]
+ // create policy
+ ps := config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{
+ config.Prefix{
+ Address: net.ParseIP("10.10.0.0"),
+ Masklength: 16,
+ MasklengthRange: "21..24",
+ }},
+ }
+ ns := config.NeighborSet{
+ NeighborSetName: "ns1",
+ NeighborInfoList: []config.NeighborInfo{
+ config.NeighborInfo{
+ Address: net.ParseIP("10.0.0.1"),
+ }},
+ }
+ ds := config.DefinedSets{
+ PrefixSetList: []config.PrefixSet{ps},
+ NeighborSetList: []config.NeighborSet{ns},
+ }
+
+ community := "65000:100"
+
+ s := config.Statement{
+ Name: "statement1",
+ Conditions: config.Conditions{
+ MatchPrefixSet: "ps1",
+ MatchNeighborSet: "ns1",
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ALL,
+ },
+ Actions: config.Actions{
+ AcceptRoute: true,
+ BgpActions: config.BgpActions{
+ SetCommunity: config.SetCommunity{
+ Communities: []string{community},
+ Options: "ADD",
+ },
+ },
+ },
+ }
+
+ pd := config.PolicyDefinition{"pd1", []config.Statement{s}}
+ pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}}
+ //test
+ df := pl.DefinedSets
+ p := NewPolicy(pl.PolicyDefinitionList[0], df)
+ match, pType, newPath := p.Apply(path)
+ assert.Equal(t, true, match)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community)}, path.GetCommunities())
+}
+
+
+func TestPolicyMatchAndReplaceCommunities(t *testing.T) {
+
+ // create path
+ 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("65001:200"),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.0.101")}
+ withdrawnRoutes := []bgp.WithdrawnRoute{}
+ updateMsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+ path := table.ProcessMessage(updateMsg, peer)[0]
+ // create policy
+ ps := config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{
+ config.Prefix{
+ Address: net.ParseIP("10.10.0.0"),
+ Masklength: 16,
+ MasklengthRange: "21..24",
+ }},
+ }
+ ns := config.NeighborSet{
+ NeighborSetName: "ns1",
+ NeighborInfoList: []config.NeighborInfo{
+ config.NeighborInfo{
+ Address: net.ParseIP("10.0.0.1"),
+ }},
+ }
+ ds := config.DefinedSets{
+ PrefixSetList: []config.PrefixSet{ps},
+ NeighborSetList: []config.NeighborSet{ns},
+ }
+
+ community := "65000:100"
+
+ s := config.Statement{
+ Name: "statement1",
+ Conditions: config.Conditions{
+ MatchPrefixSet: "ps1",
+ MatchNeighborSet: "ns1",
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ALL,
+ },
+ Actions: config.Actions{
+ AcceptRoute: true,
+ BgpActions: config.BgpActions{
+ SetCommunity: config.SetCommunity{
+ Communities: []string{community},
+ Options: "REPLACE",
+ },
+ },
+ },
+ }
+
+ pd := config.PolicyDefinition{"pd1", []config.Statement{s}}
+ pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}}
+ //test
+ df := pl.DefinedSets
+ p := NewPolicy(pl.PolicyDefinitionList[0], df)
+ match, pType, newPath := p.Apply(path)
+ assert.Equal(t, true, match)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community)}, path.GetCommunities())
+}
+
+func TestPolicyMatchAndRemoveCommunities(t *testing.T) {
+
+ // create path
+ community1 := "65000:100"
+ community2 := "65000:200"
+ 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),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.0.101")}
+ withdrawnRoutes := []bgp.WithdrawnRoute{}
+ updateMsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+ path := table.ProcessMessage(updateMsg, peer)[0]
+ // create policy
+ ps := config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{
+ config.Prefix{
+ Address: net.ParseIP("10.10.0.0"),
+ Masklength: 16,
+ MasklengthRange: "21..24",
+ }},
+ }
+ ns := config.NeighborSet{
+ NeighborSetName: "ns1",
+ NeighborInfoList: []config.NeighborInfo{
+ config.NeighborInfo{
+ Address: net.ParseIP("10.0.0.1"),
+ }},
+ }
+ ds := config.DefinedSets{
+ PrefixSetList: []config.PrefixSet{ps},
+ NeighborSetList: []config.NeighborSet{ns},
+ }
+
+ s := config.Statement{
+ Name: "statement1",
+ Conditions: config.Conditions{
+ MatchPrefixSet: "ps1",
+ MatchNeighborSet: "ns1",
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ALL,
+ },
+ Actions: config.Actions{
+ AcceptRoute: true,
+ BgpActions: config.BgpActions{
+ SetCommunity: config.SetCommunity{
+ Communities: []string{community1},
+ Options: "REMOVE",
+ },
+ },
+ },
+ }
+
+ pd := config.PolicyDefinition{"pd1", []config.Statement{s}}
+ pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}}
+ //test
+ df := pl.DefinedSets
+ p := NewPolicy(pl.PolicyDefinitionList[0], df)
+ match, pType, newPath := p.Apply(path)
+ assert.Equal(t, true, match)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community2)}, path.GetCommunities())
+}
+
+func TestPolicyMatchAndClearCommunities(t *testing.T) {
+
+ // create path
+ community1 := "65000:100"
+ community2 := "65000:200"
+ 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),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.0.101")}
+ withdrawnRoutes := []bgp.WithdrawnRoute{}
+ updateMsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+ path := table.ProcessMessage(updateMsg, peer)[0]
+ // create policy
+ ps := config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{
+ config.Prefix{
+ Address: net.ParseIP("10.10.0.0"),
+ Masklength: 16,
+ MasklengthRange: "21..24",
+ }},
+ }
+ ns := config.NeighborSet{
+ NeighborSetName: "ns1",
+ NeighborInfoList: []config.NeighborInfo{
+ config.NeighborInfo{
+ Address: net.ParseIP("10.0.0.1"),
+ }},
+ }
+ ds := config.DefinedSets{
+ PrefixSetList: []config.PrefixSet{ps},
+ NeighborSetList: []config.NeighborSet{ns},
+ }
+
+ s := config.Statement{
+ Name: "statement1",
+ Conditions: config.Conditions{
+ MatchPrefixSet: "ps1",
+ MatchNeighborSet: "ns1",
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ALL,
+ },
+ Actions: config.Actions{
+ AcceptRoute: true,
+ BgpActions: config.BgpActions{
+ SetCommunity: config.SetCommunity{
+ Communities: []string{community1},
+ Options: "NULL",
+ },
+ },
+ },
+ }
+
+ pd := config.PolicyDefinition{"pd1", []config.Statement{s}}
+ pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}}
+ //test
+ df := pl.DefinedSets
+ p := NewPolicy(pl.PolicyDefinitionList[0], df)
+ match, pType, newPath := p.Apply(path)
+ assert.Equal(t, true, match)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{}, path.GetCommunities())
+}
+
+func stringToCommunityValue(comStr string) uint32 {
+ elem := strings.Split(comStr, ":")
+ asn, _ := strconv.ParseUint(elem[0], 10, 16)
+ val, _ := strconv.ParseUint(elem[1], 10, 16)
+ return uint32(asn<<16 | val)
} \ No newline at end of file
diff --git a/table/path.go b/table/path.go
index 305a80e1..522534d9 100644
--- a/table/path.go
+++ b/table/path.go
@@ -37,6 +37,9 @@ type Path interface {
GetAsList() []uint32
GetAsSeqList() []uint32
GetCommunities() []uint32
+ SetCommunities([]uint32, bool)
+ RemoveCommunities([]uint32) int
+ ClearCommunities()
setSource(source *PeerInfo)
GetSource() *PeerInfo
GetSourceAs() uint32
@@ -380,11 +383,11 @@ func (pd *PathDefault) getAsListofSpecificType(getAsSeq, getAsSet bool) []uint32
aspath := attr.(*bgp.PathAttributeAsPath)
for _, paramIf := range aspath.Value {
segment := paramIf.(*bgp.As4PathParam)
- if getAsSeq && segment.Type == bgp.BGP_ASPATH_ATTR_TYPE_SEQ{
+ if getAsSeq && segment.Type == bgp.BGP_ASPATH_ATTR_TYPE_SEQ {
asList = append(asList, segment.AS...)
continue
}
- if getAsSet && segment.Type == bgp.BGP_ASPATH_ATTR_TYPE_SET{
+ if getAsSet && segment.Type == bgp.BGP_ASPATH_ATTR_TYPE_SET {
asList = append(asList, segment.AS...)
}
}
@@ -401,6 +404,86 @@ func (pd *PathDefault) GetCommunities() []uint32 {
return communityList
}
+// SetCommunities adds or replaces communities with new ones.
+// If the length of communites is 0, it does nothing.
+func (pd *PathDefault) SetCommunities(communities []uint32, doReplace bool) {
+
+ if len(communities) == 0 {
+ // do nothing
+ return
+ }
+
+ newList := make([]uint32, 0)
+ idx, attr := pd.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ if attr != nil {
+ c := attr.(*bgp.PathAttributeCommunities)
+ if doReplace {
+ newList = append(newList, communities...)
+ } else {
+ newList = append(newList, c.Value...)
+ newList = append(newList, communities...)
+ }
+ newCommunities := bgp.NewPathAttributeCommunities(newList)
+ pd.pathAttrs[idx] = newCommunities
+ } else {
+ newList = append(newList, communities...)
+ newCommunities := bgp.NewPathAttributeCommunities(newList)
+ pd.pathAttrs = append(pd.pathAttrs, newCommunities)
+ }
+
+}
+
+// RemoveCommunities removes specific communities.
+// If the length of communites is 0, it does nothing.
+// If all communities are removed, it removes Communities path attribute itself.
+func (pd *PathDefault) RemoveCommunities(communities []uint32) int {
+
+ if len(communities) == 0 {
+ // do nothing
+ return 0
+ }
+
+ find := func(val uint32) bool {
+ for _, com := range communities {
+ if com == val {
+ return true
+ }
+ }
+ return false
+ }
+
+ count := 0
+ idx, attr := pd.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ if attr != nil {
+ newList := make([]uint32, 0)
+ c := attr.(*bgp.PathAttributeCommunities)
+
+ for _, value := range c.Value {
+ if find(value) {
+ count += 1
+ } else {
+ newList = append(newList, value)
+ }
+ }
+
+ if len(newList) != 0 {
+ newCommunities := bgp.NewPathAttributeCommunities(newList)
+ pd.pathAttrs[idx] = newCommunities
+ } else {
+ pd.pathAttrs = append(pd.pathAttrs[:idx], pd.pathAttrs[idx+1:]...)
+ }
+ }
+ return count
+}
+
+// ClearCommunities removes Communities path attribute.
+func (pd *PathDefault) ClearCommunities() {
+ idx, _ := pd.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ if idx >= 0 {
+ pd.pathAttrs = append(pd.pathAttrs[:idx], pd.pathAttrs[idx+1:]...)
+ }
+}
+
// create Path object based on route family
func CreatePath(source *PeerInfo, nlri bgp.AddrPrefixInterface, attrs []bgp.PathAttributeInterface, isWithdraw bool, now time.Time) (Path, error) {