diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index 8be484780ee571432d159e63e7b2d4fcf86d2328..e66e834a199abde91d7c5d6d27003977d782c998 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -342,6 +342,18 @@ func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) { return bytesRead, nil } +func (pa *PathAttribute) Copy() *PathAttribute { + return &PathAttribute{ + ExtendedLength: pa.ExtendedLength, + Length: pa.Length, + Optional: pa.Optional, + Partial: pa.Partial, + Transitive: pa.Transitive, + TypeCode: pa.TypeCode, + Value: pa.Value, + } +} + // dumpNBytes is used to dump n bytes of buf. This is useful in case an path attributes // length doesn't match a fixed length's attributes length (e.g. ORIGIN is always an octet) func dumpNBytes(buf *bytes.Buffer, n uint16) error { diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index e74a660ab859e00b2d70f66175ddc6c750ffd495..f496e859f271c4c24fdee7233257695547dd6ba1 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -220,27 +220,51 @@ func (s *establishedState) updates(u *packet.BGPUpdate) { }, } - for pa := u.PathAttributes; pa != nil; pa = pa.Next { - switch pa.TypeCode { - case packet.OriginAttr: - path.BGPPath.Origin = pa.Value.(uint8) - case packet.LocalPrefAttr: - path.BGPPath.LocalPref = pa.Value.(uint32) - case packet.MEDAttr: - path.BGPPath.MED = pa.Value.(uint32) - case packet.NextHopAttr: - path.BGPPath.NextHop = pa.Value.(uint32) - case packet.ASPathAttr: - path.BGPPath.ASPath = pa.Value.(packet.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) + s.processAttributes(u.PathAttributes, path) + + s.fsm.adjRIBIn.AddPath(pfx, path) + } +} + +func (s *establishedState) processAttributes(attrs *packet.PathAttribute, path *route.Path) { + var unknownAttributes *packet.PathAttribute + var currentUnknown *packet.PathAttribute + + for pa := attrs; pa != nil; pa = pa.Next { + switch pa.TypeCode { + case packet.OriginAttr: + path.BGPPath.Origin = pa.Value.(uint8) + case packet.LocalPrefAttr: + path.BGPPath.LocalPref = pa.Value.(uint32) + case packet.MEDAttr: + path.BGPPath.MED = pa.Value.(uint32) + case packet.NextHopAttr: + path.BGPPath.NextHop = pa.Value.(uint32) + case packet.ASPathAttr: + path.BGPPath.ASPath = pa.Value.(packet.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) + default: + if !pa.Transitive { + continue } + + p := pa.Copy() + if unknownAttributes == nil { + unknownAttributes = p + currentUnknown = unknownAttributes + continue + } + + currentUnknown.Next = p + currentUnknown = p } - s.fsm.adjRIBIn.AddPath(pfx, path) } + + path.BGPPath.UnknownAttributes = unknownAttributes } func (s *establishedState) keepaliveReceived() (state, string) { diff --git a/protocols/bgp/server/fsm_established_test.go b/protocols/bgp/server/fsm_established_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4e92e4eede30ed4b0dfedeb1217e5d11962c6252 --- /dev/null +++ b/protocols/bgp/server/fsm_established_test.go @@ -0,0 +1,65 @@ +package server + +import ( + "testing" + + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" +) + +func TestProcessAttribues(t *testing.T) { + unknown3 := &packet.PathAttribute{ + Transitive: true, + TypeCode: 100, + Value: []byte{1, 2, 3, 4}, + Next: nil, + } + + unknown2 := &packet.PathAttribute{ + Transitive: false, + TypeCode: 150, + Value: []byte{20}, + Next: unknown3, + } + + unknown1 := &packet.PathAttribute{ + Transitive: true, + TypeCode: 200, + Value: []byte{5, 6}, + Next: unknown2, + } + + asPath := &packet.PathAttribute{ + Transitive: true, + TypeCode: packet.ASPathAttr, + Value: packet.ASPath{ + packet.ASPathSegment{ + Count: 0, + Type: packet.ASSequence, + ASNs: []uint32{}, + }, + }, + Next: unknown1, + } + + e := &establishedState{} + + p := &route.Path{ + BGPPath: &route.BGPPath{}, + } + e.processAttributes(asPath, p) + + expectedCodes := []uint8{200, 100} + expectedValues := [][]byte{[]byte{5, 6}, []byte{1, 2, 3, 4}} + + i := 0 + for attr := p.BGPPath.UnknownAttributes; attr != nil; attr = attr.Next { + 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") +} diff --git a/route/bgp_path.go b/route/bgp_path.go index 0ab7dc4b2a3f6a23d77be1a0613b68515a5d1604..cdb605e42cbc639d36c40c47f528dfda21a67011 100644 --- a/route/bgp_path.go +++ b/route/bgp_path.go @@ -11,18 +11,19 @@ import ( // BGPPath represents a set of BGP path attributes type BGPPath struct { - PathIdentifier uint32 - NextHop uint32 - LocalPref uint32 - ASPath packet.ASPath - ASPathLen uint16 - Origin uint8 - MED uint32 - EBGP bool - BGPIdentifier uint32 - Source uint32 - Communities []uint32 - LargeCommunities []packet.LargeCommunity + PathIdentifier uint32 + NextHop uint32 + LocalPref uint32 + ASPath packet.ASPath + ASPathLen uint16 + Origin uint8 + MED uint32 + EBGP bool + BGPIdentifier uint32 + Source uint32 + Communities []uint32 + LargeCommunities []packet.LargeCommunity + UnknownAttributes *packet.PathAttribute } // ECMP determines if routes b and c are euqal in terms of ECMP