/
BudarinSA
/
gobgp
Обзор
Документация
Войти
/
BudarinSA
/
gobgp
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
pkg/apiutil/attribute_test.go
1 961 строка
58 KB
Pau Nadeu
packet/bgp, apiutil: gofmt fixups in the RFC 9351 stack
03 июн 2026, 18:56
03 июн 2026, 18:56
0cc5ed4
Код
Авторство
О чём код?
// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or // implied. // See the License for the specific language governing permissions and // limitations under the License. package apiutil import ( "bytes" "net/netip" "testing" "google.golang.org/protobuf/proto" "github.com/osrg/gobgp/v4/api" "github.com/osrg/gobgp/v4/pkg/packet/bgp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func Test_OriginAttribute(t *testing.T) { assert := assert.New(t) input := &api.OriginAttribute{ Origin: 0, // IGP } a := &api.Attribute{Attr: &api.Attribute_Origin{Origin: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewOriginAttributeFromNative(n.(*bgp.PathAttributeOrigin)) assert.Equal(input.Origin, output.Origin) } func Test_LsAttributeDelayMetricRoundTrip(t *testing.T) { assert := assert.New(t) input := &api.LsAttribute{ Link: &api.LsAttributeLink{ UnidirectionalLinkDelayAnomalous: true, UnidirectionalLinkDelay: 8516, MinMaxUnidirectionalLinkDelayAnomalous: true, MinUnidirectionalLinkDelay: 8511, MaxUnidirectionalLinkDelay: 8527, UnidirectionalDelayVariation: 51, }, } native, err := UnmarshalLsAttribute(input) assert.NoError(err) if assert.NotNil(native.Link.UnidirectionalLinkDelay) { assert.True(native.Link.UnidirectionalLinkDelay.Flags.Anomalous) assert.Equal(uint32(8516), native.Link.UnidirectionalLinkDelay.Delay) } if assert.NotNil(native.Link.MinMaxUnidirectionalLinkDelay) { assert.True(native.Link.MinMaxUnidirectionalLinkDelay.Flags.Anomalous) assert.Equal(uint32(8511), native.Link.MinMaxUnidirectionalLinkDelay.MinDelay) assert.Equal(uint32(8527), native.Link.MinMaxUnidirectionalLinkDelay.MaxDelay) } if assert.NotNil(native.Link.UnidirectionalDelayVariation) { assert.Equal(uint32(51), *native.Link.UnidirectionalDelayVariation) } pathAttrLs := &bgp.PathAttributeLs{ TLVs: bgp.NewLsAttributeTLVs(native), } output, err := NewLsAttributeFromNative(pathAttrLs) assert.NoError(err) if assert.NotNil(output.Link) { assert.True(output.Link.UnidirectionalLinkDelayAnomalous) assert.Equal(uint32(8516), output.Link.UnidirectionalLinkDelay) assert.True(output.Link.MinMaxUnidirectionalLinkDelayAnomalous) assert.Equal(uint32(8511), output.Link.MinUnidirectionalLinkDelay) assert.Equal(uint32(8527), output.Link.MaxUnidirectionalLinkDelay) assert.Equal(uint32(51), output.Link.UnidirectionalDelayVariation) } } func Test_AsPathAttribute(t *testing.T) { assert := assert.New(t) input := &api.AsPathAttribute{ Segments: []*api.AsSegment{ { Type: 1, // SET Numbers: []uint32{100, 200}, }, { Type: 2, // SEQ Numbers: []uint32{300, 400}, }, }, } a := &api.Attribute{Attr: &api.Attribute_AsPath{AsPath: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewAsPathAttributeFromNative(n.(*bgp.PathAttributeAsPath)) assert.True(proto.Equal(input, output)) } func Test_NextHopAttribute(t *testing.T) { assert := assert.New(t) input := &api.NextHopAttribute{ NextHop: "192.168.0.1", } a := &api.Attribute{Attr: &api.Attribute_NextHop{NextHop: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewNextHopAttributeFromNative(n.(*bgp.PathAttributeNextHop)) assert.True(proto.Equal(input, output)) } func Test_MultiExitDiscAttribute(t *testing.T) { assert := assert.New(t) input := &api.MultiExitDiscAttribute{ Med: 100, } a := &api.Attribute{Attr: &api.Attribute_MultiExitDisc{MultiExitDisc: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMultiExitDiscAttributeFromNative(n.(*bgp.PathAttributeMultiExitDisc)) assert.True(proto.Equal(input, output)) } func Test_LocalPrefAttribute(t *testing.T) { assert := assert.New(t) input := &api.LocalPrefAttribute{ LocalPref: 100, } a := &api.Attribute{Attr: &api.Attribute_LocalPref{LocalPref: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewLocalPrefAttributeFromNative(n.(*bgp.PathAttributeLocalPref)) assert.True(proto.Equal(input, output)) } func Test_AtomicAggregateAttribute(t *testing.T) { assert := assert.New(t) input := &api.AtomicAggregateAttribute{} a := &api.Attribute{Attr: &api.Attribute_AtomicAggregate{AtomicAggregate: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewAtomicAggregateAttributeFromNative(n.(*bgp.PathAttributeAtomicAggregate)) // AtomicAggregateAttribute has no value assert.NotNil(output) } func Test_AggregatorAttribute(t *testing.T) { assert := assert.New(t) input := &api.AggregatorAttribute{ Asn: 65000, Address: "1.1.1.1", } a := &api.Attribute{Attr: &api.Attribute_Aggregator{Aggregator: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewAggregatorAttributeFromNative(n.(*bgp.PathAttributeAggregator)) assert.True(proto.Equal(input, output)) } func Test_CommunitiesAttribute(t *testing.T) { assert := assert.New(t) input := &api.CommunitiesAttribute{ Communities: []uint32{100, 200}, } a := &api.Attribute{Attr: &api.Attribute_Communities{Communities: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewCommunitiesAttributeFromNative(n.(*bgp.PathAttributeCommunities)) assert.True(proto.Equal(input, output)) } func Test_OriginatorIdAttribute(t *testing.T) { assert := assert.New(t) input := &api.OriginatorIdAttribute{ Id: "1.1.1.1", } a := &api.Attribute{Attr: &api.Attribute_OriginatorId{OriginatorId: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewOriginatorIdAttributeFromNative(n.(*bgp.PathAttributeOriginatorId)) assert.True(proto.Equal(input, output)) } func Test_ClusterListAttribute(t *testing.T) { assert := assert.New(t) input := &api.ClusterListAttribute{ Ids: []string{"1.1.1.1", "2.2.2.2"}, } a := &api.Attribute{Attr: &api.Attribute_ClusterList{ClusterList: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewClusterListAttributeFromNative(n.(*bgp.PathAttributeClusterList)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv4_UC(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{ {Nlri: &api.NLRI_Prefix{Prefix: &api.IPAddressPrefix{ PrefixLen: 24, Prefix: "192.168.101.0", }}}, {Nlri: &api.NLRI_Prefix{Prefix: &api.IPAddressPrefix{ PrefixLen: 24, Prefix: "192.168.201.0", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_UNICAST, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv6_UC(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{ {Nlri: &api.NLRI_Prefix{Prefix: &api.IPAddressPrefix{ PrefixLen: 64, Prefix: "2001:db8:1::", }}}, {Nlri: &api.NLRI_Prefix{Prefix: &api.IPAddressPrefix{ PrefixLen: 64, Prefix: "2001:db8:2::", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP6, Safi: api.Family_SAFI_UNICAST, }, NextHops: []string{"2001:db8::1", "fe80::1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv4_MPLS(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{ {Nlri: &api.NLRI_LabeledPrefix{LabeledPrefix: &api.LabeledIPAddressPrefix{ Labels: []uint32{100}, PrefixLen: 24, Prefix: "192.168.101.0", }}}, {Nlri: &api.NLRI_LabeledPrefix{LabeledPrefix: &api.LabeledIPAddressPrefix{ Labels: []uint32{200}, PrefixLen: 24, Prefix: "192.168.201.0", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_MPLS_LABEL, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv6_MPLS(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{ {Nlri: &api.NLRI_LabeledPrefix{LabeledPrefix: &api.LabeledIPAddressPrefix{ Labels: []uint32{100}, PrefixLen: 64, Prefix: "2001:db8:1::", }}}, {Nlri: &api.NLRI_LabeledPrefix{LabeledPrefix: &api.LabeledIPAddressPrefix{ Labels: []uint32{200}, PrefixLen: 64, Prefix: "2001:db8:2::", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP6, Safi: api.Family_SAFI_MPLS_LABEL, }, NextHops: []string{"2001:db8::1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv4_ENCAP(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{ {Nlri: &api.NLRI_Encapsulation{Encapsulation: &api.EncapsulationNLRI{ Address: "192.168.101.1", }}}, {Nlri: &api.NLRI_Encapsulation{Encapsulation: &api.EncapsulationNLRI{ Address: "192.168.201.1", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_ENCAPSULATION, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv6_ENCAP(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{ {Nlri: &api.NLRI_Encapsulation{Encapsulation: &api.EncapsulationNLRI{ Address: "2001:db8:1::1", }}}, {Nlri: &api.NLRI_Encapsulation{Encapsulation: &api.EncapsulationNLRI{ Address: "2001:db8:2::1", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP6, Safi: api.Family_SAFI_ENCAPSULATION, }, NextHops: []string{"2001:db8::1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_VPLS(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_TwoOctetAsn{ TwoOctetAsn: &api.RouteDistinguisherTwoOctetASN{ Admin: 65000, Assigned: 100, }, }} nlris := []*api.NLRI{{Nlri: &api.NLRI_Vpls{Vpls: &api.VPLSNLRI{ Rd: rd, VeId: 101, VeBlockOffset: 100, VeBlockSize: 10, LabelBlockBase: 1000, }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_VPLS, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_EVPN_AD_Route(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_TwoOctetAsn{ TwoOctetAsn: &api.RouteDistinguisherTwoOctetASN{ Admin: 65000, Assigned: 100, }, }} nlris := []*api.NLRI{ {Nlri: &api.NLRI_EvpnEthernetAd{EvpnEthernetAd: &api.EVPNEthernetAutoDiscoveryRoute{ Rd: rd, Esi: &api.EthernetSegmentIdentifier{ Type: 0, Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, }, EthernetTag: 100, Label: 200, }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_EVPN, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_EVPN_MAC_IP_Route(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} nlris := []*api.NLRI{ {Nlri: &api.NLRI_EvpnMacadv{EvpnMacadv: &api.EVPNMACIPAdvertisementRoute{ Rd: rd, Esi: &api.EthernetSegmentIdentifier{ Type: 0, Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, }, EthernetTag: 100, MacAddress: "aa:bb:cc:dd:ee:ff", IpAddress: "192.168.101.1", Labels: []uint32{200}, }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_EVPN, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_EVPN_MAC_IP_Route_MacOnly(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} nlris := []*api.NLRI{ {Nlri: &api.NLRI_EvpnMacadv{EvpnMacadv: &api.EVPNMACIPAdvertisementRoute{ Rd: rd, Esi: &api.EthernetSegmentIdentifier{ Type: 0, Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, }, EthernetTag: 100, MacAddress: "aa:bb:cc:dd:ee:ff", IpAddress: "", Labels: []uint32{200}, }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_EVPN, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_EVPN_MC_Route(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_FourOctetAsn{ FourOctetAsn: &api.RouteDistinguisherFourOctetASN{ Admin: 65000, Assigned: 100, }, }} nlris := []*api.NLRI{ {Nlri: &api.NLRI_EvpnMulticast{EvpnMulticast: &api.EVPNInclusiveMulticastEthernetTagRoute{ Rd: rd, EthernetTag: 100, IpAddress: "192.168.101.1", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_EVPN, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_EVPN_ES_Route(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} nlris := []*api.NLRI{ {Nlri: &api.NLRI_EvpnEthernetSegment{EvpnEthernetSegment: &api.EVPNEthernetSegmentRoute{ Rd: rd, Esi: &api.EthernetSegmentIdentifier{ Type: 0, Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, }, IpAddress: "192.168.101.1", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_EVPN, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_EVPN_Prefix_Route(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} nlris := []*api.NLRI{ {Nlri: &api.NLRI_EvpnIpPrefix{EvpnIpPrefix: &api.EVPNIPPrefixRoute{ Rd: rd, Esi: &api.EthernetSegmentIdentifier{ Type: 0, Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, }, EthernetTag: 100, IpPrefixLen: 24, IpPrefix: "192.168.101.0", Label: 200, GwAddress: "172.16.101.1", }}}, } input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_EVPN, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv4_VPN(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} nlris := []*api.NLRI{{Nlri: &api.NLRI_LabeledVpnIpPrefix{ LabeledVpnIpPrefix: &api.LabeledVPNIPAddressPrefix{ Labels: []uint32{100, 200}, Rd: rd, PrefixLen: 24, Prefix: "192.168.101.0", }, }}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_MPLS_VPN, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv6_VPN(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} nlris := []*api.NLRI{{Nlri: &api.NLRI_LabeledVpnIpPrefix{ LabeledVpnIpPrefix: &api.LabeledVPNIPAddressPrefix{ Labels: []uint32{100, 200}, Rd: rd, PrefixLen: 64, Prefix: "2001:db8:1::", }, }}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP6, Safi: api.Family_SAFI_MPLS_VPN, }, NextHops: []string{"2001:db8::1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_RTC_UC(t *testing.T) { assert := assert.New(t) rt := &api.RouteTarget{Rt: &api.RouteTarget_Ipv4AddressSpecific{ Ipv4AddressSpecific: &api.IPv4AddressSpecificExtended{ IsTransitive: true, SubType: 0x02, // Route Target Address: "1.1.1.1", LocalAdmin: 100, }, }} nlris := []*api.NLRI{{Nlri: &api.NLRI_RouteTargetMembership{ RouteTargetMembership: &api.RouteTargetMembershipNLRI{ Asn: 65000, Rt: rt, }, }}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_ROUTE_TARGET_CONSTRAINTS, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_FS_IPv4_UC(t *testing.T) { assert := assert.New(t) rules := []*api.FlowSpecRule{ {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 1, // Destination Prefix PrefixLen: 24, Prefix: "192.168.101.0", }}}, {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 2, // Source Prefix PrefixLen: 24, Prefix: "192.168.201.0", }}}, {Rule: &api.FlowSpecRule_Component{Component: &api.FlowSpecComponent{ Type: 3, // IP Protocol Items: []*api.FlowSpecComponentItem{ { Op: 0x80 | 0x01, // End, EQ Value: 6, // TCP }, }, }}}, } nlris := []*api.NLRI{{Nlri: &api.NLRI_FlowSpec{FlowSpec: &api.FlowSpecNLRI{ Rules: rules, }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_FLOW_SPEC_UNICAST, }, // NextHops: // No nexthop required Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_FS_IPv4_VPN(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} rules := []*api.FlowSpecRule{ {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 1, // Destination Prefix PrefixLen: 24, Prefix: "192.168.101.0", }}}, {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 2, // Source Prefix PrefixLen: 24, Prefix: "192.168.201.0", }}}, {Rule: &api.FlowSpecRule_Component{Component: &api.FlowSpecComponent{ Type: 3, // IP Protocol Items: []*api.FlowSpecComponentItem{ { Op: 0x80 | 0x01, // End, EQ Value: 6, // TCP }, }, }}}, } nlris := []*api.NLRI{{Nlri: &api.NLRI_VpnFlowSpec{VpnFlowSpec: &api.VPNFlowSpecNLRI{ Rd: rd, Rules: rules, }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_FLOW_SPEC_VPN, }, // NextHops: // No nexthop required Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_FS_IPv6_UC(t *testing.T) { assert := assert.New(t) rules := []*api.FlowSpecRule{ {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 1, // Destination Prefix PrefixLen: 64, Prefix: "2001:db8:1::", }}}, {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 2, // Source Prefix PrefixLen: 64, Prefix: "2001:db8:2::", }}}, {Rule: &api.FlowSpecRule_Component{Component: &api.FlowSpecComponent{ Type: 3, // Next Header Items: []*api.FlowSpecComponentItem{ { Op: 0x80 | 0x01, // End, EQ Value: 6, // TCP }, }, }}}, } nlris := []*api.NLRI{{Nlri: &api.NLRI_FlowSpec{FlowSpec: &api.FlowSpecNLRI{ Rules: rules, }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP6, Safi: api.Family_SAFI_FLOW_SPEC_UNICAST, }, // NextHops: // No nexthop required Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_FS_IPv6_VPN(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{ IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }, }} rules := []*api.FlowSpecRule{ {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 1, // Destination Prefix PrefixLen: 64, Prefix: "2001:db8:1::", }}}, {Rule: &api.FlowSpecRule_IpPrefix{IpPrefix: &api.FlowSpecIPPrefix{ Type: 2, // Source Prefix PrefixLen: 64, Prefix: "2001:db8:2::", }}}, {Rule: &api.FlowSpecRule_Component{Component: &api.FlowSpecComponent{ Type: 3, // Next Header Items: []*api.FlowSpecComponentItem{ { Op: 0x80 | 0x01, // End, EQ Value: 6, // TCP }, }, }}}, } nlris := []*api.NLRI{{Nlri: &api.NLRI_VpnFlowSpec{VpnFlowSpec: &api.VPNFlowSpecNLRI{ Rd: rd, Rules: rules, }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP6, Safi: api.Family_SAFI_FLOW_SPEC_VPN, }, // NextHops: // No nexthop required Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_FS_L2_VPN(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_IpAddress{IpAddress: &api.RouteDistinguisherIPAddress{ Admin: "1.1.1.1", Assigned: 100, }}} rules := []*api.FlowSpecRule{ {Rule: &api.FlowSpecRule_Mac{Mac: &api.FlowSpecMAC{ Type: 15, // Source MAC Address: "aa:bb:cc:11:22:33", }}}, {Rule: &api.FlowSpecRule_Mac{Mac: &api.FlowSpecMAC{ Type: 16, // Destination MAC Address: "dd:ee:ff:11:22:33", }}}, {Rule: &api.FlowSpecRule_Component{Component: &api.FlowSpecComponent{ Type: 21, // VLAN ID Items: []*api.FlowSpecComponentItem{ { Op: 0x80 | 0x01, // End, EQ Value: 100, }, }, }}}, } nlris := []*api.NLRI{{Nlri: &api.NLRI_VpnFlowSpec{VpnFlowSpec: &api.VPNFlowSpecNLRI{ Rd: rd, Rules: rules, }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_L2VPN, Safi: api.Family_SAFI_FLOW_SPEC_VPN, }, // NextHops: // No nexthop required Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_IPv4_Opaque(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{{Nlri: &api.NLRI_Opaque{Opaque: &api.OpaqueNLRI{ Key: []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f}, // hello Value: []byte{0x77, 0x6f, 0x72, 0x6c, 0x64}, // world }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_OPAQUE, Safi: api.Family_SAFI_KEY_VALUE, }, NextHops: []string{"192.168.1.1"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_MUPInterworkSegmentDiscoveryRoute(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_TwoOctetAsn{TwoOctetAsn: &api.RouteDistinguisherTwoOctetASN{ Admin: 65000, Assigned: 100, }}} nlris := []*api.NLRI{{Nlri: &api.NLRI_MupInterworkSegmentDiscovery{MupInterworkSegmentDiscovery: &api.MUPInterworkSegmentDiscoveryRoute{ Rd: rd, Prefix: "10.0.0.0/24", }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_MUP, }, NextHops: []string{"0.0.0.0"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_MUPDirectSegmentDiscoveryRoute(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_TwoOctetAsn{TwoOctetAsn: &api.RouteDistinguisherTwoOctetASN{ Admin: 65000, Assigned: 100, }}} nlris := []*api.NLRI{{Nlri: &api.NLRI_MupDirectSegmentDiscovery{MupDirectSegmentDiscovery: &api.MUPDirectSegmentDiscoveryRoute{ Rd: rd, Address: "10.0.0.1", }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_MUP, }, NextHops: []string{"0.0.0.0"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpReachNLRIAttribute_MUPType1SessionTransformedRoute(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_TwoOctetAsn{TwoOctetAsn: &api.RouteDistinguisherTwoOctetASN{ Admin: 65000, Assigned: 100, }}} tests := []struct { name string in *api.MUPType1SessionTransformedRoute }{ { name: "IPv4", in: &api.MUPType1SessionTransformedRoute{ Rd: rd, Prefix: "192.168.100.1/32", Teid: 12345, Qfi: 9, EndpointAddressLength: 32, EndpointAddress: "10.0.0.1", }, }, { name: "IPv4_with_SourceAddress", in: &api.MUPType1SessionTransformedRoute{ Rd: rd, Prefix: "192.168.100.1/32", Teid: 12345, Qfi: 9, EndpointAddressLength: 32, EndpointAddress: "10.0.0.1", SourceAddressLength: 32, SourceAddress: "10.0.0.2", }, }, } for _, tt := range tests { nlris := []*api.NLRI{{Nlri: &api.NLRI_MupType_1SessionTransformed{MupType_1SessionTransformed: tt.in}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_MUP, }, NextHops: []string{"0.0.0.0"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } } func Test_MpReachNLRIAttribute_MUPType2SessionTransformedRoute(t *testing.T) { assert := assert.New(t) rd := &api.RouteDistinguisher{Rd: &api.RouteDistinguisher_TwoOctetAsn{TwoOctetAsn: &api.RouteDistinguisherTwoOctetASN{ Admin: 65000, Assigned: 100, }}} nlris := []*api.NLRI{{Nlri: &api.NLRI_MupType_2SessionTransformed{MupType_2SessionTransformed: &api.MUPType2SessionTransformedRoute{ Rd: rd, Teid: 12345, EndpointAddressLength: 64, EndpointAddress: "10.0.0.1", }}}} input := &api.MpReachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_MUP, }, NextHops: []string{"0.0.0.0"}, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpReach{MpReach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) assert.True(proto.Equal(input, output)) } func Test_MpUnreachNLRIAttribute_IPv4_UC(t *testing.T) { assert := assert.New(t) nlris := []*api.NLRI{ {Nlri: &api.NLRI_Prefix{Prefix: &api.IPAddressPrefix{ PrefixLen: 24, Prefix: "192.168.101.0", }}}, {Nlri: &api.NLRI_Prefix{Prefix: &api.IPAddressPrefix{ PrefixLen: 24, Prefix: "192.168.201.0", }}}, } input := &api.MpUnreachNLRIAttribute{ Family: &api.Family{ Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_UNICAST, }, Nlris: nlris, } a := &api.Attribute{Attr: &api.Attribute_MpUnreach{MpUnreach: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewMpUnreachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpUnreachNLRI)) assert.True(proto.Equal(input, output)) } func Test_ExtendedCommunitiesAttribute(t *testing.T) { assert := assert.New(t) communities := []*api.ExtendedCommunity{ {Extcom: &api.ExtendedCommunity_TwoOctetAsSpecific{TwoOctetAsSpecific: &api.TwoOctetAsSpecificExtended{ IsTransitive: true, SubType: 0x02, // ROUTE_TARGET Asn: 65001, LocalAdmin: 100, }}}, {Extcom: &api.ExtendedCommunity_Ipv4AddressSpecific{Ipv4AddressSpecific: &api.IPv4AddressSpecificExtended{ IsTransitive: true, SubType: 0x02, // ROUTE_TARGET Address: "2.2.2.2", LocalAdmin: 200, }}}, {Extcom: &api.ExtendedCommunity_FourOctetAsSpecific{FourOctetAsSpecific: &api.FourOctetAsSpecificExtended{ IsTransitive: true, SubType: 0x02, // ROUTE_TARGET Asn: 65003, LocalAdmin: 300, }}}, {Extcom: &api.ExtendedCommunity_Validation{Validation: &api.ValidationExtended{ State: 0, // VALID }}}, {Extcom: &api.ExtendedCommunity_Color{Color: &api.ColorExtended{ Color: 400, }}}, {Extcom: &api.ExtendedCommunity_Encap{Encap: &api.EncapExtended{ TunnelType: 8, // VXLAN }}}, {Extcom: &api.ExtendedCommunity_DefaultGateway{DefaultGateway: &api.DefaultGatewayExtended{ // No value }}}, {Extcom: &api.ExtendedCommunity_Opaque{Opaque: &api.OpaqueExtended{ IsTransitive: true, Value: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}, }}}, {Extcom: &api.ExtendedCommunity_EsiLabel{EsiLabel: &api.ESILabelExtended{ IsSingleActive: true, Label: 500, }}}, {Extcom: &api.ExtendedCommunity_EsImport{EsImport: &api.ESImportRouteTarget{ EsImport: "aa:bb:cc:dd:ee:ff", }}}, {Extcom: &api.ExtendedCommunity_MacMobility{MacMobility: &api.MacMobilityExtended{ IsSticky: true, SequenceNum: 1, }}}, {Extcom: &api.ExtendedCommunity_RouterMac{RouterMac: &api.RouterMacExtended{ Mac: "ff:ee:dd:cc:bb:aa", }}}, {Extcom: &api.ExtendedCommunity_TrafficRate{TrafficRate: &api.TrafficRateExtended{ Asn: 65004, Rate: 100.0, }}}, {Extcom: &api.ExtendedCommunity_TrafficAction{TrafficAction: &api.TrafficActionExtended{ Terminal: true, Sample: false, }}}, {Extcom: &api.ExtendedCommunity_RedirectTwoOctetAsSpecific{RedirectTwoOctetAsSpecific: &api.RedirectTwoOctetAsSpecificExtended{ Asn: 65005, LocalAdmin: 500, }}}, {Extcom: &api.ExtendedCommunity_RedirectIpv4AddressSpecific{RedirectIpv4AddressSpecific: &api.RedirectIPv4AddressSpecificExtended{ Address: "6.6.6.6", LocalAdmin: 600, }}}, {Extcom: &api.ExtendedCommunity_RedirectFourOctetAsSpecific{RedirectFourOctetAsSpecific: &api.RedirectFourOctetAsSpecificExtended{ Asn: 65007, LocalAdmin: 700, }}}, {Extcom: &api.ExtendedCommunity_TrafficRemark{TrafficRemark: &api.TrafficRemarkExtended{ Dscp: 0x0a, // AF11 }}}, {Extcom: &api.ExtendedCommunity_Mup{Mup: &api.MUPExtended{ SegmentId2: 10, SegmentId4: 100, }}}, {Extcom: &api.ExtendedCommunity_Unknown{Unknown: &api.UnknownExtended{ Type: 0xff, // Max of uint8 Value: []byte{1, 2, 3, 4, 5, 6, 7}, }}}, {Extcom: &api.ExtendedCommunity_LinkBandwidth{LinkBandwidth: &api.LinkBandwidthExtended{ Asn: 65004, Bandwidth: 125000.0, }}}, {Extcom: &api.ExtendedCommunity_Vpls{Vpls: &api.VPLSExtended{ ControlFlags: 0x00, Mtu: 1500, }}}, {Extcom: &api.ExtendedCommunity_Etree{Etree: &api.ETreeExtended{ IsLeaf: true, Label: 5001, }}}, {Extcom: &api.ExtendedCommunity_MulticastFlags{MulticastFlags: &api.MulticastFlagsExtended{ IsIgmpProxy: true, IsMldProxy: false, }}}, } input := &api.ExtendedCommunitiesAttribute{ Communities: communities, } a := &api.Attribute{Attr: &api.Attribute_ExtendedCommunities{ExtendedCommunities: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewExtendedCommunitiesAttributeFromNative(n.(*bgp.PathAttributeExtendedCommunities)) assert.True(proto.Equal(input, output)) } func Test_As4PathAttribute(t *testing.T) { assert := assert.New(t) input := &api.As4PathAttribute{ Segments: []*api.AsSegment{ { Type: 1, // SET Numbers: []uint32{100, 200}, }, { Type: 2, // SEQ Numbers: []uint32{300, 400}, }, }, } a := &api.Attribute{Attr: &api.Attribute_As4Path{As4Path: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewAs4PathAttributeFromNative(n.(*bgp.PathAttributeAs4Path)) assert.True(proto.Equal(input, output)) } func Test_As4AggregatorAttribute(t *testing.T) { assert := assert.New(t) input := &api.As4AggregatorAttribute{ Asn: 65000, Address: "1.1.1.1", } a := &api.Attribute{Attr: &api.Attribute_As4Aggregator{As4Aggregator: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewAs4AggregatorAttributeFromNative(n.(*bgp.PathAttributeAs4Aggregator)) assert.True(proto.Equal(input, output)) } func Test_PmsiTunnelAttribute(t *testing.T) { assert := assert.New(t) input := &api.PmsiTunnelAttribute{ Flags: 0x01, // IsLeafInfoRequired = true Type: 6, // INGRESS_REPL Label: 100, Id: netip.MustParseAddr("1.1.1.1").AsSlice(), // IngressReplTunnelID with IPv4 } a := &api.Attribute{Attr: &api.Attribute_PmsiTunnel{PmsiTunnel: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewPmsiTunnelAttributeFromNative(n.(*bgp.PathAttributePmsiTunnel)) assert.True(proto.Equal(input, output)) } func Test_TunnelEncapAttribute(t *testing.T) { assert := assert.New(t) subTlvs := []*api.TunnelEncapTLV_TLV{ {Tlv: &api.TunnelEncapTLV_TLV_Encapsulation{Encapsulation: &api.TunnelEncapSubTLVEncapsulation{ Key: 100, Cookie: []byte{0x11, 0x22, 0x33, 0x44}, }}}, {Tlv: &api.TunnelEncapTLV_TLV_Protocol{Protocol: &api.TunnelEncapSubTLVProtocol{ Protocol: 200, }}}, {Tlv: &api.TunnelEncapTLV_TLV_Color{Color: &api.TunnelEncapSubTLVColor{ Color: 300, }}}, {Tlv: &api.TunnelEncapTLV_TLV_UdpDestPort{UdpDestPort: &api.TunnelEncapSubTLVUDPDestPort{ Port: 400, }}}, {Tlv: &api.TunnelEncapTLV_TLV_Unknown{Unknown: &api.TunnelEncapSubTLVUnknown{ Type: 0xff, // Max of uint8 Value: []byte{0x55, 0x66, 0x77, 0x88}, }}}, } input := &api.TunnelEncapAttribute{ Tlvs: []*api.TunnelEncapTLV{ { Type: 8, // VXLAN Tlvs: subTlvs, }, }, } a := &api.Attribute{Attr: &api.Attribute_TunnelEncap{TunnelEncap: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewTunnelEncapAttributeFromNative(n.(*bgp.PathAttributeTunnelEncap)) assert.True(proto.Equal(input, output)) } func Test_IP6ExtendedCommunitiesAttribute(t *testing.T) { assert := assert.New(t) communities := []*api.IP6ExtendedCommunitiesAttribute_Community{ {Extcom: &api.IP6ExtendedCommunitiesAttribute_Community_Ipv6AddressSpecific{ Ipv6AddressSpecific: &api.IPv6AddressSpecificExtended{ IsTransitive: true, SubType: 0xff, // Max of uint8 Address: "2001:db8:1::1", LocalAdmin: 100, }, }}, {Extcom: &api.IP6ExtendedCommunitiesAttribute_Community_RedirectIpv6AddressSpecific{ RedirectIpv6AddressSpecific: &api.RedirectIPv6AddressSpecificExtended{ Address: "2001:db8:2::1", LocalAdmin: 200, }, }}, } input := &api.IP6ExtendedCommunitiesAttribute{ Communities: communities, } a := &api.Attribute{Attr: &api.Attribute_Ip6ExtendedCommunities{Ip6ExtendedCommunities: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewIP6ExtendedCommunitiesAttributeFromNative(n.(*bgp.PathAttributeIP6ExtendedCommunities)) assert.True(proto.Equal(input, output)) } func Test_AigpAttribute(t *testing.T) { assert := assert.New(t) tlvs := []*api.AigpAttribute_TLV{ {Tlv: &api.AigpAttribute_TLV_IgpMetric{IgpMetric: &api.AigpTLVIGPMetric{ Metric: 50, }}}, {Tlv: &api.AigpAttribute_TLV_Unknown{Unknown: &api.AigpTLVUnknown{ Type: 0xff, // Max of uint8 Value: []byte{0x11, 0x22, 0x33, 0x44}, }}}, } input := &api.AigpAttribute{ Tlvs: tlvs, } a := &api.Attribute{Attr: &api.Attribute_Aigp{Aigp: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewAigpAttributeFromNative(n.(*bgp.PathAttributeAigp)) assert.True(proto.Equal(input, output)) } func Test_LargeCommunitiesAttribute(t *testing.T) { assert := assert.New(t) input := &api.LargeCommunitiesAttribute{ Communities: []*api.LargeCommunity{ { GlobalAdmin: 65001, LocalData1: 100, LocalData2: 200, }, { GlobalAdmin: 65002, LocalData1: 300, LocalData2: 400, }, }, } a := &api.Attribute{Attr: &api.Attribute_LargeCommunities{LargeCommunities: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewLargeCommunitiesAttributeFromNative(n.(*bgp.PathAttributeLargeCommunities)) assert.True(proto.Equal(input, output)) } func Test_UnknownAttribute(t *testing.T) { assert := assert.New(t) input := &api.UnknownAttribute{ Flags: 1<<6 | 1<<7, Type: 0xff, Value: []byte{0x11, 0x22, 0x33, 0x44}, } a := &api.Attribute{Attr: &api.Attribute_Unknown{Unknown: input}} n, err := UnmarshalAttribute(a) assert.NoError(err) output, _ := NewUnknownAttributeFromNative(n.(*bgp.PathAttributeUnknown)) assert.True(proto.Equal(input, output)) } func TestFullCyclePrefixSID(t *testing.T) { tests := []struct { name string input []byte }{ { name: "srv6 prefix sid", input: []byte{0xc0, 0x28, 0x25, 0x05, 0x00, 0x22, 0x00, 0x01, 0x00, 0x1e, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x00, 0x06, 0x28, 0x18, 0x10, 0x00, 0x10, 0x40}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { attribute := bgp.PathAttributePrefixSID{} if err := attribute.DecodeFromBytes(tt.input); err != nil { t.Fatalf("test failed with error: %+v", err) } // Converting from Native to API apiPrefixSID, err := NewPrefixSIDAttributeFromNative(&attribute) if err != nil { t.Fatalf("test failed with error: %+v", err) } // Converting back from API to Native recoveredPrefixSID, err := UnmarshalPrefixSID(apiPrefixSID) if err != nil { t.Fatalf("test failed with error: %+v", err) } if recoveredPrefixSID.Len() != attribute.Len() { t.Fatalf("recovered attribute length (%d) is not matching original attribute length (%d)", recoveredPrefixSID.Len(), attribute.Len()) } recovered, err := recoveredPrefixSID.Serialize() if err != nil { t.Fatalf("test failed with error: %+v", err) } if !bytes.Equal(tt.input, recovered) { t.Fatalf("round trip conversion test failed as expected prefix sid attribute %+v does not match actual: %+v", tt.input, recovered) } }) } } func TestUnmarshalSRSegments_NilFlags(t *testing.T) { // Flags is an optional protobuf sub-message. A caller that builds // api.SegmentTypeA or api.SegmentTypeB without setting Flags must // not cause a nil pointer dereference; the result should be all-zero // flag bits. t.Run("SegmentTypeA_nil_flags", func(t *testing.T) { segs := []*api.TunnelEncapSubTLVSRSegmentList_Segment{ {Segment: &api.TunnelEncapSubTLVSRSegmentList_Segment_A{ A: &api.SegmentTypeA{Label: 100}, // Flags intentionally omitted (nil) }}, } result, err := UnmarshalSRSegments(segs) assert.NoError(t, err) require.Len(t, result, 1) seg, ok := result[0].(*bgp.SegmentTypeA) require.True(t, ok) assert.Equal(t, uint32(100), seg.Label) assert.Equal(t, uint8(0), seg.Flags) }) t.Run("SegmentTypeB_nil_flags", func(t *testing.T) { sid := []byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01} segs := []*api.TunnelEncapSubTLVSRSegmentList_Segment{ {Segment: &api.TunnelEncapSubTLVSRSegmentList_Segment_B{ B: &api.SegmentTypeB{Sid: sid}, // Flags intentionally omitted (nil) }}, } result, err := UnmarshalSRSegments(segs) assert.NoError(t, err) require.Len(t, result, 1) seg, ok := result[0].(*bgp.SegmentTypeB) require.True(t, ok) assert.Equal(t, sid, seg.SID) assert.Equal(t, uint8(0), seg.Flags) }) } func TestUnmarshalSRSegments_RoundTrip(t *testing.T) { t.Run("SegmentTypeA", func(t *testing.T) { orig := []bgp.TunnelEncapSubTLVInterface{ &bgp.SegmentTypeA{ TunnelEncapSubTLV: bgp.TunnelEncapSubTLV{Type: bgp.EncapSubTLVType(bgp.TypeA), Length: 6}, Label: 200, Flags: 0x80 | 0x10, // VFlag + BFlag }, } marshaled, err := MarshalSRSegments(orig) require.NoError(t, err) result, err := UnmarshalSRSegments(marshaled) require.NoError(t, err) require.Len(t, result, 1) seg, ok := result[0].(*bgp.SegmentTypeA) require.True(t, ok) assert.Equal(t, uint32(200), seg.Label) assert.Equal(t, uint8(0x80|0x10), seg.Flags) }) t.Run("SegmentTypeB", func(t *testing.T) { sid := []byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01} orig := []bgp.TunnelEncapSubTLVInterface{ &bgp.SegmentTypeB{ TunnelEncapSubTLV: bgp.TunnelEncapSubTLV{Type: bgp.EncapSubTLVType(bgp.TypeB), Length: 18}, SID: sid, Flags: 0x40 | 0x20, // AFlag + SFlag }, } marshaled, err := MarshalSRSegments(orig) require.NoError(t, err) result, err := UnmarshalSRSegments(marshaled) require.NoError(t, err) require.Len(t, result, 1) seg, ok := result[0].(*bgp.SegmentTypeB) require.True(t, ok) assert.Equal(t, sid, seg.SID) assert.Equal(t, uint8(0x40|0x20), seg.Flags) }) t.Run("SegmentTypeB_with_SRv6EBS", func(t *testing.T) { sid := []byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02} orig := []bgp.TunnelEncapSubTLVInterface{ &bgp.SegmentTypeB{ TunnelEncapSubTLV: bgp.TunnelEncapSubTLV{Type: bgp.EncapSubTLVType(bgp.TypeB), Length: 18}, SID: sid, Flags: 0x80, SRv6EBS: &bgp.SRv6EndpointBehaviorStructure{ Behavior: bgp.END, BlockLen: 40, NodeLen: 24, FuncLen: 16, ArgLen: 0, }, }, } marshaled, err := MarshalSRSegments(orig) require.NoError(t, err) result, err := UnmarshalSRSegments(marshaled) require.NoError(t, err) require.Len(t, result, 1) seg, ok := result[0].(*bgp.SegmentTypeB) require.True(t, ok) assert.Equal(t, sid, seg.SID) assert.Equal(t, uint8(0x80), seg.Flags) require.NotNil(t, seg.SRv6EBS) assert.Equal(t, bgp.END, seg.SRv6EBS.Behavior) assert.Equal(t, uint8(40), seg.SRv6EBS.BlockLen) assert.Equal(t, uint8(24), seg.SRv6EBS.NodeLen) assert.Equal(t, uint8(16), seg.SRv6EBS.FuncLen) }) } func TestFullCycleSRv6SIDStructureSubSubTLV(t *testing.T) { tests := []struct { name string input []byte }{ { name: "srv6 prefix sid", input: []byte{0x01, 0x00, 0x06, 0x28, 0x18, 0x10, 0x00, 0x10, 0x40}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sstlv := bgp.SRv6SIDStructureSubSubTLV{} if err := sstlv.DecodeFromBytes(tt.input); err != nil { t.Fatalf("test failed with error: %+v", err) } // Converting from Native to API apiPrefixSID, _ := MarshalSRv6SubSubTLVs([]bgp.PrefixSIDTLVInterface{&sstlv}) // Converting back from API to Native _, recoveredPrefixSID, err := UnmarshalSubSubTLVs(apiPrefixSID) if err != nil { t.Fatalf("test failed with error: %+v", err) } recovered, err := recoveredPrefixSID[0].Serialize() if err != nil { t.Fatalf("test failed with error: %+v", err) } if !bytes.Equal(tt.input, recovered) { t.Fatalf("round trip conversion test failed as expected prefix sid attribute %+v does not match actual: %+v", tt.input, recovered) } }) } } // TestFullCycleFlexAlgoDefAndFAPM exercises the api <-> packet // round-trip for the RFC 9351 Flex-Algorithm Definition (FAD) Node // Attribute TLV and the FAPM (Prefix Attribute TLV 1044). The packet // layer round-trip is covered in pkg/packet/bgp; here we confirm the // apiutil mapping preserves every field on the way from bgp.LsAttribute // down to api.LsAttribute and back. func TestFullCycleFlexAlgoDefAndFAPM(t *testing.T) { srgbStart := uint32(16000) // Build a synthetic PathAttributeLs whose Extract output already // carries the FAD + multi-SID + FAPM fields we want to surface. // We bypass the wire by hand-building the TLV slice; the goal is // to drive NewLsAttributeFromNative and UnmarshalLsAttribute. tlvs := []bgp.LsTLVInterface{ &bgp.LsTLVFlexAlgoDef{ LsTLV: bgp.LsTLV{Type: bgp.LS_TLV_FLEX_ALGO_DEF}, Algorithm: 128, MetricType: 1, // Min-Delay CalcType: 0, Priority: 200, ExcludeAny: []uint32{0x0F}, IncludeAny: []uint32{0xF0}, IncludeAll: []uint32{0xAA}, Flags: []byte{0x80, 0x00, 0x00, 0x00}, ExcludeSRLG: []uint32{42, 43}, }, &bgp.LsTLVPrefixSID{ LsTLV: bgp.LsTLV{Type: bgp.LS_TLV_PREFIX_SID}, Algorithm: 0, Flags: 0, SID: srgbStart + 1, }, &bgp.LsTLVPrefixSID{ LsTLV: bgp.LsTLV{Type: bgp.LS_TLV_PREFIX_SID}, Algorithm: 128, Flags: 0x08, SID: srgbStart + 128, }, &bgp.LsTLVFADPrefixMetric{ LsTLV: bgp.LsTLV{Type: bgp.LS_TLV_FAD_PREFIX_METRIC}, Algorithm: 128, Flags: 0, Metric: 10000, }, } pa := &bgp.PathAttributeLs{TLVs: tlvs} apiAttr, err := NewLsAttributeFromNative(pa) require.NoError(t, err) require.NotNil(t, apiAttr) require.Len(t, apiAttr.Node.FlexAlgoDefs, 1) fad := apiAttr.Node.FlexAlgoDefs[0] assert.Equal(t, uint32(128), fad.Algorithm) assert.Equal(t, uint32(1), fad.MetricType) assert.True(t, fad.MetricTypeKnown) assert.Equal(t, uint32(200), fad.Priority) assert.Equal(t, []uint32{0x0F}, fad.ExcludeAnyAffinity) assert.Equal(t, []uint32{0xF0}, fad.IncludeAnyAffinity) assert.Equal(t, []uint32{0xAA}, fad.IncludeAllAffinity) assert.Equal(t, []byte{0x80, 0x00, 0x00, 0x00}, fad.DefinitionFlags) assert.Equal(t, []uint32{42, 43}, fad.ExcludeSrlg) require.Len(t, apiAttr.Prefix.SrPrefixSids, 2) assert.Equal(t, uint32(0), apiAttr.Prefix.SrPrefixSids[0].Algorithm) assert.Equal(t, srgbStart+1, apiAttr.Prefix.SrPrefixSids[0].Sid) assert.Equal(t, uint32(128), apiAttr.Prefix.SrPrefixSids[1].Algorithm) assert.Equal(t, uint32(0x08), apiAttr.Prefix.SrPrefixSids[1].Flags) assert.Equal(t, srgbStart+128, apiAttr.Prefix.SrPrefixSids[1].Sid) // Singular field still populated for the Algorithm-0 SID. assert.Equal(t, srgbStart+1, apiAttr.Prefix.SrPrefixSid) require.Len(t, apiAttr.Prefix.FadPrefixMetrics, 1) assert.Equal(t, uint32(128), apiAttr.Prefix.FadPrefixMetrics[0].Algorithm) assert.Equal(t, uint32(10000), apiAttr.Prefix.FadPrefixMetrics[0].Metric) // proto round-trip: marshal + unmarshal must preserve byte-for-byte. enc, err := proto.Marshal(apiAttr) require.NoError(t, err) clone := &api.LsAttribute{} require.NoError(t, proto.Unmarshal(enc, clone)) assert.True(t, proto.Equal(apiAttr, clone)) // api -> bgp rehydration: every FAD / SID / FAPM field round-trips. back, err := UnmarshalLsAttribute(clone) require.NoError(t, err) require.Len(t, back.Node.FlexAlgoDefs, 1) assert.Equal(t, uint8(128), back.Node.FlexAlgoDefs[0].Algorithm) assert.Equal(t, uint8(1), back.Node.FlexAlgoDefs[0].MetricType) assert.Equal(t, []uint32{42, 43}, back.Node.FlexAlgoDefs[0].ExcludeSRLG) require.Len(t, back.Prefix.SrPrefixSIDs, 2) assert.Equal(t, uint8(128), back.Prefix.SrPrefixSIDs[1].Algorithm) assert.Equal(t, srgbStart+128, back.Prefix.SrPrefixSIDs[1].SID) require.Len(t, back.Prefix.FadPrefixMetrics, 1) assert.Equal(t, uint32(10000), back.Prefix.FadPrefixMetrics[0].Metric) } // TestFlexAlgo_FullWirePath drives the full RFC 9351 / RFC 9085 // path end-to-end: // // wire bytes -> PathAttributeLs.DecodeFromBytes // -> Extract (structured fields) // -> NewLsAttributeFromNative (api projection) // -> proto.Marshal + proto.Unmarshal // -> UnmarshalLsAttribute (rehydrate bgp.LsAttribute) // -> Serialize (back to wire bytes) // // The fixture is a hand-built BGP-LS Attribute (MP_REACH stripped, we // only test the attribute payload) carrying a Flex-Algo Definition // (TLV 1039 with three nested sub-TLVs), two Prefix-SID TLVs (algo 0 // and algo 128) and one FAPM (TLV 1044). Every structured field on // the way out matches the wire bytes on the way in. func TestFlexAlgo_FullWirePath(t *testing.T) { const ( srgbStart = uint32(16000) ) // MP_REACH_NLRI is omitted; we test PathAttributeLs payload only. // Outer wrapper: flags 0x80 (Optional, Non-Transitive, // non-extended), type 41 (BGP_ATTR_TYPE_LS), 1-byte length. body := []byte{ // TLV 1039 FAD, length 28 (4-byte fixed header + three // 8-byte sub-TLVs): // algo=128 metric=0 (IGP) calc=0 (SPF) prio=200 // sub-TLV 1040 Exclude-Any 0x0000000F // sub-TLV 1041 Include-Any 0x000000F0 // sub-TLV 1043 Definition Flags 0x80000000 0x04, 0x0F, 0x00, 0x1C, 0x80, 0x00, 0x00, 0xC8, 0x04, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0F, 0x04, 0x11, 0x00, 0x04, 0x00, 0x00, 0x00, 0xF0, 0x04, 0x13, 0x00, 0x04, 0x80, 0x00, 0x00, 0x00, // TLV 1158 Prefix-SID algo 0 (default SPF), SID 16001 in // 8-byte (4-byte SID) form: flags + algo + 2B reserved + 4B SID. 0x04, 0x86, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x81, // TLV 1158 Prefix-SID algo 128, V-flag set (absolute label), // SID 16128 in 8-byte form. 0x04, 0x86, 0x00, 0x08, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, // TLV 1044 FAPM (Flexible Algorithm Prefix Metric): algo 128 // metric 10000 (0x2710), flags 0, reserved 0. 0x04, 0x14, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x10, } hdr := []byte{0x80, 0x29, byte(len(body))} wire := append([]byte{}, hdr...) wire = append(wire, body...) // Decode the wire bytes into the structured PathAttributeLs. pa := &bgp.PathAttributeLs{} require.NoError(t, pa.DecodeFromBytes(wire)) // Extract the structured LsAttribute and check every field. ls := pa.Extract() require.Len(t, ls.Node.FlexAlgoDefs, 1) fad := ls.Node.FlexAlgoDefs[0] assert.Equal(t, uint8(128), fad.Algorithm) assert.Equal(t, uint8(0), fad.MetricType) assert.True(t, fad.MetricTypeKnown) assert.Equal(t, uint8(0), fad.CalcType) assert.Equal(t, uint8(200), fad.Priority) assert.Equal(t, []uint32{0x0F}, fad.ExcludeAny) assert.Equal(t, []uint32{0xF0}, fad.IncludeAny) assert.Equal(t, []byte{0x80, 0x00, 0x00, 0x00}, fad.Flags) require.Len(t, ls.Prefix.SrPrefixSIDs, 2) assert.Equal(t, uint8(0), ls.Prefix.SrPrefixSIDs[0].Algorithm) assert.Equal(t, srgbStart+1, ls.Prefix.SrPrefixSIDs[0].SID) assert.Equal(t, uint8(128), ls.Prefix.SrPrefixSIDs[1].Algorithm) assert.Equal(t, uint8(0x08), ls.Prefix.SrPrefixSIDs[1].Flags) assert.Equal(t, srgbStart+128, ls.Prefix.SrPrefixSIDs[1].SID) require.NotNil(t, ls.Prefix.SrPrefixSID) assert.Equal(t, srgbStart+1, *ls.Prefix.SrPrefixSID) require.Len(t, ls.Prefix.FadPrefixMetrics, 1) assert.Equal(t, uint8(128), ls.Prefix.FadPrefixMetrics[0].Algorithm) assert.Equal(t, uint32(10000), ls.Prefix.FadPrefixMetrics[0].Metric) // Cross the apiutil boundary in both directions. apiAttr, err := NewLsAttributeFromNative(pa) require.NoError(t, err) require.Len(t, apiAttr.Node.FlexAlgoDefs, 1) require.Len(t, apiAttr.Prefix.SrPrefixSids, 2) require.Len(t, apiAttr.Prefix.FadPrefixMetrics, 1) enc, err := proto.Marshal(apiAttr) require.NoError(t, err) clone := &api.LsAttribute{} require.NoError(t, proto.Unmarshal(enc, clone)) assert.True(t, proto.Equal(apiAttr, clone)) back, err := UnmarshalLsAttribute(clone) require.NoError(t, err) require.Len(t, back.Node.FlexAlgoDefs, 1) assert.Equal(t, uint8(128), back.Node.FlexAlgoDefs[0].Algorithm) require.Len(t, back.Prefix.SrPrefixSIDs, 2) assert.Equal(t, srgbStart+128, back.Prefix.SrPrefixSIDs[1].SID) require.Len(t, back.Prefix.FadPrefixMetrics, 1) // Re-serialise the original PathAttributeLs (its TLV slice still // carries the original wire-shaped sub-TLVs) and confirm the // byte sequence matches the input. This is the strongest // regression signal against silent re-encoding drift. out, err := pa.Serialize() require.NoError(t, err) assert.True(t, bytes.Equal(wire, out), "wire bytes round-trip mismatch:\n in: % x\n out: % x", wire, out) } func TestFullCycleSRv6InformationSubTLV(t *testing.T) { tests := []struct { name string input []byte }{ { name: "srv6 prefix sid informationw sub tlv", input: []byte{0x01, 0x00, 0x1e, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x00, 0x06, 0x28, 0x18, 0x10, 0x00, 0x10, 0x40}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { stlv := bgp.SRv6InformationSubTLV{} if err := stlv.DecodeFromBytes(tt.input); err != nil { t.Fatalf("test failed with error: %+v", err) } // Converting from Native to API apiPrefixSID, _ := MarshalSRv6SubTLVs([]bgp.PrefixSIDTLVInterface{&stlv}) // Converting back from API to Native _, recoveredPrefixSID, err := UnmarshalSubTLVs(apiPrefixSID) if err != nil { t.Fatalf("test failed with error: %+v", err) } recovered, err := recoveredPrefixSID[0].Serialize() if err != nil { t.Fatalf("test failed with error: %+v", err) } if !bytes.Equal(tt.input, recovered) { t.Fatalf("round trip conversion test failed as expected prefix sid attribute %+v does not match actual: %+v", tt.input, recovered) } }) } }