diff options
author | Artur Makutunowicz <arma@linkedin.com> | 2019-01-17 10:28:23 -0800 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2019-01-26 12:40:45 +0900 |
commit | 192480fc7127ce0f266407cecceb00703807bbca (patch) | |
tree | 7f45576e8a12259db5d3a257180db38849abc932 /pkg/packet/bgp | |
parent | 526bd67f5de4cf361aaab2d10456e5113899ca54 (diff) |
Add experimental BGP-LS feature
Diffstat (limited to 'pkg/packet/bgp')
-rw-r--r-- | pkg/packet/bgp/bgp.go | 3223 | ||||
-rw-r--r-- | pkg/packet/bgp/bgp_test.go | 1932 |
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) + } + } + } +} |