From f91a3a61b48fb7a3aad6aac492d487fb72b32a34 Mon Sep 17 00:00:00 2001 From: Hiroshi Yokoi Date: Tue, 19 May 2015 19:30:26 +0900 Subject: policy: support aspath condition --- policy/policy.go | 131 ++++++++++++++++++++++++++++++++++- policy/policy_test.go | 187 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 302 insertions(+), 16 deletions(-) (limited to 'policy') diff --git a/policy/policy.go b/policy/policy.go index 236ffa6d..b701bd4a 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -23,6 +23,7 @@ import ( "github.com/osrg/gobgp/packet" "github.com/osrg/gobgp/table" "net" + "regexp" "reflect" "strconv" "strings" @@ -59,11 +60,11 @@ type Policy struct { Statements []*Statement } -func NewPolicy(name string, pd config.PolicyDefinition, ds config.DefinedSets) *Policy { +func NewPolicy(pd config.PolicyDefinition, ds config.DefinedSets) *Policy { stmtList := pd.StatementList st := make([]*Statement, 0) p := &Policy{ - Name: name, + Name: pd.Name, } for _, statement := range stmtList { @@ -87,6 +88,13 @@ func NewPolicy(name string, pd config.PolicyDefinition, ds config.DefinedSets) * conditions = append(conditions, ac) } + // AsPathCondition + asPathSetName := statement.Conditions.BgpConditions.MatchAsPathSet + asc := NewAsPathCondition(asPathSetName, ds.BgpDefinedSets.AsPathSetList) + if asc != nil { + conditions = append(conditions, asc) + } + action := &RoutingActions{ AcceptRoute: false, } @@ -265,6 +273,7 @@ func (c *NeighborCondition) evaluate(path table.Path) bool { cAddr := neighbor pAddr := path.GetSource().Address if pAddr.Equal(cAddr) { + log.Debug("neighbor matched : ", pAddr.String()) return true } } @@ -325,6 +334,124 @@ func (c *AsPathLengthCondition) evaluate(path table.Path) bool { } +type AsPathCondition struct { + DefaultCondition + AsPathList []*AsPathElement +} + +type AsnPos int + +const ( + AS_FROM AsnPos = iota + AS_ANY + AS_ORIGIN + AS_ONLY +) + +type AsPathElement struct { + postiion AsnPos + asn uint32 +} + +// create AsPathCondition object +// AsPathCondition supports only following regexp: +// - ^100 (from as100) +// - ^100$ (from as100 and originated by as100) +// - 100$ (originated by as100) +// - 100 (from or through or originated by as100) +func NewAsPathCondition(asPathSetName string, defAsPathSetList []config.AsPathSet) *AsPathCondition { + + regAsn, _ := regexp.Compile("^(\\^?)([0-9]+)(\\$?)$") + + asPathList := make([]*AsPathElement, 0) + for _, asPathSet := range defAsPathSetList { + if asPathSet.AsPathSetName == asPathSetName { + for _, as := range asPathSet.AsPathSetMembers { + if regAsn.MatchString(as) { + + group := regAsn.FindStringSubmatch(as) + asn, err := strconv.Atoi(group[2]) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "AsPath Condition", + }).Error("cannot parse AS Number.") + return nil + } + e := &AsPathElement{} + e.asn = uint32(asn) + + if len(group[1]) == 0 && len(group[3]) == 0 { + e.postiion = AS_ANY + } else if len(group[1]) == 1 && len(group[3]) == 0 { + e.postiion = AS_FROM + } else if len(group[1]) == 0 && len(group[3]) == 1 { + e.postiion = AS_ORIGIN + } else { + e.postiion = AS_ONLY + } + + asPathList = append(asPathList, e) + + } else { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "AsPath Condition", + }).Error("cannot parse AS_PATH condition value.") + + return nil + } + } + + c := &AsPathCondition{ + AsPathList: asPathList, + } + return c + } + } + return nil +} + +// compare AS_PATH in the message's AS_PATH attribute with +// the one in condition. +func (c *AsPathCondition) evaluate(path table.Path) bool { + + aspath := path.GetAsSeqList() + + if len(aspath) == 0 { + return false + } + + matched := false + for _, member := range c.AsPathList { + + switch member.postiion { + case AS_FROM: + matched = aspath[0] == member.asn + case AS_ANY: + for _, n := range aspath { + if n == member.asn { + matched = true + break + } + } + case AS_ORIGIN: + matched = aspath[len(aspath)-1] == member.asn + + case AS_ONLY: + matched = len(aspath) == 1 && aspath[0] == member.asn + + } + + if matched { + log.Debugf("aspath matched : asn=%d, pos=%v)", member.asn, member.postiion) + 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 ca006ddb..3aa50763 100644 --- a/policy/policy_test.go +++ b/policy/policy_test.go @@ -268,9 +268,8 @@ func TestPolicyNotMatch(t *testing.T) { pd := config.PolicyDefinition{"pd1", []config.Statement{s}} pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}} //test - pName := "pd1" df := pl.DefinedSets - p := NewPolicy(pName, pl.PolicyDefinitionList[0], df) + p := NewPolicy(pl.PolicyDefinitionList[0], df) match, pType, newPath := p.Apply(path) assert.Equal(t, false, match) assert.Equal(t, ROUTE_TYPE_NONE, pType) @@ -326,9 +325,8 @@ func TestPolicyMatchAndReject(t *testing.T) { pd := config.PolicyDefinition{"pd1", []config.Statement{s}} pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}} //test - pName := "pd1" df := pl.DefinedSets - p := NewPolicy(pName, pl.PolicyDefinitionList[0], df) + p := NewPolicy(pl.PolicyDefinitionList[0], df) match, pType, newPath := p.Apply(path) assert.Equal(t, true, match) assert.Equal(t, ROUTE_TYPE_REJECT, pType) @@ -384,9 +382,8 @@ func TestPolicyMatchAndAccept(t *testing.T) { pd := config.PolicyDefinition{"pd1", []config.Statement{s}} pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}} //test - pName := "pd1" df := pl.DefinedSets - p := NewPolicy(pName, pl.PolicyDefinitionList[0], df) + p := NewPolicy(pl.PolicyDefinitionList[0], df) match, pType, newPath := p.Apply(path) assert.Equal(t, true, match) assert.Equal(t, ROUTE_TYPE_ACCEPT, pType) @@ -448,9 +445,8 @@ func TestPolicyRejectOnlyPrefixSet(t *testing.T) { pd := config.PolicyDefinition{"pd1", []config.Statement{s}} pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}} //test - pName := "pd1" df := pl.DefinedSets - p := NewPolicy(pName, pl.PolicyDefinitionList[0], df) + p := NewPolicy(pl.PolicyDefinitionList[0], df) match, pType, newPath := p.Apply(path1) assert.Equal(t, true, match) assert.Equal(t, ROUTE_TYPE_REJECT, pType) @@ -515,9 +511,8 @@ func TestPolicyRejectOnlyNeighborSet(t *testing.T) { pd := config.PolicyDefinition{"pd1", []config.Statement{s}} pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}} //test - pName := "pd1" df := pl.DefinedSets - p := NewPolicy(pName, pl.PolicyDefinitionList[0], df) + p := NewPolicy(pl.PolicyDefinitionList[0], df) match, pType, newPath := p.Apply(path1) assert.Equal(t, true, match) assert.Equal(t, ROUTE_TYPE_REJECT, pType) @@ -619,9 +614,8 @@ func TestPolicyDifferentRoutefamilyOfPathAndPolicy(t *testing.T) { pd := config.PolicyDefinition{"pd1", []config.Statement{stIPv4, stIPv6}} pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}} //test - pName := "pd1" df := pl.DefinedSets - p := NewPolicy(pName, pl.PolicyDefinitionList[0], df) + p := NewPolicy(pl.PolicyDefinitionList[0], df) match1, pType1, newPath1 := p.Apply(pathIPv4) assert.Equal(t, true, match1) assert.Equal(t, ROUTE_TYPE_REJECT, pType1) @@ -751,12 +745,177 @@ func TestAsPathLengthConditionWithOtherCondition(t *testing.T) { pl := config.RoutingPolicy{ds, []config.PolicyDefinition{pd}} //test - pName := "pd1" df := pl.DefinedSets - p := NewPolicy(pName, pl.PolicyDefinitionList[0], df) + 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) } + +func TestAsPathConditionEvaluate(t *testing.T) { + + // 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) + pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med} + 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] + + aspathParam2 := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(2, []uint16{65010}), + bgp.NewAsPathParam(1, []uint16{65010}), + } + aspath2 := bgp.NewPathAttributeAsPath(aspathParam2) + pathAttributes = []bgp.PathAttributeInterface{origin, aspath2, nexthop, med} + updateMsg2 := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) + table.UpdatePathAttrs4ByteAs(updateMsg2.Body.(*bgp.BGPUpdate)) + path2 := table.ProcessMessage(updateMsg2, peer)[0] + + // create match condition + asPathSet1 := config.AsPathSet{ + AsPathSetName: "asset1", + AsPathSetMembers: []string{"^65001"}, + } + + asPathSet2 := config.AsPathSet{ + AsPathSetName: "asset2", + AsPathSetMembers: []string{"65005$"}, + } + + asPathSet3 := config.AsPathSet{ + AsPathSetName: "asset3", + AsPathSetMembers: []string{"65004", "65005$"}, + } + + asPathSet4 := config.AsPathSet{ + AsPathSetName: "asset4", + AsPathSetMembers: []string{"65000$"}, + } + + asPathSet5 := config.AsPathSet{ + AsPathSetName: "asset5", + AsPathSetMembers: []string{"65010"}, + } + + asPathSet6 := config.AsPathSet{ + AsPathSetName: "asset6", + AsPathSetMembers: []string{"^65010$"}, + } + + asPathSetList := []config.AsPathSet{asPathSet1, asPathSet2, asPathSet3, + asPathSet4, asPathSet5, asPathSet6} + + p1 := NewAsPathCondition("asset1", asPathSetList) + p2 := NewAsPathCondition("asset2", asPathSetList) + p3 := NewAsPathCondition("asset3", asPathSetList) + p4 := NewAsPathCondition("asset4", asPathSetList) + p5 := NewAsPathCondition("asset5", asPathSetList) + p6 := NewAsPathCondition("asset6", asPathSetList) + + // 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, false, p4.evaluate(path1)) + assert.Equal(t, false, p5.evaluate(path1)) + assert.Equal(t, false, p6.evaluate(path1)) + assert.Equal(t, true, p6.evaluate(path2)) + +} + +func TestAsPathConditionWithOtherCondition(t *testing.T) { + + // 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) + 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) + table.UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate)) + path := table.ProcessMessage(updateMsg, peer)[0] + + // create policy + asPathSet := config.AsPathSet{ + AsPathSetName: "asset1", + AsPathSetMembers: []string{"65005$"}, + } + + 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}, + }, + } + + s := config.Statement{ + Name: "statement1", + Conditions: config.Conditions{ + MatchPrefixSet: "ps1", + MatchNeighborSet: "ns1", + BgpConditions: config.BgpConditions{ + MatchAsPathSet: "asset1", + }, + MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY, + }, + Actions: config.Actions{ + AcceptRoute: false, + RejectRoute: true, + }, + } + + pd := config.PolicyDefinition{"pd1", []config.Statement{s}} + pl := config.RoutingPolicy{ + DefinedSets: ds, + PolicyDefinitionList: []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_REJECT, pType) + assert.Equal(t, nil, newPath) + +} \ No newline at end of file -- cgit v1.2.3