Skip to content
Snippets Groups Projects
Unverified Commit daa5286b authored by takt's avatar takt Committed by GitHub
Browse files

Merge pull request #15 from bio-routing/bgp_large_community_support

BGP large community support
parents 0aa9ac90 5eae161d
No related branches found
No related tags found
No related merge requests found
Showing
with 632 additions and 143 deletions
......@@ -22,6 +22,10 @@ func main() {
err := b.Start(&config.Global{
Listen: true,
LocalAddressList: []net.IP{
net.IPv4(169, 254, 100, 1),
net.IPv4(169, 254, 200, 0),
},
})
if err != nil {
logrus.Fatalf("Unable to start BGP server: %v", err)
......
......@@ -6,11 +6,12 @@ const (
BGP4Version = 4
MinOpenLen = 29
MarkerLen = 16
HeaderLen = 19
MinLen = 19
MaxLen = 4096
NLRIMaxLen = 5
MarkerLen = 16
HeaderLen = 19
MinLen = 19
MaxLen = 4096
NLRIMaxLen = 5
LargeCommunityLen = 12
OpenMsg = 1
UpdateMsg = 2
......@@ -55,17 +56,17 @@ const (
AdministrativeReset = 4
// Attribute Type Codes
OriginAttr = 1
ASPathAttr = 2
NextHopAttr = 3
MEDAttr = 4
LocalPrefAttr = 5
AtomicAggrAttr = 6
AggregatorAttr = 7
CommunitiesAttr = 8
AS4PathAttr = 17
AS4AggregatorAttr = 18
OriginAttr = 1
ASPathAttr = 2
NextHopAttr = 3
MEDAttr = 4
LocalPrefAttr = 5
AtomicAggrAttr = 6
AggregatorAttr = 7
CommunitiesAttr = 8
AS4PathAttr = 17
AS4AggregatorAttr = 18
LargeCommunityAttr = 32
// ORIGIN values
IGP = 0
......
package packet
import (
"fmt"
"strconv"
"strings"
)
type LargeCommunity struct {
GlobalAdministrator uint32
DataPart1 uint32
DataPart2 uint32
}
func (c LargeCommunity) String() string {
return fmt.Sprintf("(%d,%d,%d)", c.GlobalAdministrator, c.DataPart1, c.DataPart2)
}
func ParseCommunityString(s string) (com LargeCommunity, err error) {
s = strings.Trim(s, "()")
t := strings.Split(s, ",")
if len(t) != 3 {
return com, fmt.Errorf("can not parse large community %s", s)
}
v, err := strconv.ParseUint(t[0], 10, 32)
if err != nil {
return com, err
}
com.GlobalAdministrator = uint32(v)
v, err = strconv.ParseUint(t[1], 10, 32)
if err != nil {
return com, err
}
com.DataPart1 = uint32(v)
v, err = strconv.ParseUint(t[2], 10, 32)
if err != nil {
return com, err
}
com.DataPart2 = uint32(v)
return com, err
}
......@@ -96,6 +96,10 @@ func decodePathAttr(buf *bytes.Buffer) (pa *PathAttribute, consumed uint16, err
if err := pa.decodeAS4Aggregator(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Aggregator: %v", err)
}
case LargeCommunityAttr:
if err := pa.decodeLargeCommunities(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode large communities: %v", err)
}
default:
if err := pa.decodeUnknown(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode unknown attribute: %v", err)
......@@ -176,41 +180,15 @@ func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer) error {
}
func (pa *PathAttribute) decodeNextHop(buf *bytes.Buffer) error {
addr := [4]byte{}
p := uint16(0)
n, err := buf.Read(addr[:])
if err != nil {
return err
}
if n != 4 {
return fmt.Errorf("Unable to read next hop: buf.Read read %d bytes", n)
}
pa.Value = fourBytesToUint32(addr)
p += 4
return dumpNBytes(buf, pa.Length-p)
return pa.decodeUint32(buf, "next hop")
}
func (pa *PathAttribute) decodeMED(buf *bytes.Buffer) error {
med, err := pa.decodeUint32(buf)
if err != nil {
return fmt.Errorf("Unable to recode local pref: %v", err)
}
pa.Value = uint32(med)
return nil
return pa.decodeUint32(buf, "MED")
}
func (pa *PathAttribute) decodeLocalPref(buf *bytes.Buffer) error {
lpref, err := pa.decodeUint32(buf)
if err != nil {
return fmt.Errorf("Unable to recode local pref: %v", err)
}
pa.Value = uint32(lpref)
return nil
return pa.decodeUint32(buf, "local pref")
}
func (pa *PathAttribute) decodeAggregator(buf *bytes.Buffer) error {
......@@ -260,23 +238,64 @@ func (pa *PathAttribute) decodeCommunities(buf *bytes.Buffer) error {
return nil
}
func (pa *PathAttribute) decodeAS4Path(buf *bytes.Buffer) error {
as4Path, err := pa.decodeUint32(buf)
if err != nil {
return fmt.Errorf("Unable to decode AS4Path: %v", err)
func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error {
length := pa.Length
count := length / LargeCommunityLen
coms := make([]LargeCommunity, count)
for i := uint16(0); i < count; i++ {
com := LargeCommunity{}
v, err := read4BytesAsUint32(buf)
if err != nil {
return err
}
com.GlobalAdministrator = v
v, err = read4BytesAsUint32(buf)
if err != nil {
return err
}
com.DataPart1 = v
v, err = read4BytesAsUint32(buf)
if err != nil {
return err
}
com.DataPart2 = v
coms[i] = com
}
pa.Value = as4Path
return nil
pa.Value = coms
dump := pa.Length - (count * LargeCommunityLen)
return dumpNBytes(buf, dump)
}
func (pa *PathAttribute) decodeAS4Path(buf *bytes.Buffer) error {
return pa.decodeUint32(buf, "AS4Path")
}
func (pa *PathAttribute) decodeAS4Aggregator(buf *bytes.Buffer) error {
as4Aggregator, err := pa.decodeUint32(buf)
return pa.decodeUint32(buf, "AS4Aggregator")
}
func (pa *PathAttribute) decodeUint32(buf *bytes.Buffer, attrName string) error {
v, err := read4BytesAsUint32(buf)
if err != nil {
return fmt.Errorf("Unable to decode AS4Aggregator: %v", err)
return fmt.Errorf("Unable to decode %s: %v", attrName, err)
}
pa.Value = v
p := uint16(4)
err = dumpNBytes(buf, pa.Length-p)
if err != nil {
return fmt.Errorf("dumpNBytes failed: %v", err)
}
pa.Value = as4Aggregator
return nil
}
......@@ -300,24 +319,6 @@ func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) {
return bytesRead, nil
}
func (pa *PathAttribute) decodeUint32(buf *bytes.Buffer) (uint32, error) {
var v uint32
p := uint16(0)
err := decode(buf, []interface{}{&v})
if err != nil {
return 0, err
}
p += 4
err = dumpNBytes(buf, pa.Length-p)
if err != nil {
return 0, fmt.Errorf("dumpNBytes failed: %v", err)
}
return v, nil
}
func (pa *PathAttribute) ASPathString() (ret string) {
for _, p := range pa.Value.(ASPath) {
if p.Type == ASSet {
......@@ -351,6 +352,15 @@ func (pa *PathAttribute) ASPathLen() (ret uint16) {
return
}
func (a *PathAttribute) LargeCommunityString() string {
s := ""
for _, com := range a.Value.([]LargeCommunity) {
s += com.String() + " "
}
return strings.TrimRight(s, " ")
}
// dumpNBytes is used to dump n bytes of buf. This is useful in case an path attributes
// length doesn't match a fixed length's attributes length (e.g. ORIGIN is always an octet)
func dumpNBytes(buf *bytes.Buffer, n uint16) error {
......@@ -383,6 +393,8 @@ func (pa *PathAttribute) serialize(buf *bytes.Buffer) uint8 {
pathAttrLen = pa.serializeAtomicAggregate(buf)
case AggregatorAttr:
pathAttrLen = pa.serializeAggregator(buf)
case LargeCommunityAttr:
pathAttrLen = pa.serializeLargeCommunities(buf)
}
return pathAttrLen
......@@ -478,6 +490,32 @@ func (pa *PathAttribute) serializeAggregator(buf *bytes.Buffer) uint8 {
return 5
}
func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
coms := pa.Value.([]LargeCommunity)
if len(coms) == 0 {
return 0
}
attrFlags := uint8(0)
attrFlags = setOptional(attrFlags)
attrFlags = setTransitive(attrFlags)
attrFlags = setPartial(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(LargeCommunityAttr)
length := uint8(LargeCommunityLen * len(coms))
buf.WriteByte(length)
for _, com := range coms {
buf.Write(convert.Uint32Byte(com.GlobalAdministrator))
buf.Write(convert.Uint32Byte(com.DataPart1))
buf.Write(convert.Uint32Byte(com.DataPart2))
}
return length
}
/*func (pa *PathAttribute) PrependASPath(prepend []uint32) {
if pa.TypeCode != ASPathAttr {
return
......@@ -563,6 +601,24 @@ func ParseASPathStr(asPathString string) (*PathAttribute, error) {
}, nil
}
func LargeCommunityAttributeForString(s string) (*PathAttribute, error) {
strs := strings.Split(s, " ")
coms := make([]LargeCommunity, len(strs))
var err error
for i, str := range strs {
coms[i], err = ParseCommunityString(str)
if err != nil {
return nil, err
}
}
return &PathAttribute{
TypeCode: LargeCommunityAttr,
Value: coms,
}, nil
}
func isBeginOfASSet(asPathPart string) bool {
return strings.Contains(asPathPart, "(")
}
......@@ -574,3 +630,16 @@ func isEndOfASSset(asPathPart string) bool {
func fourBytesToUint32(address [4]byte) uint32 {
return uint32(address[0])<<24 + uint32(address[1])<<16 + uint32(address[2])<<8 + uint32(address[3])
}
func read4BytesAsUint32(buf *bytes.Buffer) (uint32, error) {
b := [4]byte{}
n, err := buf.Read(b[:])
if err != nil {
return 0, err
}
if n != 4 {
return 0, fmt.Errorf("Unable to read as uint32. Expected 4 bytes but got only %d", n)
}
return fourBytesToUint32(b), nil
}
......@@ -589,6 +589,74 @@ func TestDecodeAggregator(t *testing.T) {
}
}
func TestDecodeLargeCommunity(t *testing.T) {
tests := []struct {
name string
input []byte
wantFail bool
explicitLength uint16
expected *PathAttribute
}{
{
name: "two valid large communities",
input: []byte{
0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, // (1, 2, 3), (4, 5, 6)
},
wantFail: false,
expected: &PathAttribute{
Length: 24,
Value: []LargeCommunity{
{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
{
GlobalAdministrator: 4,
DataPart1: 5,
DataPart2: 6,
},
},
},
},
{
name: "Empty input",
input: []byte{},
wantFail: false,
expected: &PathAttribute{
Length: 0,
Value: []LargeCommunity{},
},
},
}
for _, test := range tests {
l := uint16(len(test.input))
if test.explicitLength != 0 {
l = test.explicitLength
}
pa := &PathAttribute{
Length: l,
}
err := pa.decodeLargeCommunities(bytes.NewBuffer(test.input))
if test.wantFail {
if err != nil {
continue
}
t.Errorf("Expected error did not happen for test %q", test.name)
continue
}
if err != nil {
t.Errorf("Unexpected failure for test %q: %v", test.name, err)
continue
}
assert.Equal(t, test.expected, pa)
}
}
func TestSetLength(t *testing.T) {
tests := []struct {
name string
......@@ -695,7 +763,7 @@ func TestDecodeUint32(t *testing.T) {
pa := &PathAttribute{
Length: l,
}
res, err := pa.decodeUint32(bytes.NewBuffer(test.input))
err := pa.decodeUint32(bytes.NewBuffer(test.input), "test")
if test.wantFail {
if err != nil {
......@@ -710,7 +778,7 @@ func TestDecodeUint32(t *testing.T) {
continue
}
assert.Equal(t, test.expected, res)
assert.Equal(t, test.expected, pa.Value)
}
}
......@@ -756,6 +824,40 @@ func TestASPathString(t *testing.T) {
}
}
func TestLargeCommunityString(t *testing.T) {
tests := []struct {
name string
pa *PathAttribute
expected string
}{
{
name: "two attributes",
pa: &PathAttribute{
Value: []LargeCommunity{
{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
{
GlobalAdministrator: 4,
DataPart1: 5,
DataPart2: 6,
},
},
},
expected: "(1,2,3) (4,5,6)",
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
res := test.pa.LargeCommunityString()
assert.Equal(te, test.expected, res)
})
}
}
func TestSetOptional(t *testing.T) {
tests := []struct {
name string
......@@ -1093,6 +1195,62 @@ func TestSerializeASPath(t *testing.T) {
}
}
func TestSerializeLargeCommunities(t *testing.T) {
tests := []struct {
name string
input *PathAttribute
expected []byte
expectedLen uint8
}{
{
name: "2 large communities",
input: &PathAttribute{
TypeCode: LargeCommunityAttr,
Value: []LargeCommunity{
{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
{
GlobalAdministrator: 4,
DataPart1: 5,
DataPart2: 6,
},
},
},
expected: []byte{
0xe0, // Attribute flags
32, // Type
24, // Length
0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, // Communities (1, 2, 3), (4, 5, 6)
},
expectedLen: 24,
},
{
name: "empty list of communities",
input: &PathAttribute{
TypeCode: LargeCommunityAttr,
Value: []LargeCommunity{},
},
expected: []byte{},
expectedLen: 0,
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
buf := bytes.NewBuffer([]byte{})
n := test.input.serializeLargeCommunities(buf)
if n != test.expectedLen {
t.Fatalf("Unexpected length for test %q: %d", test.name, n)
}
assert.Equal(t, test.expected, buf.Bytes())
})
}
}
func TestSerialize(t *testing.T) {
tests := []struct {
name string
......
......@@ -792,7 +792,7 @@ func (fsm *FSM) established() int {
case recvMsg := <-fsm.msgRecvCh:
msg, err := packet.Decode(bytes.NewBuffer(recvMsg.msg))
if err != nil {
log.WithError(err).Errorf("Failed to decode BGP message %v\n", recvMsg.msg)
log.WithError(err).Errorf("Failed to decode BGP message %v\n", recvMsg.msg)
switch bgperr := err.(type) {
case packet.BGPError:
sendNotification(fsm.con, bgperr.ErrorCode, bgperr.ErrorSubCode)
......@@ -845,6 +845,8 @@ func (fsm *FSM) established() int {
case packet.ASPathAttr:
path.BGPPath.ASPath = pa.ASPathString()
path.BGPPath.ASPathLen = pa.ASPathLen()
case packet.LargeCommunityAttr:
path.BGPPath.LargeCommunities = pa.LargeCommunityString()
}
}
fsm.adjRIBIn.AddPath(pfx, path)
......
package server
import (
"fmt"
"strings"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/route"
)
func pathAttribues(p *route.Path, fsm *FSM) (*packet.PathAttribute, error) {
asPathPA, err := packet.ParseASPathStr(strings.TrimRight(fmt.Sprintf("%d %s", fsm.localASN, p.BGPPath.ASPath), " "))
if err != nil {
return nil, fmt.Errorf("Unable to parse AS path: %v", err)
}
origin := &packet.PathAttribute{
TypeCode: packet.OriginAttr,
Value: p.BGPPath.Origin,
Next: asPathPA,
}
nextHop := &packet.PathAttribute{
TypeCode: packet.NextHopAttr,
Value: p.BGPPath.NextHop,
}
asPathPA.Next = nextHop
localPref := &packet.PathAttribute{
TypeCode: packet.LocalPrefAttr,
Value: p.BGPPath.LocalPref,
}
nextHop.Next = localPref
if p.BGPPath != nil {
err := addOptionalPathAttribues(p, localPref)
if err != nil {
return nil, err
}
}
return origin, nil
}
func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error {
current := parent
if len(p.BGPPath.LargeCommunities) > 0 {
largeCommunities, err := packet.LargeCommunityAttributeForString(p.BGPPath.LargeCommunities)
if err != nil {
return fmt.Errorf("Could not create large community attribute: %v", err)
}
current.Next = largeCommunities
current = largeCommunities
}
return nil
}
......@@ -28,31 +28,18 @@ func newUpdateSender(fsm *FSM) *UpdateSender {
// AddPath serializes a new path and sends out a BGP update message
func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error {
asPathPA, err := packet.ParseASPathStr(asPathString(u.iBGP, u.fsm.localASN, p.BGPPath.ASPath))
pathAttrs, err := pathAttribues(p, u.fsm)
if err != nil {
return fmt.Errorf("Unable to parse AS path: %v", err)
log.Errorf("Unable to create BGP Update: %v", err)
return nil
}
update := &packet.BGPUpdate{
PathAttributes: &packet.PathAttribute{
TypeCode: packet.OriginAttr,
Value: p.BGPPath.Origin,
Next: &packet.PathAttribute{
TypeCode: packet.ASPathAttr,
Value: asPathPA.Value,
Next: &packet.PathAttribute{
TypeCode: packet.NextHopAttr,
Value: p.BGPPath.NextHop,
Next: &packet.PathAttribute{
TypeCode: packet.LocalPrefAttr,
Value: p.BGPPath.LocalPref,
},
},
},
},
NLRI: &packet.NLRI{
IP: pfx.Addr(),
Pfxlen: pfx.Pfxlen(),
update := &packet.BGPUpdateAddPath{
PathAttributes: pathAttrs,
NLRI: &packet.NLRIAddPath{
PathIdentifier: p.BGPPath.PathIdentifier,
IP: pfx.Addr(),
Pfxlen: pfx.Pfxlen(),
},
}
......
......@@ -27,32 +27,17 @@ func newUpdateSenderAddPath(fsm *FSM) *UpdateSenderAddPath {
// AddPath serializes a new path and sends out a BGP update message
func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
asPathPA, err := packet.ParseASPathStr(asPathString(u.iBGP, u.fsm.localASN, p.BGPPath.ASPath))
pathAttrs, err := pathAttribues(p, u.fsm)
if err != nil {
return fmt.Errorf("Unable to parse AS path: %v", err)
log.Errorf("Unable to create BGP Update: %v", err)
return nil
}
update := &packet.BGPUpdateAddPath{
PathAttributes: &packet.PathAttribute{
TypeCode: packet.OriginAttr,
Value: p.BGPPath.Origin,
Next: &packet.PathAttribute{
TypeCode: packet.ASPathAttr,
Value: asPathPA.Value,
Next: &packet.PathAttribute{
TypeCode: packet.NextHopAttr,
Value: p.BGPPath.NextHop,
Next: &packet.PathAttribute{
TypeCode: packet.LocalPrefAttr,
Value: p.BGPPath.LocalPref,
},
},
},
},
NLRI: &packet.NLRIAddPath{
PathIdentifier: p.BGPPath.PathIdentifier,
IP: pfx.Addr(),
Pfxlen: pfx.Pfxlen(),
update := &packet.BGPUpdate{
PathAttributes: pathAttrs,
NLRI: &packet.NLRI{
IP: pfx.Addr(),
Pfxlen: pfx.Pfxlen(),
},
}
......
......@@ -10,16 +10,17 @@ import (
// BGPPath represents a set of BGP path attributes
type BGPPath struct {
PathIdentifier uint32
NextHop uint32
LocalPref uint32
ASPath string
ASPathLen uint16
Origin uint8
MED uint32
EBGP bool
BGPIdentifier uint32
Source uint32
PathIdentifier uint32
NextHop uint32
LocalPref uint32
ASPath string
ASPathLen uint16
Origin uint8
MED uint32
EBGP bool
BGPIdentifier uint32
Source uint32
LargeCommunities string
}
// ECMP determines if routes b and c are euqal in terms of ECMP
......
package actions
import (
"strings"
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/route"
)
type AddLargeCommunityAction struct {
communities []*packet.LargeCommunity
}
func NewAddLargeCommunityAction(coms []*packet.LargeCommunity) *AddLargeCommunityAction {
return &AddLargeCommunityAction{
communities: coms,
}
}
func (a *AddLargeCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
if pa.BGPPath == nil || len(a.communities) == 0 {
return pa, false
}
modified := pa.Copy()
for _, com := range a.communities {
modified.BGPPath.LargeCommunities = modified.BGPPath.LargeCommunities + " " + com.String()
}
modified.BGPPath.LargeCommunities = strings.TrimLeft(modified.BGPPath.LargeCommunities, " ")
return modified, false
}
package actions
import (
"testing"
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/route"
"github.com/stretchr/testify/assert"
)
func TestAddingLargeCommunities(t *testing.T) {
tests := []struct {
name string
current string
communities []*packet.LargeCommunity
expected string
}{
{
name: "add one to empty",
communities: []*packet.LargeCommunity{
&packet.LargeCommunity{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
},
expected: "(1,2,3)",
},
{
name: "add one to existing",
current: "(5,6,7)",
communities: []*packet.LargeCommunity{
&packet.LargeCommunity{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
},
expected: "(5,6,7) (1,2,3)",
},
{
name: "add two to existing",
current: "(5,6,7)",
communities: []*packet.LargeCommunity{
&packet.LargeCommunity{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
&packet.LargeCommunity{
GlobalAdministrator: 7,
DataPart1: 8,
DataPart2: 9,
},
},
expected: "(5,6,7) (1,2,3) (7,8,9)",
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
p := &route.Path{
BGPPath: &route.BGPPath{
LargeCommunities: test.current,
},
}
a := NewAddLargeCommunityAction(test.communities)
modPath, _ := a.Do(net.Prefix{}, p)
assert.Equal(te, test.expected, modPath.BGPPath.LargeCommunities)
})
}
}
package filter
import (
"strings"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
)
type LargeCommunityFilter struct {
community *packet.LargeCommunity
}
func (f *LargeCommunityFilter) Matches(communityString string) bool {
return strings.Contains(communityString, f.community.String())
}
......@@ -6,8 +6,9 @@ import (
)
type TermCondition struct {
prefixLists []*PrefixList
routeFilters []*RouteFilter
prefixLists []*PrefixList
routeFilters []*RouteFilter
largeCommunityFilters []*LargeCommunityFilter
}
func NewTermCondition(prefixLists []*PrefixList, routeFilters []*RouteFilter) *TermCondition {
......@@ -18,7 +19,7 @@ func NewTermCondition(prefixLists []*PrefixList, routeFilters []*RouteFilter) *T
}
func (f *TermCondition) Matches(p net.Prefix, pa *route.Path) bool {
return f.matchesAnyPrefixList(p) || f.machtchesAnyRouteFilter(p)
return f.matchesAnyPrefixList(p) || f.machtchesAnyRouteFilter(p) || f.machtchesAnyLageCommunityFilter(pa)
}
func (t *TermCondition) matchesAnyPrefixList(p net.Prefix) bool {
......@@ -40,3 +41,17 @@ func (t *TermCondition) machtchesAnyRouteFilter(p net.Prefix) bool {
return false
}
func (t *TermCondition) machtchesAnyLageCommunityFilter(pa *route.Path) bool {
if pa.BGPPath == nil {
return false
}
for _, l := range t.largeCommunityFilters {
if l.Matches(pa.BGPPath.LargeCommunities) {
return true
}
}
return false
}
......@@ -4,17 +4,20 @@ import (
"testing"
"github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
"github.com/bio-routing/bio-rd/route"
"github.com/stretchr/testify/assert"
)
func TestMatches(t *testing.T) {
tests := []struct {
name string
prefix net.Prefix
prefixLists []*PrefixList
routeFilters []*RouteFilter
expected bool
name string
prefix net.Prefix
bgpPath *route.BGPPath
prefixLists []*PrefixList
routeFilters []*RouteFilter
largeCommunityFilters []*LargeCommunityFilter
expected bool
}{
{
name: "one prefix matches in prefix list, no route filters set",
......@@ -105,16 +108,50 @@ func TestMatches(t *testing.T) {
},
expected: true,
},
{
name: "large community matches",
prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
bgpPath: &route.BGPPath{
LargeCommunities: "(1,2,0) (1,2,3)",
},
largeCommunityFilters: []*LargeCommunityFilter{
{
&packet.LargeCommunity{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
},
},
expected: true,
},
{
name: "large community does not match",
prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
bgpPath: &route.BGPPath{},
largeCommunityFilters: []*LargeCommunityFilter{
{
&packet.LargeCommunity{
GlobalAdministrator: 1,
DataPart1: 2,
DataPart2: 3,
},
},
},
expected: false,
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
f := NewTermCondition(
test.prefixLists,
test.routeFilters,
)
f := NewTermCondition(test.prefixLists, test.routeFilters)
f.largeCommunityFilters = test.largeCommunityFilters
pa := &route.Path{
BGPPath: test.bgpPath,
}
assert.Equal(te, test.expected, f.Matches(test.prefix, &route.Path{}))
assert.Equal(te, test.expected, f.Matches(test.prefix, pa))
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment