diff --git a/protocols/bgp/packet/BUILD.bazel b/protocols/bgp/packet/BUILD.bazel index 49f16b7773345b729ff25a1a486a86b1669992ed..bc081c1e463c24e67b103de106d686462fd340f5 100644 --- a/protocols/bgp/packet/BUILD.bazel +++ b/protocols/bgp/packet/BUILD.bazel @@ -3,31 +3,30 @@ 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", ], 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 a8df09179d16d6154a6df2e8b0bbfe3ea7abd481..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 diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index 748e36535f049ddb31955e8e86078aef209641cd..2e47c5e6a7d17ab0a6d4882f712f50a9ae1f047c 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" + "github.com/bio-routing/bio-rd/protocols/bgp/types" + "github.com/bio-routing/bio-rd/route" "github.com/taktv6/tflow2/convert" ) @@ -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,7 +372,8 @@ func dumpNBytes(buf *bytes.Buffer, n uint16) error { return nil } -func (pa *PathAttribute) serialize(buf *bytes.Buffer, opt *Options) uint8 { +// Serialize serializes a path attribute +func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *Options) uint8 { pathAttrLen := uint8(0) switch pa.TypeCode { @@ -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))) @@ -523,7 +528,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 } @@ -581,3 +586,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/update.go b/protocols/bgp/packet/update.go index 001f2c1cdba2227d8bdb179789a9f0bebc03bb23..38e1d7196be15fba04554f6845a8265b330a39e2 100644 --- a/protocols/bgp/packet/update.go +++ b/protocols/bgp/packet/update.go @@ -36,7 +36,7 @@ func (b *BGPUpdate) SerializeUpdate(opt *Options) ([]byte, error) { pathAttributesBuf := bytes.NewBuffer(nil) for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf, opt)) + paLen := int(pa.Serialize(pathAttributesBuf, opt)) budget -= paLen if budget < 0 { return nil, fmt.Errorf("update too long") @@ -100,7 +100,7 @@ func (b *BGPUpdate) SerializeUpdateAddPath(opt *Options) ([]byte, error) { pathAttributesBuf := bytes.NewBuffer(nil) for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf, opt)) + paLen := int(pa.Serialize(pathAttributesBuf, opt)) budget -= paLen if budget < 0 { return nil, fmt.Errorf("update too long") 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_established.go b/protocols/bgp/server/fsm_established.go index 3111055d9c0757f329c72acf27c66a1942cc34a4..e184404c2ee95ab684250e4a96a04f34a9a8a9e8 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" @@ -230,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: @@ -243,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) } } } } -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/update_helper.go b/protocols/bgp/server/update_helper.go index 38070219394100e9006e660f75f2212b1a2b5668..2c36aa2fdf7880d6f1e2c6b2ff76d8a7bade61f8 100644 --- a/protocols/bgp/server/update_helper.go +++ b/protocols/bgp/server/update_helper.go @@ -5,66 +5,9 @@ import ( "io" "github.com/bio-routing/bio-rd/protocols/bgp/packet" - "github.com/bio-routing/bio-rd/route" 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 -} - func serializeAndSendUpdate(out io.Writer, update *packet.BGPUpdate, opt *packet.Options) error { updateBytes, err := update.SerializeUpdate(opt) if err != nil { diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 3ac1e6e9349d5e31caf29b139088b20a235daacc..ccb1d869316724c23933cbf00ee1f0008282e35e 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -93,7 +93,7 @@ func (u *UpdateSender) sender() { for key, pathNLRIs := range u.toSend { budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length()) - pathAttrs, err = pathAttribues(pathNLRIs.path) + pathAttrs, err = packet.PathAttributes(pathNLRIs.path) if err != nil { log.Errorf("Unable to get path attributes: %v", err) continue diff --git a/protocols/bgp/types/BUILD.bazel b/protocols/bgp/types/BUILD.bazel new file mode 100644 index 0000000000000000000000000000000000000000..175cedfcb63e894221efe75d1e850acab65c7f2e --- /dev/null +++ b/protocols/bgp/types/BUILD.bazel @@ -0,0 +1,25 @@ +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", + "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/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..b9634f7e7b152e52e9074b507643466c71ef77a6 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", ], ) diff --git a/route/bgp_path.go b/route/bgp_path.go index 31837161b2a5a0ba91cb9b302b9a3b8ef3c975a9..6b31dd85a361f37d137e43a6daf28ac6702a4158 100644 --- a/route/bgp_path.go +++ b/route/bgp_path.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/protocols/bgp/types" "github.com/taktv6/tflow2/convert" ) @@ -14,7 +14,7 @@ type BGPPath struct { PathIdentifier uint32 NextHop uint32 LocalPref uint32 - ASPath packet.ASPath + ASPath types.ASPath ASPathLen uint16 Origin uint8 MED uint32 @@ -22,8 +22,8 @@ type BGPPath struct { BGPIdentifier uint32 Source uint32 Communities []uint32 - LargeCommunities []packet.LargeCommunity - UnknownAttributes *packet.PathAttribute + LargeCommunities []types.LargeCommunity + UnknownAttributes []types.UnknownPathAttribute } // Length get's the length of serialized path @@ -34,19 +34,12 @@ func (b *BGPPath) Length() uint16 { asPathLen += uint16(4 * len(segment.ASNs)) } - return uint16(len(b.Communities))*7 + uint16(len(b.LargeCommunities))*15 + 4*7 + 4 + asPathLen + b.unknownAttributesLength() -} - -// unknownAttributesLength calculates the length of unknown attributes -func (b *BGPPath) unknownAttributesLength() (length uint16) { - for a := b.UnknownAttributes; a != nil; a = a.Next { - length += a.Length + 3 - if a.ExtendedLength { - length++ - } + unknownAttributesLen := uint16(0) + for _, unknownAttr := range b.UnknownAttributes { + unknownAttributesLen += unknownAttr.WireLength() } - return length + return uint16(len(b.Communities))*7 + uint16(len(b.LargeCommunities))*15 + 4*7 + 4 + asPathLen + unknownAttributesLen } // ECMP determines if routes b and c are euqal in terms of ECMP @@ -229,12 +222,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() } @@ -248,24 +241,34 @@ func (b *BGPPath) Prepend(asn uint32, times uint16) { b.ASPathLen = b.ASPath.Length() } -func (b *BGPPath) insertNewASSequence() packet.ASPath { - pa := make(packet.ASPath, len(b.ASPath)+1) +func (b *BGPPath) insertNewASSequence() types.ASPath { + pa := make(types.ASPath, len(b.ASPath)+1) copy(pa[1:], b.ASPath) - pa[0] = packet.ASPathSegment{ - ASNs: make([]uint32, 0), - Count: 0, - Type: packet.ASSequence, + pa[0] = types.ASPathSegment{ + ASNs: make([]uint32, 0), + Type: types.ASSequence, } return 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 } @@ -291,7 +294,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..d1ea111a091ca1ce9c263c672b5f2a1a2e9ca73e 100644 --- a/route/bgp_path_test.go +++ b/route/bgp_path_test.go @@ -1,6 +1,7 @@ package route import ( + "bytes" "testing" "github.com/bio-routing/bio-rd/protocols/bgp/packet" @@ -64,3 +65,42 @@ func TestLargeCommunitiesString(t *testing.T) { }) } } + +func TestLength(t *testing.T) { + tests := []struct { + name string + path *BGPPath + options packet.Options + wantFail bool + }{ + { + name: "Test 1", + path: &BGPPath{}, + }, + } + + for _, test := range tests { + calcLen := test.path.Length() + pa, err := test.path.PathAttributes() + if err != nil { + if test.wantFail { + continue + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", test.name) + continue + } + + buf := bytes.Buffer(nil) + pa.Serialize(buf, test.options) + realLen := len(buf.Bytes()) + if realLen != calcLen { + t.Errorf("Unexpected result for test %q: Expected: %d Got: %d", test.name, realLen, calcLen) + } + } +} 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/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/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/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 } }