diff --git a/.gitignore b/.gitignore index 4732630cb517f763bfc35f781811cf2176e72c2a..bb99a56915e4f0f56746244dbb3affd0ee20712a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ # IDE specific files .idea/ *.iml +.vscode # 'go build' binary bio-rd diff --git a/protocols/bgp/packet/BUILD.bazel b/protocols/bgp/packet/BUILD.bazel index 49f16b7773345b729ff25a1a486a86b1669992ed..3a9b5fd463ba33831be932734b27a8c845e8069d 100644 --- a/protocols/bgp/packet/BUILD.bazel +++ b/protocols/bgp/packet/BUILD.bazel @@ -3,37 +3,36 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ - "as_path.go", "bgp.go", - "community.go", "decoder.go", "encoder.go", - "large_community.go", "nlri.go", - "options.go", "parameters.go", "path_attribute_flags.go", "path_attributes.go", + "update.go", ], importpath = "github.com/bio-routing/bio-rd/protocols/bgp/packet", visibility = ["//visibility:public"], - deps = ["//vendor/github.com/taktv6/tflow2/convert:go_default_library"], + deps = [ + "//protocols/bgp/types:go_default_library", + "//route:go_default_library", + "//vendor/github.com/taktv6/tflow2/convert:go_default_library", + ], ) go_test( name = "go_default_test", srcs = [ - "community_test.go", "decoder_test.go", "encoder_test.go", - "large_community_benchmark_test.go", - "large_community_test.go", "nlri_test.go", "path_attributes_test.go", ], embed = [":go_default_library"], deps = [ "//net:go_default_library", + "//protocols/bgp/types:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/taktv6/tflow2/convert:go_default_library", ], diff --git a/protocols/bgp/packet/as_path.go b/protocols/bgp/packet/as_path.go deleted file mode 100644 index 0cdfc96a1b6f418013ae6a6656f6cc8ecf554686..0000000000000000000000000000000000000000 --- a/protocols/bgp/packet/as_path.go +++ /dev/null @@ -1,44 +0,0 @@ -package packet - -import "fmt" - -type ASPath []ASPathSegment - -type ASPathSegment struct { - Type uint8 - Count uint8 - ASNs []uint32 -} - -func (pa ASPath) String() (ret string) { - for _, p := range pa { - if p.Type == ASSet { - ret += " (" - } - n := len(p.ASNs) - for i, asn := range p.ASNs { - if i < n-1 { - ret += fmt.Sprintf("%d ", asn) - continue - } - ret += fmt.Sprintf("%d", asn) - } - if p.Type == ASSet { - ret += ")" - } - } - - return -} - -func (pa ASPath) Length() (ret uint16) { - for _, p := range pa { - if p.Type == ASSet { - ret++ - continue - } - ret += uint16(len(p.ASNs)) - } - - return -} diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go index 8aabec7f7c729cd23d47ed6abe517742f2583708..1859f665c0a96904c5a5cd2c4e7d4d7dfe640c08 100644 --- a/protocols/bgp/packet/bgp.go +++ b/protocols/bgp/packet/bgp.go @@ -74,10 +74,6 @@ const ( EGP = 1 INCOMPLETE = 2 - // ASPath Segment Types - ASSet = 1 - ASSequence = 2 - // NOTIFICATION Cease error SubCodes (RFC4486) MaxPrefReached = 1 AdminShut = 2 @@ -133,22 +129,6 @@ type BGPNotification struct { ErrorSubcode uint8 } -type BGPUpdate struct { - WithdrawnRoutesLen uint16 - WithdrawnRoutes *NLRI - TotalPathAttrLen uint16 - PathAttributes *PathAttribute - NLRI *NLRI -} - -type BGPUpdateAddPath struct { - WithdrawnRoutesLen uint16 - WithdrawnRoutes *NLRIAddPath - TotalPathAttrLen uint16 - PathAttributes *PathAttribute - NLRI *NLRIAddPath -} - type PathAttribute struct { Length uint16 Optional bool @@ -160,19 +140,6 @@ type PathAttribute struct { Next *PathAttribute } -type NLRI struct { - IP uint32 - Pfxlen uint8 - Next *NLRI -} - -type NLRIAddPath struct { - PathIdentifier uint32 - IP uint32 - Pfxlen uint8 - Next *NLRIAddPath -} - type Aggretator struct { Addr uint32 ASN uint16 diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go index c4d583f2138edfbbfb5b9be33b2a1c11764ee0bd..ca717c781194adf20d2d5c08f332c03f060be849 100644 --- a/protocols/bgp/packet/decoder.go +++ b/protocols/bgp/packet/decoder.go @@ -6,11 +6,12 @@ import ( "fmt" "net" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/taktv6/tflow2/convert" ) // Decode decodes a BGP message -func Decode(buf *bytes.Buffer, opt *Options) (*BGPMessage, error) { +func Decode(buf *bytes.Buffer, opt *types.Options) (*BGPMessage, error) { hdr, err := decodeHeader(buf) if err != nil { return nil, fmt.Errorf("Failed to decode header: %v", err) @@ -27,7 +28,7 @@ func Decode(buf *bytes.Buffer, opt *Options) (*BGPMessage, error) { }, nil } -func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *Options) (interface{}, error) { +func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *types.Options) (interface{}, error) { switch msgType { case OpenMsg: return decodeOpenMsg(buf) @@ -41,7 +42,7 @@ func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *Options) (in return nil, fmt.Errorf("Unknown message type: %d", msgType) } -func decodeUpdateMsg(buf *bytes.Buffer, l uint16, opt *Options) (*BGPUpdate, error) { +func decodeUpdateMsg(buf *bytes.Buffer, l uint16, opt *types.Options) (*BGPUpdate, error) { msg := &BGPUpdate{} err := decode(buf, []interface{}{&msg.WithdrawnRoutesLen}) diff --git a/protocols/bgp/packet/decoder_test.go b/protocols/bgp/packet/decoder_test.go index 56416186ba1d714a13fb4f248281d3d5f48451dd..64c5cf0426596a04ce463fdfc38b7fb4d8511b87 100644 --- a/protocols/bgp/packet/decoder_test.go +++ b/protocols/bgp/packet/decoder_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/stretchr/testify/assert" "github.com/taktv6/tflow2/convert" ) @@ -71,7 +72,7 @@ func BenchmarkDecodeUpdateMsg(b *testing.B) { for i := 0; i < b.N; i++ { buf := bytes.NewBuffer(input) - _, err := decodeUpdateMsg(buf, uint16(len(input)), &Options{}) + _, err := decodeUpdateMsg(buf, uint16(len(input)), &types.Options{}) if err != nil { fmt.Printf("decodeUpdateMsg failed: %v\n", err) } @@ -252,7 +253,7 @@ func TestDecode(t *testing.T) { for _, test := range tests { buf := bytes.NewBuffer(test.input) - msg, err := Decode(buf, &Options{}) + msg, err := Decode(buf, &types.Options{}) if err != nil && !test.wantFail { t.Errorf("Unexpected error in test %d: %v", test.testNum, err) @@ -519,10 +520,9 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 6, TypeCode: 2, - Value: ASPath{ + Value: types.ASPath{ { - Type: 2, - Count: 2, + Type: 2, ASNs: []uint32{ 15169, 3320, @@ -623,18 +623,16 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 12, TypeCode: 2, - Value: ASPath{ + Value: types.ASPath{ { - Type: 2, - Count: 2, + Type: 2, ASNs: []uint32{ 15169, 3320, }, }, { - Type: 1, - Count: 2, + Type: 1, ASNs: []uint32{ 15169, 3320, @@ -702,18 +700,16 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 12, TypeCode: 2, - Value: ASPath{ + Value: types.ASPath{ { - Type: 2, - Count: 2, + Type: 2, ASNs: []uint32{ 15169, 3320, }, }, { - Type: 1, - Count: 2, + Type: 1, ASNs: []uint32{ 15169, 3320, @@ -794,18 +790,16 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 12, TypeCode: 2, - Value: ASPath{ + Value: types.ASPath{ { - Type: 2, - Count: 2, + Type: 2, ASNs: []uint32{ 15169, 3320, }, }, { - Type: 1, - Count: 2, + Type: 1, ASNs: []uint32{ 15169, 3320, @@ -899,18 +893,16 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 12, TypeCode: 2, - Value: ASPath{ + Value: types.ASPath{ { - Type: 2, - Count: 2, + Type: 2, ASNs: []uint32{ 15169, 3320, }, }, { - Type: 1, - Count: 2, + Type: 1, ASNs: []uint32{ 15169, 3320, @@ -1017,18 +1009,16 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 12, TypeCode: 2, - Value: ASPath{ + Value: types.ASPath{ { - Type: 2, - Count: 2, + Type: 2, ASNs: []uint32{ 15169, 3320, }, }, { - Type: 1, - Count: 2, + Type: 1, ASNs: []uint32{ 15169, 3320, @@ -1151,18 +1141,16 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 12, TypeCode: 2, - Value: ASPath{ + Value: types.ASPath{ { - Type: 2, - Count: 2, + Type: 2, ASNs: []uint32{ 15169, 3320, }, }, { - Type: 1, - Count: 2, + Type: 1, ASNs: []uint32{ 15169, 3320, @@ -1425,11 +1413,10 @@ func TestDecodeUpdateMsg(t *testing.T) { ExtendedLength: false, Length: 6, TypeCode: 17, - Value: ASPath{ - ASPathSegment{ - Type: 2, - Count: 1, - ASNs: []uint32{202739}, + Value: types.ASPath{ + types.ASPathSegment{ + Type: 2, + ASNs: []uint32{202739}, }, }, Next: &PathAttribute{ @@ -1465,7 +1452,7 @@ func TestDecodeUpdateMsg(t *testing.T) { if l == 0 { l = uint16(len(test.input)) } - msg, err := decodeUpdateMsg(buf, l, &Options{}) + msg, err := decodeUpdateMsg(buf, l, &types.Options{}) if err != nil && !test.wantFail { t.Fatalf("Unexpected error in test %d: %v", test.testNum, err) @@ -1501,7 +1488,7 @@ func TestDecodeMsgBody(t *testing.T) { } for _, test := range tests { - res, err := decodeMsgBody(test.buffer, test.msgType, test.length, &Options{}) + res, err := decodeMsgBody(test.buffer, test.msgType, test.length, &types.Options{}) if test.wantFail && err == nil { t.Errorf("Expected error dit not happen in test %q", test.name) } diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go index ac9bc4483199b81f4e8ecc25c6b91e3ec8361b9c..122bca972219f246346b263f666554a13f035b04 100644 --- a/protocols/bgp/packet/encoder.go +++ b/protocols/bgp/packet/encoder.go @@ -2,7 +2,6 @@ package packet import ( "bytes" - "fmt" "github.com/taktv6/tflow2/convert" ) @@ -62,121 +61,3 @@ func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) { buf.Write(convert.Uint16Byte(length)) buf.WriteByte(typ) } - -func (b *BGPUpdateAddPath) SerializeUpdate(opt *Options) ([]byte, error) { - budget := MaxLen - MinLen - buf := bytes.NewBuffer(nil) - - withdrawBuf := bytes.NewBuffer(nil) - for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { - nlriLen := int(withdraw.serialize(withdrawBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - pathAttributesBuf := bytes.NewBuffer(nil) - for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf, opt)) - budget -= paLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - nlriBuf := bytes.NewBuffer(nil) - for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { - nlriLen := int(nlri.serialize(nlriBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - withdrawnRoutesLen := withdrawBuf.Len() - if withdrawnRoutesLen > 65535 { - return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) - } - - totalPathAttributesLen := pathAttributesBuf.Len() - if totalPathAttributesLen > 65535 { - return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) - } - - totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 - if totalLength > 4096 { - return nil, fmt.Errorf("Update too long: %d bytes", totalLength) - } - - serializeHeader(buf, uint16(totalLength), UpdateMsg) - - buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) - buf.Write(withdrawBuf.Bytes()) - - buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) - buf.Write(pathAttributesBuf.Bytes()) - - buf.Write(nlriBuf.Bytes()) - - return buf.Bytes(), nil -} - -func (b *BGPUpdate) SerializeUpdate(opt *Options) ([]byte, error) { - budget := MaxLen - MinLen - buf := bytes.NewBuffer(nil) - - withdrawBuf := bytes.NewBuffer(nil) - for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { - nlriLen := int(withdraw.serialize(withdrawBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - pathAttributesBuf := bytes.NewBuffer(nil) - for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf, opt)) - budget -= paLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - nlriBuf := bytes.NewBuffer(nil) - for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { - nlriLen := int(nlri.serialize(nlriBuf)) - budget -= nlriLen - if budget < 0 { - return nil, fmt.Errorf("update too long") - } - } - - withdrawnRoutesLen := withdrawBuf.Len() - if withdrawnRoutesLen > 65535 { - return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) - } - - totalPathAttributesLen := pathAttributesBuf.Len() - if totalPathAttributesLen > 65535 { - return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) - } - - totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 - if totalLength > 4096 { - return nil, fmt.Errorf("Update too long: %d bytes", totalLength) - } - - serializeHeader(buf, uint16(totalLength), UpdateMsg) - - buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) - buf.Write(withdrawBuf.Bytes()) - - buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) - buf.Write(pathAttributesBuf.Bytes()) - - buf.Write(nlriBuf.Bytes()) - - return buf.Bytes(), nil -} diff --git a/protocols/bgp/packet/nlri.go b/protocols/bgp/packet/nlri.go index 6de92a6cd59ff167d87efbaf5d197fb46c00aaca..e4bd4cea7a7cf65a8ac5ad75ff17218a294cbfe5 100644 --- a/protocols/bgp/packet/nlri.go +++ b/protocols/bgp/packet/nlri.go @@ -9,6 +9,13 @@ import ( "github.com/taktv6/tflow2/convert" ) +type NLRI struct { + PathIdentifier uint32 + IP uint32 + Pfxlen uint8 + Next *NLRI +} + func decodeNLRIs(buf *bytes.Buffer, length uint16) (*NLRI, error) { var ret *NLRI var eol *NLRI @@ -65,7 +72,7 @@ func (n *NLRI) serialize(buf *bytes.Buffer) uint8 { a := convert.Uint32Byte(n.IP) addr := [4]byte{a[0], a[1], a[2], a[3]} - nBytes := bytesInAddr(n.Pfxlen) + nBytes := BytesInAddr(n.Pfxlen) buf.WriteByte(n.Pfxlen) buf.Write(addr[:nBytes]) @@ -73,19 +80,20 @@ func (n *NLRI) serialize(buf *bytes.Buffer) uint8 { return nBytes + 1 } -func (n *NLRIAddPath) serialize(buf *bytes.Buffer) uint8 { +func (n *NLRI) serializeAddPath(buf *bytes.Buffer) uint8 { a := convert.Uint32Byte(n.IP) addr := [4]byte{a[0], a[1], a[2], a[3]} - nBytes := bytesInAddr(n.Pfxlen) + nBytes := BytesInAddr(n.Pfxlen) buf.Write(convert.Uint32Byte(n.PathIdentifier)) buf.WriteByte(n.Pfxlen) buf.Write(addr[:nBytes]) - return nBytes + 1 + return nBytes + 4 } -func bytesInAddr(pfxlen uint8) uint8 { +// BytesInAddr gets the amount of bytes needed to encode an NLRI of prefix length pfxlen +func BytesInAddr(pfxlen uint8) uint8 { return uint8(math.Ceil(float64(pfxlen) / 8)) } diff --git a/protocols/bgp/packet/nlri_test.go b/protocols/bgp/packet/nlri_test.go index 17a17dd3bf5ae997fa4676de27a5db5a4376907b..4cefc5adb3a664564ac201a81afc5efaebe015a8 100644 --- a/protocols/bgp/packet/nlri_test.go +++ b/protocols/bgp/packet/nlri_test.go @@ -162,7 +162,7 @@ func TestBytesInAddr(t *testing.T) { } for _, test := range tests { - res := bytesInAddr(test.input) + res := BytesInAddr(test.input) if res != test.expected { t.Errorf("Unexpected result for test %q: %d", test.name, res) } @@ -212,12 +212,12 @@ func TestNLRISerialize(t *testing.T) { func TestNLRIAddPathSerialize(t *testing.T) { tests := []struct { name string - nlri *NLRIAddPath + nlri *NLRI expected []byte }{ { name: "Test #1", - nlri: &NLRIAddPath{ + nlri: &NLRI{ PathIdentifier: 100, IP: strAddr("1.2.3.0"), Pfxlen: 25, @@ -226,7 +226,7 @@ func TestNLRIAddPathSerialize(t *testing.T) { }, { name: "Test #2", - nlri: &NLRIAddPath{ + nlri: &NLRI{ PathIdentifier: 100, IP: strAddr("1.2.3.0"), Pfxlen: 24, @@ -235,7 +235,7 @@ func TestNLRIAddPathSerialize(t *testing.T) { }, { name: "Test #3", - nlri: &NLRIAddPath{ + nlri: &NLRI{ PathIdentifier: 100, IP: strAddr("100.200.128.0"), Pfxlen: 17, @@ -246,7 +246,7 @@ func TestNLRIAddPathSerialize(t *testing.T) { for _, test := range tests { buf := bytes.NewBuffer(nil) - test.nlri.serialize(buf) + test.nlri.serializeAddPath(buf) res := buf.Bytes() assert.Equal(t, test.expected, res) } diff --git a/protocols/bgp/packet/options.go b/protocols/bgp/packet/options.go deleted file mode 100644 index b2d35bc60cbe17490f94314c28da26de9a19e40a..0000000000000000000000000000000000000000 --- a/protocols/bgp/packet/options.go +++ /dev/null @@ -1,5 +0,0 @@ -package packet - -type Options struct { - Supports4OctetASN bool -} diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index 748e36535f049ddb31955e8e86078aef209641cd..000f0d8e95fe82ca67c8d57e9601dc090e58ca6f 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -4,10 +4,12 @@ import ( "bytes" "fmt" + "github.com/bio-routing/bio-rd/protocols/bgp/types" + "github.com/bio-routing/bio-rd/route" "github.com/taktv6/tflow2/convert" ) -func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *Options) (*PathAttribute, error) { +func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *types.Options) (*PathAttribute, error) { var ret *PathAttribute var eol *PathAttribute var pa *PathAttribute @@ -34,7 +36,7 @@ func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *Options) (*PathAttribu return ret, nil } -func decodePathAttr(buf *bytes.Buffer, opt *Options) (pa *PathAttribute, consumed uint16, err error) { +func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, consumed uint16, err error) { pa = &PathAttribute{} err = decodePathAttrFlags(buf, pa) @@ -143,27 +145,28 @@ func (pa *PathAttribute) decodeOrigin(buf *bytes.Buffer) error { } func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer, asnLength uint8) error { - pa.Value = make(ASPath, 0) + pa.Value = make(types.ASPath, 0) p := uint16(0) for p < pa.Length { - segment := ASPathSegment{} + segment := types.ASPathSegment{} + count := uint8(0) - err := decode(buf, []interface{}{&segment.Type, &segment.Count}) + err := decode(buf, []interface{}{&segment.Type, &count}) if err != nil { return err } p += 2 - if segment.Type != ASSet && segment.Type != ASSequence { + if segment.Type != types.ASSet && segment.Type != types.ASSequence { return fmt.Errorf("Invalid AS Path segment type: %d", segment.Type) } - if segment.Count == 0 { - return fmt.Errorf("Invalid AS Path segment length: %d", segment.Count) + if count == 0 { + return fmt.Errorf("Invalid AS Path segment length: %d", count) } - segment.ASNs = make([]uint32, segment.Count) - for i := uint8(0); i < segment.Count; i++ { + segment.ASNs = make([]uint32, count) + for i := uint8(0); i < count; i++ { asn, err := pa.decodeASN(buf, asnLength) if err != nil { return err @@ -173,7 +176,7 @@ func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer, asnLength uint8) error segment.ASNs[i] = asn } - pa.Value = append(pa.Value.(ASPath), segment) + pa.Value = append(pa.Value.(types.ASPath), segment) } return nil @@ -271,10 +274,10 @@ func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error { } count := pa.Length / LargeCommunityLen - coms := make([]LargeCommunity, count) + coms := make([]types.LargeCommunity, count) for i := uint16(0); i < count; i++ { - com := LargeCommunity{} + com := types.LargeCommunity{} v, err := read4BytesAsUint32(buf) if err != nil { @@ -342,6 +345,7 @@ func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) { return bytesRead, nil } +// Copy create a copy of a path attribute func (pa *PathAttribute) Copy() *PathAttribute { return &PathAttribute{ ExtendedLength: pa.ExtendedLength, @@ -368,28 +372,29 @@ func dumpNBytes(buf *bytes.Buffer, n uint16) error { return nil } -func (pa *PathAttribute) serialize(buf *bytes.Buffer, opt *Options) uint8 { - pathAttrLen := uint8(0) +// Serialize serializes a path attribute +func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *types.Options) uint16 { + pathAttrLen := uint16(0) switch pa.TypeCode { case OriginAttr: - pathAttrLen = pa.serializeOrigin(buf) + pathAttrLen = uint16(pa.serializeOrigin(buf)) case ASPathAttr: - pathAttrLen = pa.serializeASPath(buf, opt) + pathAttrLen = uint16(pa.serializeASPath(buf, opt)) case NextHopAttr: - pathAttrLen = pa.serializeNextHop(buf) + pathAttrLen = uint16(pa.serializeNextHop(buf)) case MEDAttr: - pathAttrLen = pa.serializeMED(buf) + pathAttrLen = uint16(pa.serializeMED(buf)) case LocalPrefAttr: - pathAttrLen = pa.serializeLocalpref(buf) + pathAttrLen = uint16(pa.serializeLocalpref(buf)) case AtomicAggrAttr: - pathAttrLen = pa.serializeAtomicAggregate(buf) + pathAttrLen = uint16(pa.serializeAtomicAggregate(buf)) case AggregatorAttr: - pathAttrLen = pa.serializeAggregator(buf) + pathAttrLen = uint16(pa.serializeAggregator(buf)) case CommunitiesAttr: - pathAttrLen = pa.serializeCommunities(buf) + pathAttrLen = uint16(pa.serializeCommunities(buf)) case LargeCommunitiesAttr: - pathAttrLen = pa.serializeLargeCommunities(buf) + pathAttrLen = uint16(pa.serializeLargeCommunities(buf)) default: pathAttrLen = pa.serializeUnknownAttribute(buf) } @@ -408,7 +413,7 @@ func (pa *PathAttribute) serializeOrigin(buf *bytes.Buffer) uint8 { return 4 } -func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *Options) uint8 { +func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *types.Options) uint8 { attrFlags := uint8(0) attrFlags = setTransitive(attrFlags) buf.WriteByte(attrFlags) @@ -421,7 +426,7 @@ func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *Options) uint8 length := uint8(0) segmentsBuf := bytes.NewBuffer(nil) - for _, segment := range pa.Value.(ASPath) { + for _, segment := range pa.Value.(types.ASPath) { segmentsBuf.WriteByte(segment.Type) segmentsBuf.WriteByte(uint8(len(segment.ASNs))) @@ -432,7 +437,6 @@ func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *Options) uint8 segmentsBuf.Write(convert.Uint32Byte(asn)) } } - fmt.Println(segment.ASNs) length += 2 + uint8(len(segment.ASNs))*asnLength } @@ -523,7 +527,7 @@ func (pa *PathAttribute) serializeCommunities(buf *bytes.Buffer) uint8 { } func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 { - coms := pa.Value.([]LargeCommunity) + coms := pa.Value.([]types.LargeCommunity) if len(coms) == 0 { return 0 } @@ -548,21 +552,30 @@ func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 { return length } -func (pa *PathAttribute) serializeUnknownAttribute(buf *bytes.Buffer) uint8 { +func (pa *PathAttribute) serializeUnknownAttribute(buf *bytes.Buffer) uint16 { attrFlags := uint8(0) if pa.Optional { attrFlags = setOptional(attrFlags) } + if pa.ExtendedLength { + attrFlags = setExtendedLength(attrFlags) + } attrFlags = setTransitive(attrFlags) buf.WriteByte(attrFlags) buf.WriteByte(pa.TypeCode) b := pa.Value.([]byte) - buf.WriteByte(uint8(len(b))) + if pa.ExtendedLength { + l := len(b) + buf.WriteByte(uint8(l >> 8)) + buf.WriteByte(uint8(l & 0x0000FFFF)) + } else { + buf.WriteByte(uint8(len(b))) + } buf.Write(b) - return uint8(len(b) + 2) + return uint16(len(b) + 2) } func fourBytesToUint32(address [4]byte) uint32 { @@ -581,3 +594,70 @@ func read4BytesAsUint32(buf *bytes.Buffer) (uint32, error) { return fourBytesToUint32(b), nil } + +// AddOptionalPathAttributes adds optional path attributes to linked list pa +func (pa *PathAttribute) AddOptionalPathAttributes(p *route.Path) *PathAttribute { + current := pa + + if len(p.BGPPath.Communities) > 0 { + communities := &PathAttribute{ + TypeCode: CommunitiesAttr, + Value: p.BGPPath.Communities, + } + current.Next = communities + current = communities + } + + if len(p.BGPPath.LargeCommunities) > 0 { + largeCommunities := &PathAttribute{ + TypeCode: LargeCommunitiesAttr, + Value: p.BGPPath.LargeCommunities, + } + current.Next = largeCommunities + current = largeCommunities + } + + return current +} + +// PathAttributes converts a path object into a linked list of path attributes +func PathAttributes(p *route.Path) (*PathAttribute, error) { + asPath := &PathAttribute{ + TypeCode: ASPathAttr, + Value: p.BGPPath.ASPath, + } + + origin := &PathAttribute{ + TypeCode: OriginAttr, + Value: p.BGPPath.Origin, + } + asPath.Next = origin + + nextHop := &PathAttribute{ + TypeCode: NextHopAttr, + Value: p.BGPPath.NextHop, + } + origin.Next = nextHop + + localPref := &PathAttribute{ + TypeCode: LocalPrefAttr, + Value: p.BGPPath.LocalPref, + } + nextHop.Next = localPref + + optionals := localPref.AddOptionalPathAttributes(p) + + last := optionals + for _, unknownAttr := range p.BGPPath.UnknownAttributes { + last.Next = &PathAttribute{ + TypeCode: unknownAttr.TypeCode, + Optional: unknownAttr.Optional, + Transitive: unknownAttr.Transitive, + Partial: unknownAttr.Partial, + Value: unknownAttr.Value, + } + last = last.Next + } + + return asPath, nil +} diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index fbf1d4806e127b1b2e4b15255a11e018bd00bbaf..93a0b2e515f0dfb9f0a0dde543085a37fb3c52c3 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -4,6 +4,7 @@ import ( "bytes" "testing" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/stretchr/testify/assert" ) @@ -50,7 +51,7 @@ func TestDecodePathAttrs(t *testing.T) { } for _, test := range tests { - res, err := decodePathAttrs(bytes.NewBuffer(test.input), uint16(len(test.input)), &Options{}) + res, err := decodePathAttrs(bytes.NewBuffer(test.input), uint16(len(test.input)), &types.Options{}) if test.wantFail && err == nil { t.Errorf("Expected error did not happen for test %q", test.name) @@ -173,7 +174,7 @@ func TestDecodePathAttr(t *testing.T) { } for _, test := range tests { - res, _, err := decodePathAttr(bytes.NewBuffer(test.input), &Options{}) + res, _, err := decodePathAttr(bytes.NewBuffer(test.input), &types.Options{}) if test.wantFail && err == nil { t.Errorf("Expected error did not happen for test %q", test.name) @@ -277,10 +278,9 @@ func TestDecodeASPath(t *testing.T) { wantFail: false, expected: &PathAttribute{ Length: 10, - Value: ASPath{ - ASPathSegment{ - Type: 2, - Count: 4, + Value: types.ASPath{ + types.ASPathSegment{ + Type: 2, ASNs: []uint32{ 100, 200, 222, 240, }, @@ -298,10 +298,9 @@ func TestDecodeASPath(t *testing.T) { wantFail: false, expected: &PathAttribute{ Length: 8, - Value: ASPath{ - ASPathSegment{ - Type: 1, - Count: 3, + Value: types.ASPath{ + types.ASPathSegment{ + Type: 1, ASNs: []uint32{ 100, 222, 240, }, @@ -320,10 +319,9 @@ func TestDecodeASPath(t *testing.T) { use4OctetASNs: true, expected: &PathAttribute{ Length: 14, - Value: ASPath{ - ASPathSegment{ - Type: 1, - Count: 3, + Value: types.ASPath{ + types.ASPathSegment{ + Type: 1, ASNs: []uint32{ 100, 222, 240, }, @@ -636,7 +634,7 @@ func TestDecodeLargeCommunity(t *testing.T) { wantFail: false, expected: &PathAttribute{ Length: 24, - Value: []LargeCommunity{ + Value: []types.LargeCommunity{ { GlobalAdministrator: 1, DataPart1: 2, @@ -656,7 +654,7 @@ func TestDecodeLargeCommunity(t *testing.T) { wantFail: false, expected: &PathAttribute{ Length: 0, - Value: []LargeCommunity{}, + Value: []types.LargeCommunity{}, }, }, } @@ -1175,7 +1173,7 @@ func TestSerializeASPath(t *testing.T) { name: "Test #1", input: &PathAttribute{ TypeCode: ASPathAttr, - Value: ASPath{ + Value: types.ASPath{ { Type: 2, // Sequence ASNs: []uint32{ @@ -1200,7 +1198,7 @@ func TestSerializeASPath(t *testing.T) { name: "32bit ASN", input: &PathAttribute{ TypeCode: ASPathAttr, - Value: ASPath{ + Value: types.ASPath{ { Type: 2, // Sequence ASNs: []uint32{ @@ -1229,7 +1227,7 @@ func TestSerializeASPath(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { buf := bytes.NewBuffer(nil) - opt := &Options{ + opt := &types.Options{ Supports4OctetASN: test.use32BitASN, } n := test.input.serializeASPath(buf, opt) @@ -1253,7 +1251,7 @@ func TestSerializeLargeCommunities(t *testing.T) { name: "2 large communities", input: &PathAttribute{ TypeCode: LargeCommunitiesAttr, - Value: []LargeCommunity{ + Value: []types.LargeCommunity{ { GlobalAdministrator: 1, DataPart1: 2, @@ -1278,7 +1276,7 @@ func TestSerializeLargeCommunities(t *testing.T) { name: "empty list of communities", input: &PathAttribute{ TypeCode: LargeCommunitiesAttr, - Value: []LargeCommunity{}, + Value: []types.LargeCommunity{}, }, expected: []byte{}, expectedLen: 0, @@ -1350,7 +1348,7 @@ func TestSerializeUnknownAttribute(t *testing.T) { name string input *PathAttribute expected []byte - expectedLen uint8 + expectedLen uint16 }{ { name: "Arbritary attribute", @@ -1367,6 +1365,36 @@ func TestSerializeUnknownAttribute(t *testing.T) { }, expectedLen: 6, }, + { + name: "Extended length", + input: &PathAttribute{ + TypeCode: 200, + Value: make([]byte, 256), + Transitive: true, + ExtendedLength: true, + }, + expected: []byte{ + 80, // Attribute flags + 200, // Type + 1, 0, // Length + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Payload + }, + expectedLen: 258, + }, } for _, test := range tests { @@ -1459,7 +1487,7 @@ func TestSerialize(t *testing.T) { Value: uint8(0), Next: &PathAttribute{ TypeCode: ASPathAttr, - Value: ASPath{ + Value: types.ASPath{ { Type: 2, ASNs: []uint32{100, 155, 200}, @@ -1559,7 +1587,9 @@ func TestSerialize(t *testing.T) { } for _, test := range tests { - opt := &Options{} + opt := &types.Options{ + AddPathRX: false, + } res, err := test.msg.SerializeUpdate(opt) if err != nil { if test.wantFail { @@ -1582,14 +1612,14 @@ func TestSerialize(t *testing.T) { func TestSerializeAddPath(t *testing.T) { tests := []struct { name string - msg *BGPUpdateAddPath + msg *BGPUpdate expected []byte wantFail bool }{ { name: "Withdraw only", - msg: &BGPUpdateAddPath{ - WithdrawnRoutes: &NLRIAddPath{ + msg: &BGPUpdate{ + WithdrawnRoutes: &NLRI{ PathIdentifier: 257, IP: strAddr("100.110.120.0"), Pfxlen: 24, @@ -1607,8 +1637,8 @@ func TestSerializeAddPath(t *testing.T) { }, { name: "NLRI only", - msg: &BGPUpdateAddPath{ - NLRI: &NLRIAddPath{ + msg: &BGPUpdate{ + NLRI: &NLRI{ PathIdentifier: 257, IP: strAddr("100.110.128.0"), Pfxlen: 17, @@ -1626,7 +1656,7 @@ func TestSerializeAddPath(t *testing.T) { }, { name: "Path Attributes only", - msg: &BGPUpdateAddPath{ + msg: &BGPUpdate{ PathAttributes: &PathAttribute{ Optional: true, Transitive: true, @@ -1648,11 +1678,11 @@ func TestSerializeAddPath(t *testing.T) { }, { name: "Full test", - msg: &BGPUpdateAddPath{ - WithdrawnRoutes: &NLRIAddPath{ + msg: &BGPUpdate{ + WithdrawnRoutes: &NLRI{ IP: strAddr("10.0.0.0"), Pfxlen: 8, - Next: &NLRIAddPath{ + Next: &NLRI{ IP: strAddr("192.168.0.0"), Pfxlen: 16, }, @@ -1662,7 +1692,7 @@ func TestSerializeAddPath(t *testing.T) { Value: uint8(0), Next: &PathAttribute{ TypeCode: ASPathAttr, - Value: ASPath{ + Value: types.ASPath{ { Type: 2, ASNs: []uint32{100, 155, 200}, @@ -1693,10 +1723,10 @@ func TestSerializeAddPath(t *testing.T) { }, }, }, - NLRI: &NLRIAddPath{ + NLRI: &NLRI{ IP: strAddr("8.8.8.0"), Pfxlen: 24, - Next: &NLRIAddPath{ + Next: &NLRI{ IP: strAddr("185.65.240.0"), Pfxlen: 22, }, @@ -1766,7 +1796,9 @@ func TestSerializeAddPath(t *testing.T) { } for _, test := range tests { - opt := &Options{} + opt := &types.Options{ + AddPathRX: true, + } res, err := test.msg.SerializeUpdate(opt) if err != nil { if test.wantFail { diff --git a/protocols/bgp/packet/update.go b/protocols/bgp/packet/update.go new file mode 100644 index 0000000000000000000000000000000000000000..55b0cc0b9099076edbaef35fcfab1cfc83443330 --- /dev/null +++ b/protocols/bgp/packet/update.go @@ -0,0 +1,147 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/protocols/bgp/types" + "github.com/taktv6/tflow2/convert" +) + +type BGPUpdate struct { + WithdrawnRoutesLen uint16 + WithdrawnRoutes *NLRI + TotalPathAttrLen uint16 + PathAttributes *PathAttribute + NLRI *NLRI +} + +// SerializeUpdate serializes an BGPUpdate to wire format +func (b *BGPUpdate) SerializeUpdate(opt *types.Options) ([]byte, error) { + budget := MaxLen - MinLen + nlriLen := 0 + buf := bytes.NewBuffer(nil) + + withdrawBuf := bytes.NewBuffer(nil) + for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { + if opt.AddPathRX { + nlriLen = int(withdraw.serializeAddPath(withdrawBuf)) + } else { + nlriLen = int(withdraw.serialize(withdrawBuf)) + } + + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + pathAttributesBuf := bytes.NewBuffer(nil) + for pa := b.PathAttributes; pa != nil; pa = pa.Next { + paLen := int(pa.Serialize(pathAttributesBuf, opt)) + budget -= paLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + nlriBuf := bytes.NewBuffer(nil) + for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { + if opt.AddPathRX { + nlriLen = int(nlri.serializeAddPath(nlriBuf)) + } else { + nlriLen = int(nlri.serialize(nlriBuf)) + } + + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + withdrawnRoutesLen := withdrawBuf.Len() + if withdrawnRoutesLen > 65535 { + return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) + } + + totalPathAttributesLen := pathAttributesBuf.Len() + if totalPathAttributesLen > 65535 { + return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) + } + + totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 + if totalLength > 4096 { + return nil, fmt.Errorf("Update too long: %d bytes", totalLength) + } + + serializeHeader(buf, uint16(totalLength), UpdateMsg) + + buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) + buf.Write(withdrawBuf.Bytes()) + + buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) + buf.Write(pathAttributesBuf.Bytes()) + + buf.Write(nlriBuf.Bytes()) + + return buf.Bytes(), nil +} + +func (b *BGPUpdate) SerializeUpdateAddPath(opt *types.Options) ([]byte, error) { + budget := MaxLen - MinLen + buf := bytes.NewBuffer(nil) + + withdrawBuf := bytes.NewBuffer(nil) + for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next { + nlriLen := int(withdraw.serialize(withdrawBuf)) + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + pathAttributesBuf := bytes.NewBuffer(nil) + for pa := b.PathAttributes; pa != nil; pa = pa.Next { + paLen := int(pa.Serialize(pathAttributesBuf, opt)) + budget -= paLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + nlriBuf := bytes.NewBuffer(nil) + for nlri := b.NLRI; nlri != nil; nlri = nlri.Next { + nlriLen := int(nlri.serialize(nlriBuf)) + budget -= nlriLen + if budget < 0 { + return nil, fmt.Errorf("update too long") + } + } + + withdrawnRoutesLen := withdrawBuf.Len() + if withdrawnRoutesLen > 65535 { + return nil, fmt.Errorf("Invalid Withdrawn Routes Length: %d", withdrawnRoutesLen) + } + + totalPathAttributesLen := pathAttributesBuf.Len() + if totalPathAttributesLen > 65535 { + return nil, fmt.Errorf("Invalid Total Path Attribute Length: %d", totalPathAttributesLen) + } + + totalLength := 2 + withdrawnRoutesLen + totalPathAttributesLen + 2 + nlriBuf.Len() + 19 + if totalLength > 4096 { + return nil, fmt.Errorf("Update too long: %d bytes", totalLength) + } + + serializeHeader(buf, uint16(totalLength), UpdateMsg) + + buf.Write(convert.Uint16Byte(uint16(withdrawnRoutesLen))) + buf.Write(withdrawBuf.Bytes()) + + buf.Write(convert.Uint16Byte(uint16(totalPathAttributesLen))) + buf.Write(pathAttributesBuf.Bytes()) + + buf.Write(nlriBuf.Bytes()) + + return buf.Bytes(), nil +} diff --git a/protocols/bgp/server/BUILD.bazel b/protocols/bgp/server/BUILD.bazel index 89926f590b80e2c43dab3aa54a8a2d0f285e1189..e2e0edbc799e433d18180c5143a12536ef108aa9 100644 --- a/protocols/bgp/server/BUILD.bazel +++ b/protocols/bgp/server/BUILD.bazel @@ -19,7 +19,6 @@ go_library( "tcplistener.go", "update_helper.go", "update_sender.go", - "update_sender_add_path.go", "util.go", "withdraw.go", ], @@ -29,6 +28,7 @@ go_library( "//config:go_default_library", "//net:go_default_library", "//protocols/bgp/packet:go_default_library", + "//protocols/bgp/types:go_default_library", "//route:go_default_library", "//routingtable:go_default_library", "//routingtable/adjRIBIn:go_default_library", @@ -42,6 +42,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "fsm_established_test.go", "fsm_open_sent_test.go", "fsm_test.go", "server_test.go", diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index cf970b40483511c2ecafc1d36597f66117de06ef..e85bda8cc21d6c47ac2c300639177aab8623859f 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -7,6 +7,7 @@ import ( "time" "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/routingtable" "github.com/bio-routing/bio-rd/routingtable/locRIB" log "github.com/sirupsen/logrus" @@ -54,10 +55,7 @@ type FSM struct { msgRecvFailCh chan error stopMsgRecvCh chan struct{} - capAddPathSend bool - capAddPathRecv bool - - options *packet.Options + options *types.Options local net.IP @@ -65,7 +63,7 @@ type FSM struct { adjRIBIn routingtable.RouteTableClient adjRIBOut routingtable.RouteTableClient rib *locRIB.LocRIB - updateSender routingtable.RouteTableClient + updateSender *UpdateSender neighborID uint32 state state @@ -102,7 +100,7 @@ func newFSM2(peer *peer) *FSM { msgRecvFailCh: make(chan error), stopMsgRecvCh: make(chan struct{}), rib: peer.rib, - options: &packet.Options{}, + options: &types.Options{}, } } diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index fdb6ea30a1463ad06e0cb44507205eed71d7c75b..20ae668232984fead709cbfae9cceeecfec97217 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -7,6 +7,7 @@ import ( bnet "github.com/bio-routing/bio-rd/net" "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" "github.com/bio-routing/bio-rd/routingtable" "github.com/bio-routing/bio-rd/routingtable/adjRIBIn" @@ -77,20 +78,21 @@ func (s *establishedState) init() error { LocalASN: s.fsm.peer.localASN, RouteServerClient: s.fsm.peer.routeServerClient, LocalAddress: bnet.IPv4ToUint32(hostIP), - CapAddPathRX: s.fsm.capAddPathSend, + CapAddPathRX: s.fsm.options.AddPathRX, } s.fsm.adjRIBOut = adjRIBOut.New(n, s.fsm.peer.exportFilter) clientOptions := routingtable.ClientOptions{ BestOnly: true, } - if s.fsm.capAddPathSend { + if s.fsm.options.AddPathRX { s.fsm.updateSender = newUpdateSenderAddPath(s.fsm) clientOptions = s.fsm.peer.addPathSend } else { s.fsm.updateSender = newUpdateSender(s.fsm) } + s.fsm.updateSender.Start() s.fsm.adjRIBOut.Register(s.fsm.updateSender) s.fsm.rib.RegisterWithOptions(s.fsm.adjRIBOut, clientOptions) @@ -103,6 +105,7 @@ func (s *establishedState) uninit() { s.fsm.adjRIBIn.Unregister(s.fsm.rib) s.fsm.rib.Unregister(s.fsm.adjRIBOut) s.fsm.adjRIBOut.Unregister(s.fsm.updateSender) + s.fsm.updateSender.Destroy() s.fsm.adjRIBIn = nil s.fsm.adjRIBOut = nil @@ -228,8 +231,6 @@ func (s *establishedState) updates(u *packet.BGPUpdate) { } func (s *establishedState) processAttributes(attrs *packet.PathAttribute, path *route.Path) { - var currentUnknown *packet.PathAttribute - for pa := attrs; pa != nil; pa = pa.Next { switch pa.TypeCode { case packet.OriginAttr: @@ -241,33 +242,35 @@ func (s *establishedState) processAttributes(attrs *packet.PathAttribute, path * case packet.NextHopAttr: path.BGPPath.NextHop = pa.Value.(uint32) case packet.ASPathAttr: - path.BGPPath.ASPath = pa.Value.(packet.ASPath) + path.BGPPath.ASPath = pa.Value.(types.ASPath) path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length() case packet.CommunitiesAttr: path.BGPPath.Communities = pa.Value.([]uint32) case packet.LargeCommunitiesAttr: - path.BGPPath.LargeCommunities = pa.Value.([]packet.LargeCommunity) + path.BGPPath.LargeCommunities = pa.Value.([]types.LargeCommunity) default: - currentUnknown = s.processUnknownAttribute(pa, currentUnknown) - if path.BGPPath.UnknownAttributes == nil { - path.BGPPath.UnknownAttributes = currentUnknown + unknownAttr := s.processUnknownAttribute(pa) + if unknownAttr != nil { + path.BGPPath.UnknownAttributes = append(path.BGPPath.UnknownAttributes, *unknownAttr) } } } } -func (s *establishedState) processUnknownAttribute(attr, current *packet.PathAttribute) *packet.PathAttribute { +func (s *establishedState) processUnknownAttribute(attr *packet.PathAttribute) *types.UnknownPathAttribute { if !attr.Transitive { - return current + return nil } - p := attr.Copy() - if current == nil { - return p + u := &types.UnknownPathAttribute{ + Transitive: true, + Optional: attr.Optional, + Partial: attr.Partial, + TypeCode: attr.TypeCode, + Value: attr.Value.([]byte), } - current.Next = p - return p + return u } func (s *establishedState) keepaliveReceived() (state, string) { diff --git a/protocols/bgp/server/fsm_established_test.go b/protocols/bgp/server/fsm_established_test.go index 4e92e4eede30ed4b0dfedeb1217e5d11962c6252..61103d99c133e01e47ab923e81d121f5acc48018 100644 --- a/protocols/bgp/server/fsm_established_test.go +++ b/protocols/bgp/server/fsm_established_test.go @@ -4,11 +4,12 @@ import ( "testing" "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" "github.com/stretchr/testify/assert" ) -func TestProcessAttribues(t *testing.T) { +func TestProcessAttributes(t *testing.T) { unknown3 := &packet.PathAttribute{ Transitive: true, TypeCode: 100, @@ -33,11 +34,10 @@ func TestProcessAttribues(t *testing.T) { asPath := &packet.PathAttribute{ Transitive: true, TypeCode: packet.ASPathAttr, - Value: packet.ASPath{ - packet.ASPathSegment{ - Count: 0, - Type: packet.ASSequence, - ASNs: []uint32{}, + Value: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{}, }, }, Next: unknown1, @@ -54,12 +54,12 @@ func TestProcessAttribues(t *testing.T) { expectedValues := [][]byte{[]byte{5, 6}, []byte{1, 2, 3, 4}} i := 0 - for attr := p.BGPPath.UnknownAttributes; attr != nil; attr = attr.Next { + for _, attr := range p.BGPPath.UnknownAttributes { assert.Equal(t, true, attr.Transitive, "Transitive") assert.Equal(t, expectedCodes[i], attr.TypeCode, "Code") assert.Equal(t, expectedValues[i], attr.Value, "Value") i++ } - assert.Equal(t, i, 2, "Count") + assert.Equal(t, 2, i, "Count") } diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index 121ea883a1c4d2de0e9384e275c394608b8665b7..928ed450b7b2a4cb230acaca5db418af80923c11 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -183,18 +183,18 @@ func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapabi switch addPathCap.SendReceive { case packet.AddPathReceive: if !s.fsm.peer.addPathSend.BestOnly { - s.fsm.capAddPathSend = true + s.fsm.options.AddPathRX = true } case packet.AddPathSend: if s.fsm.peer.addPathRecv { - s.fsm.capAddPathRecv = true + s.fsm.options.AddPathRX = true } case packet.AddPathSendReceive: if !s.fsm.peer.addPathSend.BestOnly { - s.fsm.capAddPathSend = true + s.fsm.options.AddPathRX = true } if s.fsm.peer.addPathRecv { - s.fsm.capAddPathRecv = true + s.fsm.options.AddPathRX = true } } } diff --git a/protocols/bgp/server/fsm_test.go b/protocols/bgp/server/fsm_test.go index a6f6bd7a19e2c571c374df2daa59914d02640a66..9cff29b3706dc30d11a1017a7d963d30bafcb1f2 100644 --- a/protocols/bgp/server/fsm_test.go +++ b/protocols/bgp/server/fsm_test.go @@ -88,6 +88,7 @@ func TestFSM100Updates(t *testing.T) { } fsmA.msgRecvCh <- update + } time.Sleep(time.Second) diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go index 1fe4fdbaf05d6e483047712861874365a3af74a3..2cb82934a0a12d2f25243db2bc82cd60fca10e02 100644 --- a/protocols/bgp/server/update_helper.go +++ b/protocols/bgp/server/update_helper.go @@ -4,72 +4,11 @@ import ( "fmt" "io" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" - "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/protocols/bgp/types" log "github.com/sirupsen/logrus" ) -func pathAttribues(p *route.Path) (*packet.PathAttribute, error) { - asPath := &packet.PathAttribute{ - TypeCode: packet.ASPathAttr, - Value: p.BGPPath.ASPath, - } - - origin := &packet.PathAttribute{ - TypeCode: packet.OriginAttr, - Value: p.BGPPath.Origin, - } - asPath.Next = origin - - nextHop := &packet.PathAttribute{ - TypeCode: packet.NextHopAttr, - Value: p.BGPPath.NextHop, - } - origin.Next = nextHop - - localPref := &packet.PathAttribute{ - TypeCode: packet.LocalPrefAttr, - Value: p.BGPPath.LocalPref, - } - nextHop.Next = localPref - - if p.BGPPath != nil { - optionals := addOptionalPathAttribues(p, localPref) - optionals.Next = p.BGPPath.UnknownAttributes - } - - return asPath, nil -} - -func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) *packet.PathAttribute { - current := parent - - if len(p.BGPPath.Communities) > 0 { - communities := &packet.PathAttribute{ - TypeCode: packet.CommunitiesAttr, - Value: p.BGPPath.Communities, - } - current.Next = communities - current = communities - } - - if len(p.BGPPath.LargeCommunities) > 0 { - largeCommunities := &packet.PathAttribute{ - TypeCode: packet.LargeCommunitiesAttr, - Value: p.BGPPath.LargeCommunities, - } - current.Next = largeCommunities - current = largeCommunities - } - - return current -} - -type serializeAbleUpdate interface { - SerializeUpdate(opt *packet.Options) ([]byte, error) -} - -func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *packet.Options) error { +func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *types.Options) error { updateBytes, err := update.SerializeUpdate(opt) if err != nil { log.Errorf("Unable to serialize BGP Update: %v", err) @@ -82,3 +21,7 @@ func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *pack } return nil } + +type serializeAbleUpdate interface { + SerializeUpdate(opt *types.Options) ([]byte, error) +} diff --git a/protocols/bgp/server/update_helper_test.go b/protocols/bgp/server/update_helper_test.go index d2d0a7fcf424276b6a351176cebc9a3959f9ec69..6c599f18c965d55a9a611509ea10e2c5d5ffa35e 100644 --- a/protocols/bgp/server/update_helper_test.go +++ b/protocols/bgp/server/update_helper_test.go @@ -1,14 +1,13 @@ package server import ( + "bytes" + "errors" "io" "testing" - "bytes" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" - - "errors" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/net" "github.com/stretchr/testify/assert" @@ -16,7 +15,7 @@ import ( type failingUpdate struct{} -func (f *failingUpdate) SerializeUpdate(opt *packet.Options) ([]byte, error) { +func (f *failingUpdate) SerializeUpdate(opt *types.Options) ([]byte, error) { return nil, errors.New("general error") } @@ -94,7 +93,7 @@ func TestSerializeAndSendUpdate(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - opt := &packet.Options{} + opt := &types.Options{} err := serializeAndSendUpdate(test.buf, test.testUpdate, opt) assert.Equal(t, test.err, err) diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 23da78c532952a70bce63b8cc9570dba2d9e503e..078faebfde101b8d774a8785ad0a71c9dce3b159 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -1,12 +1,12 @@ package server import ( - "fmt" - "strings" + "sync" + "time" log "github.com/sirupsen/logrus" - "github.com/bio-routing/bio-rd/net" + bnet "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/bio-routing/bio-rd/routingtable" @@ -15,59 +15,152 @@ import ( // UpdateSender converts table changes into BGP update messages type UpdateSender struct { routingtable.ClientManager - fsm *FSM - iBGP bool + fsm *FSM + iBGP bool + addPath bool + toSendMu sync.Mutex + toSend map[string]*pathPfxs + destroyCh chan struct{} +} + +type pathPfxs struct { + path *route.Path + pfxs []bnet.Prefix +} + +func newUpdateSenderAddPath(fsm *FSM) *UpdateSender { + u := newUpdateSender(fsm) + u.addPath = true + return u } func newUpdateSender(fsm *FSM) *UpdateSender { return &UpdateSender{ - fsm: fsm, - iBGP: fsm.peer.localASN == fsm.peer.peerASN, + fsm: fsm, + iBGP: fsm.peer.localASN == fsm.peer.peerASN, + destroyCh: make(chan struct{}), } } -// AddPath serializes a new path and sends out a BGP update message -func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error { - pathAttrs, err := pathAttribues(p) - if err != nil { - log.Errorf("Unable to create BGP Update: %v", err) +// Start starts the update sender +func (u *UpdateSender) Start() { + go u.sender() +} + +// Destroy destroys everything (with greetings to Hatebreed) +func (u *UpdateSender) Destroy() { + u.destroyCh <- struct{}{} +} + +// AddPath adds path p for pfx to toSend queue +func (u *UpdateSender) AddPath(pfx bnet.Prefix, p *route.Path) error { + u.toSendMu.Lock() + + hash := p.BGPPath.ComputeHash() + if _, exists := u.toSend[hash]; exists { + u.toSend[hash].pfxs = append(u.toSend[hash].pfxs, pfx) + u.toSendMu.Unlock() return nil } - update := &packet.BGPUpdate{ - PathAttributes: pathAttrs, - NLRI: &packet.NLRI{ - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), + u.toSend[p.BGPPath.ComputeHash()] = &pathPfxs{ + path: p, + pfxs: []bnet.Prefix{ + pfx, }, } - return serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) + u.toSendMu.Unlock() + return nil +} + +// sender serializes BGP update messages +func (u *UpdateSender) sender() { + ticker := time.NewTicker(time.Millisecond * 5) + var err error + var pathAttrs *packet.PathAttribute + var budget int + var nlri *packet.NLRI + + for { + select { + case <-u.destroyCh: + return + case <-ticker.C: + } + + u.toSendMu.Lock() + + for key, pathNLRIs := range u.toSend { + budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length()) + pathAttrs, err = packet.PathAttributes(pathNLRIs.path) + if err != nil { + log.Errorf("Unable to get path attributes: %v", err) + continue + } + + updatesPrefixes := make([][]bnet.Prefix, 1) + prefixes := make([]bnet.Prefix, 1) + for _, pfx := range pathNLRIs.pfxs { + budget -= int(packet.BytesInAddr(nlri.Pfxlen)) - 5 + if budget < 0 { + updatesPrefixes = append(updatesPrefixes, prefixes) + prefixes = make([]bnet.Prefix, 1) + } + + prefixes = append(prefixes, pfx) + } + + delete(u.toSend, key) + u.toSendMu.Unlock() + + u.sendUpdates(pathAttrs, updatesPrefixes, pathNLRIs.path.BGPPath.PathIdentifier) + u.toSendMu.Lock() + } + u.toSendMu.Unlock() + } +} + +func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) { + var nlri *packet.NLRI + var err error + + for _, updatePrefix := range updatePrefixes { + update := &packet.BGPUpdate{ + PathAttributes: pathAttrs, + } + + for _, pfx := range updatePrefix { + nlri = &packet.NLRI{ + PathIdentifier: pathID, + IP: pfx.Addr(), + Pfxlen: pfx.Pfxlen(), + Next: update.NLRI, + } + update.NLRI = nlri + } + + err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) + if err != nil { + log.Errorf("Failed to serialize and send: %v", err) + } + } } // RemovePath withdraws prefix `pfx` from a peer -func (u *UpdateSender) RemovePath(pfx net.Prefix, p *route.Path) bool { - err := withDrawPrefixes(u.fsm.con, u.fsm.options, pfx) +func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool { + err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) return err == nil } // UpdateNewClient does nothing func (u *UpdateSender) UpdateNewClient(client routingtable.RouteTableClient) error { - log.Warningf("BGP Update Sender: UpdateNewClient() not supported") + log.Warningf("BGP Update Sender: UpdateNewClient not implemented") return nil } -// RouteCount does nothing +// RouteCount returns the number of stored routes func (u *UpdateSender) RouteCount() int64 { - log.Warningf("BGP Update Sender: RouteCount() not supported") + log.Warningf("BGP Update Sender: RouteCount not implemented") return 0 } - -func asPathString(iBGP bool, localASN uint16, asPath string) string { - ret := "" - if iBGP { - ret = ret + fmt.Sprintf("%d ", localASN) - } - ret = ret + asPath - return strings.TrimRight(ret, " ") -} diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go deleted file mode 100644 index 66d4a594cc6b59852bf206447c59cdb949d25abf..0000000000000000000000000000000000000000 --- a/protocols/bgp/server/update_sender_add_path.go +++ /dev/null @@ -1,61 +0,0 @@ -package server - -import ( - log "github.com/sirupsen/logrus" - - "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/bio-routing/bio-rd/routingtable" -) - -// UpdateSenderAddPath converts table changes into BGP update messages with add path -type UpdateSenderAddPath struct { - routingtable.ClientManager - fsm *FSM - iBGP bool -} - -func newUpdateSenderAddPath(fsm *FSM) *UpdateSenderAddPath { - return &UpdateSenderAddPath{ - fsm: fsm, - iBGP: fsm.peer.localASN == fsm.peer.peerASN, - } -} - -// AddPath serializes a new path and sends out a BGP update message -func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error { - pathAttrs, err := pathAttribues(p) - - if err != nil { - log.Errorf("Unable to create BGP Update: %v", err) - return nil - } - update := &packet.BGPUpdateAddPath{ - PathAttributes: pathAttrs, - NLRI: &packet.NLRIAddPath{ - PathIdentifier: p.BGPPath.PathIdentifier, - IP: pfx.Addr(), - Pfxlen: pfx.Pfxlen(), - }, - } - return serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) -} - -// RemovePath withdraws prefix `pfx` from a peer -func (u *UpdateSenderAddPath) RemovePath(pfx net.Prefix, p *route.Path) bool { - err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) - return err == nil -} - -// UpdateNewClient does nothing -func (u *UpdateSenderAddPath) UpdateNewClient(client routingtable.RouteTableClient) error { - log.Warningf("BGP Update Sender: UpdateNewClient not implemented") - return nil -} - -// RouteCount returns the number of stored routes -func (u *UpdateSenderAddPath) RouteCount() int64 { - log.Warningf("BGP Update Sender: RouteCount not implemented") - return 0 -} diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 089f9f2c0f7a0e25d1c5d1f2e9f292cdeec74a3c..e3f1137e1e96469dfa69f3bd825a3a644a582fc2 100644 --- a/protocols/bgp/server/withdraw.go +++ b/protocols/bgp/server/withdraw.go @@ -6,12 +6,13 @@ import ( "github.com/bio-routing/bio-rd/net" "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" ) // withDrawPrefixes generates a BGPUpdate message and write it to the given // io.Writer. -func withDrawPrefixes(out io.Writer, opt *packet.Options, prefixes ...net.Prefix) error { +func withDrawPrefixes(out io.Writer, opt *types.Options, prefixes ...net.Prefix) error { if len(prefixes) < 1 { return nil } @@ -41,15 +42,15 @@ func withDrawPrefixes(out io.Writer, opt *packet.Options, prefixes ...net.Prefix // withDrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given // io.Writer. -func withDrawPrefixesAddPath(out io.Writer, opt *packet.Options, pfx net.Prefix, p *route.Path) error { +func withDrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix, p *route.Path) error { if p.Type != route.BGPPathType { return errors.New("wrong path type, expected BGPPathType") } if p.BGPPath == nil { return errors.New("got nil BGPPath") } - update := &packet.BGPUpdateAddPath{ - WithdrawnRoutes: &packet.NLRIAddPath{ + update := &packet.BGPUpdate{ + WithdrawnRoutes: &packet.NLRI{ PathIdentifier: p.BGPPath.PathIdentifier, IP: pfx.Addr(), Pfxlen: pfx.Pfxlen(), diff --git a/protocols/bgp/server/withdraw_test.go b/protocols/bgp/server/withdraw_test.go index d8831c3eb526d3afdaceb0a3506bf5dca39c2c41..c21b6ada956e4c694a8408e962a67efe611574c2 100644 --- a/protocols/bgp/server/withdraw_test.go +++ b/protocols/bgp/server/withdraw_test.go @@ -3,7 +3,7 @@ package server import ( "testing" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "errors" @@ -54,7 +54,7 @@ func TestWithDrawPrefixes(t *testing.T) { } for _, tc := range testcases { buf := bytes.NewBuffer([]byte{}) - opt := &packet.Options{} + opt := &types.Options{} err := withDrawPrefixes(buf, opt, tc.Prefix...) assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) @@ -111,7 +111,9 @@ func TestWithDrawPrefixesAddPath(t *testing.T) { } for _, tc := range testcases { buf := bytes.NewBuffer([]byte{}) - opt := &packet.Options{} + opt := &types.Options{ + AddPathRX: true, + } err := withDrawPrefixesAddPath(buf, opt, tc.Prefix, tc.Path) assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) diff --git a/protocols/bgp/types/BUILD.bazel b/protocols/bgp/types/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..cd582779e7275027d66fadef922165706b2c5003 --- /dev/null +++ b/protocols/bgp/types/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "as_path.go", + "community.go", + "large_community.go", + "options.go", + "unknown_attribute.go", + ], + importpath = "github.com/bio-routing/bio-rd/protocols/bgp/types", + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = [ + "community_test.go", + "large_community_benchmark_test.go", + "large_community_test.go", + "unknown_attrinute_test.go", + ], + embed = [":go_default_library"], + deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"], +) diff --git a/protocols/bgp/types/as_path.go b/protocols/bgp/types/as_path.go new file mode 100644 index 0000000000000000000000000000000000000000..a1f7bd3a64ef629b3ced72a5fa60a5a99a0a74f4 --- /dev/null +++ b/protocols/bgp/types/as_path.go @@ -0,0 +1,59 @@ +package types + +import "fmt" + +// ASPath Segment Types +const ( + // ASSet is the AS Path type used to indicate an AS Set (RFC4271) + ASSet = 1 + + // ASSequence is tha AS Path type used to indicate an AS Sequence (RFC4271) + ASSequence = 2 + + // MaxASNsSegment is the maximum number of ASNs in an AS segment + MaxASNsSegment = 255 +) + +// ASPath represents an AS Path (RFC4271) +type ASPath []ASPathSegment + +// ASPathSegment represents an AS Path Segment (RFC4271) +type ASPathSegment struct { + Type uint8 + ASNs []uint32 +} + +// String converts an ASPath to it's human redable representation +func (pa ASPath) String() (ret string) { + for _, p := range pa { + if p.Type == ASSet { + ret += " (" + } + n := len(p.ASNs) + for i, asn := range p.ASNs { + if i < n-1 { + ret += fmt.Sprintf("%d ", asn) + continue + } + ret += fmt.Sprintf("%d", asn) + } + if p.Type == ASSet { + ret += ")" + } + } + + return +} + +// Length returns the AS path length as used by path selection +func (pa ASPath) Length() (ret uint16) { + for _, p := range pa { + if p.Type == ASSet { + ret++ + continue + } + ret += uint16(len(p.ASNs)) + } + + return +} diff --git a/protocols/bgp/packet/community.go b/protocols/bgp/types/community.go similarity index 62% rename from protocols/bgp/packet/community.go rename to protocols/bgp/types/community.go index 05b3d8fcb15d295df75853067eea5e124b1e47b2..c1748b649295355a97894d292daa803d61918035 100644 --- a/protocols/bgp/packet/community.go +++ b/protocols/bgp/types/community.go @@ -1,4 +1,4 @@ -package packet +package types import ( "fmt" @@ -7,10 +7,13 @@ import ( ) const ( - WellKnownCommunityNoExport = 0xFFFFFF01 + // WellKnownCommunityNoExport is the well known no export BGP community (RFC1997) + WellKnownCommunityNoExport = 0xFFFFFF01 + // WellKnownCommunityNoAdvertise is the well known no advertise BGP community (RFC1997) WellKnownCommunityNoAdvertise = 0xFFFFFF02 ) +// CommunityStringForUint32 transforms a community into a human readable representation func CommunityStringForUint32(v uint32) string { e1 := v >> 16 e2 := v & 0x0000FFFF @@ -18,6 +21,7 @@ func CommunityStringForUint32(v uint32) string { return fmt.Sprintf("(%d,%d)", e1, e2) } +// ParseCommunityString parses human readable community representation func ParseCommunityString(s string) (uint32, error) { s = strings.Trim(s, "()") t := strings.Split(s, ",") diff --git a/protocols/bgp/packet/community_test.go b/protocols/bgp/types/community_test.go similarity index 99% rename from protocols/bgp/packet/community_test.go rename to protocols/bgp/types/community_test.go index f2dced9a4749cfb9d9aeabbe06ed1497595f1cc9..504859769aa4fd1b9eb5bb8ce1e18e69000e9701 100644 --- a/protocols/bgp/packet/community_test.go +++ b/protocols/bgp/types/community_test.go @@ -1,4 +1,4 @@ -package packet +package types import ( "testing" diff --git a/protocols/bgp/packet/large_community.go b/protocols/bgp/types/large_community.go similarity index 78% rename from protocols/bgp/packet/large_community.go rename to protocols/bgp/types/large_community.go index 4d93eb600bff85f3899e3a3df8246002f903dfb4..18f859c2ff97fd3c62fc3ba310a8e824da46a3a6 100644 --- a/protocols/bgp/packet/large_community.go +++ b/protocols/bgp/types/large_community.go @@ -1,4 +1,4 @@ -package packet +package types import ( "fmt" @@ -6,16 +6,19 @@ import ( "strings" ) +// LargeCommunity represents a large community (RFC8195) type LargeCommunity struct { GlobalAdministrator uint32 DataPart1 uint32 DataPart2 uint32 } +// String transitions a large community to it's human readable representation func (c *LargeCommunity) String() string { return fmt.Sprintf("(%d,%d,%d)", c.GlobalAdministrator, c.DataPart1, c.DataPart2) } +// ParseLargeCommunityString parses a human readable large community representation func ParseLargeCommunityString(s string) (com LargeCommunity, err error) { s = strings.Trim(s, "()") t := strings.Split(s, ",") diff --git a/protocols/bgp/packet/large_community_benchmark_test.go b/protocols/bgp/types/large_community_benchmark_test.go similarity index 97% rename from protocols/bgp/packet/large_community_benchmark_test.go rename to protocols/bgp/types/large_community_benchmark_test.go index 7e37bcdf98de064442e4e10d343d31c45448ab74..d2ca3a4d9bedaf9e3a63df7cef843032fe891806 100644 --- a/protocols/bgp/packet/large_community_benchmark_test.go +++ b/protocols/bgp/types/large_community_benchmark_test.go @@ -1,4 +1,4 @@ -package packet +package types import ( "fmt" diff --git a/protocols/bgp/packet/large_community_test.go b/protocols/bgp/types/large_community_test.go similarity index 99% rename from protocols/bgp/packet/large_community_test.go rename to protocols/bgp/types/large_community_test.go index 6319ccff41daa9443cb887777129be52545c2e10..f3ff70222bf794e6edbc31fce9b811f02e40c582 100644 --- a/protocols/bgp/packet/large_community_test.go +++ b/protocols/bgp/types/large_community_test.go @@ -1,4 +1,4 @@ -package packet +package types import ( "errors" diff --git a/protocols/bgp/types/options.go b/protocols/bgp/types/options.go new file mode 100644 index 0000000000000000000000000000000000000000..19b580d2829b96d8f974311536b299211a98ca95 --- /dev/null +++ b/protocols/bgp/types/options.go @@ -0,0 +1,7 @@ +package types + +// Options represents options to the update sender, decoder and encoder +type Options struct { + Supports4OctetASN bool + AddPathRX bool +} diff --git a/protocols/bgp/types/unknown_attribute.go b/protocols/bgp/types/unknown_attribute.go new file mode 100644 index 0000000000000000000000000000000000000000..66e7d9f0a90eb88d6b05ede9d561e2130d0dbed4 --- /dev/null +++ b/protocols/bgp/types/unknown_attribute.go @@ -0,0 +1,19 @@ +package types + +// UnknownPathAttribute represents an unknown path attribute BIO does not support +type UnknownPathAttribute struct { + Optional bool + Transitive bool + Partial bool + TypeCode uint8 + Value []byte +} + +// WireLength returns the number of bytes the attribute need on the wire +func (u *UnknownPathAttribute) WireLength() uint16 { + length := uint16(len(u.Value)) + if length > 255 { + length++ // Extended length + } + return length + 3 +} diff --git a/protocols/bgp/types/unknown_attrinute_test.go b/protocols/bgp/types/unknown_attrinute_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0ab31f6ecc5095b5fa8a377643608e3c27edb97e --- /dev/null +++ b/protocols/bgp/types/unknown_attrinute_test.go @@ -0,0 +1,94 @@ +package types + +import "testing" + +func TestWireLength(t *testing.T) { + tests := []struct { + name string + pa *UnknownPathAttribute + expected uint16 + }{ + { + name: "Test #1", + pa: &UnknownPathAttribute{ + Value: []byte{1, 2, 3}, + }, + expected: 6, + }, + { + name: "Non extended length corner case", + pa: &UnknownPathAttribute{ + Value: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, + }, + }, + expected: 258, + }, + { + name: "Extended length corner case", + pa: &UnknownPathAttribute{ + Value: []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, + }, + }, + expected: 260, + }, + } + + for _, test := range tests { + res := test.pa.WireLength() + if res != test.expected { + t.Errorf("Unexpected result for test %q: Expected: %d Got: %d", test.name, test.expected, res) + } + } +} diff --git a/route/BUILD.bazel b/route/BUILD.bazel index 43ece0720b95d36285849f9f80d9d07d2d259b6d..3673d29995e0ef11a14bbd7043135457f7ea1d7e 100644 --- a/route/BUILD.bazel +++ b/route/BUILD.bazel @@ -13,7 +13,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//net:go_default_library", - "//protocols/bgp/packet:go_default_library", + "//protocols/bgp/types:go_default_library", "//vendor/github.com/taktv6/tflow2/convert:go_default_library", ], ) @@ -29,7 +29,7 @@ go_test( embed = [":go_default_library"], deps = [ "//net:go_default_library", - "//protocols/bgp/packet:go_default_library", + "//protocols/bgp/types:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", ], ) diff --git a/route/bgp_path.go b/route/bgp_path.go index d2aa5247abb665a53e3073a06a9d9ec1d0c52369..4eec57b3326b968a221f79c0359cfdcf73ba95c0 100644 --- a/route/bgp_path.go +++ b/route/bgp_path.go @@ -6,7 +6,7 @@ import ( "strings" bnet "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/taktv6/tflow2/convert" ) @@ -15,7 +15,7 @@ type BGPPath struct { PathIdentifier uint32 NextHop bnet.IP LocalPref uint32 - ASPath packet.ASPath + ASPath types.ASPath ASPathLen uint16 Origin uint8 MED uint32 @@ -23,8 +23,34 @@ type BGPPath struct { BGPIdentifier uint32 Source bnet.IP Communities []uint32 - LargeCommunities []packet.LargeCommunity - UnknownAttributes *packet.PathAttribute + LargeCommunities []types.LargeCommunity + UnknownAttributes []types.UnknownPathAttribute +} + +// Length get's the length of serialized path +func (b *BGPPath) Length() uint16 { + asPathLen := uint16(3) + for _, segment := range b.ASPath { + asPathLen++ + asPathLen += uint16(4 * len(segment.ASNs)) + } + + communitiesLen := uint16(0) + if len(b.Communities) != 0 { + communitiesLen += 3 + uint16(len(b.Communities)*4) + } + + largeCommunitiesLen := uint16(0) + if len(b.LargeCommunities) != 0 { + largeCommunitiesLen += 3 + uint16(len(b.LargeCommunities)*12) + } + + unknownAttributesLen := uint16(0) + for _, unknownAttr := range b.UnknownAttributes { + unknownAttributesLen += unknownAttr.WireLength() + } + + return communitiesLen + largeCommunitiesLen + 4*7 + 4 + asPathLen + unknownAttributesLen } // ECMP determines if routes b and c are euqal in terms of ECMP @@ -207,12 +233,12 @@ func (b *BGPPath) Prepend(asn uint32, times uint16) { } first := b.ASPath[0] - if first.Type == packet.ASSet { + if first.Type == types.ASSet { b.insertNewASSequence() } for i := 0; i < int(times); i++ { - if len(b.ASPath) == packet.MaxASNsSegment { + if len(b.ASPath) == types.MaxASNsSegment { b.insertNewASSequence() } @@ -227,23 +253,33 @@ func (b *BGPPath) Prepend(asn uint32, times uint16) { } func (b *BGPPath) insertNewASSequence() { - pa := make(packet.ASPath, len(b.ASPath)+1) - copy(b.ASPath, pa[1:]) - pa[0] = packet.ASPathSegment{ - ASNs: make([]uint32, 0), - Count: 0, - Type: packet.ASSequence, + pa := make(types.ASPath, len(b.ASPath)+1) + copy(pa[1:], b.ASPath) + pa[0] = types.ASPathSegment{ + ASNs: make([]uint32, 0), + Type: types.ASSequence, } b.ASPath = pa } -func (p *BGPPath) Copy() *BGPPath { - if p == nil { +// Copy creates a deep copy of a BGPPath +func (b *BGPPath) Copy() *BGPPath { + if b == nil { return nil } - cp := *p + cp := *b + + cp.ASPath = make(types.ASPath, len(cp.ASPath)) + copy(cp.ASPath, b.ASPath) + + cp.Communities = make([]uint32, len(cp.Communities)) + copy(cp.Communities, b.Communities) + + cp.LargeCommunities = make([]types.LargeCommunity, len(cp.LargeCommunities)) + copy(cp.LargeCommunities, b.LargeCommunities) + return &cp } @@ -269,7 +305,7 @@ func (b *BGPPath) ComputeHash() string { func (b *BGPPath) CommunitiesString() string { str := "" for _, com := range b.Communities { - str += packet.CommunityStringForUint32(com) + " " + str += types.CommunityStringForUint32(com) + " " } return strings.TrimRight(str, " ") diff --git a/route/bgp_path_test.go b/route/bgp_path_test.go index d0f0a9937a7ded1309664ffd31321d5edf844344..9ea931153ea5778061e852e34af7666204e97b0b 100644 --- a/route/bgp_path_test.go +++ b/route/bgp_path_test.go @@ -3,7 +3,7 @@ package route import ( "testing" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/stretchr/testify/assert" ) @@ -34,12 +34,12 @@ func TestCommunitiesString(t *testing.T) { func TestLargeCommunitiesString(t *testing.T) { tests := []struct { name string - comms []packet.LargeCommunity + comms []types.LargeCommunity expected string }{ { name: "two attributes", - comms: []packet.LargeCommunity{ + comms: []types.LargeCommunity{ { GlobalAdministrator: 1, DataPart1: 2, @@ -64,3 +64,72 @@ func TestLargeCommunitiesString(t *testing.T) { }) } } + +func TestLength(t *testing.T) { + tests := []struct { + name string + path *BGPPath + expected uint16 + }{ + { + name: "No communities", + path: &BGPPath{ + ASPath: []types.ASPathSegment{ + { + Type: types.ASSequence, + ASNs: []uint32{15169, 199714}, + }, + }, + LargeCommunities: []types.LargeCommunity{}, + Communities: []uint32{}, + }, + expected: 44, + }, + { + name: "communities", + path: &BGPPath{ + ASPath: []types.ASPathSegment{ + { + Type: types.ASSequence, + ASNs: []uint32{15169, 199714}, + }, + }, + LargeCommunities: []types.LargeCommunity{}, + Communities: []uint32{10, 20, 30}, + }, + expected: 59, + }, + { + name: "large communities", + path: &BGPPath{ + ASPath: []types.ASPathSegment{ + { + Type: types.ASSequence, + ASNs: []uint32{15169, 199714}, + }, + }, + LargeCommunities: []types.LargeCommunity{ + { + GlobalAdministrator: 199714, + DataPart1: 100, + DataPart2: 200, + }, + { + GlobalAdministrator: 199714, + DataPart1: 100, + DataPart2: 201, + }, + }, + }, + expected: 71, + }, + } + + for _, test := range tests { + calcLen := test.path.Length() + + if calcLen != test.expected { + t.Errorf("Unexpected result for test %q: Expected: %d Got: %d", test.name, test.expected, calcLen) + } + } +} diff --git a/route/bgp_test.go b/route/bgp_test.go index 2eb017c2a6b33e5174b94cb9d43e4325750e968d..46ceb9af2364746aba48711400e9091cc882457f 100644 --- a/route/bgp_test.go +++ b/route/bgp_test.go @@ -3,18 +3,16 @@ package route import ( "testing" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" - + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/stretchr/testify/assert" ) func TestComputeHash(t *testing.T) { p := &BGPPath{ - ASPath: packet.ASPath{ - packet.ASPathSegment{ - ASNs: []uint32{123, 456}, - Count: 2, - Type: packet.ASSequence, + ASPath: types.ASPath{ + types.ASPathSegment{ + ASNs: []uint32{123, 456}, + Type: types.ASSequence, }, }, BGPIdentifier: 1, @@ -22,8 +20,8 @@ func TestComputeHash(t *testing.T) { 123, 456, }, EBGP: false, - LargeCommunities: []packet.LargeCommunity{ - packet.LargeCommunity{ + LargeCommunities: []types.LargeCommunity{ + types.LargeCommunity{ DataPart1: 1, DataPart2: 2, GlobalAdministrator: 3, diff --git a/route/path.go b/route/path.go index 7c0edb7e11443fcad6f2bb0024794c32036fb629..ef4da4068f7bd81b4051e5d91d93d137d8832af1 100644 --- a/route/path.go +++ b/route/path.go @@ -1,6 +1,8 @@ package route -import "fmt" +import ( + "fmt" +) type Path struct { Type uint8 diff --git a/routingtable/BUILD.bazel b/routingtable/BUILD.bazel index 1344741ba00193ae2cf24e4e93065d83c62985b0..4bde0bc33e4fc282414ac1a4849e5ce01bb6ecb1 100644 --- a/routingtable/BUILD.bazel +++ b/routingtable/BUILD.bazel @@ -17,7 +17,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//net:go_default_library", - "//protocols/bgp/packet:go_default_library", + "//protocols/bgp/types:go_default_library", "//route:go_default_library", ], ) diff --git a/routingtable/adjRIBOut/adj_rib_out.go b/routingtable/adjRIBOut/adj_rib_out.go index e211479b0480a338259a0dccce1a66b060ebcfe1..a2179fc02d3d9cff82bc67f95ba1e86fa2a293bd 100644 --- a/routingtable/adjRIBOut/adj_rib_out.go +++ b/routingtable/adjRIBOut/adj_rib_out.go @@ -69,19 +69,21 @@ func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error { a.mu.Lock() defer a.mu.Unlock() - if !a.neighbor.CapAddPathRX { + // AddPathRX capable neighbor + if a.neighbor.CapAddPathRX { + pathID, err := a.pathIDManager.addPath(p) + if err != nil { + return fmt.Errorf("Unable to get path ID: %v", err) + } + + p.BGPPath.PathIdentifier = pathID + a.rt.AddPath(pfx, p) + } else { + // rt.ReplacePath will add this path to the rt in any case, so no rt.AddPath here! oldPaths := a.rt.ReplacePath(pfx, p) a.removePathsFromClients(pfx, oldPaths) } - pathID, err := a.pathIDManager.addPath(p) - if err != nil { - return fmt.Errorf("Unable to get path ID: %v", err) - } - - p.BGPPath.PathIdentifier = pathID - a.rt.AddPath(pfx, p) - for _, client := range a.ClientManager.Clients() { err := client.AddPath(pfx, p) if err != nil { @@ -166,3 +168,17 @@ func (a *AdjRIBOut) Print() string { return ret } + +// Dump all routes present in this AdjRIBOut +func (a *AdjRIBOut) Dump() string { + a.mu.RLock() + defer a.mu.RUnlock() + + ret := fmt.Sprintf("DUMPING ADJ-RIB-OUT:\n") + routes := a.rt.Dump() + for _, r := range routes { + ret += fmt.Sprintf("%s\n", r.Print()) + } + + return ret +} diff --git a/routingtable/filter/BUILD.bazel b/routingtable/filter/BUILD.bazel index 9a27beb4b87a501ff7d18880248ef2418dda2199..4555e6a8dcb20f8f469dcb3826649ce69ba5877c 100644 --- a/routingtable/filter/BUILD.bazel +++ b/routingtable/filter/BUILD.bazel @@ -17,7 +17,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//net:go_default_library", - "//protocols/bgp/packet:go_default_library", + "//protocols/bgp/types:go_default_library", "//route:go_default_library", "//routingtable/filter/actions:go_default_library", ], diff --git a/routingtable/filter/actions/BUILD.bazel b/routingtable/filter/actions/BUILD.bazel index 3f841a07d88ce65c8a65e31a5842ff08378a8058..994b95bf04dbb769eb30c209b87e484261d4254f 100644 --- a/routingtable/filter/actions/BUILD.bazel +++ b/routingtable/filter/actions/BUILD.bazel @@ -16,7 +16,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//net:go_default_library", - "//protocols/bgp/packet:go_default_library", + "//protocols/bgp/types:go_default_library", "//route:go_default_library", ], ) diff --git a/routingtable/filter/actions/add_large_community_action.go b/routingtable/filter/actions/add_large_community_action.go index 534ad30b66e4580ca6b8078bf7a077f87b6e758f..cb5f1a020ea6bfdde33948cc8535077df0fec26d 100644 --- a/routingtable/filter/actions/add_large_community_action.go +++ b/routingtable/filter/actions/add_large_community_action.go @@ -2,15 +2,15 @@ package actions import ( "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" ) type AddLargeCommunityAction struct { - communities []packet.LargeCommunity + communities []types.LargeCommunity } -func NewAddLargeCommunityAction(coms []packet.LargeCommunity) *AddLargeCommunityAction { +func NewAddLargeCommunityAction(coms []types.LargeCommunity) *AddLargeCommunityAction { return &AddLargeCommunityAction{ communities: coms, } diff --git a/routingtable/filter/actions/add_large_community_action_test.go b/routingtable/filter/actions/add_large_community_action_test.go index f7af91396fdd7d697cf667d7712e18c9784d68e3..6b569d2ebd79e7225791b0172c2ad32fab764d88 100644 --- a/routingtable/filter/actions/add_large_community_action_test.go +++ b/routingtable/filter/actions/add_large_community_action_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" "github.com/stretchr/testify/assert" ) @@ -12,14 +12,14 @@ import ( func TestAddingLargeCommunities(t *testing.T) { tests := []struct { name string - current []packet.LargeCommunity - communities []packet.LargeCommunity + current []types.LargeCommunity + communities []types.LargeCommunity expected string }{ { name: "add one to empty", - communities: []packet.LargeCommunity{ - packet.LargeCommunity{ + communities: []types.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 3, @@ -29,15 +29,15 @@ func TestAddingLargeCommunities(t *testing.T) { }, { name: "add one to existing", - current: []packet.LargeCommunity{ - packet.LargeCommunity{ + current: []types.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 5, DataPart1: 6, DataPart2: 7, }, }, - communities: []packet.LargeCommunity{ - packet.LargeCommunity{ + communities: []types.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 3, @@ -47,20 +47,20 @@ func TestAddingLargeCommunities(t *testing.T) { }, { name: "add two to existing", - current: []packet.LargeCommunity{ - packet.LargeCommunity{ + current: []types.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 5, DataPart1: 6, DataPart2: 7, }, }, - communities: []packet.LargeCommunity{ - packet.LargeCommunity{ + communities: []types.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 3, }, - packet.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 7, DataPart1: 8, DataPart2: 9, diff --git a/routingtable/filter/actions/as_path_prepend_action_test.go b/routingtable/filter/actions/as_path_prepend_action_test.go index 253c119ffc91d702e0f4d4cf31e1f88c35d2008c..702a9497cc767cb6b240805a7b4af6f20e17277b 100644 --- a/routingtable/filter/actions/as_path_prepend_action_test.go +++ b/routingtable/filter/actions/as_path_prepend_action_test.go @@ -3,7 +3,7 @@ package actions import ( "testing" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/net" "github.com/bio-routing/bio-rd/route" @@ -25,11 +25,10 @@ func TestAppendPath(t *testing.T) { name: "append 0", times: 0, bgpPath: &route.BGPPath{ - ASPath: packet.ASPath{ - packet.ASPathSegment{ - Count: 2, - Type: packet.ASSequence, - ASNs: []uint32{12345, 12345}, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{12345, 12345}, }, }, ASPathLen: 2, @@ -41,11 +40,10 @@ func TestAppendPath(t *testing.T) { name: "append 3", times: 3, bgpPath: &route.BGPPath{ - ASPath: packet.ASPath{ - packet.ASPathSegment{ - Count: 2, - Type: packet.ASSequence, - ASNs: []uint32{12345, 15169}, + ASPath: types.ASPath{ + types.ASPathSegment{ + Type: types.ASSequence, + ASNs: []uint32{12345, 15169}, }, }, ASPathLen: 2, diff --git a/routingtable/filter/large_community_filter.go b/routingtable/filter/large_community_filter.go index cb4722bdfe98a68cd6c6e9938f188bbbf606c968..d119d4542dd591316bbe6e86e2aeed30215f29bf 100644 --- a/routingtable/filter/large_community_filter.go +++ b/routingtable/filter/large_community_filter.go @@ -1,14 +1,16 @@ package filter import ( - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" ) +// LargeCommunityFilter represents a filter for large communities type LargeCommunityFilter struct { - community packet.LargeCommunity + community types.LargeCommunity } -func (f *LargeCommunityFilter) Matches(coms []packet.LargeCommunity) bool { +// Matches checks if a community f.community is on the filter list +func (f *LargeCommunityFilter) Matches(coms []types.LargeCommunity) bool { for _, com := range coms { if com == f.community { return true diff --git a/routingtable/filter/term_condition_test.go b/routingtable/filter/term_condition_test.go index e1b3d4bd6173b196ddaca029e768274a08364883..5217feaaacc67a57539a90369664a81663f4e642 100644 --- a/routingtable/filter/term_condition_test.go +++ b/routingtable/filter/term_condition_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" "github.com/stretchr/testify/assert" ) @@ -137,13 +137,13 @@ func TestMatches(t *testing.T) { name: "large community matches", prefix: net.NewPfx(strAddr("10.0.0.0"), 24), bgpPath: &route.BGPPath{ - LargeCommunities: []packet.LargeCommunity{ - packet.LargeCommunity{ + LargeCommunities: []types.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 3, }, - packet.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 0, @@ -152,7 +152,7 @@ func TestMatches(t *testing.T) { }, largeCommunityFilters: []*LargeCommunityFilter{ { - packet.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 3, @@ -167,7 +167,7 @@ func TestMatches(t *testing.T) { bgpPath: &route.BGPPath{}, largeCommunityFilters: []*LargeCommunityFilter{ { - packet.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 3, @@ -181,7 +181,7 @@ func TestMatches(t *testing.T) { prefix: net.NewPfx(strAddr("10.0.0.0"), 24), largeCommunityFilters: []*LargeCommunityFilter{ { - packet.LargeCommunity{ + types.LargeCommunity{ GlobalAdministrator: 1, DataPart1: 2, DataPart2: 3, diff --git a/routingtable/update_helper.go b/routingtable/update_helper.go index 6709bd43ab7e2d39914c6ebd1c59abb6518e8189..48110df0a43f0951efce6384262b208402b998e6 100644 --- a/routingtable/update_helper.go +++ b/routingtable/update_helper.go @@ -2,7 +2,7 @@ package routingtable import ( "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" ) @@ -30,7 +30,7 @@ func isDisallowedByCommunity(p *route.Path, n *Neighbor) bool { } for _, com := range p.BGPPath.Communities { - if (com == packet.WellKnownCommunityNoExport && !n.IBGP) || com == packet.WellKnownCommunityNoAdvertise { + if (com == types.WellKnownCommunityNoExport && !n.IBGP) || com == types.WellKnownCommunityNoAdvertise { return true } } diff --git a/routingtable/update_helper_test.go b/routingtable/update_helper_test.go index 452a7e4af04152e21c13631c38190b8d4795649d..451b983beae4df2ce289b60be0be8d1b7bd7f67a 100644 --- a/routingtable/update_helper_test.go +++ b/routingtable/update_helper_test.go @@ -6,7 +6,7 @@ import ( "testing" bnet "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/bio-routing/bio-rd/route" "github.com/stretchr/testify/assert" ) @@ -67,7 +67,7 @@ func TestShouldPropagateUpdate(t *testing.T) { continue } - com, err := packet.ParseCommunityString(s) + com, err := types.ParseCommunityString(s) if err != nil { t.Fatalf("test failed: %s", err) }