summaryrefslogtreecommitdiffhomepage
path: root/pkg/packet/bgp
diff options
context:
space:
mode:
authorArtur Makutunowicz <arma@linkedin.com>2019-01-17 10:28:23 -0800
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2019-01-26 12:40:45 +0900
commit192480fc7127ce0f266407cecceb00703807bbca (patch)
tree7f45576e8a12259db5d3a257180db38849abc932 /pkg/packet/bgp
parent526bd67f5de4cf361aaab2d10456e5113899ca54 (diff)
Add experimental BGP-LS feature
Diffstat (limited to 'pkg/packet/bgp')
-rw-r--r--pkg/packet/bgp/bgp.go3223
-rw-r--r--pkg/packet/bgp/bgp_test.go1932
2 files changed, 5152 insertions, 3 deletions
diff --git a/pkg/packet/bgp/bgp.go b/pkg/packet/bgp/bgp.go
index dfedba9b..d78e04b6 100644
--- a/pkg/packet/bgp/bgp.go
+++ b/pkg/packet/bgp/bgp.go
@@ -53,6 +53,7 @@ const (
AFI_IP = 1
AFI_IP6 = 2
AFI_L2VPN = 25
+ AFI_LS = 16388
AFI_OPAQUE = 16397
)
@@ -63,6 +64,7 @@ const (
SAFI_ENCAPSULATION = 7
SAFI_VPLS = 65
SAFI_EVPN = 70
+ SAFI_LS = 71
SAFI_MPLS_VPN = 128
SAFI_MPLS_VPN_MULTICAST = 129
SAFI_ROUTE_TARGET_CONSTRAINTS = 132
@@ -4711,6 +4713,3210 @@ func NewOpaqueNLRI(key, value []byte) *OpaqueNLRI {
}
}
+type LsNLRIType uint16
+
+const (
+ LS_NLRI_TYPE_UNKNOWN LsNLRIType = iota
+ LS_NLRI_TYPE_NODE
+ LS_NLRI_TYPE_LINK
+ LS_NLRI_TYPE_PREFIX_IPV4
+ LS_NLRI_TYPE_PREFIX_IPV6
+)
+
+type LsNLRIInterface interface {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+ Len() int
+ Type() LsNLRIType
+ String() string
+}
+
+type LsProtocolID uint8
+
+const (
+ LS_PROTOCOL_UNKNOWN = iota
+ LS_PROTOCOL_ISIS_L1
+ LS_PROTOCOL_ISIS_L2
+ LS_PROTOCOL_OSPF_V2
+ LS_PROTOCOL_DIRECT
+ LS_PROTOCOL_STATIC
+ LS_PROTOCOL_OSPF_V3
+)
+
+func (l LsProtocolID) String() string {
+ switch l {
+ case LS_PROTOCOL_ISIS_L1:
+ return "ISIS-L1"
+ case LS_PROTOCOL_ISIS_L2:
+ return "ISIS-L2"
+ case LS_PROTOCOL_OSPF_V2:
+ return "OSPFv2"
+ case LS_PROTOCOL_DIRECT:
+ return "DIRECT"
+ case LS_PROTOCOL_STATIC:
+ return "STATIC"
+ case LS_PROTOCOL_OSPF_V3:
+ return "OSPFv3"
+ default:
+ return fmt.Sprintf("LsProtocolID(%d)", uint8(l))
+ }
+}
+
+type LsNLRI struct {
+ NLRIType LsNLRIType
+ Length uint16
+ ProtocolID LsProtocolID
+ Identifier uint64
+}
+
+const lsNLRIHdrLen = 9
+
+func (l *LsNLRI) DecodeFromBytes(data []byte) error {
+ if len(data) < lsNLRIHdrLen {
+ return malformedAttrListErr("Malformed NLRI")
+ }
+
+ l.ProtocolID = LsProtocolID(data[0])
+ l.Identifier = binary.BigEndian.Uint64(data[1:lsNLRIHdrLen])
+
+ return nil
+}
+
+func (l *LsNLRI) Serialize(value []byte) ([]byte, error) {
+ buf := make([]byte, lsNLRIHdrLen)
+ buf[0] = uint8(l.ProtocolID)
+ binary.BigEndian.PutUint64(buf[1:], l.Identifier)
+ buf = append(buf, value...)
+
+ return buf, nil
+}
+
+func (l *LsNLRI) Len() int {
+ return int(l.Length)
+}
+
+func (l *LsNLRI) Type() LsNLRIType {
+ return l.NLRIType
+}
+
+type LsNodeNLRI struct {
+ LsNLRI
+ LocalNodeDesc LsTLVInterface
+}
+
+func (l *LsNodeNLRI) DecodeFromBytes(data []byte) error {
+ if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
+ return nil
+ }
+
+ tlv := data[lsNLRIHdrLen:]
+ if len(tlv) < tlvHdrLen {
+ return malformedAttrListErr("Malformed Node NLRI")
+ }
+
+ tlvType := LsTLVType(binary.BigEndian.Uint16(tlv[:2]))
+ if tlvType != LS_TLV_LOCAL_NODE_DESC {
+ return malformedAttrListErr("Mandatory TLV missing")
+ }
+
+ l.LocalNodeDesc = &LsTLVNodeDescriptor{}
+ if err := l.LocalNodeDesc.DecodeFromBytes(tlv); err != nil {
+ return malformedAttrListErr(fmt.Sprintf("Malformed Node NLRI: %v", err))
+ }
+
+ return nil
+}
+
+func (l *LsNodeNLRI) String() string {
+ if l.LocalNodeDesc == nil {
+ return "NODE { EMPTY }"
+ }
+
+ local := l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract()
+ return fmt.Sprintf("NODE { AS:%v BGP-LS ID:%v %v %v:%v }", local.Asn, local.BGPLsID, local.IGPRouterID, l.ProtocolID.String(), l.Identifier)
+}
+
+func (l *LsNodeNLRI) Serialize() ([]byte, error) {
+ if l.LocalNodeDesc == nil {
+ return nil, fmt.Errorf("local node descriptor missing")
+ }
+ ser, err := l.LocalNodeDesc.Serialize()
+ if err != nil {
+ return nil, err
+ }
+
+ return l.LsNLRI.Serialize(ser)
+}
+
+func (l *LsNodeNLRI) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsNLRIType `json:"type"`
+ LocalNode LsNodeDescriptor `json:"local_node_desc"`
+ }{
+ Type: l.Type(),
+ LocalNode: *l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract(),
+ })
+}
+
+type LsLinkDescriptor struct {
+ LinkLocalID *uint32
+ LinkRemoteID *uint32
+ InterfaceAddrIPv4 *net.IP
+ NeighborAddrIPv4 *net.IP
+ InterfaceAddrIPv6 *net.IP
+ NeighborAddrIPv6 *net.IP
+}
+
+func (l *LsLinkDescriptor) ParseTLVs(tlvs []LsTLVInterface) {
+ for _, tlv := range tlvs {
+ switch v := tlv.(type) {
+ case *LsTLVLinkID:
+ l.LinkLocalID = &v.Local
+ l.LinkRemoteID = &v.Remote
+
+ case *LsTLVIPv4InterfaceAddr:
+ l.InterfaceAddrIPv4 = &v.IP
+
+ case *LsTLVIPv4NeighborAddr:
+ l.NeighborAddrIPv4 = &v.IP
+
+ case *LsTLVIPv6InterfaceAddr:
+ l.InterfaceAddrIPv6 = &v.IP
+
+ case *LsTLVIPv6NeighborAddr:
+ l.NeighborAddrIPv6 = &v.IP
+ }
+ }
+}
+
+func (l *LsLinkDescriptor) String() string {
+ if l.InterfaceAddrIPv4 != nil && l.NeighborAddrIPv4 != nil {
+ return fmt.Sprintf("%v->%v", l.InterfaceAddrIPv4, l.NeighborAddrIPv4)
+ }
+
+ if l.InterfaceAddrIPv6 != nil && l.NeighborAddrIPv6 != nil {
+ return fmt.Sprintf("%v->%v", l.InterfaceAddrIPv6, l.NeighborAddrIPv6)
+ }
+
+ if l.LinkLocalID != nil && l.LinkRemoteID != nil {
+ return fmt.Sprintf("%v->%v", *l.LinkLocalID, *l.LinkRemoteID)
+ }
+
+ return "UNKNOWN"
+}
+
+type LsLinkNLRI struct {
+ LsNLRI
+ LocalNodeDesc LsTLVInterface
+ RemoteNodeDesc LsTLVInterface
+ LinkDesc []LsTLVInterface
+}
+
+func (l *LsLinkNLRI) String() string {
+ if l.LocalNodeDesc == nil || l.RemoteNodeDesc == nil {
+ return "LINK { EMPTY }"
+ }
+
+ local := l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract()
+ remote := l.RemoteNodeDesc.(*LsTLVNodeDescriptor).Extract()
+ link := &LsLinkDescriptor{}
+ link.ParseTLVs(l.LinkDesc)
+
+ return fmt.Sprintf("LINK { LOCAL_NODE: %v REMOTE_NODE: %v LINK: %v}", local.IGPRouterID, remote.IGPRouterID, link)
+}
+
+func (l *LsLinkNLRI) DecodeFromBytes(data []byte) error {
+ if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
+ return nil
+ }
+
+ tlv := data[lsNLRIHdrLen:]
+ m := make(map[LsTLVType]bool)
+
+ for len(tlv) >= tlvHdrLen {
+ sub := &LsTLV{}
+ _, err := sub.DecodeFromBytes(tlv)
+ if err != nil {
+ return err
+ }
+ m[sub.Type] = true
+
+ var subTLV LsTLVInterface
+ switch sub.Type {
+ case LS_TLV_LOCAL_NODE_DESC, LS_TLV_REMOTE_NODE_DESC:
+ subTLV = &LsTLVNodeDescriptor{}
+ case LS_TLV_LINK_ID:
+ subTLV = &LsTLVLinkID{}
+ case LS_TLV_IPV4_INTERFACE_ADDR:
+ subTLV = &LsTLVIPv4InterfaceAddr{}
+ case LS_TLV_IPV4_NEIGHBOR_ADDR:
+ subTLV = &LsTLVIPv4NeighborAddr{}
+ case LS_TLV_IPV6_INTERFACE_ADDR:
+ subTLV = &LsTLVIPv6InterfaceAddr{}
+ case LS_TLV_IPV6_NEIGHBOR_ADDR:
+ subTLV = &LsTLVIPv6NeighborAddr{}
+
+ default:
+ tlv = tlv[sub.Len():]
+ l.Length -= uint16(sub.Len())
+ continue
+ }
+
+ if err := subTLV.DecodeFromBytes(tlv); err != nil {
+ return err
+ }
+ tlv = tlv[subTLV.Len():]
+
+ switch sub.Type {
+ case LS_TLV_LOCAL_NODE_DESC:
+ l.LocalNodeDesc = subTLV
+ case LS_TLV_REMOTE_NODE_DESC:
+ l.RemoteNodeDesc = subTLV
+ default:
+ l.LinkDesc = append(l.LinkDesc, subTLV)
+ }
+ }
+
+ required := []LsTLVType{LS_TLV_LOCAL_NODE_DESC, LS_TLV_REMOTE_NODE_DESC}
+ for _, tlv := range required {
+ if _, ok := m[tlv]; !ok {
+ return malformedAttrListErr("Required TLV missing")
+ }
+ }
+
+ return nil
+}
+
+func (l *LsLinkNLRI) Serialize() ([]byte, error) {
+ if l.LocalNodeDesc == nil || l.RemoteNodeDesc == nil {
+ return nil, fmt.Errorf("required TLV missing")
+ }
+
+ buf := make([]byte, 0)
+ s, err := l.LocalNodeDesc.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+
+ s, err = l.RemoteNodeDesc.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+
+ for _, tlv := range l.LinkDesc {
+ s, err := tlv.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+ }
+
+ return l.LsNLRI.Serialize(buf)
+}
+
+func (l *LsLinkNLRI) MarshalJSON() ([]byte, error) {
+ linkDesc := &LsLinkDescriptor{}
+ linkDesc.ParseTLVs(l.LinkDesc)
+
+ return json.Marshal(struct {
+ Type LsNLRIType `json:"type"`
+ LocalNode LsNodeDescriptor `json:"local_node_desc"`
+ RemoteNode LsNodeDescriptor `json:"remote_node_desc"`
+ LinkDesc LsLinkDescriptor `json:"link_desc"`
+ }{
+ Type: l.Type(),
+ LocalNode: *l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract(),
+ RemoteNode: *l.RemoteNodeDesc.(*LsTLVNodeDescriptor).Extract(),
+ LinkDesc: *linkDesc,
+ })
+}
+
+type LsPrefixDescriptor struct {
+ IPReachability []net.IPNet
+ OSPFRouteType LsOspfRouteType
+}
+
+func (l *LsPrefixDescriptor) ParseTLVs(tlvs []LsTLVInterface, ipv6 bool) {
+ for _, tlv := range tlvs {
+ switch v := tlv.(type) {
+ case *LsTLVIPReachability:
+ l.IPReachability = append(l.IPReachability, v.ToIPNet(ipv6))
+
+ case *LsTLVOspfRouteType:
+ l.OSPFRouteType = v.RouteType
+ }
+ }
+}
+
+type LsPrefixV4NLRI struct {
+ LsNLRI
+ LocalNodeDesc LsTLVInterface
+ PrefixDesc []LsTLVInterface
+}
+
+func (l *LsPrefixV4NLRI) String() string {
+ if l.LocalNodeDesc == nil {
+ return "PREFIXv4 { EMPTY }"
+ }
+
+ local := l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract()
+ prefix := &LsPrefixDescriptor{}
+ prefix.ParseTLVs(l.PrefixDesc, false)
+ ips := []string{}
+ for _, ip := range prefix.IPReachability {
+ ips = append(ips, ip.String())
+ }
+
+ ospf := ""
+ if prefix.OSPFRouteType != LS_OSPF_ROUTE_TYPE_UNKNOWN {
+ ospf = fmt.Sprintf("OSPF_ROUTE_TYPE:%v ", prefix.OSPFRouteType)
+ }
+
+ return fmt.Sprintf("PREFIXv4 { LOCAL_NODE: %v PREFIX: %v %v}", local.IGPRouterID, ips, ospf)
+}
+
+func (l *LsPrefixV4NLRI) DecodeFromBytes(data []byte) error {
+ if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
+ return nil
+ }
+
+ tlv := data[lsNLRIHdrLen:]
+ m := make(map[LsTLVType]bool)
+
+ for len(tlv) >= tlvHdrLen {
+ sub := &LsTLV{}
+ _, err := sub.DecodeFromBytes(tlv)
+ if err != nil {
+ return err
+ }
+ m[sub.Type] = true
+
+ var subTLV LsTLVInterface
+ switch sub.Type {
+ case LS_TLV_LOCAL_NODE_DESC:
+ subTLV = &LsTLVNodeDescriptor{}
+ case LS_TLV_OSPF_ROUTE_TYPE:
+ subTLV = &LsTLVOspfRouteType{}
+ case LS_TLV_IP_REACH_INFO:
+ subTLV = &LsTLVIPReachability{}
+
+ default:
+ tlv = tlv[sub.Len():]
+ l.Length -= uint16(sub.Len())
+ continue
+ }
+
+ if err := subTLV.DecodeFromBytes(tlv); err != nil {
+ return err
+ }
+ tlv = tlv[subTLV.Len():]
+
+ switch sub.Type {
+ case LS_TLV_LOCAL_NODE_DESC:
+ l.LocalNodeDesc = subTLV
+ default:
+ l.PrefixDesc = append(l.PrefixDesc, subTLV)
+ }
+ }
+
+ required := []LsTLVType{LS_TLV_IP_REACH_INFO, LS_TLV_LOCAL_NODE_DESC}
+ for _, tlv := range required {
+ if _, ok := m[tlv]; !ok {
+ return malformedAttrListErr("Required TLV missing")
+ }
+ }
+
+ for _, tlv := range l.PrefixDesc {
+ switch v := tlv.(type) {
+ case *LsTLVIPReachability:
+ if v.PrefixLength > 8*net.IPv4len {
+ return malformedAttrListErr("Unexpected IP Reachability info")
+ }
+ }
+ }
+
+ return nil
+}
+
+func (l *LsPrefixV4NLRI) Serialize() ([]byte, error) {
+ if l.LocalNodeDesc == nil {
+ return nil, fmt.Errorf("required TLV missing")
+ }
+
+ buf := make([]byte, 0)
+ s, err := l.LocalNodeDesc.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+
+ for _, tlv := range l.PrefixDesc {
+ s, err := tlv.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+ }
+
+ return l.LsNLRI.Serialize(buf)
+}
+
+func (l *LsPrefixV4NLRI) MarshalJSON() ([]byte, error) {
+ prefixDesc := &LsPrefixDescriptor{}
+ prefixDesc.ParseTLVs(l.PrefixDesc, false)
+
+ return json.Marshal(struct {
+ Type LsNLRIType `json:"type"`
+ LocalNode LsNodeDescriptor `json:"local_node_desc"`
+ PrefixDesc LsPrefixDescriptor `json:"prefix_desc"`
+ }{
+ Type: l.Type(),
+ LocalNode: *l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract(),
+ PrefixDesc: *prefixDesc,
+ })
+}
+
+type LsPrefixV6NLRI struct {
+ LsNLRI
+ LocalNodeDesc LsTLVInterface
+ PrefixDesc []LsTLVInterface
+}
+
+func (l *LsPrefixV6NLRI) String() string {
+ if l.LocalNodeDesc == nil {
+ return "PREFIXv6 { EMPTY }"
+ }
+
+ local := l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract()
+ prefix := &LsPrefixDescriptor{}
+ prefix.ParseTLVs(l.PrefixDesc, true)
+ ips := []string{}
+ for _, ip := range prefix.IPReachability {
+ ips = append(ips, ip.String())
+ }
+
+ ospf := ""
+ if prefix.OSPFRouteType != LS_OSPF_ROUTE_TYPE_UNKNOWN {
+ ospf = fmt.Sprintf("OSPF_ROUTE_TYPE:%v ", prefix.OSPFRouteType)
+ }
+
+ return fmt.Sprintf("PREFIXv6 { LOCAL_NODE: %v PREFIX: %v %v}", local.IGPRouterID, ips, ospf)
+}
+
+func (l *LsPrefixV6NLRI) DecodeFromBytes(data []byte) error {
+ if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
+ return nil
+ }
+
+ tlv := data[lsNLRIHdrLen:]
+ m := make(map[LsTLVType]bool)
+
+ for len(tlv) >= tlvHdrLen {
+ sub := &LsTLV{}
+ _, err := sub.DecodeFromBytes(tlv)
+ if err != nil {
+ return err
+ }
+ m[sub.Type] = true
+
+ var subTLV LsTLVInterface
+ switch sub.Type {
+ case LS_TLV_LOCAL_NODE_DESC:
+ subTLV = &LsTLVNodeDescriptor{}
+ case LS_TLV_OSPF_ROUTE_TYPE:
+ subTLV = &LsTLVOspfRouteType{}
+ case LS_TLV_IP_REACH_INFO:
+ subTLV = &LsTLVIPReachability{}
+
+ default:
+ tlv = tlv[sub.Len():]
+ l.Length -= uint16(sub.Len())
+ continue
+ }
+
+ if err := subTLV.DecodeFromBytes(tlv); err != nil {
+ return err
+ }
+ tlv = tlv[subTLV.Len():]
+
+ switch sub.Type {
+ case LS_TLV_LOCAL_NODE_DESC:
+ l.LocalNodeDesc = subTLV
+ default:
+ l.PrefixDesc = append(l.PrefixDesc, subTLV)
+ }
+ }
+
+ required := []LsTLVType{LS_TLV_IP_REACH_INFO, LS_TLV_LOCAL_NODE_DESC}
+ for _, tlv := range required {
+ if _, ok := m[tlv]; !ok {
+ return malformedAttrListErr("Required TLV missing")
+ }
+ }
+
+ return nil
+}
+
+func (l *LsPrefixV6NLRI) Serialize() ([]byte, error) {
+ if l.LocalNodeDesc == nil {
+ return nil, fmt.Errorf("required TLV missing")
+ }
+
+ buf := make([]byte, 0)
+ s, err := l.LocalNodeDesc.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+
+ for _, tlv := range l.PrefixDesc {
+ s, err := tlv.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+ }
+
+ return l.LsNLRI.Serialize(buf)
+}
+
+func (l *LsPrefixV6NLRI) MarshalJSON() ([]byte, error) {
+ prefixDesc := &LsPrefixDescriptor{}
+ prefixDesc.ParseTLVs(l.PrefixDesc, true)
+
+ return json.Marshal(struct {
+ Type LsNLRIType `json:"type"`
+ LocalNode LsNodeDescriptor `json:"local_node_desc"`
+ PrefixDesc LsPrefixDescriptor `json:"prefix_desc"`
+ }{
+ Type: l.Type(),
+ LocalNode: *l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract(),
+ PrefixDesc: *prefixDesc,
+ })
+}
+
+type LsTLVType uint16
+
+// Based on https://www.iana.org/assignments/bgp-ls-parameters/bgp-ls-parameters.xhtml
+const (
+ LS_TLV_UNKNOWN LsTLVType = iota
+
+ LS_TLV_LOCAL_NODE_DESC = 256
+ LS_TLV_REMOTE_NODE_DESC = 257
+ LS_TLV_LINK_ID = 258
+ LS_TLV_IPV4_INTERFACE_ADDR = 259
+ LS_TLV_IPV4_NEIGHBOR_ADDR = 260
+ LS_TLV_IPV6_INTERFACE_ADDR = 261
+ LS_TLV_IPV6_NEIGHBOR_ADDR = 262
+ LS_TLV_MULTI_TOPO_ID = 263
+ LS_TLV_OSPF_ROUTE_TYPE = 264
+ LS_TLV_IP_REACH_INFO = 265
+
+ LS_TLV_AS = 512
+ LS_TLV_BGP_LS_ID = 513
+ LS_TLV_OSPF_AREA = 514
+ LS_TLV_IGP_ROUTER_ID = 515
+ LS_TLV_BGP_ROUTER_ID = 516 // draft-ietf-idr-bgpls-segment-routing-epe, TODO
+ LS_TLV_BGP_CONFEDERATION_MEMBER = 517 // draft-ietf-idr-bgpls-segment-routing-epe, TODO
+
+ LS_TLV_NODE_FLAG_BITS = 1024
+ LS_TLV_OPAQUE_NODE_ATTR = 1025
+ LS_TLV_NODE_NAME = 1026
+ LS_TLV_ISIS_AREA = 1027
+ LS_TLV_IPV4_LOCAL_ROUTER_ID = 1028
+ LS_TLV_IPV6_LOCAL_ROUTER_ID = 1029
+ LS_TLV_IPV4_REMOTE_ROUTER_ID = 1030
+ LS_TLV_IPV6_REMOTE_ROUTER_ID = 1031
+
+ LS_TLV_SR_CAPABILITIES = 1034 // draft-ietf-idr-bgp-ls-segment-routing-ext
+ LS_TLV_SR_ALGORITHM = 1035 // draft-ietf-idr-bgp-ls-segment-routing-ext
+ LS_TLV_SR_LOCAL_BLOCK = 1036 // draft-ietf-idr-bgp-ls-segment-routing-ext
+ LS_TLV_SRMS_PREFERENCE = 1037 // draft-ietf-idr-bgp-ls-segment-routing-ext, TODO
+
+ LS_TLV_ADMIN_GROUP = 1088
+ LS_TLV_MAX_LINK_BANDWIDTH = 1089
+ LS_TLV_MAX_RESERVABLE_BANDWIDTH = 1090
+ LS_TLV_UNRESERVED_BANDWIDTH = 1091
+ LS_TLV_TE_DEFAULT_METRIC = 1092
+ LS_TLV_LINK_PROTECTION_TYPE = 1093 // TODO
+ LS_TLV_MPLS_PROTOCOL_MASK = 1094 // TODO
+ LS_TLV_IGP_METRIC = 1095
+ LS_TLV_SRLG = 1096 // TODO
+ LS_TLV_OPAQUE_LINK_ATTR = 1097
+ LS_TLV_LINK_NAME = 1098
+ LS_TLV_ADJACENCY_SID = 1099 // draft-ietf-idr-bgp-ls-segment-routing-ext
+ LS_TLV_LAN_ADJACENCY_SID = 1100 // draft-ietf-idr-bgp-ls-segment-routing-ext, TODO
+ LS_TLV_PEER_NODE_SID = 1101 // draft-ietf-idr-bgpls-segment-routing-epe, TODO
+ LS_TLV_PEER_ADJACENCY_SID = 1102 // draft-ietf-idr-bgpls-segment-routing-epe, TODO
+ LS_TLV_PEER_SET_SID = 1103 // draft-ietf-idr-bgpls-segment-routing-epe, TODO
+
+ LS_TLV_RTM_CAPABILITY = 1105 // RFC8169, TODO
+
+ LS_TLV_IGP_FLAGS = 1152
+ LS_TLV_IGP_ROUTE_TAG = 1153 // TODO
+ LS_TLV_EXTENDED_ROUTE_TAG = 1154 // TODO
+ LS_TLV_PREFIX_METRIC = 1155 // TODO
+ LS_TLV_OSPF_FORWARDING_ADDR = 1156 // TODO
+ LS_TLV_OPAQUE_PREFIX_ATTR = 1157
+ LS_TLV_PREFIX_SID = 1158 // draft-ietf-idr-bgp-ls-segment-routing-ext
+ LS_TLV_RANGE = 1159 // draft-ietf-idr-bgp-ls-segment-routing-ext, TODO
+ LS_TLV_SID_LABEL_TLV = 1161 // draft-ietf-idr-bgp-ls-segment-routing-ext
+ LS_TLV_PREFIX_ATTRIBUTE_FLAGS = 1170 // draft-ietf-idr-bgp-ls-segment-routing-ext, TODO
+ LS_TLV_SOURCE_ROUTER_ID = 1171 // draft-ietf-idr-bgp-ls-segment-routing-ext, TODO
+ LS_TLV_L2_BUNDLE_MEMBER_TLV = 1172 // draft-ietf-idr-bgp-ls-segment-routing-ext, TODO
+)
+
+type LsTLVInterface interface {
+ Len() int
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+ String() string
+ MarshalJSON() ([]byte, error)
+}
+
+type LsTLV struct {
+ Type LsTLVType
+ Length uint16
+}
+
+func malformedAttrListErr(s string) error {
+ return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, s)
+}
+
+const tlvHdrLen = 4
+
+func (l *LsTLV) Len() int {
+ return int(l.Length) + tlvHdrLen
+}
+
+func (l *LsTLV) Serialize(value []byte) ([]byte, error) {
+ if len(value) != int(l.Length) {
+ return nil, malformedAttrListErr("serialization failed: LS TLV malformed")
+ }
+
+ buf := make([]byte, tlvHdrLen)
+ binary.BigEndian.PutUint16(buf[:2], uint16(l.Type))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(l.Length))
+ buf = append(buf, value...)
+
+ return buf, nil
+}
+
+func (l *LsTLV) DecodeFromBytes(data []byte) ([]byte, error) {
+ if len(data) < tlvHdrLen {
+ return nil, malformedAttrListErr("decoding failed: LS TLV malformed")
+ }
+ l.Type = LsTLVType(binary.BigEndian.Uint16(data[:2]))
+ l.Length = binary.BigEndian.Uint16(data[2:4])
+
+ if len(data) < l.Len() {
+ return nil, malformedAttrListErr("decoding failed: LS TLV malformed")
+ }
+
+ return data[tlvHdrLen:l.Len()], nil
+}
+
+type LsTLVLinkID struct {
+ LsTLV
+ Local uint32
+ Remote uint32
+}
+
+func (l *LsTLVLinkID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_LINK_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5307#section-1.1
+ if len(value) != 8 {
+ return malformedAttrListErr("Incorrect Link ID length")
+ }
+
+ l.Local = binary.BigEndian.Uint32(value[:4])
+ l.Remote = binary.BigEndian.Uint32(value[4:])
+
+ return nil
+}
+
+func (l *LsTLVLinkID) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ binary.BigEndian.PutUint32(buf[:4], l.Local)
+ binary.BigEndian.PutUint32(buf[4:], l.Remote)
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVLinkID) String() string {
+ return fmt.Sprintf("{Link ID Remote: %v Local: %v}", l.Local, l.Remote)
+}
+
+func (l *LsTLVLinkID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Local uint32 `json:"local_link_id"`
+ Remote uint32 `json:"remote_link_id"`
+ }{
+ Type: l.Type,
+ Local: l.Local,
+ Remote: l.Remote,
+ })
+}
+
+type LsTLVIPv4InterfaceAddr struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVIPv4InterfaceAddr) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV4_INTERFACE_ADDR {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-3.2
+ if len(value) != 4 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ return nil
+}
+
+func (l *LsTLVIPv4InterfaceAddr) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVIPv4InterfaceAddr) String() string {
+ return fmt.Sprintf("{IPv4 Interface Address: %v}", l.IP)
+}
+
+func (l *LsTLVIPv4InterfaceAddr) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"ipv4_interface_address"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+type LsTLVIPv4NeighborAddr struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVIPv4NeighborAddr) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV4_NEIGHBOR_ADDR {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-3.3
+ if len(value) != 4 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ return nil
+}
+
+func (l *LsTLVIPv4NeighborAddr) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVIPv4NeighborAddr) String() string {
+ return fmt.Sprintf("{IPv4 Neighbor Address: %v}", l.IP)
+}
+
+func (l *LsTLVIPv4NeighborAddr) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"ipv4_neighbor_address"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+type LsTLVIPv6InterfaceAddr struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVIPv6InterfaceAddr) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV6_INTERFACE_ADDR {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc6119#section-4.2
+ if len(value) != 16 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ if l.IP.IsLinkLocalUnicast() {
+ return malformedAttrListErr("Unexpected link local address")
+ }
+
+ return nil
+}
+
+func (l *LsTLVIPv6InterfaceAddr) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVIPv6InterfaceAddr) String() string {
+ return fmt.Sprintf("{IPv6 Interface Address: %v}", l.IP)
+}
+
+func (l *LsTLVIPv6InterfaceAddr) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"ipv6_interface_address"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+type LsTLVIPv6NeighborAddr struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVIPv6NeighborAddr) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV6_NEIGHBOR_ADDR {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc6119#section-4.3
+ if len(value) != 16 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ if l.IP.IsLinkLocalUnicast() {
+ return malformedAttrListErr("Unexpected link local address")
+ }
+
+ return nil
+}
+
+func (l *LsTLVIPv6NeighborAddr) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVIPv6NeighborAddr) String() string {
+ return fmt.Sprintf("{IPv6 Neighbor Address: %v}", l.IP)
+}
+
+func (l *LsTLVIPv6NeighborAddr) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"ipv6_neighbor_address"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+// https://tools.ietf.org/html/rfc7752#section-3.3.1.1
+type LsNodeFlags struct {
+ Overload bool `json:"overload"`
+ Attached bool `json:"attached"`
+ External bool `json:"external"`
+ ABR bool `json:"abr"`
+ Router bool `json:"router"`
+ V6 bool `json:"v6"`
+}
+
+type LsTLVNodeFlagBits struct {
+ LsTLV
+ Flags uint8
+}
+
+func (l *LsTLVNodeFlagBits) Extract() *LsNodeFlags {
+ return &LsNodeFlags{
+ Overload: (l.Flags & (1 << 7)) > 0,
+ Attached: (l.Flags & (1 << 6)) > 0,
+ External: (l.Flags & (1 << 5)) > 0,
+ ABR: (l.Flags & (1 << 4)) > 0,
+ Router: (l.Flags & (1 << 3)) > 0,
+ V6: (l.Flags & (1 << 2)) > 0,
+ }
+}
+
+func (l *LsTLVNodeFlagBits) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_NODE_FLAG_BITS {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ if l.Length != 1 {
+ return malformedAttrListErr("Node Flag Bits TLV malformed")
+ }
+
+ l.Flags = value[0]
+
+ return nil
+}
+
+func (l *LsTLVNodeFlagBits) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize([]byte{l.Flags})
+}
+
+func (l *LsTLVNodeFlagBits) String() string {
+ flags := "XXVRBETO"
+
+ var buf bytes.Buffer
+
+ for i := 0; i < len(flags); i++ {
+ if l.Flags&(1<<uint(i)) > 0 {
+ buf.WriteString(flags[i : i+1])
+ } else {
+ buf.WriteString("*")
+ }
+ }
+
+ return fmt.Sprintf("{Node Flags: %s}", buf.String())
+}
+
+func (l *LsTLVNodeFlagBits) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Flags string `json:"node_flags"`
+ }{
+ Type: l.Type,
+ Flags: l.String(),
+ })
+}
+
+type LsTLVNodeName struct {
+ LsTLV
+ Name string
+}
+
+func (l *LsTLVNodeName) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_NODE_NAME {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // RFC5301, section 3.
+ if l.Length < 1 || l.Length > 255 {
+ return malformedAttrListErr("Incorrect Node Name")
+ }
+
+ l.Name = string(value)
+
+ return nil
+}
+
+func (l *LsTLVNodeName) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize([]byte(l.Name))
+}
+
+func (l *LsTLVNodeName) String() string {
+ return fmt.Sprintf("{Node Name: %s}", l.Name)
+}
+
+func (l *LsTLVNodeName) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Name string `json:"node_name"`
+ }{
+ Type: l.Type,
+ Name: l.Name,
+ })
+}
+
+type LsTLVIsisArea struct {
+ LsTLV
+ Area []byte
+}
+
+func (l *LsTLVIsisArea) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_ISIS_AREA {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ if len(value) < 1 || len(value) > 13 {
+ return malformedAttrListErr("Incorrect ISIS Area size")
+ }
+
+ l.Area = value
+
+ return nil
+}
+
+func (l *LsTLVIsisArea) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.Area)
+}
+
+func (l *LsTLVIsisArea) String() string {
+ return fmt.Sprintf("{ISIS Area ID: %v}", l.Area)
+}
+
+func (l *LsTLVIsisArea) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Area string `json:"isis_area_id"`
+ }{
+ Type: l.Type,
+ Area: fmt.Sprintf("%v", l.Area),
+ })
+}
+
+type LsTLVLocalIPv4RouterID struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVLocalIPv4RouterID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV4_LOCAL_ROUTER_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-4.3
+ if len(value) != 4 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ return nil
+}
+
+func (l *LsTLVLocalIPv4RouterID) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVLocalIPv4RouterID) String() string {
+ return fmt.Sprintf("{Local RouterID IPv4: %v}", l.IP)
+}
+
+func (l *LsTLVLocalIPv4RouterID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"node_local_router_id_ipv4"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+type LsTLVRemoteIPv4RouterID struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVRemoteIPv4RouterID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV4_REMOTE_ROUTER_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-4.3
+ if len(value) != 4 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ return nil
+}
+
+func (l *LsTLVRemoteIPv4RouterID) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVRemoteIPv4RouterID) String() string {
+ return fmt.Sprintf("{Remote RouterID IPv4: %v}", l.IP)
+}
+
+func (l *LsTLVRemoteIPv4RouterID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"node_remote_router_id_ipv4"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+type LsTLVLocalIPv6RouterID struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVLocalIPv6RouterID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV6_LOCAL_ROUTER_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc6119#section-4.1
+ if len(value) != 16 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ return nil
+}
+
+func (l *LsTLVLocalIPv6RouterID) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVLocalIPv6RouterID) String() string {
+ return fmt.Sprintf("{Local RouterID IPv6: %v}", l.IP)
+}
+
+func (l *LsTLVLocalIPv6RouterID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"node_local_router_id_ipv6"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+type LsTLVRemoteIPv6RouterID struct {
+ LsTLV
+ IP net.IP
+}
+
+func (l *LsTLVRemoteIPv6RouterID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IPV6_REMOTE_ROUTER_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc6119#section-4.1
+ if len(value) != 16 {
+ return malformedAttrListErr("Unexpected address size")
+ }
+
+ l.IP = net.IP(value)
+
+ return nil
+}
+
+func (l *LsTLVRemoteIPv6RouterID) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.IP)
+}
+
+func (l *LsTLVRemoteIPv6RouterID) String() string {
+ return fmt.Sprintf("{Remote RouterID IPv6: %v}", l.IP)
+}
+
+func (l *LsTLVRemoteIPv6RouterID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"node_remote_router_id_ipv6"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.IP),
+ })
+}
+
+type LsTLVOpaqueNodeAttr struct {
+ LsTLV
+ Attr []byte
+}
+
+func (l *LsTLVOpaqueNodeAttr) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_OPAQUE_NODE_ATTR {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ l.Attr = value
+
+ return nil
+}
+
+func (l *LsTLVOpaqueNodeAttr) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.Attr)
+}
+
+func (l *LsTLVOpaqueNodeAttr) String() string {
+ return fmt.Sprintf("{Opaque attribute: %v}", l.Attr)
+}
+
+func (l *LsTLVOpaqueNodeAttr) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"node_opaque_attribute"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.Attr),
+ })
+}
+
+type LsTLVAutonomousSystem struct {
+ LsTLV
+ ASN uint32
+}
+
+func (l *LsTLVAutonomousSystem) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_AS {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.2.1.4
+ if len(value) != 4 {
+ return malformedAttrListErr("Incorrect AS length")
+ }
+
+ l.ASN = binary.BigEndian.Uint32(value)
+
+ return nil
+}
+
+func (l *LsTLVAutonomousSystem) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, l.ASN)
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVAutonomousSystem) String() string {
+ return fmt.Sprintf("{ASN: %d}", l.ASN)
+}
+
+func (l *LsTLVAutonomousSystem) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ ASN uint32 `json:"asn"`
+ }{
+ Type: l.Type,
+ ASN: l.ASN,
+ })
+}
+
+type LsTLVBgpLsID struct {
+ LsTLV
+ BGPLsID uint32
+}
+
+func (l *LsTLVBgpLsID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_BGP_LS_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.2.1.4
+ if len(value) != 4 {
+ return malformedAttrListErr("Incorrect BGP-LS ID length")
+ }
+
+ l.BGPLsID = binary.BigEndian.Uint32(value)
+
+ return nil
+}
+
+func (l *LsTLVBgpLsID) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, l.BGPLsID)
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVBgpLsID) String() string {
+ return fmt.Sprintf("{BGP LS ID: %d}", l.BGPLsID)
+}
+
+func (l *LsTLVBgpLsID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ BgpLsID uint32 `json:"bgp_ls_id"`
+ }{
+ Type: l.Type,
+ BgpLsID: l.BGPLsID,
+ })
+}
+
+type LsTLVIgpRouterID struct {
+ LsTLV
+ RouterID []byte
+}
+
+func (l *LsTLVIgpRouterID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IGP_ROUTER_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.2.1.4
+ // 6, 7, and 8 are the only valid values.
+ if len(value) < 6 || len(value) > 8 {
+ return malformedAttrListErr("Incorrect IGP Router ID length")
+ }
+
+ l.RouterID = value
+
+ return nil
+}
+
+func (l *LsTLVIgpRouterID) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.RouterID)
+}
+
+func (l *LsTLVIgpRouterID) String() string {
+ return fmt.Sprintf("{IGP Router ID: %v}", l.RouterID)
+}
+
+func (l *LsTLVIgpRouterID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ RouterID string `json:"igp_router_id"`
+ }{
+ Type: l.Type,
+ RouterID: fmt.Sprintf("%v", l.RouterID),
+ })
+}
+
+type LsTLVOspfAreaID struct {
+ LsTLV
+ AreaID uint32
+}
+
+func (l *LsTLVOspfAreaID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_OSPF_AREA {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.2.1.4
+ if len(value) != 4 {
+ return malformedAttrListErr("Incorrect OSPF Area ID length")
+ }
+
+ l.AreaID = binary.BigEndian.Uint32(value)
+
+ return nil
+}
+
+func (l *LsTLVOspfAreaID) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, l.AreaID)
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVOspfAreaID) String() string {
+ return fmt.Sprintf("{OSPF Area ID: %d}", l.AreaID)
+}
+
+func (l *LsTLVOspfAreaID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ AreaID uint32 `json:"ospf_area_id"`
+ }{
+ Type: l.Type,
+ AreaID: l.AreaID,
+ })
+}
+
+type LsOspfRouteType uint8
+
+const (
+ LS_OSPF_ROUTE_TYPE_UNKNOWN = iota
+ LS_OSPF_ROUTE_TYPE_INTRA_AREA
+ LS_OSPF_ROUTE_TYPE_INTER_AREA
+ LS_OSPF_ROUTE_TYPE_EXTERNAL1
+ LS_OSPF_ROUTE_TYPE_EXTERNAL2
+ LS_OSPF_ROUTE_TYPE_NSSA1
+ LS_OSPF_ROUTE_TYPE_NSSA2
+)
+
+func (l LsOspfRouteType) String() string {
+ switch l {
+ case LS_OSPF_ROUTE_TYPE_INTRA_AREA:
+ return "INTRA-AREA"
+ case LS_OSPF_ROUTE_TYPE_INTER_AREA:
+ return "INTER-AREA"
+ case LS_OSPF_ROUTE_TYPE_EXTERNAL1:
+ return "EXTERNAL1"
+ case LS_OSPF_ROUTE_TYPE_EXTERNAL2:
+ return "EXTERNAL2"
+ case LS_OSPF_ROUTE_TYPE_NSSA1:
+ return "NSSA1"
+ case LS_OSPF_ROUTE_TYPE_NSSA2:
+ return "NSSA2"
+ default:
+ return fmt.Sprintf("LsOspfRouteType(%d)", uint8(l))
+ }
+}
+
+type LsTLVOspfRouteType struct {
+ LsTLV
+ RouteType LsOspfRouteType
+}
+
+func (l *LsTLVOspfRouteType) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_OSPF_ROUTE_TYPE {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.2.3.1
+ if len(value) != 1 {
+ return malformedAttrListErr("Incorrect OSPF Route type length")
+ }
+
+ if value[0] < byte(LS_OSPF_ROUTE_TYPE_INTRA_AREA) || value[0] > LS_OSPF_ROUTE_TYPE_NSSA2 {
+ return malformedAttrListErr("Incorrect OSPF Route type")
+ }
+
+ l.RouteType = LsOspfRouteType(value[0])
+
+ return nil
+}
+
+func (l *LsTLVOspfRouteType) Serialize() ([]byte, error) {
+ buf := make([]byte, 1)
+ buf[0] = byte(l.RouteType)
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVOspfRouteType) String() string {
+ return fmt.Sprintf("{OSPF Route Type: %v}", l.RouteType)
+}
+
+func (l *LsTLVOspfRouteType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ RouteType string `json:"ospf_route_type"`
+ }{
+ Type: l.Type,
+ RouteType: l.RouteType.String(),
+ })
+}
+
+type LsTLVIPReachability struct {
+ LsTLV
+ PrefixLength uint8
+ Prefix []byte
+}
+
+func (l *LsTLVIPReachability) ToIPNet(ipv6 bool) net.IPNet {
+ b := make([]byte, 16)
+ for i := 0; i < int(((l.PrefixLength-1)/8)+1); i++ {
+ b[i] = l.Prefix[i]
+ }
+
+ ip := net.IPv4(b[0], b[1], b[2], b[3]).To4()
+ if ipv6 {
+ ip = net.IP(b).To16()
+ }
+
+ _, n, err := net.ParseCIDR(fmt.Sprintf("%v/%v", ip, l.PrefixLength))
+ if err != nil {
+ return net.IPNet{}
+ }
+
+ return *n
+}
+
+func (l *LsTLVIPReachability) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IP_REACH_INFO {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ if len(value) < 2 {
+ return malformedAttrListErr("Incorrect IP reachability Info length")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.2.3.2
+ if value[0] > 128 || value[0] == 0 {
+ return malformedAttrListErr("Incorrect IP prefix length")
+ }
+
+ ll := int(((value[0] - 1) / 8) + 1)
+ if len(value[1:]) != ll {
+ return malformedAttrListErr("Malformed IP reachability TLV")
+ }
+
+ l.PrefixLength = value[0]
+ l.Prefix = value[1 : 1+ll]
+
+ return nil
+}
+
+func (l *LsTLVIPReachability) Serialize() ([]byte, error) {
+ b := []byte{l.PrefixLength}
+
+ return l.LsTLV.Serialize(append(b, l.Prefix...))
+}
+
+func (l *LsTLVIPReachability) String() string {
+ return fmt.Sprintf("{IP Reachability: %v/%v}", l.Prefix, l.PrefixLength)
+}
+
+func (l *LsTLVIPReachability) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ PrefixLength uint8 `json:"prefix_length"`
+ Prefix string `json:"prefix"`
+ }{
+ Type: l.Type,
+ PrefixLength: l.PrefixLength,
+ Prefix: fmt.Sprintf("%v", l.Prefix),
+ })
+}
+
+type LsTLVAdminGroup struct {
+ LsTLV
+ AdminGroup uint32
+}
+
+func (l *LsTLVAdminGroup) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_ADMIN_GROUP {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-3.1
+ if len(value) != 4 {
+ return malformedAttrListErr("Incorrect Admin Group length")
+ }
+
+ l.AdminGroup = binary.BigEndian.Uint32(value)
+
+ return nil
+}
+
+func (l *LsTLVAdminGroup) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, l.AdminGroup)
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVAdminGroup) String() string {
+ return fmt.Sprintf("{Admin Group: %08x}", l.AdminGroup)
+}
+
+func (l *LsTLVAdminGroup) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ AdminGroup string `json:"admin_group"`
+ }{
+ Type: l.Type,
+ AdminGroup: fmt.Sprintf("%08x", l.AdminGroup),
+ })
+}
+
+type LsTLVMaxLinkBw struct {
+ LsTLV
+ Bandwidth float32
+}
+
+func (l *LsTLVMaxLinkBw) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_MAX_LINK_BANDWIDTH {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-3.4
+ if len(value) != 4 {
+ return malformedAttrListErr("Incorrect maximum link bandwidth length")
+ }
+
+ l.Bandwidth = math.Float32frombits(binary.BigEndian.Uint32(value))
+
+ if l.Bandwidth < 0 || math.IsNaN(float64(l.Bandwidth)) || math.IsInf(float64(l.Bandwidth), 0) {
+ return malformedAttrListErr("Incorrect maximum link bandwidth value")
+ }
+
+ return nil
+}
+
+func (l *LsTLVMaxLinkBw) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, math.Float32bits(l.Bandwidth))
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVMaxLinkBw) String() string {
+ return fmt.Sprintf("{Max Link BW: %v}", l.Bandwidth)
+}
+
+func (l *LsTLVMaxLinkBw) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Bandwidth float32 `json:"max_link_bw"`
+ }{
+ Type: l.Type,
+ Bandwidth: l.Bandwidth,
+ })
+}
+
+type LsTLVMaxReservableLinkBw struct {
+ LsTLV
+ Bandwidth float32
+}
+
+func (l *LsTLVMaxReservableLinkBw) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_MAX_RESERVABLE_BANDWIDTH {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-3.5
+ if len(value) != 4 {
+ return malformedAttrListErr("Incorrect maximum reservable link bandwidth length")
+ }
+
+ l.Bandwidth = math.Float32frombits(binary.BigEndian.Uint32(value))
+
+ if l.Bandwidth < 0 || math.IsNaN(float64(l.Bandwidth)) || math.IsInf(float64(l.Bandwidth), 0) {
+ return malformedAttrListErr("Incorrect maximum reservable link bandwidth value")
+ }
+
+ return nil
+}
+
+func (l *LsTLVMaxReservableLinkBw) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, math.Float32bits(l.Bandwidth))
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVMaxReservableLinkBw) String() string {
+ return fmt.Sprintf("{Max Reservable Link BW: %v}", l.Bandwidth)
+}
+
+func (l *LsTLVMaxReservableLinkBw) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Bandwidth float32 `json:"max_reservable_link_bw"`
+ }{
+ Type: l.Type,
+ Bandwidth: l.Bandwidth,
+ })
+}
+
+type LsTLVUnreservedBw struct {
+ LsTLV
+ Bandwidth [8]float32
+}
+
+func (l *LsTLVUnreservedBw) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_UNRESERVED_BANDWIDTH {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc5305#section-3.6
+ if len(value) != 32 {
+ return malformedAttrListErr("Incorrect unreserved bandwidth length")
+ }
+
+ for i := 0; i < len(l.Bandwidth); i++ {
+ l.Bandwidth[i] = math.Float32frombits(binary.BigEndian.Uint32(value[:4]))
+ value = value[4:]
+
+ if l.Bandwidth[i] < 0 || math.IsNaN(float64(l.Bandwidth[i])) || math.IsInf(float64(l.Bandwidth[i]), 0) {
+ return malformedAttrListErr("Incorrect unreserved bandwidth value")
+ }
+ }
+
+ return nil
+}
+
+func (l *LsTLVUnreservedBw) Serialize() ([]byte, error) {
+ buf := make([]byte, 0)
+
+ for i := 0; i < len(l.Bandwidth); i++ {
+ b := make([]byte, 4)
+ binary.BigEndian.PutUint32(b, math.Float32bits(l.Bandwidth[i]))
+ buf = append(buf, b...)
+ }
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVUnreservedBw) String() string {
+ return fmt.Sprintf("{Unreserved BW: %v}", l.Bandwidth)
+}
+
+func (l *LsTLVUnreservedBw) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Bandwidth [8]float32 `json:"unreserved_bw"`
+ }{
+ Type: l.Type,
+ Bandwidth: l.Bandwidth,
+ })
+}
+
+type LsTLVTEDefaultMetric struct {
+ LsTLV
+ Metric uint32
+}
+
+func (l *LsTLVTEDefaultMetric) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_TE_DEFAULT_METRIC {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.3.2.3
+ if len(value) != 4 {
+ return malformedAttrListErr("Incorrect metric length length")
+ }
+
+ l.Metric = binary.BigEndian.Uint32(value)
+
+ return nil
+}
+
+func (l *LsTLVTEDefaultMetric) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, l.Metric)
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVTEDefaultMetric) String() string {
+ return fmt.Sprintf("{TE Default metric: %d}", l.Metric)
+}
+
+func (l *LsTLVTEDefaultMetric) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ DefaultMetric uint32 `json:"te_default_metric"`
+ }{
+ Type: l.Type,
+ DefaultMetric: l.Metric,
+ })
+}
+
+type LsTLVIGPMetric struct {
+ LsTLV
+ Metric uint32
+}
+
+func (l *LsTLVIGPMetric) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IGP_METRIC {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.3.2.4
+ switch len(value) {
+ case 1:
+ l.Metric = uint32(value[0] & 0x3F)
+
+ case 2:
+ l.Metric = uint32(binary.BigEndian.Uint16(value))
+
+ case 3:
+ l.Metric = binary.BigEndian.Uint32([]byte{0, value[0], value[1], value[2]})
+
+ default:
+ return malformedAttrListErr("Incorrect metric length")
+ }
+
+ return nil
+}
+
+func (l *LsTLVIGPMetric) Serialize() ([]byte, error) {
+ switch l.Length {
+ case 1:
+ return l.LsTLV.Serialize([]byte{uint8(l.Metric) & 0x3F})
+
+ case 2:
+ buf := make([]byte, 2)
+ binary.BigEndian.PutUint16(buf, uint16(l.Metric))
+ return l.LsTLV.Serialize(buf)
+
+ case 3:
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, l.Metric)
+ return l.LsTLV.Serialize(buf[1:])
+
+ default:
+ return nil, malformedAttrListErr("Incorrect metric length")
+ }
+}
+
+func (l *LsTLVIGPMetric) String() string {
+ return fmt.Sprintf("{IGP metric: %d}", l.Metric)
+}
+
+func (l *LsTLVIGPMetric) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Metric uint32 `json:"igp_metric"`
+ }{
+ Type: l.Type,
+ Metric: l.Metric,
+ })
+}
+
+type LsTLVLinkName struct {
+ LsTLV
+ Name string
+}
+
+func (l *LsTLVLinkName) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_LINK_NAME {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/rfc7752#section-3.3.2.7
+ if len(value) < 1 || len(value) > 255 {
+ return malformedAttrListErr("Incorrect Link Name")
+ }
+
+ l.Name = string(value)
+
+ return nil
+}
+
+func (l *LsTLVLinkName) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize([]byte(l.Name))
+}
+
+func (l *LsTLVLinkName) String() string {
+ return fmt.Sprintf("{Link Name: %s}", l.Name)
+}
+
+func (l *LsTLVLinkName) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Name string `json:"link_name"`
+ }{
+ Type: l.Type,
+ Name: l.Name,
+ })
+}
+
+type LsTLVSrAlgorithm struct {
+ LsTLV
+ Algorithm []byte
+}
+
+func (l *LsTLVSrAlgorithm) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_SR_ALGORITHM {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ if len(value) < 1 {
+ return malformedAttrListErr("Incorrect SR algorithm length")
+ }
+
+ l.Algorithm = value
+
+ return nil
+}
+
+func (l *LsTLVSrAlgorithm) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.Algorithm)
+}
+
+func (l *LsTLVSrAlgorithm) String() string {
+ return fmt.Sprintf("{SR Algorithms: %v}", l.Algorithm)
+}
+
+func (l *LsTLVSrAlgorithm) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Algorithms string `json:"sr_algorithm"`
+ }{
+ Type: l.Type,
+ Algorithms: fmt.Sprintf("%v", l.Algorithm),
+ })
+}
+
+type LsSrLabelRange struct {
+ Range uint32
+ FirstLabel LsTLVSIDLabel
+}
+
+type LsTLVSrCapabilities struct {
+ LsTLV
+ Flags uint8
+ Ranges []LsSrLabelRange
+}
+
+type LsSrRange struct {
+ Begin uint32 `json:"begin"`
+ End uint32 `json:"end"`
+}
+
+type LsSrCapabilities struct {
+ IPv4Supported bool `json:"ipv4_supported"`
+ IPv6Supported bool `json:"ipv6_supported"`
+ Ranges []LsSrRange `json:"ranges"`
+}
+
+func (l *LsTLVSrCapabilities) Extract() *LsSrCapabilities {
+ lsc := &LsSrCapabilities{
+ IPv4Supported: (l.Flags & (1 << 0)) > 0,
+ IPv6Supported: (l.Flags & (1 << 1)) > 0,
+ }
+
+ for _, r := range l.Ranges {
+ lsc.Ranges = append(lsc.Ranges, LsSrRange{
+ Begin: r.FirstLabel.SID,
+ End: r.FirstLabel.SID + r.Range,
+ })
+ }
+
+ return lsc
+}
+
+func (l *LsTLVSrCapabilities) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_SR_CAPABILITIES {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ if len(value) < 2 {
+ return malformedAttrListErr("Incorrect SR Capabilities length")
+ }
+ l.Flags = value[0]
+
+ // Skip two bytes: flags and reserved.
+ value = value[2:]
+
+ // The value field should be at least eight bytes long. Three bytes
+ // for the range size and five or six bytes for the SID/Label TLV.
+ for len(value) > 8 {
+ // First, parse range size (3 bytes)
+ buf := []byte{0, 0, 0, 0}
+ for i := 1; i < len(buf); i++ {
+ buf[i] = value[i-1]
+ }
+ r := binary.BigEndian.Uint32(buf)
+ value = value[3:]
+
+ // Second, parse SID/Label sub-TLV.
+ label := LsTLVSIDLabel{}
+ if err := label.DecodeFromBytes(value); err != nil {
+ return err
+ }
+
+ l.Ranges = append(l.Ranges, LsSrLabelRange{
+ Range: r,
+ FirstLabel: label,
+ })
+
+ value = value[label.Len():]
+ }
+
+ if len(value) > 0 {
+ return malformedAttrListErr("Malformed SR Capabilities TLV")
+ }
+
+ return nil
+}
+
+func (l *LsTLVSrCapabilities) Serialize() ([]byte, error) {
+ buf := make([]byte, 0)
+ buf = append(buf, l.Flags)
+ buf = append(buf, 0)
+
+ for _, r := range l.Ranges {
+ b := make([]byte, 4)
+ binary.BigEndian.PutUint32(b, r.Range)
+ buf = append(buf, b[1:]...)
+ ser, err := r.FirstLabel.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, ser...)
+ }
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVSrCapabilities) String() string {
+ var buf bytes.Buffer
+
+ for _, r := range l.Ranges {
+ buf.WriteString(fmt.Sprintf("%v:%v ", r.FirstLabel.SID, r.FirstLabel.SID+r.Range))
+ }
+
+ return fmt.Sprintf("{SR Capabilities: Flags:%v SRGB Ranges: %v}", l.Flags, buf.String())
+}
+
+func (l *LsTLVSrCapabilities) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Flags uint8 `json:"flags"`
+ Ranges []LsSrLabelRange `json:"ranges"`
+ }{
+ Type: l.Type,
+ Flags: l.Flags,
+ Ranges: l.Ranges,
+ })
+}
+
+type LsTLVSrLocalBlock struct {
+ LsTLV
+ Flags uint8
+ Ranges []LsSrLabelRange
+}
+
+type LsSrLocalBlock struct {
+ Ranges []LsSrRange `json:"ranges"`
+}
+
+func (l *LsTLVSrLocalBlock) Extract() *LsSrLocalBlock {
+ lb := &LsSrLocalBlock{}
+
+ for _, r := range l.Ranges {
+ lb.Ranges = append(lb.Ranges, LsSrRange{
+ Begin: r.FirstLabel.SID,
+ End: r.FirstLabel.SID + r.Range,
+ })
+ }
+
+ return lb
+}
+
+func (l *LsTLVSrLocalBlock) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_SR_LOCAL_BLOCK {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ if len(value) < 2 {
+ return malformedAttrListErr("Incorrect SR Local Block length")
+ }
+ l.Flags = value[0]
+
+ // Skip two bytes: flags and reserved.
+ value = value[2:]
+
+ // The value field should be at least eight bytes long. Three bytes
+ // for the range size and five or six bytes for the SID/Label TLV.
+ for len(value) > 8 {
+ // First, parse range size (3 bytes)
+ buf := []byte{0, 0, 0, 0}
+ for i := 1; i < len(buf); i++ {
+ buf[i] = value[i-1]
+ }
+ r := binary.BigEndian.Uint32(buf)
+ value = value[3:]
+
+ // Second, parse SID/Label sub-TLV.
+ label := LsTLVSIDLabel{}
+ if err := label.DecodeFromBytes(value); err != nil {
+ return err
+ }
+
+ l.Ranges = append(l.Ranges, LsSrLabelRange{
+ Range: r,
+ FirstLabel: label,
+ })
+
+ value = value[label.Len():]
+ }
+
+ if len(value) > 0 {
+ return malformedAttrListErr("Malformed SR Local Block TLV")
+ }
+
+ return nil
+}
+
+func (l *LsTLVSrLocalBlock) Serialize() ([]byte, error) {
+ buf := make([]byte, 0)
+ buf = append(buf, l.Flags)
+ buf = append(buf, 0)
+
+ for _, r := range l.Ranges {
+ b := make([]byte, 4)
+ binary.BigEndian.PutUint32(b, r.Range)
+ buf = append(buf, b[1:]...)
+ ser, err := r.FirstLabel.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, ser...)
+ }
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVSrLocalBlock) String() string {
+ var buf bytes.Buffer
+
+ for _, r := range l.Ranges {
+ buf.WriteString(fmt.Sprintf("%v:%v ", r.FirstLabel.SID, r.FirstLabel.SID+r.Range))
+ }
+
+ return fmt.Sprintf("{SR LocalBlock: Flags:%v SRGB Ranges: %v}", l.Flags, buf.String())
+}
+
+func (l *LsTLVSrLocalBlock) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Flags uint8 `json:"flags"`
+ Ranges []LsSrLabelRange `json:"ranges"`
+ }{
+ Type: l.Type,
+ Flags: l.Flags,
+ Ranges: l.Ranges,
+ })
+}
+
+type LsTLVAdjacencySID struct {
+ LsTLV
+ Flags uint8
+ Weight uint8
+ SID uint32
+}
+
+func (l *LsTLVAdjacencySID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_ADJACENCY_SID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/draft-ietf-idr-bgp-ls-segment-routing-ext-08#section-2.2.1
+ if len(value) != 7 && len(value) != 8 {
+ return malformedAttrListErr("Incorrect Adjacency SID length")
+ }
+
+ l.Flags = value[0]
+ l.Weight = value[1]
+
+ v := value[4:]
+ if len(v) == 4 {
+ l.SID = binary.BigEndian.Uint32(v)
+ } else {
+ buf := []byte{0, 0, 0, 0}
+ for i := 1; i < len(buf); i++ {
+ buf[i] = v[i-1]
+ }
+ // Label is represented by 20 rightmost bits.
+ l.SID = binary.BigEndian.Uint32(buf) & 0xfffff
+ }
+
+ return nil
+}
+
+func (l *LsTLVAdjacencySID) Serialize() ([]byte, error) {
+ buf := make([]byte, 0)
+ buf = append(buf, l.Flags)
+ buf = append(buf, l.Weight)
+ // Reserved
+ buf = append(buf, []byte{0, 0}...)
+
+ b := make([]byte, 4)
+ binary.BigEndian.PutUint32(b, l.SID)
+
+ if l.Length == 7 {
+ return l.LsTLV.Serialize(append(buf, b[1:]...))
+ }
+
+ return l.LsTLV.Serialize(append(buf, b...))
+}
+
+func (l *LsTLVAdjacencySID) String() string {
+ return fmt.Sprintf("{Adjacency SID: %v}", l.SID)
+}
+
+func (l *LsTLVAdjacencySID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ SID uint32 `json:"adjacency_sid"`
+ }{
+ Type: l.Type,
+ SID: l.SID,
+ })
+}
+
+type LsTLVSIDLabel struct {
+ LsTLV
+ SID uint32
+}
+
+func (l *LsTLVSIDLabel) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_SID_LABEL_TLV {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/draft-ietf-idr-bgp-ls-segment-routing-ext-08#section-2.1.1
+ if len(value) != 4 && len(value) != 3 {
+ return malformedAttrListErr("Incorrect SID length")
+ }
+
+ if len(value) == 4 {
+ l.SID = binary.BigEndian.Uint32(value)
+ } else {
+ buf := []byte{0, 0, 0, 0}
+ for i := 1; i < len(buf); i++ {
+ buf[i] = value[i-1]
+ }
+ // Label is represented by 20 rightmost bits.
+ l.SID = binary.BigEndian.Uint32(buf) & 0xfffff
+ }
+
+ return nil
+}
+
+func (l *LsTLVSIDLabel) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, l.SID)
+
+ if l.Length == 3 {
+ return l.LsTLV.Serialize(buf[1:])
+ }
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVSIDLabel) String() string {
+ return fmt.Sprintf("{SID/Label: %v}", l.SID)
+}
+
+func (l *LsTLVSIDLabel) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ SID uint32 `json:"sid_label"`
+ }{
+ Type: l.Type,
+ SID: l.SID,
+ })
+}
+
+type LsTLVPrefixSID struct {
+ LsTLV
+ Flags uint8
+ Algorithm uint8
+ SID uint32
+}
+
+func (l *LsTLVPrefixSID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_PREFIX_SID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/draft-ietf-idr-bgp-ls-segment-routing-ext-08#section-2.3.1
+ if len(value) != 7 && len(value) != 8 {
+ return malformedAttrListErr("Incorrect Prefix SID length")
+ }
+
+ l.Flags = value[0]
+ l.Algorithm = value[1]
+
+ // Flags (1) + Algorithm (1) + Reserved (2)
+ v := value[4:]
+ if len(v) == 4 {
+ l.SID = binary.BigEndian.Uint32(v)
+ } else {
+ buf := []byte{0, 0, 0, 0}
+ for i := 1; i < len(buf); i++ {
+ buf[i] = v[i-1]
+ }
+ // Label is represented by 20 rightmost bits.
+ l.SID = binary.BigEndian.Uint32(buf) & 0xfffff
+ }
+
+ return nil
+}
+
+func (l *LsTLVPrefixSID) Serialize() ([]byte, error) {
+ buf := make([]byte, 0)
+ buf = append(buf, l.Flags)
+ buf = append(buf, l.Algorithm)
+ // Reserved
+ buf = append(buf, []byte{0, 0}...)
+
+ b := make([]byte, 4)
+ binary.BigEndian.PutUint32(b, l.SID)
+
+ if l.Length == 7 {
+ return l.LsTLV.Serialize(append(buf, b[1:]...))
+ }
+
+ return l.LsTLV.Serialize(append(buf, b...))
+}
+
+func (l *LsTLVPrefixSID) String() string {
+ return fmt.Sprintf("{Prefix SID: %v}", l.SID)
+}
+
+func (l *LsTLVPrefixSID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ SID uint32 `json:"prefix_sid"`
+ }{
+ Type: l.Type,
+ SID: l.SID,
+ })
+}
+
+type LsTLVSourceRouterID struct {
+ LsTLV
+ RouterID []byte
+}
+
+func (l *LsTLVSourceRouterID) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_SOURCE_ROUTER_ID {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // https://tools.ietf.org/html/draft-ietf-idr-bgp-ls-segment-routing-ext-08#section-2.3.3
+ if len(value) != 4 && len(value) != 16 {
+ return malformedAttrListErr("Incorrect Source Router ID length")
+ }
+
+ l.RouterID = value
+
+ return nil
+}
+
+func (l *LsTLVSourceRouterID) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.RouterID)
+}
+
+func (l *LsTLVSourceRouterID) String() string {
+ return fmt.Sprintf("{Source Router ID: %v}", net.IP(l.RouterID))
+}
+
+func (l *LsTLVSourceRouterID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ RouterID string `json:"source_router_id"`
+ }{
+ Type: l.Type,
+ RouterID: fmt.Sprintf("%v", net.IP(l.RouterID)),
+ })
+}
+
+type LsTLVOpaqueLinkAttr struct {
+ LsTLV
+ Attr []byte
+}
+
+func (l *LsTLVOpaqueLinkAttr) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_OPAQUE_LINK_ATTR {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ l.Attr = value
+
+ return nil
+}
+
+func (l *LsTLVOpaqueLinkAttr) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.Attr)
+}
+
+func (l *LsTLVOpaqueLinkAttr) String() string {
+ return fmt.Sprintf("{Opaque link attribute: %v}", l.Attr)
+}
+
+func (l *LsTLVOpaqueLinkAttr) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"link_opaque_attribute"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.Attr),
+ })
+}
+
+type LsTLVIGPFlags struct {
+ LsTLV
+ Flags uint8
+}
+
+// https://tools.ietf.org/html/rfc7752#section-3.3.3.1
+type LsIGPFlags struct {
+ Down bool `json:"down"`
+ NoUnicast bool `json:"no_unicast"`
+ LocalAddress bool `json:"local_address"`
+ PropagateNSSA bool `json:"propagate_nssa"`
+}
+
+func (l *LsTLVIGPFlags) Extract() *LsIGPFlags {
+ return &LsIGPFlags{
+ Down: (l.Flags & (1 << 0)) > 0,
+ NoUnicast: (l.Flags & (1 << 1)) > 0,
+ LocalAddress: (l.Flags & (1 << 2)) > 0,
+ PropagateNSSA: (l.Flags & (1 << 3)) > 0,
+ }
+}
+
+func (l *LsTLVIGPFlags) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_IGP_FLAGS {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ if l.Length != 1 {
+ return malformedAttrListErr("Node Flag Bits TLV malformed")
+ }
+
+ l.Flags = value[0]
+
+ return nil
+}
+
+func (l *LsTLVIGPFlags) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize([]byte{l.Flags})
+}
+
+func (l *LsTLVIGPFlags) String() string {
+ flags := "XXXXPLND"
+
+ var buf bytes.Buffer
+
+ for i := 0; i < len(flags); i++ {
+ if l.Flags&(1<<uint(i)) > 0 {
+ buf.WriteString(flags[i : i+1])
+ } else {
+ buf.WriteString("*")
+ }
+ }
+
+ return fmt.Sprintf("{IGP Flags: %s}", buf.String())
+}
+
+func (l *LsTLVIGPFlags) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Flags string `json:"igp_flags"`
+ }{
+ Type: l.Type,
+ Flags: l.String(),
+ })
+}
+
+type LsTLVOpaquePrefixAttr struct {
+ LsTLV
+ Attr []byte
+}
+
+func (l *LsTLVOpaquePrefixAttr) DecodeFromBytes(data []byte) error {
+ value, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_OPAQUE_PREFIX_ATTR {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ l.Attr = value
+
+ return nil
+}
+
+func (l *LsTLVOpaquePrefixAttr) Serialize() ([]byte, error) {
+ return l.LsTLV.Serialize(l.Attr)
+}
+
+func (l *LsTLVOpaquePrefixAttr) String() string {
+ return fmt.Sprintf("{Prefix opaque attribute: %v}", l.Attr)
+}
+
+func (l *LsTLVOpaquePrefixAttr) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ Value string `json:"prefix_opaque_attribute"`
+ }{
+ Type: l.Type,
+ Value: fmt.Sprintf("%v", l.Attr),
+ })
+}
+
+type LsTLVNodeDescriptor struct {
+ LsTLV
+ SubTLVs []LsTLVInterface
+}
+
+func (l *LsTLVNodeDescriptor) DecodeFromBytes(data []byte) error {
+ tlv, err := l.LsTLV.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if l.Type != LS_TLV_LOCAL_NODE_DESC && l.Type != LS_TLV_REMOTE_NODE_DESC {
+ return malformedAttrListErr("Unexpected TLV type")
+ }
+
+ // RFC7752, 3.2.1.4
+ // There can be at most one instance of each sub-TLV type present in
+ // any Node Descriptor. The sub-TLVs within a Node Descriptor MUST
+ // be arranged in ascending order by sub-TLV type.
+ prevType := uint16(0)
+ m := make(map[LsTLVType]bool)
+
+ for len(tlv) >= tlvHdrLen {
+ sub := &LsTLV{}
+ _, err := sub.DecodeFromBytes(tlv)
+ if err != nil {
+ return err
+ }
+
+ if uint16(sub.Type) < prevType {
+ return malformedAttrListErr("Incorrect TLV order")
+ }
+ if _, ok := m[sub.Type]; ok {
+ return malformedAttrListErr("Duplicate TLV")
+ }
+ prevType = uint16(sub.Type)
+ m[sub.Type] = true
+
+ var subTLV LsTLVInterface
+ switch sub.Type {
+ case LS_TLV_AS:
+ subTLV = &LsTLVAutonomousSystem{}
+ case LS_TLV_BGP_LS_ID:
+ subTLV = &LsTLVBgpLsID{}
+ case LS_TLV_OSPF_AREA:
+ subTLV = &LsTLVOspfAreaID{}
+ case LS_TLV_IGP_ROUTER_ID:
+ subTLV = &LsTLVIgpRouterID{}
+
+ default:
+ tlv = tlv[sub.Len():]
+ l.Length -= uint16(sub.Len())
+ continue
+ }
+
+ if err := subTLV.DecodeFromBytes(tlv); err != nil {
+ return err
+ }
+ l.SubTLVs = append(l.SubTLVs, subTLV)
+ tlv = tlv[subTLV.Len():]
+ }
+
+ if _, ok := m[LS_TLV_IGP_ROUTER_ID]; !ok {
+ return malformedAttrListErr("Required TLV missing")
+ }
+
+ return nil
+}
+
+func (l *LsTLVNodeDescriptor) Serialize() ([]byte, error) {
+ buf := []byte{}
+ for _, tlv := range l.SubTLVs {
+ ser, err := tlv.Serialize()
+ if err != nil {
+ return nil, err
+ }
+
+ buf = append(buf, ser...)
+ }
+
+ return l.LsTLV.Serialize(buf)
+}
+
+func (l *LsTLVNodeDescriptor) String() string {
+ nd := l.Extract()
+
+ return fmt.Sprintf("{ASN: %v, BGP LS ID: %v, OSPF AREA: %v, IGP ROUTER ID: %v}", nd.Asn, nd.BGPLsID, nd.OspfAreaID, nd.IGPRouterID)
+}
+
+func (l *LsTLVNodeDescriptor) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type LsTLVType `json:"type"`
+ LsNodeDescriptor
+ }{
+ l.Type,
+ *l.Extract(),
+ })
+}
+
+type LsNodeDescriptor struct {
+ Asn uint32 `json:"asn"`
+ BGPLsID uint32 `json:"bgp_ls_id"`
+ OspfAreaID uint32 `json:"ospf_area_id"`
+ PseudoNode bool `json:"pseudo_node"`
+ IGPRouterID string `json:"igp_router_id"`
+}
+
+func parseIGPRouterID(id []byte) (string, bool) {
+ switch len(id) {
+ // OSPF or OSPFv3 non-pseudonode
+ case 4:
+ return net.IP(id).String(), false
+
+ // ISIS non-pseudonode
+ case 6:
+ return fmt.Sprintf("%0.2x%0.2x.%0.2x%0.2x.%0.2x%0.2x", id[0], id[1], id[2], id[3], id[4], id[5]), false
+
+ // ISIS pseudonode
+ case 7:
+ return fmt.Sprintf("%0.2x%0.2x.%0.2x%0.2x.%0.2x%0.2x-%0.2x", id[0], id[1], id[2], id[3], id[4], id[5], id[6]), true
+
+ // OSPF or OSPFv3 pseudonode
+ case 8:
+ return fmt.Sprintf("%v:%v", net.IP(id[:4]).String(), net.IP(id[4:]).String()), true
+
+ default:
+ return fmt.Sprintf("%v", id), false
+ }
+}
+
+func (l *LsTLVNodeDescriptor) Extract() *LsNodeDescriptor {
+ nd := &LsNodeDescriptor{}
+
+ for _, tlv := range l.SubTLVs {
+ switch v := tlv.(type) {
+ case *LsTLVAutonomousSystem:
+ nd.Asn = v.ASN
+ case *LsTLVBgpLsID:
+ nd.BGPLsID = v.BGPLsID
+ case *LsTLVOspfAreaID:
+ nd.OspfAreaID = v.AreaID
+ case *LsTLVIgpRouterID:
+ nd.IGPRouterID, nd.PseudoNode = parseIGPRouterID(v.RouterID)
+ }
+ }
+
+ return nd
+}
+
+type LsAddrPrefix struct {
+ PrefixDefault
+ Type LsNLRIType
+ Length uint16
+ NLRI LsNLRIInterface
+}
+
+func (l *LsAddrPrefix) AFI() uint16 {
+ return AFI_LS
+}
+
+func (l *LsAddrPrefix) SAFI() uint8 {
+ return SAFI_LS
+}
+
+func (l *LsAddrPrefix) Len(...*MarshallingOption) int {
+ return int(4 + l.Length)
+}
+
+func (l *LsAddrPrefix) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
+ if len(data) < 4 {
+ return malformedAttrListErr("Malformed BGP-LS Address Prefix")
+ }
+
+ l.Type = LsNLRIType(binary.BigEndian.Uint16(data[:2]))
+ l.Length = binary.BigEndian.Uint16(data[2:4])
+
+ switch l.Type {
+ case LS_NLRI_TYPE_NODE:
+ node := &LsNodeNLRI{}
+ node.Length = l.Length
+ node.NLRIType = LS_NLRI_TYPE_NODE
+ l.NLRI = node
+
+ case LS_NLRI_TYPE_LINK:
+ link := &LsLinkNLRI{}
+ link.Length = l.Length
+ link.NLRIType = LS_NLRI_TYPE_LINK
+ l.NLRI = link
+
+ case LS_NLRI_TYPE_PREFIX_IPV4:
+ prefixv4 := &LsPrefixV4NLRI{}
+ prefixv4.Length = l.Length
+ prefixv4.NLRIType = LS_NLRI_TYPE_PREFIX_IPV4
+ l.NLRI = prefixv4
+
+ case LS_NLRI_TYPE_PREFIX_IPV6:
+ prefixv6 := &LsPrefixV6NLRI{}
+ prefixv6.Length = l.Length
+ prefixv6.NLRIType = LS_NLRI_TYPE_PREFIX_IPV4
+ l.NLRI = prefixv6
+
+ default:
+ return malformedAttrListErr("Unsupported BGP-LS NLRI")
+ }
+
+ if l.NLRI != nil {
+ return l.NLRI.DecodeFromBytes(data[4:])
+ }
+
+ return nil
+}
+
+func (l *LsAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) {
+ if l.NLRI == nil {
+ return nil, fmt.Errorf("empty NLRI")
+ }
+
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint16(buf, uint16(l.Type))
+ binary.BigEndian.PutUint16(buf[2:], l.Length)
+
+ ser, err := l.NLRI.Serialize()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(buf, ser...), nil
+}
+
+func (l *LsAddrPrefix) MarshalJSON() ([]byte, error) {
+ return nil, nil
+}
+
+func (l *LsAddrPrefix) String() string {
+ if l.NLRI == nil {
+ return fmt.Sprintf("NLRI: (nil)")
+ }
+
+ return fmt.Sprintf("NLRI { %v }", l.NLRI.String())
+}
+
+func (l *LsAddrPrefix) Flat() map[string]string {
+ return map[string]string{}
+}
+
+type LsAttributeNode struct {
+ Flags *LsNodeFlags `json:"flags,omitempty"`
+ Opaque *[]byte `json:"opaque,omitempty"`
+ Name *string `json:"name,omitempty"`
+ IsisArea *[]byte `json:"isis_area,omitempty"`
+ LocalRouterID *net.IP `json:"local_router_id_ipv4,omitempty"`
+ LocalRouterIDv6 *net.IP `json:"local_router_id_ipv6,omitempty"`
+
+ // Segment Routing
+ SrCapabilties *LsSrCapabilities `json:"sr_capabilities,omitempty"`
+ SrAlgorithms *[]byte `json:"sr_algorithms,omitempty"`
+ SrLocalBlock *LsSrLocalBlock `json:"sr_local_block,omitempty"`
+}
+
+type LsAttributeLink struct {
+ Name *string `json:"name,omitempty"`
+ LocalRouterID *net.IP `json:"local_router_id_ipv4,omitempty"`
+ LocalRouterIDv6 *net.IP `json:"local_router_id_ipv6,omitempty"`
+ RemoteRouterID *net.IP `json:"remote_router_id_ipv4,omitempty"`
+ RemoteRouterIDv6 *net.IP `json:"remote_router_id_ipv6,omitempty"`
+ AdminGroup *uint32 `json:"admin_group,omitempty"`
+ DefaultTEMetric *uint32 `json:"default_te_metric,omitempty"`
+ IGPMetric *uint32 `json:"igp_metric,omitempty"`
+ Opaque *[]byte `json:"opaque,omitempty"`
+
+ // Bandwidth is expressed in bytes (not bits) per second.
+ Bandwidth *float32 `json:"bandwidth,omitempty"`
+ ReservableBandwidth *float32 `json:"reservable_bandwidth,omitempty"`
+ UnreservedBandwidth *[8]float32 `json:"unreserved_bandwidth,omitempty"`
+
+ SrAdjacencySID *uint32 `json:"adjacency_sid,omitempty"`
+}
+
+type LsAttributePrefix struct {
+ IGPFlags *LsIGPFlags `json:"igp_flags,omitempty"`
+ Opaque *[]byte `json:"opaque,omitempty"`
+
+ SrPrefixSID *uint32 `json:"sr_prefix_sid,omitempty"`
+}
+
+type LsAttribute struct {
+ Node LsAttributeNode `json:"node"`
+ Link LsAttributeLink `json:"link"`
+ Prefix LsAttributePrefix `json:"prefix"`
+}
+
+type PathAttributeLs struct {
+ PathAttribute
+ TLVs []LsTLVInterface
+}
+
+func (p *PathAttributeLs) Extract() *LsAttribute {
+ l := &LsAttribute{}
+
+ for _, tlv := range p.TLVs {
+ switch v := tlv.(type) {
+ case *LsTLVNodeFlagBits:
+ l.Node.Flags = v.Extract()
+
+ case *LsTLVOpaqueNodeAttr:
+ l.Node.Opaque = &v.Attr
+
+ case *LsTLVNodeName:
+ l.Node.Name = &v.Name
+
+ case *LsTLVIsisArea:
+ l.Node.IsisArea = &v.Area
+
+ case *LsTLVLocalIPv4RouterID:
+ l.Node.LocalRouterID = &v.IP
+ l.Link.LocalRouterID = &v.IP
+
+ case *LsTLVLocalIPv6RouterID:
+ l.Node.LocalRouterIDv6 = &v.IP
+ l.Link.LocalRouterIDv6 = &v.IP
+
+ case *LsTLVSrCapabilities:
+ l.Node.SrCapabilties = v.Extract()
+
+ case *LsTLVSrAlgorithm:
+ l.Node.SrAlgorithms = &v.Algorithm
+
+ case *LsTLVSrLocalBlock:
+ l.Node.SrLocalBlock = v.Extract()
+
+ case *LsTLVRemoteIPv4RouterID:
+ l.Link.RemoteRouterID = &v.IP
+
+ case *LsTLVRemoteIPv6RouterID:
+ l.Link.RemoteRouterIDv6 = &v.IP
+
+ case *LsTLVAdminGroup:
+ l.Link.AdminGroup = &v.AdminGroup
+
+ case *LsTLVMaxLinkBw:
+ l.Link.Bandwidth = &v.Bandwidth
+
+ case *LsTLVMaxReservableLinkBw:
+ l.Link.ReservableBandwidth = &v.Bandwidth
+
+ case *LsTLVUnreservedBw:
+ l.Link.UnreservedBandwidth = &v.Bandwidth
+
+ case *LsTLVTEDefaultMetric:
+ l.Link.DefaultTEMetric = &v.Metric
+
+ case *LsTLVIGPMetric:
+ l.Link.IGPMetric = &v.Metric
+
+ case *LsTLVOpaqueLinkAttr:
+ l.Link.Opaque = &v.Attr
+
+ case *LsTLVLinkName:
+ l.Link.Name = &v.Name
+
+ case *LsTLVAdjacencySID:
+ l.Link.SrAdjacencySID = &v.SID
+
+ case *LsTLVIGPFlags:
+ l.Prefix.IGPFlags = v.Extract()
+
+ case *LsTLVOpaquePrefixAttr:
+ l.Prefix.Opaque = &v.Attr
+
+ case *LsTLVPrefixSID:
+ l.Prefix.SrPrefixSID = &v.SID
+ }
+ }
+
+ return l
+}
+
+func (p *PathAttributeLs) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
+ tlvs, err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ for len(tlvs) >= tlvHdrLen {
+ t := &LsTLV{}
+ _, err := t.DecodeFromBytes(tlvs)
+ if err != nil {
+ return err
+ }
+
+ var tlv LsTLVInterface
+ switch t.Type {
+ // Node NLRI-related TLVs (https://tools.ietf.org/html/rfc7752#section-3.3.1)
+ case LS_TLV_NODE_FLAG_BITS:
+ tlv = &LsTLVNodeFlagBits{}
+
+ case LS_TLV_OPAQUE_NODE_ATTR:
+ tlv = &LsTLVOpaqueNodeAttr{}
+
+ case LS_TLV_NODE_NAME:
+ tlv = &LsTLVNodeName{}
+
+ case LS_TLV_ISIS_AREA:
+ tlv = &LsTLVIsisArea{}
+
+ // Used by Link NLRI as well.
+ case LS_TLV_IPV4_LOCAL_ROUTER_ID:
+ tlv = &LsTLVLocalIPv4RouterID{}
+
+ // Used by Link NLRI as well.
+ case LS_TLV_IPV6_LOCAL_ROUTER_ID:
+ tlv = &LsTLVLocalIPv6RouterID{}
+
+ // SR-related TLVs (draft-ietf-idr-bgp-ls-segment-routing-ext-08) for Node NLRI
+ case LS_TLV_SR_CAPABILITIES:
+ tlv = &LsTLVSrCapabilities{}
+
+ case LS_TLV_SR_ALGORITHM:
+ tlv = &LsTLVSrAlgorithm{}
+
+ case LS_TLV_SR_LOCAL_BLOCK:
+ tlv = &LsTLVSrLocalBlock{}
+
+ // Link NLRI-related TLVs (https://tools.ietf.org/html/rfc7752#section-3.3.2)
+ case LS_TLV_IPV4_REMOTE_ROUTER_ID:
+ tlv = &LsTLVRemoteIPv4RouterID{}
+
+ case LS_TLV_IPV6_REMOTE_ROUTER_ID:
+ tlv = &LsTLVRemoteIPv6RouterID{}
+
+ case LS_TLV_ADMIN_GROUP:
+ tlv = &LsTLVAdminGroup{}
+
+ case LS_TLV_MAX_LINK_BANDWIDTH:
+ tlv = &LsTLVMaxLinkBw{}
+
+ case LS_TLV_MAX_RESERVABLE_BANDWIDTH:
+ tlv = &LsTLVMaxReservableLinkBw{}
+
+ case LS_TLV_UNRESERVED_BANDWIDTH:
+ tlv = &LsTLVUnreservedBw{}
+
+ case LS_TLV_TE_DEFAULT_METRIC:
+ tlv = &LsTLVTEDefaultMetric{}
+
+ case LS_TLV_IGP_METRIC:
+ tlv = &LsTLVIGPMetric{}
+
+ case LS_TLV_OPAQUE_LINK_ATTR:
+ tlv = &LsTLVOpaqueLinkAttr{}
+
+ case LS_TLV_LINK_NAME:
+ tlv = &LsTLVLinkName{}
+
+ // SR-related TLVs (draft-ietf-idr-bgp-ls-segment-routing-ext-08) for Link NLRI
+ case LS_TLV_ADJACENCY_SID:
+ tlv = &LsTLVAdjacencySID{}
+
+ // Prefix NLRI-related TLVs (https://tools.ietf.org/html/rfc7752#section-3.3.3)
+ case LS_TLV_IGP_FLAGS:
+ tlv = &LsTLVIGPFlags{}
+
+ case LS_TLV_OPAQUE_PREFIX_ATTR:
+ tlv = &LsTLVOpaquePrefixAttr{}
+
+ // SR-related TLVs (draft-ietf-idr-bgp-ls-segment-routing-ext-08) for Prefix NLRI
+ case LS_TLV_PREFIX_SID:
+ tlv = &LsTLVPrefixSID{}
+
+ default:
+ tlvs = tlvs[t.Len():]
+ continue
+ }
+
+ if err := tlv.DecodeFromBytes(tlvs); err != nil {
+ return err
+ }
+ tlvs = tlvs[t.Len():]
+
+ p.TLVs = append(p.TLVs, tlv)
+ }
+
+ return nil
+}
+
+func (p *PathAttributeLs) Serialize(options ...*MarshallingOption) ([]byte, error) {
+ buf := []byte{}
+
+ for _, tlv := range p.TLVs {
+ s, err := tlv.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, s...)
+ }
+
+ return p.PathAttribute.Serialize(buf, options...)
+}
+
+func (p *PathAttributeLs) String() string {
+ var buf bytes.Buffer
+
+ for _, tlv := range p.TLVs {
+ buf.WriteString(fmt.Sprintf("%s ", tlv.String()))
+ }
+
+ return fmt.Sprintf("{LsAttributes: %s}", buf.String())
+}
+
+func (p *PathAttributeLs) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type BGPAttrType `json:"type"`
+ Flags BGPAttrFlag `json:"flags"`
+ LsAttribute
+ }{
+ p.GetType(),
+ p.GetFlags(),
+ *p.Extract(),
+ })
+}
+
func AfiSafiToRouteFamily(afi uint16, safi uint8) RouteFamily {
return RouteFamily(int(afi)<<16 | int(safi))
}
@@ -4750,6 +7956,7 @@ const (
RF_FS_IPv6_VPN RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_VPN
RF_FS_L2_VPN RouteFamily = AFI_L2VPN<<16 | SAFI_FLOW_SPEC_VPN
RF_OPAQUE RouteFamily = AFI_OPAQUE<<16 | SAFI_KEY_VALUE
+ RF_LS RouteFamily = AFI_LS<<16 | SAFI_LS
)
var AddressFamilyNameMap = map[RouteFamily]string{
@@ -4774,6 +7981,7 @@ var AddressFamilyNameMap = map[RouteFamily]string{
RF_FS_IPv6_VPN: "l3vpn-ipv6-flowspec",
RF_FS_L2_VPN: "l2vpn-flowspec",
RF_OPAQUE: "opaque",
+ RF_LS: "ls",
}
var AddressFamilyValueMap = map[string]RouteFamily{
@@ -4798,6 +8006,7 @@ var AddressFamilyValueMap = map[string]RouteFamily{
AddressFamilyNameMap[RF_FS_IPv6_VPN]: RF_FS_IPv6_VPN,
AddressFamilyNameMap[RF_FS_L2_VPN]: RF_FS_L2_VPN,
AddressFamilyNameMap[RF_OPAQUE]: RF_OPAQUE,
+ AddressFamilyNameMap[RF_LS]: RF_LS,
}
func GetRouteFamily(name string) (RouteFamily, error) {
@@ -4861,6 +8070,8 @@ func NewPrefixFromRouteFamily(afi uint16, safi uint8, prefixStr ...string) (pref
prefix = &FlowSpecL2VPN{FlowSpecNLRI{rf: RF_FS_L2_VPN}}
case RF_OPAQUE:
prefix = &OpaqueNLRI{}
+ case RF_LS:
+ prefix = &LsAddrPrefix{}
default:
err = fmt.Errorf("unknown route family. AFI: %d, SAFI: %d", afi, safi)
}
@@ -4921,9 +8132,12 @@ const (
BGP_ATTR_TYPE_PMSI_TUNNEL // = 22
BGP_ATTR_TYPE_TUNNEL_ENCAP
_
- BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES // = 25
- BGP_ATTR_TYPE_AIGP // = 26
- BGP_ATTR_TYPE_LARGE_COMMUNITY BGPAttrType = 32
+ BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES // = 25
+ BGP_ATTR_TYPE_AIGP // = 26
+ _
+ _
+ BGP_ATTR_TYPE_LS // = 29
+ BGP_ATTR_TYPE_LARGE_COMMUNITY BGPAttrType = 32
)
// NOTIFICATION Error Code RFC 4271 4.5.
@@ -5117,6 +8331,7 @@ var PathAttrFlags map[BGPAttrType]BGPAttrFlag = map[BGPAttrType]BGPAttrFlag{
BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
BGP_ATTR_TYPE_AIGP: BGP_ATTR_FLAG_OPTIONAL,
BGP_ATTR_TYPE_LARGE_COMMUNITY: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_TYPE_LS: BGP_ATTR_FLAG_OPTIONAL,
}
// getPathAttrFlags returns BGP Path Attribute flags value from its type and
@@ -8999,6 +12214,8 @@ func GetPathAttribute(data []byte) (PathAttributeInterface, error) {
return &PathAttributeAigp{}, nil
case BGP_ATTR_TYPE_LARGE_COMMUNITY:
return &PathAttributeLargeCommunities{}, nil
+ case BGP_ATTR_TYPE_LS:
+ return &PathAttributeLs{}, nil
}
return &PathAttributeUnknown{}, nil
}
diff --git a/pkg/packet/bgp/bgp_test.go b/pkg/packet/bgp/bgp_test.go
index 6a2d5f12..43bf73a0 100644
--- a/pkg/packet/bgp/bgp_test.go
+++ b/pkg/packet/bgp/bgp_test.go
@@ -1203,3 +1203,1935 @@ func Test_PathAttributeNextHop(t *testing.T) {
f("192.0.2.1")
f("2001:db8::68")
}
+
+func Test_LsTLVDecode(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ t LsTLVType
+ l uint16
+ v []byte
+ err bool
+ }{
+ {[]byte{0x01, 0x09, 0x00, 0x1, 0xef}, LS_TLV_IP_REACH_INFO, 5, []byte{0xef}, false},
+ {[]byte{0x01, 0x09, 0x00, 0x0}, LS_TLV_IP_REACH_INFO, 4, []byte{}, false},
+ {[]byte{0x01, 0x09, 0x01, 0xff}, LS_TLV_IP_REACH_INFO, 0, []byte{}, true},
+ {[]byte{0x01, 0x09, 0x01}, LS_TLV_L2_BUNDLE_MEMBER_TLV, 1, []byte{}, true},
+ }
+
+ for _, test := range tests {
+ tlv := &LsTLV{}
+
+ got, err := tlv.DecodeFromBytes(test.in)
+ if test.err {
+ assert.Error(err)
+ continue
+ } else {
+ assert.NoError(err)
+ }
+ assert.Equal(tlv.Len(), int(test.l))
+ assert.Equal(got, test.v)
+ }
+}
+
+func Test_LsTLVSerialize(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ tlv LsTLV
+ val []byte
+ want []byte
+ err bool
+ }{
+ {LsTLV{Type: LS_TLV_SID_LABEL_TLV, Length: 2}, []byte{0x11, 0x22}, []byte{0x04, 0x89, 0x00, 0x02, 0x11, 0x22}, false},
+ {LsTLV{Type: LS_TLV_SID_LABEL_TLV, Length: 2}, []byte{0x11}, nil, true},
+ {LsTLV{Type: LS_TLV_IGP_FLAGS, Length: 0}, []byte{}, []byte{0x04, 0x80, 0x00, 0x00}, false},
+ }
+
+ for _, test := range tests {
+ got, err := test.tlv.Serialize(test.val)
+ if test.err {
+ assert.Error(err)
+ } else {
+ assert.NoError(err)
+ }
+
+ assert.Equal(got, test.want)
+ }
+}
+
+func Test_LsTLVLinkID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x01, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}, `{"type":258,"local_link_id":1,"remote_link_id":2}`, true, false},
+ {[]byte{0x01, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0xFF}, `{"type":258,"local_link_id":1,"remote_link_id":2}`, false, false},
+ {[]byte{0x01, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVLinkID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVIPv4InterfaceAddr(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x01, 0x03, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01}, `{"type":259,"ipv4_interface_address":"1.1.1.1"}`, true, false},
+ {[]byte{0x01, 0x03, 0x00, 0x04, 0x0a, 0x0a, 0x0a, 0x0a, 0x12}, `{"type":259,"ipv4_interface_address":"10.10.10.10"}`, false, false},
+ {[]byte{0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIPv4InterfaceAddr{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVIPv4NeighborAddr(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x01, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01}, `{"type":260,"ipv4_neighbor_address":"1.1.1.1"}`, true, false},
+ {[]byte{0x01, 0x04, 0x00, 0x04, 0x0a, 0x0a, 0x0a, 0x0a, 0x12}, `{"type":260,"ipv4_neighbor_address":"10.10.10.10"}`, false, false},
+ {[]byte{0x01, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIPv4NeighborAddr{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVIPv6InterfaceAddr(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x01, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF}, `{"type":261,"ipv6_interface_address":"2001:db8::beef"}`, true, false},
+ {[]byte{0x01, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, 0xFF}, `{"type":261,"ipv6_interface_address":"2001:db8::beef"}`, false, false},
+ {[]byte{0x01, 0x05, 0x00, 0x10, 0xfe, 0x80, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF}, "", false, true},
+ {[]byte{0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIPv6InterfaceAddr{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVIPv6NeighborAddr(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x01, 0x06, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF}, `{"type":262,"ipv6_neighbor_address":"2001:db8::beef"}`, true, false},
+ {[]byte{0x01, 0x06, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, 0xFF}, `{"type":262,"ipv6_neighbor_address":"2001:db8::beef"}`, false, false},
+ {[]byte{0x01, 0x06, 0x00, 0x10, 0xfe, 0x81, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF}, "", false, true},
+ {[]byte{0x01, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIPv6NeighborAddr{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVNodeFlagBits(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x00, 0x00, 0x01, 0xFF}, `{"type":1024,"node_flags":"{Node Flags: XXVRBETO}"}`, false},
+ {[]byte{0x04, 0x00, 0x00, 0x01, 0x80}, `{"type":1024,"node_flags":"{Node Flags: *******O}"}`, false},
+ {[]byte{0x04, 0x00, 0x00, 0x01, 0x80, 0xAA}, `{"type":1024,"node_flags":"{Node Flags: *******O}"}`, false},
+ {[]byte{0x04, 0x00, 0x00, 0x02, 0x80, 0x44}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVNodeFlagBits{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVNodeName(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x02, 0x00, 0x03, 0x72, 0x74, 0x72}, `{"type":1026,"node_name":"rtr"}`, false},
+ {[]byte{0x04, 0x02, 0x00, 0x03, 0x72, 0x74, 0x72, 0x00}, `{"type":1026,"node_name":"rtr"}`, false},
+ {[]byte{0x04, 0x02, 0x00, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVNodeName{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVIsisArea(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x03, 0x00, 0x03, 0x72, 0x74, 0x72}, `{"type":1027,"isis_area_id":"[114 116 114]"}`, false},
+ {[]byte{0x04, 0x03, 0x00, 0x03, 0x72, 0x74, 0x72, 0x44}, `{"type":1027,"isis_area_id":"[114 116 114]"}`, false},
+ {[]byte{0x04, 0x03, 0x00, 0x00}, "", true},
+ {[]byte{0x04, 0x03, 0x00, 0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIsisArea{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVLocalIPv4RouterID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01}, `{"type":1028,"node_local_router_id_ipv4":"1.1.1.1"}`, false},
+ {[]byte{0x04, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, 0x12}, `{"type":1028,"node_local_router_id_ipv4":"1.1.1.1"}`, false},
+ {[]byte{0x04, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVLocalIPv4RouterID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVRemoteIPv4RouterID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x06, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02}, `{"type":1030,"node_remote_router_id_ipv4":"2.2.2.2"}`, false},
+ {[]byte{0x04, 0x06, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, 0x44}, `{"type":1030,"node_remote_router_id_ipv4":"2.2.2.2"}`, false},
+ {[]byte{0x04, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVRemoteIPv4RouterID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVLocalIPv6RouterID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF}, `{"type":1029,"node_local_router_id_ipv6":"2001:db8::beef"}`, false},
+ {[]byte{0x04, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, 0xFF}, `{"type":1029,"node_local_router_id_ipv6":"2001:db8::beef"}`, false},
+ {[]byte{0x04, 0x05, 0x00, 0x01, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVLocalIPv6RouterID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVRemoteIPv6RouterID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x07, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF}, `{"type":1031,"node_remote_router_id_ipv6":"2001:db8::beef"}`, false},
+ {[]byte{0x04, 0x07, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, 0xFF}, `{"type":1031,"node_remote_router_id_ipv6":"2001:db8::beef"}`, false},
+ {[]byte{0x04, 0x07, 0x00, 0x01, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVRemoteIPv6RouterID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVOpaqueNodeAttr(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x01, 0x00, 0x03, 0x01, 0x02, 0x03}, `{"type":1025,"node_opaque_attribute":"[1 2 3]"}`, false},
+ {[]byte{0x04, 0x01, 0x00, 0x03, 0x01, 0x02, 0x03, 0x04}, `{"type":1025,"node_opaque_attribute":"[1 2 3]"}`, false},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVOpaqueNodeAttr{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVAutonomousSystem(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07}, `{"type":512,"asn":117901063}`, false},
+ {[]byte{0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, 0xFF}, `{"type":512,"asn":117901063}`, false},
+ {[]byte{0x02, 0x00, 0x00, 0x03, 0x07, 0x07, 0x07}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVAutonomousSystem{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVBgpLsID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07}, `{"type":513,"bgp_ls_id":117901063}`, false},
+ {[]byte{0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, 0xFF}, `{"type":513,"bgp_ls_id":117901063}`, false},
+ {[]byte{0x02, 0x01, 0x00, 0x03, 0x07, 0x07, 0x07}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVBgpLsID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVIgpRouterID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, `{"type":515,"igp_router_id":"[1 2 3 4 5 6]"}`, false},
+ {[]byte{0x02, 0x03, 0x00, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, `{"type":515,"igp_router_id":"[1 2 3 4 5 6 7]"}`, false},
+ {[]byte{0x02, 0x03, 0x00, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, `{"type":515,"igp_router_id":"[1 2 3 4 5 6 7 8]"}`, false},
+ {[]byte{0x02, 0x03, 0x00, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIgpRouterID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVOspfAreaID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07}, `{"type":514,"ospf_area_id":117901063}`, false},
+ {[]byte{0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, 0xFF}, `{"type":514,"ospf_area_id":117901063}`, false},
+ {[]byte{0x02, 0x02, 0x00, 0x03, 0x07, 0x07, 0x07}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVOspfAreaID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVOspfRouteType(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x01, 0x08, 0x00, 0x01, 0x06}, `{"type":264,"ospf_route_type":"NSSA2"}`, false},
+ {[]byte{0x01, 0x08, 0x00, 0x01, 0x01, 0xFF}, `{"type":264,"ospf_route_type":"INTRA-AREA"}`, false},
+ {[]byte{0x01, 0x08, 0x00, 0x02, 0x01, 0x01}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVOspfRouteType{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVIPReachability(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x01, 0x09, 0x00, 0x02, 0x08, 0x0a}, `{"type":265,"prefix_length":8,"prefix":"[10]"}`, true, false},
+ {[]byte{0x01, 0x09, 0x00, 0x03, 0x10, 0x0a, 0x0b, 0xFF}, `{"type":265,"prefix_length":16,"prefix":"[10 11]"}`, false, false},
+ {[]byte{0x01, 0x09, 0x00, 0x02, 0x08}, ``, false, true},
+ {[]byte{0x01, 0x09, 0x00, 0x01, 0x01}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIPReachability{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVIPReachabilityToIPNet(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ tlv LsTLVIPReachability
+ ipv6 bool
+ want net.IPNet
+ }{
+ {
+ tlv: LsTLVIPReachability{
+ PrefixLength: 8,
+ Prefix: []byte{0x0a},
+ },
+ ipv6: false,
+ want: net.IPNet{
+ IP: net.IPv4(10, 0, 0, 0),
+ Mask: net.CIDRMask(8, 32),
+ },
+ },
+ {
+ tlv: LsTLVIPReachability{
+ PrefixLength: 4,
+ Prefix: []byte{0xaa},
+ },
+ ipv6: false,
+ want: net.IPNet{
+ IP: net.IPv4(160, 0, 0, 0),
+ Mask: net.CIDRMask(4, 32),
+ },
+ },
+ {
+ tlv: LsTLVIPReachability{
+ PrefixLength: 31,
+ Prefix: []byte{0x0a, 0x0a, 0x0a, 0xfe},
+ },
+ ipv6: false,
+ want: net.IPNet{
+ IP: net.IPv4(10, 10, 10, 254),
+ Mask: net.CIDRMask(31, 32),
+ },
+ },
+ {
+ tlv: LsTLVIPReachability{
+ PrefixLength: 16,
+ Prefix: []byte{0x20, 0x01},
+ },
+ ipv6: true,
+ want: net.IPNet{
+ IP: net.ParseIP("2001::"),
+ Mask: net.CIDRMask(16, 128),
+ },
+ },
+ {
+ tlv: LsTLVIPReachability{
+ PrefixLength: 24,
+ Prefix: []byte{0x20, 0x01, 0x0d},
+ },
+ ipv6: true,
+ want: net.IPNet{
+ IP: net.ParseIP("2001:d00::"),
+ Mask: net.CIDRMask(24, 128),
+ },
+ },
+ }
+
+ for _, test := range tests {
+ got := test.tlv.ToIPNet(test.ipv6)
+ assert.Equal(test.want.IP.String(), got.IP.String())
+ assert.Equal(test.want.Mask.String(), got.Mask.String())
+ }
+}
+
+func Test_LsTLVAdminGroup(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x40, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07}, `{"type":1088,"admin_group":"07070707"}`, false},
+ {[]byte{0x04, 0x40, 0x00, 0x04, 0xAE, 0xAE, 0xAE, 0xAE, 0xFF}, `{"type":1088,"admin_group":"aeaeaeae"}`, false},
+ {[]byte{0x04, 0x40, 0x00, 0x03, 0x07, 0x07, 0x07}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVAdminGroup{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVMaxLinkBw(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x41, 0x00, 0x04, 0x43, 0xA4, 0xB2, 0x00}, `{"type":1089,"max_link_bw":329.39062}`, false},
+ {[]byte{0x04, 0x41, 0x00, 0x04, 0x43, 0xA4, 0xB2, 0x00, 0xFF}, `{"type":1089,"max_link_bw":329.39062}`, false},
+ {[]byte{0x04, 0x41, 0x00, 0x03, 0x07, 0x07, 0x07}, "", true},
+ {[]byte{0x04, 0x41, 0x00, 0x04, 0x7f, 0x80, 0x00, 0x00}, "", true}, // +Inf
+ {[]byte{0x04, 0x41, 0x00, 0x04, 0xff, 0x80, 0x00, 0x00}, "", true}, // -Inf
+ {[]byte{0x04, 0x41, 0x00, 0x04, 0xff, 0xbf, 0xff, 0xff}, "", true}, // NaN
+ {[]byte{0x04, 0x41, 0x00, 0x04, 0xc2, 0xc8, 0x00, 0x00}, "", true}, // -100
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVMaxLinkBw{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVMaxReservableLinkBw(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x42, 0x00, 0x04, 0x43, 0xA4, 0xB2, 0x00}, `{"type":1090,"max_reservable_link_bw":329.39062}`, false},
+ {[]byte{0x04, 0x42, 0x00, 0x04, 0x43, 0xA4, 0xB2, 0x00, 0xFF}, `{"type":1090,"max_reservable_link_bw":329.39062}`, false},
+ {[]byte{0x04, 0x42, 0x00, 0x03, 0x07, 0x07, 0x07}, "", true},
+ {[]byte{0x04, 0x42, 0x00, 0x04, 0x7f, 0x80, 0x00, 0x00}, "", true}, // +Inf
+ {[]byte{0x04, 0x42, 0x00, 0x04, 0xff, 0x80, 0x00, 0x00}, "", true}, // -Inf
+ {[]byte{0x04, 0x42, 0x00, 0x04, 0xff, 0xbf, 0xff, 0xff}, "", true}, // NaN
+ {[]byte{0x04, 0x42, 0x00, 0x04, 0xc2, 0xc8, 0x00, 0x00}, "", true}, // -100
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVMaxReservableLinkBw{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVUnreservedBw(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x43, 0x00, 0x20,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB3, 0x00},
+ `{"type":1091,"unreserved_bw":[329.39062,329.39062,329.39062,329.39062,329.39062,329.39062,329.39062,329.39844]}`, false},
+ {[]byte{0x04, 0x43, 0x00, 0x20,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00, 0xff},
+ `{"type":1091,"unreserved_bw":[329.39062,329.39062,329.39062,329.39062,329.39062,329.39062,329.39062,329.39062]}`, false},
+ {[]byte{0x04, 0x43, 0x00, 0x20,
+ 0x7f, 0x80, 0x00, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB3, 0x00},
+ "", true},
+ {[]byte{0x04, 0x43, 0x00, 0x20,
+ 0xff, 0x80, 0x00, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB3, 0x00},
+ "", true},
+ {[]byte{0x04, 0x43, 0x00, 0x20,
+ 0x43, 0xA4, 0xB3, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0xff, 0xbf, 0xff, 0xff,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB3, 0x00},
+ "", true},
+ {[]byte{0x04, 0x43, 0x00, 0x20,
+ 0x43, 0xA4, 0xB3, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB3, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0x43, 0xA4, 0xB2, 0x00,
+ 0xc2, 0xc8, 0x00, 0x00},
+ "", true},
+ {[]byte{0x04, 0x43, 0x00, 0x03, 0x07, 0x07, 0x07}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVUnreservedBw{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVTEDefaultMetric(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x04, 0x44, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07}, `{"type":1092,"te_default_metric":117901063}`, true, false},
+ {[]byte{0x04, 0x44, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, 0xFF}, `{"type":1092,"te_default_metric":117901063}`, false, false},
+ {[]byte{0x04, 0x44, 0x00, 0x03, 0x07, 0x07, 0x07}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVTEDefaultMetric{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ got, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, got)
+ }
+ }
+}
+
+func Test_LsTLVIGPMetric(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x04, 0x47, 0x00, 0x01, 0x01}, `{"type":1095,"igp_metric":1}`, true, false},
+ {[]byte{0x04, 0x47, 0x00, 0x01, 0x3F}, `{"type":1095,"igp_metric":63}`, true, false},
+ {[]byte{0x04, 0x47, 0x00, 0x01, 0xFF}, `{"type":1095,"igp_metric":63}`, false, false},
+ {[]byte{0x04, 0x47, 0x00, 0x02, 0x00, 0x01}, `{"type":1095,"igp_metric":1}`, true, false},
+ {[]byte{0x04, 0x47, 0x00, 0x02, 0xff, 0xff}, `{"type":1095,"igp_metric":65535}`, true, false},
+ {[]byte{0x04, 0x47, 0x00, 0x03, 0x00, 0x00, 0x01}, `{"type":1095,"igp_metric":1}`, true, false},
+ {[]byte{0x04, 0x47, 0x00, 0x03, 0xff, 0xff, 0xff}, `{"type":1095,"igp_metric":16777215}`, true, false},
+ {[]byte{0x04, 0x47, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIGPMetric{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ got, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, got)
+ }
+ }
+}
+
+func Test_LsTLVNLinkName(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x4a, 0x00, 0x03, 0x72, 0x74, 0x72}, `{"type":1098,"link_name":"rtr"}`, false},
+ {[]byte{0x04, 0x4a, 0x00, 0x03, 0x72, 0x74, 0x72, 0x00}, `{"type":1098,"link_name":"rtr"}`, false},
+ {[]byte{0x04, 0x4a, 0x00, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVLinkName{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVSrAlgorithm(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x0b, 0x00, 0x03, 0x01, 0x02, 0x03}, `{"type":1035,"sr_algorithm":"[1 2 3]"}`, false},
+ {[]byte{0x04, 0x0b, 0x00, 0x03, 0x01, 0x02, 0x03, 0x04}, `{"type":1035,"sr_algorithm":"[1 2 3]"}`, false},
+ {[]byte{0x04, 0x0b, 0x00, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVSrAlgorithm{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVSrCapabilities(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {
+ []byte{
+ 0x04, 0x0a, 0x00, 0x0c, // type 1034, length 12
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ },
+ `{"type":1034,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":100500}}]}`,
+ true,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0a, 0x00, 0x0d, // type 1034, length 13
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ },
+ `{"type":1034,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":71005001}}]}`,
+ true,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0a, 0x00, 0x17, // type 1034, length 23
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ 0x0f, 0x42, 0x40, // range: 1000000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ },
+ `{"type":1034,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":71005001}},{"Range":1000000,"FirstLabel":{"type":1161,"sid_label":100500}}]}`,
+ true,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0a, 0x00, 0x17, // type 1034, length 23
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ 0x0f, 0x42, 0x40, // range: 1000000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ 0xff, 0xff, 0xff, // some random bytes - should be ignored
+ },
+ `{"type":1034,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":71005001}},{"Range":1000000,"FirstLabel":{"type":1161,"sid_label":100500}}]}`,
+ false,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0a, 0x00, 0xcc, // type 1034, length 204 (corrupted)
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ },
+ "",
+ false,
+ true,
+ },
+ {
+ []byte{
+ 0x04, 0x0a, 0x00, 0x11, // type 1034, length 23
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ 0x0f, 0x42, 0x40, // range: 1000000
+ 0x04, // No SID/Label sub-TLV
+ },
+ "",
+ false,
+ true,
+ },
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVSrCapabilities{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVLocalBlock(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {
+ []byte{
+ 0x04, 0x0c, 0x00, 0x0c, // type 1036, length 12
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ },
+ `{"type":1036,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":100500}}]}`,
+ true,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0c, 0x00, 0x0d, // type 1036, length 13
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ },
+ `{"type":1036,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":71005001}}]}`,
+ true,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0c, 0x00, 0x17, // type 1036, length 23
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ 0x0f, 0x42, 0x40, // range: 1000000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ },
+ `{"type":1036,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":71005001}},{"Range":1000000,"FirstLabel":{"type":1161,"sid_label":100500}}]}`,
+ true,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0c, 0x00, 0x17, // type 1036, length 23
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ 0x0f, 0x42, 0x40, // range: 1000000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ 0xff, 0xff, 0xff, // some random bytes - should be ignored
+ },
+ `{"type":1036,"flags":0,"ranges":[{"Range":35000,"FirstLabel":{"type":1161,"sid_label":71005001}},{"Range":1000000,"FirstLabel":{"type":1161,"sid_label":100500}}]}`,
+ false,
+ false,
+ },
+ {
+ []byte{
+ 0x04, 0x0c, 0x00, 0xcc, // type 1036, length 204 (corrupted)
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // SID/Label TLV, SID: 100500
+ },
+ "",
+ false,
+ true,
+ },
+ {
+ []byte{
+ 0x04, 0x0c, 0x00, 0x11, // type 1036, length 23
+ 0x00, 0x00, // flags and reserved
+ 0x00, 0x88, 0xb8, // range: 35000
+ 0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49, // SID/Label TLV, SID: 71005001
+ 0x0f, 0x42, 0x40, // range: 1000000
+ 0x04, // No SID/Label sub-TLV
+ },
+ "",
+ false,
+ true,
+ },
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVSrLocalBlock{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVAdjacencySID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x04, 0x4b, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0x01, 0x88, 0x94}, `{"type":1099,"adjacency_sid":100500}`, true, false},
+ {[]byte{0x04, 0x4b, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff}, `{"type":1099,"adjacency_sid":1048575}`, false, false},
+ {[]byte{0x04, 0x4b, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x04, 0x3B, 0x73, 0x49}, `{"type":1099,"adjacency_sid":71005001}`, true, false},
+ {[]byte{0x04, 0x4b, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x11}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x07, 0x04, 0x3B, 0x73, 0x49, 0x05, 0x06, 0x07}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVAdjacencySID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVSIDLabel(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94}, `{"type":1161,"sid_label":100500}`, true, false},
+ {[]byte{0x04, 0x89, 0x00, 0x03, 0x0f, 0xff, 0xff}, `{"type":1161,"sid_label":1048575}`, true, false},
+ {[]byte{0x04, 0x89, 0x00, 0x03, 0xff, 0xff, 0xff}, `{"type":1161,"sid_label":1048575}`, false, false},
+ {[]byte{0x04, 0x89, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49}, `{"type":1161,"sid_label":71005001}`, false, false},
+ {[]byte{0x04, 0x89, 0x00, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x04, 0x04, 0x3B, 0x73, 0x49}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVSIDLabel{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVPrefixSID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x04, 0x86, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0x01, 0x88, 0x94}, `{"type":1158,"prefix_sid":100500}`, true, false},
+ {[]byte{0x04, 0x86, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff}, `{"type":1158,"prefix_sid":1048575}`, false, false},
+ {[]byte{0x04, 0x86, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x04, 0x3B, 0x73, 0x49}, `{"type":1158,"prefix_sid":71005001}`, true, false},
+ {[]byte{0x04, 0x86, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x11}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x07, 0x04, 0x3B, 0x73, 0x49, 0x05, 0x06, 0x07}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVPrefixSID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+
+ if test.serialize {
+ s, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, s)
+ }
+ }
+}
+
+func Test_LsTLVSourceRouterID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x93, 0x00, 0x04, 0x0a, 0x0a, 0x0a, 0x0a}, `{"type":1171,"source_router_id":"10.10.10.10"}`, false},
+ {[]byte{0x04, 0x93, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF}, `{"type":1171,"source_router_id":"2001:db8::beef"}`, false},
+ {[]byte{0x04, 0x93, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, 0xFF}, `{"type":1171,"source_router_id":"2001:db8::beef"}`, false},
+ {[]byte{0x04, 0x93, 0x00, 0x01, 0x00}, "", true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVSourceRouterID{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVOpaqueLinkAttr(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x49, 0x00, 0x03, 0x01, 0x02, 0x03}, `{"type":1097,"link_opaque_attribute":"[1 2 3]"}`, false},
+ {[]byte{0x04, 0x49, 0x00, 0x03, 0x01, 0x02, 0x03, 0x04}, `{"type":1097,"link_opaque_attribute":"[1 2 3]"}`, false},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVOpaqueLinkAttr{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_LsTLVIGPFlags(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ serialize bool
+ err bool
+ }{
+ {[]byte{0x04, 0x80, 0x00, 0x01, 0xFF}, `{"type":1152,"igp_flags":"{IGP Flags: XXXXPLND}"}`, true, false},
+ {[]byte{0x04, 0x80, 0x00, 0x01, 0x80}, `{"type":1152,"igp_flags":"{IGP Flags: *******D}"}`, true, false},
+ {[]byte{0x04, 0x80, 0x00, 0x01, 0x80, 0xAA}, `{"type":1152,"igp_flags":"{IGP Flags: *******D}"}`, false, false},
+ {[]byte{0x04, 0x80, 0x00, 0x02, 0x80, 0x44}, "", false, true},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVIGPFlags{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ if test.serialize {
+ got, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, got)
+ }
+ }
+ }
+}
+
+func Test_LsTLVOpaquePrefixAttr(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ want string
+ err bool
+ }{
+ {[]byte{0x04, 0x85, 0x00, 0x03, 0x01, 0x02, 0x03}, `{"type":1157,"prefix_opaque_attribute":"[1 2 3]"}`, false},
+ {[]byte{0x04, 0x85, 0x00, 0x03, 0x01, 0x02, 0x03, 0x04}, `{"type":1157,"prefix_opaque_attribute":"[1 2 3]"}`, false},
+ {[]byte{0xfe, 0xfe, 0x00, 0x00}, "", true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVOpaquePrefixAttr{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ continue
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ }
+
+ got, err := tlv.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(got, []byte(test.want))
+ }
+}
+
+func Test_parseIGPRouterID(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ str string
+ pseudo bool
+ }{
+ {[]byte{1, 2, 3, 4}, "1.2.3.4", false},
+ {[]byte{1, 2, 3, 4, 5, 255}, "0102.0304.05ff", false},
+ {[]byte{1, 2, 3, 4, 5, 255, 0}, "0102.0304.05ff-00", true},
+ {[]byte{1, 2, 3, 4, 5, 6, 7, 8}, "1.2.3.4:5.6.7.8", true},
+ }
+
+ for _, test := range tests {
+ str, pseudo := parseIGPRouterID(test.in)
+ assert.Equal(test.str, str)
+ assert.Equal(test.pseudo, pseudo)
+ }
+}
+
+func Test_LsNodeDescriptor(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ str string
+ err bool
+ serialize bool
+ }{
+ {[]byte{
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ }, "{ASN: 117901063, BGP LS ID: 117901063, OSPF AREA: 117901063, IGP ROUTER ID: 0102.0304.0506}",
+ false, true},
+ {[]byte{
+ 0x01, 0x01, 0x00, 0x22, // Remote Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ }, "{ASN: 117901063, BGP LS ID: 117901063, OSPF AREA: 117901063, IGP ROUTER ID: 0102.0304.0506}",
+ false, true},
+ {[]byte{
+ 0x01, 0x00, 0x00, 0x21, // Truncated Length
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ }, "", true, false},
+ {[]byte{
+ 0x01, 0x00, 0x00, 0x22, // Missing mandatory TLV
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ }, "", true, false},
+ {[]byte{
+ 0x01, 0x00, 0x00, 0x22, // Incorrect TLV order
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ }, "", true, false},
+ {[]byte{
+ 0x01, 0x00, 0x00, 0x26, // Unexpected TLV
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0xfe, 0x01, 0x00, 0x00, // Unsupported
+ }, "{ASN: 117901063, BGP LS ID: 117901063, OSPF AREA: 117901063, IGP ROUTER ID: 0102.0304.0506}",
+ false, false},
+ {[]byte{
+ 0x01, 0x00, 0x00, 0x0a, // Missing optional TLVs
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ }, "{ASN: 0, BGP LS ID: 0, OSPF AREA: 0, IGP ROUTER ID: 0102.0304.0506}", false, true},
+ }
+
+ for _, test := range tests {
+ tlv := LsTLVNodeDescriptor{}
+ if test.err {
+ assert.Error(tlv.DecodeFromBytes(test.in))
+ } else {
+ assert.NoError(tlv.DecodeFromBytes(test.in))
+ assert.Equal(test.str, tlv.String())
+ if test.serialize {
+ got, err := tlv.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, got)
+ }
+ }
+ }
+}
+
+func Test_LsAddrPrefix(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ str string
+ err bool
+ serialize bool
+ }{
+ {[]byte{
+ 0x00, 0x01, 0x00, 0x2f, // Node NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ }, "NLRI { NODE { AS:117901063 BGP-LS ID:117901063 0102.0304.0506 ISIS-L2:0 } }", false, true},
+ {[]byte{
+ 0x00, 0x01, 0x00, 0x2e, // Node NLRI, truncated length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, // TLV IGP Router ID: 0102.0304.05
+ }, "", true, false},
+ {[]byte{
+ 0x00, 0x01, 0x00, 0x2f, // Node NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x01, 0x00, 0x22, // Remote Node Desc (unexpected)
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ }, "", true, false},
+ {[]byte{
+ 0x00, 0x01, 0x00, 0x2d, // Node NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ // Mandatory TLV missing
+ }, "", true, false},
+ {[]byte{
+ 0x00, 0x02, 0x00, 0x65, // Link NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x01, 0x00, 0x22, // Remote Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // TLV IGP Router ID: 0605.0403.0201
+ 0x01, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, // LinkID TLV, Local: 1, Remote: 2
+ }, "NLRI { LINK { LOCAL_NODE: 0102.0304.0506 REMOTE_NODE: 0605.0403.0201 LINK: 1->2} }", false, true},
+ {[]byte{
+ 0x00, 0x02, 0x00, 0x69, // Link NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x01, 0x00, 0x22, // Remote Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // TLV IGP Router ID: 0605.0403.0201
+ 0x01, 0x03, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // IPv4 Interface Addr TLV: 1.1.1.1
+ 0x01, 0x04, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, // IPv4 Neighbor Addr TLV: 2.2.2.2
+ }, "NLRI { LINK { LOCAL_NODE: 0102.0304.0506 REMOTE_NODE: 0605.0403.0201 LINK: 1.1.1.1->2.2.2.2} }", false, true},
+ {[]byte{
+ 0x00, 0x02, 0x00, 0x81, // Link NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x01, 0x00, 0x22, // Remote Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // TLV IGP Router ID: 0605.0403.0201
+ 0x01, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // IPv6 Interface Addr TLV: 2001:db8::beef
+ 0x01, 0x06, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD, // IPv6 Interface Addr TLV: 2001:db8::dead
+ }, "NLRI { LINK { LOCAL_NODE: 0102.0304.0506 REMOTE_NODE: 0605.0403.0201 LINK: 2001:db8::beef->2001:db8::dead} }", false, true},
+ {[]byte{
+ 0x00, 0x02, 0x00, 0x59, // Link NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x01, 0x00, 0x22, // Remote Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // TLV IGP Router ID: 0605.0403.0201
+ }, "NLRI { LINK { LOCAL_NODE: 0102.0304.0506 REMOTE_NODE: 0605.0403.0201 LINK: UNKNOWN} }", false, true},
+ {[]byte{
+ 0x00, 0x02, 0x00, 0x33, // Link NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ // Missing mandatory TLV
+ }, "", true, false},
+ {[]byte{
+ 0x00, 0x03, 0x00, 0x35, // Prefix IPv4 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x09, 0x00, 0x02, 0x08, 0x0a, // IP ReachabilityInfo TLV, 10.0.0.0/8
+ }, "NLRI { PREFIXv4 { LOCAL_NODE: 0102.0304.0506 PREFIX: [10.0.0.0/8] } }", false, true},
+ {[]byte{
+ 0x00, 0x03, 0x00, 0x43, // Prefix IPv4 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x09, 0x00, 0x02, 0x08, 0x0a, // IP ReachabilityInfo TLV, 10.0.0.0/8
+ 0x01, 0x09, 0x00, 0x05, 0x1f, 0xc0, 0xa8, 0x07, 0xfe, // IP ReachabilityInfo TLV, 192.168.7.254/31
+ 0x01, 0x08, 0x00, 0x01, 0x06, // OSPF Route Type TLV (NSSA2)
+ }, "NLRI { PREFIXv4 { LOCAL_NODE: 0102.0304.0506 PREFIX: [10.0.0.0/8 192.168.7.254/31] OSPF_ROUTE_TYPE:NSSA2 } }", false, true},
+ {[]byte{
+ 0x00, 0x03, 0x00, 0x35, // Prefix IPv4 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x09, 0x00, 0x02, 0x08, 0x0a, // IP ReachabilityInfo TLV, 10.0.0.0/8
+ }, "NLRI { PREFIXv4 { LOCAL_NODE: 0102.0304.0506 PREFIX: [10.0.0.0/8] } }", false, true},
+ {[]byte{
+ 0x00, 0x03, 0x00, 0x2f, // Prefix IPv4 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ // Missing mandatory TLV (IP Reachability info)
+ }, "", true, false},
+ {[]byte{
+ 0x00, 0x03, 0x00, 0x39, // Prefix IPv4 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ // IPv6 IP Reachability info in v4 prefix
+ 0x01, 0x09, 0x00, 0x06, 0x40, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ }, "", true, false},
+ {[]byte{
+ 0x00, 0x04, 0x00, 0x35, // Prefix IPv6 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x09, 0x00, 0x02, 0x08, 0x0a, // IP ReachabilityInfo TLV, 10.0.0.0/8
+ }, "NLRI { PREFIXv6 { LOCAL_NODE: 0102.0304.0506 PREFIX: [a00::/8] } }", false, true},
+ {[]byte{
+ 0x00, 0x04, 0x00, 0x43, // Prefix IPv6 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x09, 0x00, 0x02, 0x08, 0x0a, // IP ReachabilityInfo TLV, 10.0.0.0/8
+ 0x01, 0x09, 0x00, 0x05, 0x1f, 0xc0, 0xa8, 0x07, 0xfe, // IP ReachabilityInfo TLV, 192.168.7.254/31
+ 0x01, 0x08, 0x00, 0x01, 0x06, // OSPF Route Type TLV (NSSA2)
+ }, "NLRI { PREFIXv6 { LOCAL_NODE: 0102.0304.0506 PREFIX: [a00::/8 c0a8:7fe::/31] OSPF_ROUTE_TYPE:NSSA2 } }", false, true},
+ {[]byte{
+ 0x00, 0x04, 0x00, 0x35, // Prefix IPv6 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ 0x01, 0x09, 0x00, 0x02, 0x08, 0x0a, // IP ReachabilityInfo TLV, 10.0.0.0/8
+ }, "NLRI { PREFIXv6 { LOCAL_NODE: 0102.0304.0506 PREFIX: [a00::/8] } }", false, true},
+ {[]byte{
+ 0x00, 0x04, 0x00, 0x2f, // Prefix IPv6 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ // Missing mandatory TLV (IP Reachability info)
+ }, "", true, false},
+ {[]byte{
+ 0x00, 0x04, 0x00, 0x39, // Prefix IPv6 NLRI, correct length
+ 0x02, // Protocol ISIS Level 2
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ID
+ 0x01, 0x00, 0x00, 0x22, // Local Node Desc
+ 0x02, 0x00, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV ASN: 117901063
+ 0x02, 0x01, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV BGP LS ID: 117901063
+ 0x02, 0x02, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TLV OSPF Area ID: 117901063
+ 0x02, 0x03, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // TLV IGP Router ID: 0102.0304.0506
+ // IPv6 IP Reachability info in v4 prefix
+ 0x01, 0x09, 0x00, 0x06, 0x40, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ }, "", true, false},
+ }
+
+ for _, test := range tests {
+ nlri := LsAddrPrefix{}
+ if test.err {
+ assert.Error(nlri.DecodeFromBytes(test.in))
+ } else {
+ assert.NoError(nlri.DecodeFromBytes(test.in))
+ assert.Equal(test.str, nlri.String())
+ if test.serialize {
+ got, err := nlri.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, got)
+ }
+ }
+ }
+}
+
+func Test_PathAttributeLs(t *testing.T) {
+ assert := assert.New(t)
+
+ var tests = []struct {
+ in []byte
+ str string
+ json string
+ serialize bool
+ err bool
+ }{
+ {[]byte{
+ // LS Attribute with all Node-related TLVs.
+ 0x80, 0x29, 0x62, // Optional attribute, BGP_ATTR_TYPE_LS, correct length
+ 0x04, 0x00, 0x00, 0x01, 0xFF, // Node flags (all set)
+ 0x04, 0x01, 0x00, 0x03, 0x01, 0x02, 0x03, // Opaque Node Attr [1 2 3]
+ 0x04, 0x02, 0x00, 0x03, 0x72, 0x74, 0x72, // Node name: "rtr"
+ 0x04, 0x03, 0x00, 0x03, 0x72, 0x74, 0x72, // ISIS area: [114 116 114]
+ 0x04, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // Local RouterID 1.1.1.1
+ 0x04, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // Local Router ID 2001:db8::beef
+ 0x04, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x88, 0xb8, 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // Capabilities: Range 35000, first label: 100500
+ 0x04, 0x0b, 0x00, 0x03, 0x01, 0x02, 0x03, // SR ALgorithm [1 2 3]
+ 0x04, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x88, 0xb8, 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // Local block: Range 35000, first label: 100500
+ 0xde, 0xad, 0x00, 0x01, 0xFF, // Unknown TLV
+ },
+ "{LsAttributes: {Node Flags: XXVRBETO} {Opaque attribute: [1 2 3]} {Node Name: rtr} {ISIS Area ID: [114 116 114]} {Local RouterID IPv4: 1.1.1.1} {Local RouterID IPv6: 2001:db8::beef} {SR Capabilities: Flags:0 SRGB Ranges: 100500:135500 } {SR Algorithms: [1 2 3]} {SR LocalBlock: Flags:0 SRGB Ranges: 100500:135500 } }",
+ `{"type":41,"flags":128,"node":{"flags":{"overload":true,"attached":true,"external":true,"abr":true,"router":true,"v6":true},"opaque":"AQID","name":"rtr","isis_area":"cnRy","local_router_id_ipv4":"1.1.1.1","local_router_id_ipv6":"2001:db8::beef","sr_capabilities":{"ipv4_supported":false,"ipv6_supported":false,"ranges":[{"begin":100500,"end":135500}]},"sr_algorithms":"AQID","sr_local_block":{"ranges":[{"begin":100500,"end":135500}]}},"link":{"local_router_id_ipv4":"1.1.1.1","local_router_id_ipv6":"2001:db8::beef"},"prefix":{}}`,
+ false, false},
+ {[]byte{
+ // LS Attribute with all Node-related TLVs.
+ 0x80, 0x29, 0x5d, // Optional attribute, BGP_ATTR_TYPE_LS, correct length
+ 0x04, 0x00, 0x00, 0x01, 0xFF, // Node flags (all set)
+ 0x04, 0x01, 0x00, 0x03, 0x01, 0x02, 0x03, // Opaque Node Attr [1 2 3]
+ 0x04, 0x02, 0x00, 0x03, 0x72, 0x74, 0x72, // Node name: "rtr"
+ 0x04, 0x03, 0x00, 0x03, 0x72, 0x74, 0x72, // ISIS area: [114 116 114]
+ 0x04, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // Local RouterID 1.1.1.1
+ 0x04, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // Local Router ID 2001:db8::beef
+ 0x04, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x88, 0xb8, 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // Capabilities: Range 35000, first label: 100500
+ 0x04, 0x0b, 0x00, 0x03, 0x01, 0x02, 0x03, // SR Algorithm [1 2 3]
+ 0x04, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x88, 0xb8, 0x04, 0x89, 0x00, 0x03, 0x01, 0x88, 0x94, // Local block: Range 35000, first label: 100500
+ },
+ "{LsAttributes: {Node Flags: XXVRBETO} {Opaque attribute: [1 2 3]} {Node Name: rtr} {ISIS Area ID: [114 116 114]} {Local RouterID IPv4: 1.1.1.1} {Local RouterID IPv6: 2001:db8::beef} {SR Capabilities: Flags:0 SRGB Ranges: 100500:135500 } {SR Algorithms: [1 2 3]} {SR LocalBlock: Flags:0 SRGB Ranges: 100500:135500 } }",
+ `{"type":41,"flags":128,"node":{"flags":{"overload":true,"attached":true,"external":true,"abr":true,"router":true,"v6":true},"opaque":"AQID","name":"rtr","isis_area":"cnRy","local_router_id_ipv4":"1.1.1.1","local_router_id_ipv6":"2001:db8::beef","sr_capabilities":{"ipv4_supported":false,"ipv6_supported":false,"ranges":[{"begin":100500,"end":135500}]},"sr_algorithms":"AQID","sr_local_block":{"ranges":[{"begin":100500,"end":135500}]}},"link":{"local_router_id_ipv4":"1.1.1.1","local_router_id_ipv6":"2001:db8::beef"},"prefix":{}}`,
+ true, false},
+ {[]byte{
+ // LS Attribute with truncated length
+ 0x80, 0x29, 0x04, // Optional attribute, BGP_ATTR_TYPE_LS, truncated length
+ 0x04, 0x00, 0x00, 0x01, 0xFF, // Node flags (all set)
+ }, "", "", false, true},
+ {[]byte{
+ // LS Attribute with all Link-related TLVs.
+ 0x80, 0x29, 0x9a, // Optional attribute, BGP_ATTR_TYPE_LS, correct length
+ 0x04, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01, // Local RouterID 1.1.1.1
+ 0x04, 0x05, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // Local Router ID 2001:db8::beef
+ 0x04, 0x06, 0x00, 0x04, 0x02, 0x02, 0x02, 0x02, // Local RouterID 2.2.2.2
+ 0x04, 0x07, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD, // Local Router ID 2001:db8::dead
+ 0x04, 0x40, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // Admin Group 0x07070707
+ 0x04, 0x41, 0x00, 0x04, 0x43, 0xA4, 0xB2, 0x00, // Max Link Bandwidth 329.39062
+ 0x04, 0x42, 0x00, 0x04, 0x43, 0xA4, 0xB2, 0x00, // Max Reservable Bandwidth 329.39062
+ 0x04, 0x43, 0x00, 0x20, 0x43, 0xA4, 0xB2, 0x00, 0x43, 0xA4, 0xB2, 0x00, 0x43, 0xA4, 0xB2, 0x00, 0x43, 0xA4, 0xB2, 0x00, 0x43, 0xA4, 0xB2, 0x00, 0x43, 0xA4, 0xB2, 0x00, 0x43, 0xA4, 0xB2, 0x00, 0x43, 0xA4, 0xB2, 0x00, // Unreserved Bandwidth 329.39062
+ 0x04, 0x44, 0x00, 0x04, 0x07, 0x07, 0x07, 0x07, // TE Default Metric: 117901063
+ 0x04, 0x47, 0x00, 0x01, 0x01, // IGP Metric 1
+ 0x04, 0x49, 0x00, 0x03, 0x01, 0x02, 0x03, // Opaque Link Attr: [1 2 3]
+ 0x04, 0x4a, 0x00, 0x03, 0x72, 0x74, 0x72, // Link Name: "rtr"
+ 0x04, 0x4b, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0x01, 0x88, 0x94, // Adjacency SID: 100500
+ },
+ "{LsAttributes: {Local RouterID IPv4: 1.1.1.1} {Local RouterID IPv6: 2001:db8::beef} {Remote RouterID IPv4: 2.2.2.2} {Remote RouterID IPv6: 2001:db8::dead} {Admin Group: 07070707} {Max Link BW: 329.39062} {Max Reservable Link BW: 329.39062} {Unreserved BW: [329.39062 329.39062 329.39062 329.39062 329.39062 329.39062 329.39062 329.39062]} {TE Default metric: 117901063} {IGP metric: 1} {Opaque link attribute: [1 2 3]} {Link Name: rtr} {Adjacency SID: 100500} }",
+ `{"type":41,"flags":128,"node":{"local_router_id_ipv4":"1.1.1.1","local_router_id_ipv6":"2001:db8::beef"},"link":{"name":"rtr","local_router_id_ipv4":"1.1.1.1","local_router_id_ipv6":"2001:db8::beef","remote_router_id_ipv4":"2.2.2.2","remote_router_id_ipv6":"2001:db8::dead","admin_group":117901063,"default_te_metric":117901063,"igp_metric":1,"opaque":"AQID","bandwidth":329.39062,"reservable_bandwidth":329.39062,"unreserved_bandwidth":[329.39062,329.39062,329.39062,329.39062,329.39062,329.39062,329.39062,329.39062],"adjacency_sid":100500},"prefix":{}}`,
+ true, false},
+ {[]byte{
+ // LS Attribute with all Link-related TLVs.
+ 0x80, 0x29, 0x17, // Optional attribute, BGP_ATTR_TYPE_LS, correct length
+ 0x04, 0x80, 0x00, 0x01, 0xFF, // IGP Flags: PLND
+ 0x04, 0x85, 0x00, 0x03, 0x01, 0x02, 0x03, // Opaque prefix: [1 2 3]
+ 0x04, 0x86, 0x00, 0x07, 0x01, 0x01, 0x00, 0x00, 0x01, 0x88, 0x94, // Prefix SID: 100500
+ },
+ "{LsAttributes: {IGP Flags: XXXXPLND} {Prefix opaque attribute: [1 2 3]} {Prefix SID: 100500} }",
+ `{"type":41,"flags":128,"node":{},"link":{},"prefix":{"igp_flags":{"down":true,"no_unicast":true,"local_address":true,"propagate_nssa":true},"opaque":"AQID","sr_prefix_sid":100500}}`,
+ true, false},
+ }
+
+ for _, test := range tests {
+ attr := PathAttributeLs{}
+ if test.err {
+ assert.Error(attr.DecodeFromBytes(test.in))
+ } else {
+ assert.NoError(attr.DecodeFromBytes(test.in))
+ got, err := attr.MarshalJSON()
+ assert.NoError(err)
+ assert.Equal(test.json, string(got))
+ assert.Equal(test.str, attr.String())
+
+ if test.serialize {
+ got, err := attr.Serialize()
+ assert.NoError(err)
+ assert.Equal(test.in, got)
+ }
+ }
+ }
+}