diff options
author | Hiroshi Yokoi <yokoi.hiroshi@po.ntts.co.jp> | 2015-05-20 23:09:52 +0900 |
---|---|---|
committer | Hiroshi Yokoi <yokoi.hiroshi@po.ntts.co.jp> | 2015-05-21 16:46:24 +0900 |
commit | dffb57e43dcdbd419b49fcab089ad4c68342c76a (patch) | |
tree | f359c16e62209e9744ef9d63ad8b32d9214cd76c | |
parent | 34e43ed4fbabd9960a45a8a384ac8180aae39814 (diff) |
policy: support community action
-rw-r--r-- | policy/policy.go | 261 | ||||
-rw-r--r-- | policy/policy_test.go | 322 | ||||
-rw-r--r-- | table/path.go | 87 |
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) { |