summaryrefslogtreecommitdiffhomepage
path: root/policy
diff options
context:
space:
mode:
authorHiroshi Yokoi <yokoi.hiroshi@po.ntts.co.jp>2015-05-19 19:41:40 +0900
committerHiroshi Yokoi <yokoi.hiroshi@po.ntts.co.jp>2015-05-19 20:14:43 +0900
commitb549c670b1fa749bd047e649d77091221d348c47 (patch)
tree160935a939391fe719817f3e9eb94f2c202368c4 /policy
parentf91a3a61b48fb7a3aad6aac492d487fb72b32a34 (diff)
policy: support community condition
Diffstat (limited to 'policy')
-rw-r--r--policy/policy.go174
-rw-r--r--policy/policy_test.go244
2 files changed, 418 insertions, 0 deletions
diff --git a/policy/policy.go b/policy/policy.go
index b701bd4a..977116fd 100644
--- a/policy/policy.go
+++ b/policy/policy.go
@@ -95,6 +95,13 @@ func NewPolicy(pd config.PolicyDefinition, ds config.DefinedSets) *Policy {
conditions = append(conditions, asc)
}
+ // CommunityCondition
+ communitySetName := statement.Conditions.BgpConditions.MatchCommunitySet
+ cc := NewCommunityCondition(communitySetName, ds.BgpDefinedSets.CommunitySetList)
+ if cc != nil {
+ conditions = append(conditions, cc)
+ }
+
action := &RoutingActions{
AcceptRoute: false,
}
@@ -452,6 +459,173 @@ func (c *AsPathCondition) evaluate(path table.Path) bool {
return false
}
+type CommunityCondition struct {
+ DefaultCondition
+ CommunityList []*CommunityElement
+}
+
+const (
+ COMMUNITY_INTERNET string = "INTERNET"
+ COMMUNITY_NO_EXPORT string = "NO_EXPORT"
+ COMMUNITY_NO_ADVERTISE string = "NO_ADVERTISE"
+ COMMUNITY_NO_EXPORT_SUBCONFED string = "NO_EXPORT_SUBCONFED"
+)
+
+const (
+ COMMUNITY_INTERNET_VAL uint32 = 0x00000000
+ COMMUNITY_NO_EXPORT_VAL = 0xFFFFFF01
+ COMMUNITY_NO_ADVERTISE_VAL = 0xFFFFFF02
+ COMMUNITY_NO_EXPORT_SUBCONFED_VAL = 0xFFFFFF03
+)
+
+type CommunityElement struct {
+ community uint32
+ communityStr string
+ isRegExp bool
+ communityRegExp *regexp.Regexp
+}
+
+// create CommunityCondition object
+// 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 + ")$")
+
+ communityList := make([]*CommunityElement, 0)
+ for _, asPathSet := range defAsPathSetList {
+ if asPathSet.CommunitySetName == communitySetName {
+ for _, as := range asPathSet.CommunityMembers {
+
+ e := &CommunityElement{
+ isRegExp: false,
+ }
+
+ 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
+ }
+
+ } else {
+ // specified by regular expression
+ e.isRegExp = true
+ e.communityStr = as
+ reg, err := regexp.Compile(as)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Community Condition",
+ }).Error("Regular expression can't be compiled.")
+ return nil
+ }
+ e.communityRegExp = reg
+ }
+ communityList = append(communityList, e)
+ }
+
+ c := &CommunityCondition{
+ CommunityList: communityList,
+ }
+ return c
+ }
+ }
+ return nil
+}
+
+// compare community in the message's attribute with
+// the one in the condition.
+func (c *CommunityCondition) evaluate(path table.Path) bool {
+
+ communities := path.GetCommunities()
+
+ if len(communities) == 0 {
+ return false
+ }
+
+ // create community string in advance.
+ strCommunities := make([]string, len(communities))
+ for i, c := range communities {
+ upper := strconv.FormatUint(uint64(c&0xFFFF0000>>16), 10)
+ lower := strconv.FormatUint(uint64(c&0x0000FFFF), 10)
+ strCommunities[i] = upper + ":" + lower
+ }
+
+ matched := false
+ idx := -1
+ for _, member := range c.CommunityList {
+ if member.isRegExp {
+ for i, c := range strCommunities {
+ if member.communityRegExp.MatchString(c) {
+ matched = true
+ idx = i
+ break
+ }
+ }
+ } else {
+ for i, c := range communities {
+ if c == member.community {
+ matched = true
+ idx = i
+ break
+ }
+ }
+ }
+
+ if matched {
+ log.Debugf("community matched : community=%s)", strCommunities[idx])
+ return true
+ }
+ }
+ return false
+}
+
type Actions interface {
apply(table.Path) table.Path
}
diff --git a/policy/policy_test.go b/policy/policy_test.go
index 3aa50763..8a0467bb 100644
--- a/policy/policy_test.go
+++ b/policy/policy_test.go
@@ -22,6 +22,8 @@ import (
"github.com/osrg/gobgp/table"
"github.com/stretchr/testify/assert"
"net"
+ "strconv"
+ "strings"
"testing"
)
@@ -918,4 +920,246 @@ func TestAsPathConditionWithOtherCondition(t *testing.T) {
assert.Equal(t, ROUTE_TYPE_REJECT, pType)
assert.Equal(t, nil, newPath)
+}
+
+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")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65005}),
+ bgp.NewAsPathParam(1, []uint16{65001, 65010, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ 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"),
+ 0x00000000,
+ 0xFFFFFF01,
+ 0xFFFFFF02,
+ 0xFFFFFF03})
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.0.101")}
+ withdrawnRoutes := []bgp.WithdrawnRoute{}
+ updateMsg1 := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+ table.UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := table.ProcessMessage(updateMsg1, peer)[0]
+
+ // create match condition
+ comSet1 := config.CommunitySet{
+ CommunitySetName: "comset1",
+ CommunityMembers: []string{"65001:10", "65001:50", "65001:100"},
+ }
+
+ comSet2 := config.CommunitySet{
+ CommunitySetName: "comset2",
+ CommunityMembers: []string{"65001:200"},
+ }
+
+ comSet3 := config.CommunitySet{
+ CommunitySetName: "comset3",
+ CommunityMembers: []string{"4259905936"},
+ }
+
+ comSet4 := config.CommunitySet{
+ CommunitySetName: "comset4",
+ CommunityMembers: []string{"^[0-9]*:300$"},
+ }
+
+ comSet5 := config.CommunitySet{
+ CommunitySetName: "comset5",
+ CommunityMembers: []string{"INTERNET"},
+ }
+
+ comSet6 := config.CommunitySet{
+ CommunitySetName: "comset6",
+ CommunityMembers: []string{"NO_EXPORT"},
+ }
+
+ comSet7 := config.CommunitySet{
+ CommunitySetName: "comset7",
+ CommunityMembers: []string{"NO_ADVERTISE"},
+ }
+
+ comSet8 := config.CommunitySet{
+ CommunitySetName: "comset8",
+ CommunityMembers: []string{"NO_EXPORT_SUBCONFED"},
+ }
+
+ comSetList := []config.CommunitySet{comSet1, comSet2, comSet3,
+ comSet4, comSet5, comSet6, comSet7, comSet8}
+ p1 := NewCommunityCondition("comset1", comSetList)
+ p2 := NewCommunityCondition("comset2", comSetList)
+ p3 := NewCommunityCondition("comset3", comSetList)
+ p4 := NewCommunityCondition("comset4", comSetList)
+ p5 := NewCommunityCondition("comset5", comSetList)
+ p6 := NewCommunityCondition("comset6", comSetList)
+ p7 := NewCommunityCondition("comset7", comSetList)
+ p8 := NewCommunityCondition("comset8", comSetList)
+
+ // test
+ assert.Equal(t, true, p1.evaluate(path1))
+ assert.Equal(t, true, p2.evaluate(path1))
+ assert.Equal(t, true, p3.evaluate(path1))
+ assert.Equal(t, true, p4.evaluate(path1))
+ assert.Equal(t, true, p5.evaluate(path1))
+ assert.Equal(t, true, p6.evaluate(path1))
+ assert.Equal(t, true, p7.evaluate(path1))
+ assert.Equal(t, true, p8.evaluate(path1))
+
+}
+
+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")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65004, 65005}),
+ bgp.NewAsPathParam(1, []uint16{65001, 65000, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ 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"),
+ 0x00000000,
+ 0xFFFFFF01,
+ 0xFFFFFF02,
+ 0xFFFFFF03})
+ 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)
+ table.UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := table.ProcessMessage(updateMsg, peer)[0]
+
+ // create policy
+ asPathSet := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathSetMembers: []string{"65004$"},
+ }
+
+ comSet1 := config.CommunitySet{
+ CommunitySetName: "comset1",
+ CommunityMembers: []string{"65001:10", "65001:50", "65001:100"},
+ }
+
+ comSet2 := config.CommunitySet{
+ CommunitySetName: "comset2",
+ CommunityMembers: []string{"65050:\\d+"},
+ }
+
+ prefixSet := config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{
+ config.Prefix{
+ Address: net.ParseIP("10.11.1.0"),
+ Masklength: 16,
+ MasklengthRange: "21..24",
+ }},
+ }
+
+ neighborSet := config.NeighborSet{
+ NeighborSetName: "ns1",
+ NeighborInfoList: []config.NeighborInfo{
+ config.NeighborInfo{
+ Address: net.ParseIP("10.2.1.1"),
+ }},
+ }
+
+ ds := config.DefinedSets{
+ PrefixSetList: []config.PrefixSet{prefixSet},
+ NeighborSetList: []config.NeighborSet{neighborSet},
+ BgpDefinedSets: config.BgpDefinedSets{
+ AsPathSetList: []config.AsPathSet{asPathSet},
+ CommunitySetList: []config.CommunitySet{comSet1, comSet2},
+ },
+ }
+
+ s1 := config.Statement{
+ Name: "statement1",
+ Conditions: config.Conditions{
+ MatchPrefixSet: "ps1",
+ MatchNeighborSet: "ns1",
+ BgpConditions: config.BgpConditions{
+ MatchAsPathSet: "asset1",
+ MatchCommunitySet: "comset1",
+ },
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY,
+ },
+ Actions: config.Actions{
+ AcceptRoute: false,
+ RejectRoute: true,
+ },
+ }
+
+ s2 := config.Statement{
+ Name: "statement1",
+ Conditions: config.Conditions{
+ MatchPrefixSet: "ps1",
+ MatchNeighborSet: "ns1",
+ BgpConditions: config.BgpConditions{
+ MatchAsPathSet: "asset1",
+ MatchCommunitySet: "comset2",
+ },
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY,
+ },
+ Actions: config.Actions{
+ AcceptRoute: false,
+ RejectRoute: true,
+ },
+ }
+
+ pd1 := config.PolicyDefinition{"pd1", []config.Statement{s1}}
+ pd2 := config.PolicyDefinition{"pd2", []config.Statement{s2}}
+ pl := config.RoutingPolicy{
+ DefinedSets: ds,
+ PolicyDefinitionList: []config.PolicyDefinition{pd1, pd2},
+ }
+
+ //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_REJECT, pType)
+ assert.Equal(t, nil, newPath)
+
+ df = pl.DefinedSets
+ p = NewPolicy(pl.PolicyDefinitionList[1], df)
+ match, pType, newPath = p.Apply(path)
+ assert.Equal(t, false, match)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType)
+ assert.Equal(t, nil, newPath)
+
} \ No newline at end of file