diff options
-rw-r--r-- | policy/policy.go | 65 | ||||
-rw-r--r-- | policy/policy_test.go | 409 | ||||
-rw-r--r-- | table/path.go | 58 |
3 files changed, 468 insertions, 64 deletions
diff --git a/policy/policy.go b/policy/policy.go index 4f56fab6..49d210aa 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -791,28 +791,29 @@ func (a *CommunityAction) apply(path table.Path) table.Path { type MedAction struct { DefaultAction - Value uint32 + Value int64 action ActionType } const ( MED_ACTION_NONE ActionType = iota + MED_ACTION_REPLACE MED_ACTION_ADD MED_ACTION_SUB -// MED_ACTION_IGP ) -//const ( -// MED_IGP string = "IGP" -//) // NewMedAction creates MedAction object. // If it cannot parse med string, then return nil. -// Similarly, if option string is invalid, return nil. func NewMedAction(med config.BgpSetMedType) *MedAction { + if med == ""{ + return nil + } + m := &MedAction{} - matched, value, action := getMedValue(fmt.Sprintf("%v", med)) + + matched, value, action := getMedValue(fmt.Sprintf("%s", med)) if !matched { log.WithFields(log.Fields{ "Topic": "Policy", @@ -826,59 +827,45 @@ func NewMedAction(med config.BgpSetMedType) *MedAction { } // getMedValue returns uint32 med value and action type (+ or -). -// if the string doesn't match a number or operator or well known med name, -// it returns false and 0 and MED_ACTION_NONE. -func getMedValue(medStr string) (bool, uint32, ActionType) { - regUint, _ := regexp.Compile("^([0-9]+)$") - regUintAc, _ := regexp.Compile("^(\\+|\\-)([0-9]+)$") -// regIGP, _ := regexp.Compile("^(IGP)$") - if regUint.MatchString(medStr) { - val, err := strconv.ParseUint(medStr, 10, 32) - if err != nil { - log.WithFields(log.Fields{ - "Topic": "Policy", - "Type": "Med Action", - }).Error("failed to parser as number or med value.") - } - return true, uint32(val), MED_ACTION_NONE - } - if regUintAc.MatchString(medStr) { - group := regUintAc.FindStringSubmatch(medStr) - action := MED_ACTION_ADD - if group[1] == "-" { +// if the string doesn't match a number or operator, +// it returns false and 0. +func getMedValue(medStr string) (bool, int64, ActionType) { + regMed, _ := regexp.Compile("^(\\+|\\-)?([0-9]+)$") + if regMed.MatchString(medStr) { + group := regMed.FindStringSubmatch(medStr) + action := MED_ACTION_REPLACE + if group[1] == "+" { + action = MED_ACTION_ADD + } else if group[1] == "-" { action = MED_ACTION_SUB } - val, err := strconv.ParseUint(group[2], 10, 32) + val, err := strconv.ParseInt(medStr, 10, 64) if err != nil { log.WithFields(log.Fields{ "Topic": "Policy", "Type": "Med Action", }).Error("failed to parser as number or med value.") } - return true, uint32(val), action + return true, int64(val), action } -// if regIGP.MatchString(medStr) { -// return true, uint32(0), MED_IGP -// } - return false, uint32(0), MED_ACTION_NONE + return false, int64(0), MED_ACTION_NONE } - func (a *MedAction) apply(path table.Path) table.Path { var err error switch a.action { - case MED_ACTION_NONE: - err = path.SetMed(a.Value, true, false) + case MED_ACTION_REPLACE: + err = path.SetMed(a.Value, true) case MED_ACTION_ADD: - err = path.SetMed(a.Value, false, false) + err = path.SetMed(a.Value, false) case MED_ACTION_SUB: - err = path.SetMed(a.Value, false, true) + err = path.SetMed(a.Value, false) } if err != nil { log.WithFields(log.Fields{ "Topic": "Policy", "Type": "Med Action", - }).Error(err) + }).Warn(err) } return path diff --git a/policy/policy_test.go b/policy/policy_test.go index 2eced491..ebabb3c8 100644 --- a/policy/policy_test.go +++ b/policy/policy_test.go @@ -25,6 +25,8 @@ import ( "strconv" "strings" "testing" + "fmt" + "math" ) func TestPrefixCalcurateNoRange(t *testing.T) { @@ -1441,4 +1443,409 @@ func stringToCommunityValue(comStr string) uint32 { 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 +} + +func TestPolicyMatchAndReplaceMed(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(100) + + 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}, + } + + m := "200" + 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{ + SetMed: config.BgpSetMedType(m), + }, + }, + } + + 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) + + newMed := fmt.Sprintf("%d", newPath.GetMed()) + assert.Equal(t, m, newMed) +} + +func TestPolicyMatchAndAddingMed(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(100) + + 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}, + } + + m := "+200" + ma := "300" + 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{ + SetMed: config.BgpSetMedType(m), + }, + }, + } + + 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) + + newMed := fmt.Sprintf("%d", newPath.GetMed()) + assert.Equal(t, ma, newMed) +} + +func TestPolicyMatchAndAddingMedOverFlow(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(1) + + 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}, + } + + m := fmt.Sprintf("+%d",math.MaxUint32) + ma := "1" + 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{ + SetMed: config.BgpSetMedType(m), + }, + }, + } + + 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) + + newMed := fmt.Sprintf("%d", newPath.GetMed()) + assert.Equal(t, ma, newMed) +} + +func TestPolicyMatchAndSubtractMed(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(100) + + 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}, + } + + m := "-50" + ma := "50" + 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{ + SetMed: config.BgpSetMedType(m), + }, + }, + } + + 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) + + newMed := fmt.Sprintf("%d", newPath.GetMed()) + assert.Equal(t, ma, newMed) +} + +func TestPolicyMatchAndSubtractMedUnderFlow(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(100) + + 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}, + } + + m := "-101" + ma := "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{ + SetMed: config.BgpSetMedType(m), + }, + }, + } + + 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) + + newMed := fmt.Sprintf("%d", newPath.GetMed()) + assert.Equal(t, ma, newMed) +} + +func TestPolicyMatchWhenPathHaveNotMed(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") + + pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop} + 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}, + } + + m := "-50" + 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{ + SetMed: config.BgpSetMedType(m), + }, + }, + } + + 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) + + newMed := fmt.Sprintf("%d", newPath.GetMed()) + assert.Equal(t, "0", newMed) +} diff --git a/table/path.go b/table/path.go index a82c5e59..390d5f1d 100644 --- a/table/path.go +++ b/table/path.go @@ -41,7 +41,8 @@ type Path interface { SetCommunities([]uint32, bool) RemoveCommunities([]uint32) int ClearCommunities() - SetMed(uint32, bool, bool) error + GetMed() uint32 + SetMed(int64, bool) error setSource(source *PeerInfo) GetSource() *PeerInfo GetSourceAs() uint32 @@ -481,37 +482,46 @@ func (pd *PathDefault) ClearCommunities() { } } +func (pd *PathDefault) GetMed() uint32 { + _, attr := pd.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + med := attr.(*bgp.PathAttributeMultiExitDisc).Value + return med +} + // SetMed replace, add or subtraction med with new ones. -func (pd *PathDefault) SetMed(med uint32, doReplace bool, doSubstruction bool) error { - newMed := &bgp.PathAttributeMultiExitDisc{} - idx, attr := pd.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) - if attr != nil{ - m := attr.(*bgp.PathAttributeMultiExitDisc) +func (pd *PathDefault) SetMed(med int64, doReplace bool) error { + + parseMed := func(orgMed uint32, med int64, doReplace bool) (*bgp.PathAttributeMultiExitDisc, error) { + newMed := &bgp.PathAttributeMultiExitDisc{} if doReplace { - newMed = bgp.NewPathAttributeMultiExitDisc(med) + newMed = bgp.NewPathAttributeMultiExitDisc(uint32(med)) } else { - if doSubstruction { - if m.Value - med < 0 { - return fmt.Errorf("med value invalid. the underflow threshold") - } - newMed = bgp.NewPathAttributeMultiExitDisc(m.Value - med) - } else { - if m.Value + med > math.MaxUint32 { - return fmt.Errorf("med value invalid. the overflow threshold") - } - newMed = bgp.NewPathAttributeMultiExitDisc(m.Value + med) + if int64(orgMed) + med < 0 { + return nil, fmt.Errorf("med value invalid. it's underflow threshold.") + } else if int64(orgMed) + med > int64(math.MaxUint32) { + return nil, fmt.Errorf("med value invalid. it's overflow threshold.") } + newMed = bgp.NewPathAttributeMultiExitDisc(uint32(int64(orgMed) + med)) + } + return newMed, nil + } + + idx, attr := pd.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + if attr != nil{ + m := attr.(*bgp.PathAttributeMultiExitDisc) + newMed, err := parseMed(m.Value, med, doReplace) + if err != nil{ + return err } pd.pathAttrs[idx] = newMed } else { - if doReplace { - newMed = bgp.NewPathAttributeMultiExitDisc(med) - } else { - if !doSubstruction { - newMed = bgp.NewPathAttributeMultiExitDisc(med) - } + m := 0 + newMed, err := parseMed(uint32(m), med, doReplace) + if err != nil{ + pd.pathAttrs = append(pd.pathAttrs, &bgp.PathAttributeMultiExitDisc{}) + return err } - pd.pathAttrs[idx] = newMed + pd.pathAttrs = append(pd.pathAttrs, newMed) } return nil } |