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

Merge pull request #37 from bio-routing/feature/community_support

Feature/community support
parents daa5286b c8db0cbf
No related branches found
No related tags found
No related merge requests found
......@@ -11,6 +11,7 @@ const (
MinLen = 19
MaxLen = 4096
NLRIMaxLen = 5
CommunityLen = 4
LargeCommunityLen = 12
OpenMsg = 1
......@@ -56,17 +57,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
LargeCommunityAttr = 32
OriginAttr = 1
ASPathAttr = 2
NextHopAttr = 3
MEDAttr = 4
LocalPrefAttr = 5
AtomicAggrAttr = 6
AggregatorAttr = 7
CommunitiesAttr = 8
AS4PathAttr = 17
AS4AggregatorAttr = 18
LargeCommunitiesAttr = 32
// ORIGIN values
IGP = 0
......
package packet
import (
"fmt"
"strconv"
"strings"
)
func CommunityStringForUint32(v uint32) string {
e1 := v >> 16
e2 := v - e1<<16
return fmt.Sprintf("(%d,%d)", e1, e2)
}
func ParseCommunityString(s string) (uint32, error) {
s = strings.Trim(s, "()")
t := strings.Split(s, ",")
if len(t) != 2 {
return 0, fmt.Errorf("can not parse community %s", s)
}
v, err := strconv.ParseUint(t[0], 10, 16)
if err != nil {
return 0, err
}
e1 := uint32(v)
v, err = strconv.ParseUint(t[1], 10, 16)
if err != nil {
return 0, err
}
e2 := uint32(v)
return e1<<16 + e2, nil
}
package packet
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommunityStringFromUint32(t *testing.T) {
tests := []struct {
name string
value uint32
expected string
}{
{
name: "both elements",
value: 131080,
expected: "(2,8)",
},
{
name: "right element only",
value: 250,
expected: "(0,250)",
},
{
name: "left element only",
value: 131072,
expected: "(2,0)",
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
assert.Equal(te, test.expected, CommunityStringForUint32(test.value))
})
}
}
func TestParseCommunityString(t *testing.T) {
tests := []struct {
name string
value string
expected uint32
wantFail bool
}{
{
name: "both elements",
expected: 131080,
value: "(2,8)",
},
{
name: "right element only",
expected: 250,
value: "(0,250)",
},
{
name: "left element only",
expected: 131072,
value: "(2,0)",
},
{
name: "too big",
value: "(131072,256)",
wantFail: true,
},
{
name: "empty string",
value: "",
wantFail: true,
},
{
name: "random string",
value: "foo-bar",
wantFail: true,
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
c, err := ParseCommunityString(test.value)
if test.wantFail {
if err == nil {
te.Fatal("test was expected to fail, but did not")
}
return
}
assert.Equal(te, test.expected, c)
})
}
}
......@@ -16,7 +16,7 @@ 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) {
func ParseLargeCommunityString(s string) (com LargeCommunity, err error) {
s = strings.Trim(s, "()")
t := strings.Split(s, ",")
......
......@@ -96,7 +96,7 @@ 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:
case LargeCommunitiesAttr:
if err := pa.decodeLargeCommunities(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode large communities: %v", err)
}
......@@ -218,30 +218,31 @@ func (pa *PathAttribute) decodeAggregator(buf *bytes.Buffer) error {
}
func (pa *PathAttribute) decodeCommunities(buf *bytes.Buffer) error {
if pa.Length%4 != 0 {
return fmt.Errorf("Unable to read community path attribute length %d is not divisible by 4", pa.Length)
}
comNumber := pa.Length / 4
var com = make([]uint32, comNumber)
for i := uint16(0); i < comNumber; i++ {
c := [4]byte{}
n, err := buf.Read(c[:])
if pa.Length%CommunityLen != 0 {
return fmt.Errorf("Unable to read community path attribute. Length %d is not divisible by 4", pa.Length)
}
count := pa.Length / CommunityLen
coms := make([]uint32, count)
for i := uint16(0); i < count; i++ {
v, err := read4BytesAsUint32(buf)
if err != nil {
return err
}
if n != 4 {
return fmt.Errorf("Unable to read next hop: buf.Read read %d bytes", n)
}
com[i] = fourBytesToUint32(c)
coms[i] = v
}
pa.Value = com
pa.Value = coms
return nil
}
func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error {
length := pa.Length
count := length / LargeCommunityLen
if pa.Length%LargeCommunityLen != 0 {
return fmt.Errorf("Unable to read large community path attribute. Length %d is not divisible by 12", pa.Length)
}
count := pa.Length / LargeCommunityLen
coms := make([]LargeCommunity, count)
for i := uint16(0); i < count; i++ {
......@@ -269,9 +270,7 @@ func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error {
}
pa.Value = coms
dump := pa.Length - (count * LargeCommunityLen)
return dumpNBytes(buf, dump)
return nil
}
func (pa *PathAttribute) decodeAS4Path(buf *bytes.Buffer) error {
......@@ -352,6 +351,15 @@ func (pa *PathAttribute) ASPathLen() (ret uint16) {
return
}
func (a *PathAttribute) CommunityString() string {
s := ""
for _, com := range a.Value.([]uint32) {
s += CommunityStringForUint32(com) + " "
}
return strings.TrimRight(s, " ")
}
func (a *PathAttribute) LargeCommunityString() string {
s := ""
for _, com := range a.Value.([]LargeCommunity) {
......@@ -393,7 +401,9 @@ func (pa *PathAttribute) serialize(buf *bytes.Buffer) uint8 {
pathAttrLen = pa.serializeAtomicAggregate(buf)
case AggregatorAttr:
pathAttrLen = pa.serializeAggregator(buf)
case LargeCommunityAttr:
case CommunitiesAttr:
pathAttrLen = pa.serializeCommunities(buf)
case LargeCommunitiesAttr:
pathAttrLen = pa.serializeLargeCommunities(buf)
}
......@@ -490,6 +500,30 @@ func (pa *PathAttribute) serializeAggregator(buf *bytes.Buffer) uint8 {
return 5
}
func (pa *PathAttribute) serializeCommunities(buf *bytes.Buffer) uint8 {
coms := pa.Value.([]uint32)
if len(coms) == 0 {
return 0
}
attrFlags := uint8(0)
attrFlags = setOptional(attrFlags)
attrFlags = setTransitive(attrFlags)
attrFlags = setPartial(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(CommunitiesAttr)
length := uint8(CommunityLen * len(coms))
buf.WriteByte(length)
for _, com := range coms {
buf.Write(convert.Uint32Byte(com))
}
return length
}
func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
coms := pa.Value.([]LargeCommunity)
if len(coms) == 0 {
......@@ -501,7 +535,7 @@ func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
attrFlags = setTransitive(attrFlags)
attrFlags = setPartial(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(LargeCommunityAttr)
buf.WriteByte(LargeCommunitiesAttr)
length := uint8(LargeCommunityLen * len(coms))
......@@ -605,6 +639,24 @@ 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 = ParseLargeCommunityString(str)
if err != nil {
return nil, err
}
}
return &PathAttribute{
TypeCode: LargeCommunitiesAttr,
Value: coms,
}, nil
}
func CommunityAttributeForString(s string) (*PathAttribute, error) {
strs := strings.Split(s, " ")
coms := make([]uint32, len(strs))
var err error
for i, str := range strs {
coms[i], err = ParseCommunityString(str)
......@@ -614,7 +666,7 @@ func LargeCommunityAttributeForString(s string) (*PathAttribute, error) {
}
return &PathAttribute{
TypeCode: LargeCommunityAttr,
TypeCode: CommunitiesAttr,
Value: coms,
}, nil
}
......
......@@ -657,6 +657,65 @@ func TestDecodeLargeCommunity(t *testing.T) {
}
}
func TestDecodeCommunity(t *testing.T) {
tests := []struct {
name string
input []byte
wantFail bool
explicitLength uint16
expected *PathAttribute
}{
{
name: "two valid communities",
input: []byte{
0, 2, 0, 8, 1, 0, 4, 1, // (2,8), (256,1025)
},
wantFail: false,
expected: &PathAttribute{
Length: 8,
Value: []uint32{
131080, 16778241,
},
},
},
{
name: "Empty input",
input: []byte{},
wantFail: false,
expected: &PathAttribute{
Length: 0,
Value: []uint32{},
},
},
}
for _, test := range tests {
l := uint16(len(test.input))
if test.explicitLength != 0 {
l = test.explicitLength
}
pa := &PathAttribute{
Length: l,
}
err := pa.decodeCommunities(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
......@@ -858,6 +917,31 @@ func TestLargeCommunityString(t *testing.T) {
}
}
func TestCommunityString(t *testing.T) {
tests := []struct {
name string
pa *PathAttribute
expected string
}{
{
name: "two attributes",
pa: &PathAttribute{
Value: []uint32{
131080, 16778241,
},
},
expected: "(2,8) (256,1025)",
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
res := test.pa.CommunityString()
assert.Equal(te, test.expected, res)
})
}
}
func TestSetOptional(t *testing.T) {
tests := []struct {
name string
......@@ -1205,7 +1289,7 @@ func TestSerializeLargeCommunities(t *testing.T) {
{
name: "2 large communities",
input: &PathAttribute{
TypeCode: LargeCommunityAttr,
TypeCode: LargeCommunitiesAttr,
Value: []LargeCommunity{
{
GlobalAdministrator: 1,
......@@ -1230,7 +1314,7 @@ func TestSerializeLargeCommunities(t *testing.T) {
{
name: "empty list of communities",
input: &PathAttribute{
TypeCode: LargeCommunityAttr,
TypeCode: LargeCommunitiesAttr,
Value: []LargeCommunity{},
},
expected: []byte{},
......@@ -1251,6 +1335,53 @@ func TestSerializeLargeCommunities(t *testing.T) {
}
}
func TestSerializeCommunities(t *testing.T) {
tests := []struct {
name string
input *PathAttribute
expected []byte
expectedLen uint8
}{
{
name: "2 communities",
input: &PathAttribute{
TypeCode: LargeCommunitiesAttr,
Value: []uint32{
131080, 16778241,
},
},
expected: []byte{
0xe0, // Attribute flags
8, // Type
8, // Length
0, 2, 0, 8, 1, 0, 4, 1, // Communities (2,8), (256,1025)
},
expectedLen: 8,
},
{
name: "empty list of communities",
input: &PathAttribute{
TypeCode: CommunitiesAttr,
Value: []uint32{},
},
expected: []byte{},
expectedLen: 0,
},
}
for _, test := range tests {
t.Run(test.name, func(te *testing.T) {
buf := bytes.NewBuffer([]byte{})
n := test.input.serializeCommunities(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
......
......@@ -845,7 +845,9 @@ func (fsm *FSM) established() int {
case packet.ASPathAttr:
path.BGPPath.ASPath = pa.ASPathString()
path.BGPPath.ASPathLen = pa.ASPathLen()
case packet.LargeCommunityAttr:
case packet.CommunitiesAttr:
path.BGPPath.Communities = pa.CommunityString()
case packet.LargeCommunitiesAttr:
path.BGPPath.LargeCommunities = pa.LargeCommunityString()
}
}
......
......@@ -46,10 +46,20 @@ func pathAttribues(p *route.Path, fsm *FSM) (*packet.PathAttribute, error) {
func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error {
current := parent
if len(p.BGPPath.Communities) > 0 {
communities, err := packet.CommunityAttributeForString(p.BGPPath.Communities)
if err != nil {
return fmt.Errorf("Could not create communities attribute: %v", err)
}
current.Next = communities
current = communities
}
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)
return fmt.Errorf("Could not create large communities attribute: %v", err)
}
current.Next = largeCommunities
......
......@@ -20,6 +20,7 @@ type BGPPath struct {
EBGP bool
BGPIdentifier uint32
Source uint32
Communities string
LargeCommunities string
}
......
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