From bc59ab0a0f5b954001fdb7b27526903447f59800 Mon Sep 17 00:00:00 2001 From: Oliver Geiselhardt-Herms Date: Sat, 4 Oct 2025 11:48:45 +0200 Subject: [PATCH] ISIS: Add support for router capability TLV --- protocols/isis/packet/tlv.go | 11 + .../isis/packet/tlv_ipv6_te_router_id.go | 61 +++++ .../isis/packet/tlv_node_maximum_sid_depth.go | 77 ++++++ .../isis/packet/tlv_router_capability.go | 139 ++++++++++ .../isis/packet/tlv_router_capability_test.go | 243 ++++++++++++++++++ .../isis/packet/tlv_segment_routing_algo.go | 69 +++++ .../isis/packet/tlv_segment_routing_cap.go | 121 +++++++++ .../tlv_segment_routing_cap_sid_label.go | 68 +++++ 8 files changed, 789 insertions(+) create mode 100644 protocols/isis/packet/tlv_ipv6_te_router_id.go create mode 100644 protocols/isis/packet/tlv_node_maximum_sid_depth.go create mode 100644 protocols/isis/packet/tlv_router_capability.go create mode 100644 protocols/isis/packet/tlv_router_capability_test.go create mode 100644 protocols/isis/packet/tlv_segment_routing_algo.go create mode 100644 protocols/isis/packet/tlv_segment_routing_cap.go create mode 100644 protocols/isis/packet/tlv_segment_routing_cap_sid_label.go diff --git a/protocols/isis/packet/tlv.go b/protocols/isis/packet/tlv.go index 2ad94751..94577b3e 100644 --- a/protocols/isis/packet/tlv.go +++ b/protocols/isis/packet/tlv.go @@ -77,6 +77,8 @@ func readTLV(buf *bytes.Buffer) (TLV, error) { tlv, err = readISNeighborsTLV(buf, tlvType, tlvLength) case LSPEntriesTLVType: tlv, err = readLSPEntriesTLV(buf, tlvType, tlvLength) + case RouterCapabilityTLVType: + tlv, err = readRouterCapabilityTLV(buf, tlvType, tlvLength) default: tlv, err = readUnknownTLV(buf, tlvType, tlvLength) } @@ -87,3 +89,12 @@ func readTLV(buf *bytes.Buffer) (TLV, error) { return tlv, nil } + +func copyTLVs(tlvs []TLV) []TLV { + ret := make([]TLV, 0, len(tlvs)) + for _, tlv := range tlvs { + ret = append(ret, tlv.Copy()) + } + + return ret +} diff --git a/protocols/isis/packet/tlv_ipv6_te_router_id.go b/protocols/isis/packet/tlv_ipv6_te_router_id.go new file mode 100644 index 00000000..1b88791a --- /dev/null +++ b/protocols/isis/packet/tlv_ipv6_te_router_id.go @@ -0,0 +1,61 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" +) + +type IPv6TERouterIDTLV struct { + TLVType uint8 + TLVLength uint8 + RouterID [16]byte +} + +const IPv6TERouterIDTLVType = 12 +const IPv6TERouterIDTLVLength = 18 + +func readIPv6TERouterIDTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*IPv6TERouterIDTLV, error) { + pdu := &IPv6TERouterIDTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + } + + fields := []any{ + &pdu.RouterID, + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + + return pdu, nil +} + +func (i IPv6TERouterIDTLV) Copy() TLV { + ret := i + return &ret +} + +// Type gets the type of the TLV +func (i IPv6TERouterIDTLV) Type() uint8 { + return i.TLVType +} + +// Length gets the length of the TLV +func (i IPv6TERouterIDTLV) Length() uint8 { + return i.TLVLength +} + +// Value returns the TLV itself +func (i *IPv6TERouterIDTLV) Value() any { + return i +} + +func (i *IPv6TERouterIDTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(i.TLVType) + buf.WriteByte(i.TLVLength) + buf.Write(i.RouterID[:]) +} diff --git a/protocols/isis/packet/tlv_node_maximum_sid_depth.go b/protocols/isis/packet/tlv_node_maximum_sid_depth.go new file mode 100644 index 00000000..e0b7d573 --- /dev/null +++ b/protocols/isis/packet/tlv_node_maximum_sid_depth.go @@ -0,0 +1,77 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" +) + +const NodeMaximumSIDDepthTLVType = 23 +const NodeMaximumSIDDepthTLVLen = 0 + +type NodeMaximumSIDDepthTLV struct { + TLVType uint8 + TLVLength uint8 + MSDs []MSD +} + +type MSD struct { + Type uint8 + Value uint8 +} + +func readNodeMaximumSIDDepthTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*NodeMaximumSIDDepthTLV, error) { + pdu := &NodeMaximumSIDDepthTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + } + + for i := 0; i < int(tlvLength/2); i++ { + msd := MSD{} + fields := []any{ + &msd.Type, + &msd.Value, + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + pdu.MSDs = append(pdu.MSDs, msd) + } + + return pdu, nil +} + +func (n NodeMaximumSIDDepthTLV) Copy() TLV { + ret := n + ret.MSDs = make([]MSD, len(n.MSDs)) + copy(ret.MSDs, n.MSDs) + return &ret +} + +// Type gets the type of the TLV +func (n NodeMaximumSIDDepthTLV) Type() uint8 { + return n.TLVType +} + +// Length gets the length of the TLV +func (n NodeMaximumSIDDepthTLV) Length() uint8 { + return n.TLVLength +} + +// Value returns the TLV itself +func (n *NodeMaximumSIDDepthTLV) Value() any { + return n +} + +// Serialize serializes an NodeMaximumSIDDepthTLV +func (n NodeMaximumSIDDepthTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(n.TLVType) + buf.WriteByte(n.TLVLength) + for _, msd := range n.MSDs { + buf.WriteByte(msd.Type) + buf.WriteByte(msd.Value) + } +} diff --git a/protocols/isis/packet/tlv_router_capability.go b/protocols/isis/packet/tlv_router_capability.go new file mode 100644 index 00000000..37ad668b --- /dev/null +++ b/protocols/isis/packet/tlv_router_capability.go @@ -0,0 +1,139 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" + "github.com/bio-routing/tflow2/convert" +) + +const RouterCapabilityTLVType = 242 + +type RouterCapabilityTLV struct { + TLVType uint8 + TLVLength uint8 + RouterID uint32 + Flags uint8 + SubTLVs []TLV +} + +const RouterCapabilityTLVLen = 5 + +func readRouterCapabilityTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*RouterCapabilityTLV, error) { + pdu := &RouterCapabilityTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + } + fields := []interface{}{ + &pdu.RouterID, + &pdu.Flags, + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + + toRead := tlvLength - RouterCapabilityTLVLen + fmt.Printf("toRead: %d\n", toRead) + if toRead > 0 { + tlvsBytes := make([]byte, toRead) + _, err := buf.Read(tlvsBytes) + if err != nil { + return nil, fmt.Errorf("failed to read TLV bytes from buf: %w", err) + } + + subTLVs, err := readRouterCapSubTLVs(bytes.NewBuffer(tlvsBytes)) + if err != nil { + return nil, fmt.Errorf("unable to decode sub TLVs: %w", err) + } + + pdu.SubTLVs = subTLVs + } + + return pdu, nil +} + +func (r RouterCapabilityTLV) Copy() TLV { + ret := r + ret.SubTLVs = copyTLVs(r.SubTLVs) + + return &ret +} + +// Type returns the type of the TLV +func (r RouterCapabilityTLV) Type() uint8 { + return r.TLVType +} + +// Length returns the length of the TLV +func (r RouterCapabilityTLV) Length() uint8 { + return r.TLVLength +} + +// Value returns the TLV itself +func (r RouterCapabilityTLV) Value() interface{} { + return r +} + +// Serialize serializes an WriteByte into a buffer +func (r RouterCapabilityTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(r.TLVType) + buf.WriteByte(r.TLVLength) + buf.Write(convert.Uint32Byte(r.RouterID)) + buf.WriteByte(r.Flags) + + for i := range r.SubTLVs { + r.SubTLVs[i].Serialize(buf) + } +} + +func readRouterCapSubTLVs(buf *bytes.Buffer) ([]TLV, error) { + TLVs := make([]TLV, 0) + for buf.Len() > 0 { + tlv, err := readRouterCapSubTLV(buf) + if err != nil { + return nil, fmt.Errorf("unable to read TLV: %w", err) + } + + TLVs = append(TLVs, tlv) + } + + return TLVs, nil +} + +func readRouterCapSubTLV(buf *bytes.Buffer) (TLV, error) { + tlvType := uint8(0) + tlvLength := uint8(0) + + headFields := []any{ + &tlvType, + &tlvLength, + } + + err := decode.Decode(buf, headFields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + + var tlv TLV + switch tlvType { + case SegmentRoutingCapabilityTLVType: + tlv, err = readSegmentRoutingCapabilityTLV(buf, tlvType, tlvLength) + case SegmentRoutingAlgorithmsTLVType: + tlv, err = readSegmentRoutingAlgorithmsTLV(buf, tlvType, tlvLength) + case NodeMaximumSIDDepthTLVType: + tlv, err = readNodeMaximumSIDDepthTLV(buf, tlvType, tlvLength) + case IPv6TERouterIDTLVType: + tlv, err = readIPv6TERouterIDTLV(buf, tlvType, tlvLength) + default: + tlv, err = readUnknownTLV(buf, tlvType, tlvLength) + } + + if err != nil { + return nil, fmt.Errorf("unable to read TLV (type %d): %v", tlvType, err) + } + + return tlv, nil +} diff --git a/protocols/isis/packet/tlv_router_capability_test.go b/protocols/isis/packet/tlv_router_capability_test.go new file mode 100644 index 00000000..63a173d1 --- /dev/null +++ b/protocols/isis/packet/tlv_router_capability_test.go @@ -0,0 +1,243 @@ +package packet + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadRouterCapabilityTLV(t *testing.T) { + tests := []struct { + name string + input []byte + expectErr bool + expected *RouterCapabilityTLV + }{ + { + name: "empty input", + input: []byte{}, + expectErr: true, + }, + { + name: "valid input without sub TLVs", + input: []byte{1, 2, 3, 4, 5}, + expectErr: false, + expected: &RouterCapabilityTLV{ + TLVType: 242, + TLVLength: 5, + RouterID: 0x01020304, + Flags: 5, + SubTLVs: nil, + }, + }, + { + name: "valid input with sub TLVs", + input: []byte{1, 2, 3, 4, 5, 255, 1, 3}, + expectErr: false, + expected: &RouterCapabilityTLV{ + TLVType: 242, + TLVLength: 8, + RouterID: 0x01020304, + Flags: 5, + SubTLVs: []TLV{ + &UnknownTLV{TLVType: 255, TLVLength: 1, TLVValue: []byte{3}}, + }, + }, + }, + { + name: "real world input from junos with SR enabled", + input: []byte{ + 0x0a, 0x00, 0x01, 0x00, // Router ID + 0x00, // Flags + 0x02, 0x09, // SR capability TLV + 0xc0, // SR capability TLV flags + 0x00, 0x03, 0xe8, // SR capability TLV range + 0x01, 0x03, 0x00, 0x13, 0x88, // SID/Label TLV + 0x13, 0x02, 0x00, 0x01, // SR alrotighms TLV + 0x17, 0x04, 0x01, 0x03, 0x02, 0x10, // Node Maximum SID Depth TLV + 0x0c, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // IPv6 TE Router ID TLV + }, + expectErr: false, + expected: &RouterCapabilityTLV{ + TLVType: 242, + TLVLength: 44, + RouterID: 0x0a000100, + Flags: 0, + SubTLVs: []TLV{ + &SegmentRoutingCapabilityTLV{ + TLVType: 2, + TLVLength: 9, + Flags: 0xc0, + Range: [3]byte{0x00, 0x03, 0xe8}, + SubTLVs: []TLV{ + &SRCapSidLabelTLV{ + TLVType: 1, + TLVLength: 3, + Label: [3]byte{0, 0x13, 0x88}, + }, + }, + }, + &SegmentRoutingAlgorithmsTLV{ + TLVType: 19, + TLVLength: 2, + Algorithms: []uint8{ + 0, + 1, + }, + }, + &NodeMaximumSIDDepthTLV{ + TLVType: 23, + TLVLength: 4, + MSDs: []MSD{ + { + Type: 1, + Value: 3, + }, + { + Type: 2, + Value: 16, + }, + }, + }, + &IPv6TERouterIDTLV{ + TLVType: 12, + TLVLength: 16, + RouterID: [16]byte{ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := bytes.NewBuffer(tt.input) + result, err := readRouterCapabilityTLV(buf, 242, uint8(len(tt.input))) + if tt.expectErr && err == nil { + t.Errorf("expected error but got none for test case %s", tt.name) + return + } + + if !tt.expectErr && err != nil { + t.Errorf("did not expect error but got: %q for test case %s", err, tt.name) + return + } + + assert.Equal(t, tt.expected, result, tt.name) + }) + } +} + +func TestRouterCapabilityTLVSerialize(t *testing.T) { + tests := []struct { + name string + tlv *RouterCapabilityTLV + expected []byte + }{ + { + name: "serialize without sub TLVs", + tlv: &RouterCapabilityTLV{ + TLVType: 242, + TLVLength: 7, + RouterID: 0x01020304, + Flags: 5, + SubTLVs: nil, + }, + expected: []byte{242, 7, 1, 2, 3, 4, 5}, + }, + { + name: "serialize with sub TLVs", + tlv: &RouterCapabilityTLV{ + TLVType: 242, + TLVLength: 10, + RouterID: 0x01020304, + Flags: 5, + SubTLVs: []TLV{ + &UnknownTLV{TLVType: 2, TLVLength: 1, TLVValue: []byte{3}}, + }, + }, + expected: []byte{242, 10, 1, 2, 3, 4, 5, 2, 1, 3}, + }, + { + name: "real world input from junos with SR enabled", + tlv: &RouterCapabilityTLV{ + TLVType: 242, + TLVLength: 44, + RouterID: 0x0a000100, + Flags: 0, + SubTLVs: []TLV{ + &SegmentRoutingCapabilityTLV{ + TLVType: 2, + TLVLength: 9, + Flags: 0xc0, + Range: [3]byte{0x00, 0x03, 0xe8}, + SubTLVs: []TLV{ + &SRCapSidLabelTLV{ + TLVType: 1, + TLVLength: 3, + Label: [3]byte{0, 0x13, 0x88}, + }, + }, + }, + &SegmentRoutingAlgorithmsTLV{ + TLVType: 19, + TLVLength: 2, + Algorithms: []uint8{ + 0, + 1, + }, + }, + &NodeMaximumSIDDepthTLV{ + TLVType: 23, + TLVLength: 4, + MSDs: []MSD{ + { + Type: 1, + Value: 3, + }, + { + Type: 2, + Value: 16, + }, + }, + }, + &IPv6TERouterIDTLV{ + TLVType: 12, + TLVLength: 16, + RouterID: [16]byte{ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }, + }, + }, + }, + expected: []byte{ + 0xf2, // Router Capability TLV + 0x2c, // Length + 0x0a, 0x00, 0x01, 0x00, // Router ID + 0x00, // Flags + 0x02, 0x09, // SR capability TLV + 0xc0, // SR capability TLV flags + 0x00, 0x03, 0xe8, // SR capability TLV range + 0x01, 0x03, 0x00, 0x13, 0x88, // SID/Label TLV + 0x13, 0x02, 0x00, 0x01, // SR alrotighms TLV + 0x17, 0x04, 0x01, 0x03, 0x02, 0x10, // Node Maximum SID Depth TLV + 0x0c, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // IPv6 TE Router ID TLV + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := bytes.NewBuffer(nil) + tt.tlv.Serialize(buf) + result := buf.Bytes() + + assert.Equal(t, tt.expected, result, tt.name) + }) + } +} diff --git a/protocols/isis/packet/tlv_segment_routing_algo.go b/protocols/isis/packet/tlv_segment_routing_algo.go new file mode 100644 index 00000000..068b5a97 --- /dev/null +++ b/protocols/isis/packet/tlv_segment_routing_algo.go @@ -0,0 +1,69 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" +) + +const SegmentRoutingAlgorithmsTLVType = 19 +const SegmentRoutingAlgorithmsTLVLength = 0 + +type SegmentRoutingAlgorithmsTLV struct { + TLVType uint8 + TLVLength uint8 + Algorithms []uint8 +} + +func readSegmentRoutingAlgorithmsTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*SegmentRoutingAlgorithmsTLV, error) { + pdu := &SegmentRoutingAlgorithmsTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + } + fields := []any{ + &pdu.Algorithms, + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + + algos := make([]uint8, tlvLength) + _, err = buf.Read(algos) + if err != nil { + return nil, fmt.Errorf("unable to read algorithms: %v", err) + } + + pdu.Algorithms = algos + return pdu, nil +} + +func (s SegmentRoutingAlgorithmsTLV) Copy() TLV { + ret := s + ret.Algorithms = make([]uint8, len(s.Algorithms)) + copy(ret.Algorithms, s.Algorithms) + return &ret +} + +// Type gets the type of the TLV +func (s SegmentRoutingAlgorithmsTLV) Type() uint8 { + return s.TLVType +} + +// Length gets the length of the TLV +func (s SegmentRoutingAlgorithmsTLV) Length() uint8 { + return s.TLVLength +} + +// Value returns the TLV itself +func (s *SegmentRoutingAlgorithmsTLV) Value() any { + return s +} + +func (s *SegmentRoutingAlgorithmsTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(s.TLVType) + buf.WriteByte(s.TLVLength) + buf.Write(s.Algorithms) +} diff --git a/protocols/isis/packet/tlv_segment_routing_cap.go b/protocols/isis/packet/tlv_segment_routing_cap.go new file mode 100644 index 00000000..819341d2 --- /dev/null +++ b/protocols/isis/packet/tlv_segment_routing_cap.go @@ -0,0 +1,121 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" +) + +const SegmentRoutingCapabilityTLVType = 2 + +type SegmentRoutingCapabilityTLV struct { + TLVType uint8 + TLVLength uint8 + Flags uint8 + Range [3]byte + SubTLVs []TLV +} + +const SegmentRoutingCapabilityTLVLength = 4 + +func readSegmentRoutingCapabilityTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*SegmentRoutingCapabilityTLV, error) { + pdu := &SegmentRoutingCapabilityTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + } + fields := []any{ + &pdu.Flags, + &pdu.Range, + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + + toRead := tlvLength - SegmentRoutingCapabilityTLVLength + if toRead > 0 { + tlvsBytes := make([]byte, toRead) + _, err := buf.Read(tlvsBytes) + if err != nil { + return nil, fmt.Errorf("failed to read TLV bytes from buf: %w", err) + } + + subTLVs, err := readSRSubTLVs(bytes.NewBuffer(tlvsBytes)) + if err != nil { + return nil, fmt.Errorf("unable to decode sub TLVs: %w", err) + } + + pdu.SubTLVs = subTLVs + } + + return pdu, nil +} + +func (s SegmentRoutingCapabilityTLV) Copy() TLV { + ret := s + ret.SubTLVs = copyTLVs(s.SubTLVs) + return &ret +} + +// Type gets the type of the TLV +func (s SegmentRoutingCapabilityTLV) Type() uint8 { + return s.TLVType +} + +// Length gets the length of the TLV +func (s SegmentRoutingCapabilityTLV) Length() uint8 { + return s.TLVLength +} + +// Value returns the TLV itself +func (s *SegmentRoutingCapabilityTLV) Value() any { + return s +} + +func (s SegmentRoutingCapabilityTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(s.TLVType) + buf.WriteByte(s.TLVLength) + buf.WriteByte(s.Flags) + buf.Write(s.Range[:]) + for _, tlv := range s.SubTLVs { + tlv.Serialize(buf) + } +} + +func readSRSubTLVs(buf *bytes.Buffer) ([]TLV, error) { + var tlvs []TLV + for buf.Len() > 0 { + tlv, err := readSRSubTLV(buf) + if err != nil { + return nil, fmt.Errorf("unable to decode TLV: %v", err) + } + tlvs = append(tlvs, tlv) + } + + return tlvs, nil +} + +func readSRSubTLV(buf *bytes.Buffer) (TLV, error) { + var err error + tlvType := uint8(0) + tlvLength := uint8(0) + + headFields := []any{ + &tlvType, + &tlvLength, + } + + err = decode.Decode(buf, headFields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + + switch tlvType { + case SRCapSidLabelTLVType: + return readSRSidLabelTLV(buf, tlvType, tlvLength) + default: + return readUnknownTLV(buf, tlvType, tlvLength) + } +} diff --git a/protocols/isis/packet/tlv_segment_routing_cap_sid_label.go b/protocols/isis/packet/tlv_segment_routing_cap_sid_label.go new file mode 100644 index 00000000..6a9e1c81 --- /dev/null +++ b/protocols/isis/packet/tlv_segment_routing_cap_sid_label.go @@ -0,0 +1,68 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" +) + +const SRCapSidLabelTLVType = 1 + +// SRSidLabelTLV represents a Segment Routing SID/Label TLV as described in +// RFC 8665 Section 3.3.1 +// +------+--------+-------+-------+-------+ +// | Type | Length | SID/Label (3 octets) | +// +------+--------+-------+-------+-------+ + +type SRCapSidLabelTLV struct { + TLVType uint8 + TLVLength uint8 + Label [3]byte +} + +const SRCapSidLabelTLVLength = 3 + +func readSRSidLabelTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*SRCapSidLabelTLV, error) { + pdu := &SRCapSidLabelTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + } + fields := []any{ + &pdu.Label, + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("unable to decode fields: %v", err) + } + + return pdu, nil +} + +func (s SRCapSidLabelTLV) Copy() TLV { + ret := s + return &ret +} + +// Type gets the type of the TLV +func (s SRCapSidLabelTLV) Type() uint8 { + return s.TLVType +} + +// Length gets the length of the TLV +func (s SRCapSidLabelTLV) Length() uint8 { + return s.TLVLength +} + +// Value returns the TLV itself +func (s *SRCapSidLabelTLV) Value() any { + return s +} + +// Serialize serializes an SRSidLabelTLV +func (s SRCapSidLabelTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(s.TLVType) + buf.WriteByte(s.TLVLength) + buf.Write(s.Label[:]) +}