diff options
-rw-r--r-- | config/bgp_configs.go | 3 | ||||
-rw-r--r-- | policy/policy.go | 82 | ||||
-rw-r--r-- | policy/policy_test.go | 149 | ||||
-rw-r--r-- | table/path.go | 101 | ||||
-rw-r--r-- | table/path_test.go | 160 |
5 files changed, 447 insertions, 48 deletions
diff --git a/config/bgp_configs.go b/config/bgp_configs.go index db07195c..3f0381b5 100644 --- a/config/bgp_configs.go +++ b/config/bgp_configs.go @@ -149,6 +149,9 @@ type SetCommunity struct { //struct for container bgp-pol:set-as-path-prepend type SetAsPathPrepend struct { + // original -> bgp-pol:as + //bgp-pol:as's original type is union + As string // original -> bgp-pol:repeat-n RepeatN uint8 } diff --git a/policy/policy.go b/policy/policy.go index d3e4ef03..300fcadf 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -17,16 +17,17 @@ package policy import ( "fmt" - log "github.com/Sirupsen/logrus" - "github.com/osrg/gobgp/api" - "github.com/osrg/gobgp/config" - "github.com/osrg/gobgp/packet" - "github.com/osrg/gobgp/table" "net" "reflect" "regexp" "strconv" "strings" + + log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/config" + "github.com/osrg/gobgp/packet" + "github.com/osrg/gobgp/table" ) type RouteType int @@ -118,6 +119,12 @@ func NewPolicy(pd config.PolicyDefinition, ds config.DefinedSets) *Policy { mda = append(mda, med) } + //AsPathPrependAction + ppa := NewAsPathPrependAction(statement.Actions.BgpActions.SetAsPathPrepend) + if ppa != nil { + mda = append(mda, ppa) + } + s := &Statement{ Name: statement.Name, Conditions: conditions, @@ -870,6 +877,69 @@ func (a *MedAction) apply(path *table.Path) *table.Path { return path } +type AsPathPrependAction struct { + DefaultAction + asn uint32 + useLeftMost bool + repeat uint8 +} + +// NewAsPathPrependAction creates AsPathPrependAction object. +// If ASN cannot be parsed, nil will be returned. +func NewAsPathPrependAction(action config.SetAsPathPrepend) *AsPathPrependAction { + + a := &AsPathPrependAction{} + + if action.As == "" { + return nil + } + + if action.As == "last-as" { + a.useLeftMost = true + } else { + asn, err := strconv.ParseUint(action.As, 10, 32) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "AsPathPrepend Action", + "Value": action.As, + }).Error("As number string invalid.") + return nil + } + a.asn = uint32(asn) + } + a.repeat = action.RepeatN + + return a +} + +func (a *AsPathPrependAction) apply(path *table.Path) *table.Path { + + var asn uint32 + if a.useLeftMost { + asns := path.GetAsSeqList() + if len(asns) == 0 { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "AsPathPrepend Action", + }).Error("aspath length is zero.") + return path + } + asn = asns[0] + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "AsPathPrepend Action", + "LastAs": asn, + "Repeat": a.repeat, + }).Debug("use last AS.") + } else { + asn = a.asn + } + + path.PrependAsn(asn, a.repeat) + return path +} + type Prefix struct { Address net.IP AddressFamily bgp.RouteFamily @@ -1349,7 +1419,7 @@ func ActionsToApiStruct(conActions config.Actions) *api.Actions { func ActionsToConfigStruct(reqActions *api.Actions) config.Actions { actions := config.Actions{} - if reqActions == nil{ + if reqActions == nil { return actions } if reqActions.Community != nil { diff --git a/policy/policy_test.go b/policy/policy_test.go index ae615935..d1155335 100644 --- a/policy/policy_test.go +++ b/policy/policy_test.go @@ -1856,3 +1856,152 @@ func TestPolicyMatchWhenPathHaveNotMed(t *testing.T) { _, err := newPath.GetMed() assert.NotNil(t, err) } + + +func TestPolicyAsPathPrepend(t *testing.T) { + + assert := assert.New(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, 65000})} + 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) + + body := updateMsg.Body.(*bgp.BGPUpdate) + table.UpdatePathAttrs4ByteAs(body) + 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{ + SetAsPathPrepend: config.SetAsPathPrepend{ + As: "65002", + RepeatN: 10, + }, + }, + }, + } + + 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(true, match) + assert.Equal(ROUTE_TYPE_ACCEPT, pType) + assert.NotEqual(nil, newPath) + assert.Equal([]uint32{65002, 65002, 65002, 65002, 65002, 65002, 65002, 65002, 65002, 65002, 65001, 65000}, newPath.GetAsSeqList()) +} + + +func TestPolicyAsPathPrependLastAs(t *testing.T) { + + assert := assert.New(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{65002, 65001, 65000})} + 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) + + body := updateMsg.Body.(*bgp.BGPUpdate) + table.UpdatePathAttrs4ByteAs(body) + 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{ + SetAsPathPrepend: config.SetAsPathPrepend{ + As: "last-as", + RepeatN: 5, + }, + }, + }, + } + + 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(true, match) + assert.Equal(ROUTE_TYPE_ACCEPT, pType) + assert.NotEqual(nil, newPath) + assert.Equal([]uint32{65002, 65002, 65002, 65002, 65002, 65002, 65001, 65000}, newPath.GetAsSeqList()) +} diff --git a/table/path.go b/table/path.go index bd368ec6..302847f8 100644 --- a/table/path.go +++ b/table/path.go @@ -79,48 +79,10 @@ func (path *Path) UpdatePathAttrs(global *config.Global, peer *config.Neighbor) path.SetNexthop(peer.LocalAddress) // AS_PATH handling - // - // When a given BGP speaker advertises the route to an external - // peer, the advertising speaker updates the AS_PATH attribute - // as follows: - // 1) if the first path segment of the AS_PATH is of type - // AS_SEQUENCE, the local system prepends its own AS num as - // the last element of the sequence (put it in the left-most - // position with respect to the position of octets in the - // protocol message). If the act of prepending will cause an - // overflow in the AS_PATH segment (i.e., more than 255 - // ASes), it SHOULD prepend a new segment of type AS_SEQUENCE - // and prepend its own AS number to this new segment. - // - // 2) if the first path segment of the AS_PATH is of type AS_SET - // , the local system prepends a new path segment of type - // AS_SEQUENCE to the AS_PATH, including its own AS number in - // that segment. - // - // 3) if the AS_PATH is empty, the local system creates a path - // segment of type AS_SEQUENCE, places its own AS into that - // segment, and places that segment into the AS_PATH. - idx, originalAsPath := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) - if idx < 0 { - p := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{global.As}) - asPath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{p}) - path.pathAttrs = append(path.pathAttrs, asPath) - } else { - asPath := cloneAsPath(originalAsPath.(*bgp.PathAttributeAsPath)) - path.pathAttrs[idx] = asPath - fst := asPath.Value[0].(*bgp.As4PathParam) - if len(asPath.Value) > 0 && fst.Type == bgp.BGP_ASPATH_ATTR_TYPE_SEQ && - fst.ASLen() < 255 { - fst.AS = append([]uint32{global.As}, fst.AS...) - fst.Num += 1 - } else { - p := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{global.As}) - asPath.Value = append([]bgp.AsPathParamInterface{p}, asPath.Value...) - } - } + path.PrependAsn(global.As, 1) // MED Handling - idx, _ = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + idx, _ := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) if idx >= 0 { path.pathAttrs = append(path.pathAttrs[:idx], path.pathAttrs[idx+1:]...) } @@ -328,6 +290,65 @@ func (path *Path) getAsListofSpecificType(getAsSeq, getAsSet bool) []uint32 { return asList } +// PrependAsn prepends AS number. +// This function updates the AS_PATH attribute as follows. +// 1) if the first path segment of the AS_PATH is of type +// AS_SEQUENCE, the local system prepends the specified AS num as +// the last element of the sequence (put it in the left-most +// position with respect to the position of octets in the +// protocol message) the specified number of times. +// If the act of prepending will cause an overflow in the AS_PATH +// segment (i.e., more than 255 ASes), +// it SHOULD prepend a new segment of type AS_SEQUENCE +// and prepend its own AS number to this new segment. +// +// 2) if the first path segment of the AS_PATH is of other than type +// AS_SEQUENCE, the local system prepends a new path segment of type +// AS_SEQUENCE to the AS_PATH, including the specified AS number in +// that segment. +// +// 3) if the AS_PATH is empty, the local system creates a path +// segment of type AS_SEQUENCE, places the specified AS number +// into that segment, and places that segment into the AS_PATH. +func (path *Path) PrependAsn(asn uint32, repeat uint8) { + + idx, aspath := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) + + asns := make([]uint32, repeat) + for i, _ := range asns { + asns[i] = asn + } + + if idx < 0 { + p := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, asns) + asPath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{p}) + path.pathAttrs = append(path.pathAttrs, asPath) + } else { + aspathClone := cloneAsPath(aspath.(*bgp.PathAttributeAsPath)) + path.pathAttrs[idx] = aspathClone + fst := aspathClone.Value[0].(*bgp.As4PathParam) + + if fst.Type == bgp.BGP_ASPATH_ATTR_TYPE_SEQ && len(fst.AS) < 255 { + + // overflow case + if len(fst.AS)+int(repeat) > 255 { + rest := 255 - len(fst.AS) + fst.AS = append(asns[0:rest], fst.AS...) + fst.Num = 255 + + p := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, asns[rest:]) + aspathClone.Value = append([]bgp.AsPathParamInterface{p}, aspathClone.Value...) + } else { + fst.AS = append(asns, fst.AS...) + fst.Num += repeat + } + } else { + p := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, asns) + aspathClone.Value = append([]bgp.AsPathParamInterface{p}, aspathClone.Value...) + } + } +} + func (path *Path) GetCommunities() []uint32 { communityList := []uint32{} if _, attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES); attr != nil { diff --git a/table/path_test.go b/table/path_test.go index 44381fd7..976a5620 100644 --- a/table/path_test.go +++ b/table/path_test.go @@ -3,10 +3,12 @@ package table import ( //"fmt" - "github.com/osrg/gobgp/packet" - "github.com/stretchr/testify/assert" + "fmt" "testing" "time" + + "github.com/osrg/gobgp/packet" + "github.com/stretchr/testify/assert" ) func TestPathNewIPv4(t *testing.T) { @@ -120,6 +122,160 @@ func TestASPathLen(t *testing.T) { assert.Equal(10, p.GetAsPathLen()) } +func TestPathPrependAsnToExistingSeqAttr(t *testing.T) { + assert := assert.New(t) + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint16{65001, 65002, 65003, 65004, 65005}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + bgpmsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) + update := bgpmsg.Body.(*bgp.BGPUpdate) + UpdatePathAttrs4ByteAs(update) + peer := PathCreatePeer() + p := NewPath(peer[0], &update.NLRI[0], false, update.PathAttributes, false, time.Now()) + + p.PrependAsn(65000, 1) + assert.Equal([]uint32{65000, 65001, 65002, 65003, 65004, 65005}, p.GetAsSeqList()) + fmt.Printf("asns: %v", p.GetAsSeqList()) +} + +func TestPathPrependAsnToNewAsPathAttr(t *testing.T) { + assert := assert.New(t) + origin := bgp.NewPathAttributeOrigin(0) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + nexthop, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + bgpmsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) + update := bgpmsg.Body.(*bgp.BGPUpdate) + UpdatePathAttrs4ByteAs(update) + peer := PathCreatePeer() + p := NewPath(peer[0], &update.NLRI[0], false, update.PathAttributes, false, time.Now()) + + asn := uint32(65000) + p.PrependAsn(asn, 1) + assert.Equal([]uint32{asn}, p.GetAsSeqList()) +} + +func TestPathPrependAsnToNewAsPathSeq(t *testing.T) { + assert := assert.New(t) + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + bgpmsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) + update := bgpmsg.Body.(*bgp.BGPUpdate) + UpdatePathAttrs4ByteAs(update) + peer := PathCreatePeer() + p := NewPath(peer[0], &update.NLRI[0], false, update.PathAttributes, false, time.Now()) + + asn := uint32(65000) + p.PrependAsn(asn, 1) + assert.Equal([]uint32{asn}, p.GetAsSeqList()) + fmt.Printf("asns: %v", p.GetAsSeqList()) +} + +func TestPathPrependAsnToEmptyAsPathAttr(t *testing.T) { + assert := assert.New(t) + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint16{}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + bgpmsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) + update := bgpmsg.Body.(*bgp.BGPUpdate) + UpdatePathAttrs4ByteAs(update) + peer := PathCreatePeer() + p := NewPath(peer[0], &update.NLRI[0], false, update.PathAttributes, false, time.Now()) + + asn := uint32(65000) + p.PrependAsn(asn, 1) + assert.Equal([]uint32{asn}, p.GetAsSeqList()) + fmt.Printf("asns: %v", p.GetAsSeqList()) +} + +func TestPathPrependAsnToFullPathAttr(t *testing.T) { + assert := assert.New(t) + origin := bgp.NewPathAttributeOrigin(0) + + asns := make([]uint16, 255) + for i, _ := range asns { + asns[i] = 65000 + uint16(i) + } + + aspathParam := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, asns), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}), + bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + bgpmsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) + update := bgpmsg.Body.(*bgp.BGPUpdate) + UpdatePathAttrs4ByteAs(update) + peer := PathCreatePeer() + p := NewPath(peer[0], &update.NLRI[0], false, update.PathAttributes, false, time.Now()) + + expected := []uint32{65000, 65000} + for _, v := range asns { + expected = append(expected, uint32(v)) + } + p.PrependAsn(65000, 2) + assert.Equal(expected, p.GetAsSeqList()) + fmt.Printf("asns: %v", p.GetAsSeqList()) +} + func PathCreatePeer() []*PeerInfo { peerP1 := &PeerInfo{AS: 65000} peerP2 := &PeerInfo{AS: 65001} |