From cff156156a2c612e7c3f99653fa398b61e3ce43a Mon Sep 17 00:00:00 2001 From: Oliver Herms <oliver.herms@exaring.de> Date: Wed, 27 Jun 2018 11:54:30 +0100 Subject: [PATCH] Cleanup --- protocols/bgp/packet/BUILD.bazel | 13 ++- protocols/bgp/packet/as_path.go | 44 -------- protocols/bgp/packet/bgp.go | 4 - protocols/bgp/packet/path_attributes.go | 100 +++++++++++++++--- protocols/bgp/packet/update.go | 4 +- protocols/bgp/server/BUILD.bazel | 3 +- protocols/bgp/server/fsm_established.go | 29 ++--- protocols/bgp/server/update_helper.go | 57 ---------- protocols/bgp/server/update_sender.go | 2 +- protocols/bgp/types/BUILD.bazel | 25 +++++ protocols/bgp/types/as_path.go | 59 +++++++++++ protocols/bgp/{packet => types}/community.go | 8 +- .../bgp/{packet => types}/community_test.go | 2 +- .../bgp/{packet => types}/large_community.go | 5 +- .../large_community_benchmark_test.go | 2 +- .../{packet => types}/large_community_test.go | 2 +- protocols/bgp/types/unknown_attribute.go | 19 ++++ protocols/bgp/types/unknown_attrinute_test.go | 94 ++++++++++++++++ route/BUILD.bazel | 2 +- route/bgp_path.go | 57 +++++----- route/bgp_path_test.go | 40 +++++++ route/path.go | 4 +- routingtable/BUILD.bazel | 2 +- routingtable/filter/BUILD.bazel | 2 +- routingtable/filter/actions/BUILD.bazel | 2 +- .../actions/add_large_community_action.go | 6 +- routingtable/filter/large_community_filter.go | 8 +- routingtable/update_helper.go | 4 +- 28 files changed, 409 insertions(+), 190 deletions(-) delete mode 100644 protocols/bgp/packet/as_path.go create mode 100644 protocols/bgp/types/BUILD.bazel create mode 100644 protocols/bgp/types/as_path.go rename protocols/bgp/{packet => types}/community.go (62%) rename protocols/bgp/{packet => types}/community_test.go (99%) rename protocols/bgp/{packet => types}/large_community.go (78%) rename protocols/bgp/{packet => types}/large_community_benchmark_test.go (97%) rename protocols/bgp/{packet => types}/large_community_test.go (99%) create mode 100644 protocols/bgp/types/unknown_attribute.go create mode 100644 protocols/bgp/types/unknown_attrinute_test.go diff --git a/protocols/bgp/packet/BUILD.bazel b/protocols/bgp/packet/BUILD.bazel index 49f16b77..bc081c1e 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 0cdfc96a..00000000 --- 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 a8df0917..1859f665 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 748e3653..2e47c5e6 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 001f2c1c..38e1d719 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 89926f59..e2e0edbc 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 3111055d..e184404c 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 38070219..2c36aa2f 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 3ac1e6e9..ccb1d869 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 00000000..175cedfc --- /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 00000000..a1f7bd3a --- /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 05b3d8fc..c1748b64 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 f2dced9a..50485976 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 4d93eb60..18f859c2 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 7e37bcdf..d2ca3a4d 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 6319ccff..f3ff7022 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 00000000..66e7d9f0 --- /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 00000000..0ab31f6e --- /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 43ece072..b9634f7e 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 31837161..6b31dd85 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 d0f0a993..d1ea111a 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 7c0edb7e..ef4da406 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 1344741b..4bde0bc3 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 9a27beb4..4555e6a8 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 3f841a07..994b95bf 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 534ad30b..cb5f1a02 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 cb4722bd..d119d454 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 6709bd43..48110df0 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 } } -- GitLab