// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bgp

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net"
	"net/netip"
	"reflect"
	"regexp"
	"slices"
	"sort"
	"strconv"
	"strings"
)

type MarshallingOption struct {
	AddPath    map[Family]BGPAddPathMode
	MRT        bool
	Use2ByteAS bool // true if peer does NOT support 4-byte AS capability
	// ExtendedMessage signals that the BGP Extended Message Capability
	// (RFC 8654) is negotiated on the session this serialisation is
	// for. When true the per-message-type length cap rises from 4096
	// to 65535 octets for UPDATE, NOTIFICATION and ROUTE-REFRESH;
	// OPEN and KEEPALIVE keep the 4096-octet cap per RFC 8654 Section 6.
	ExtendedMessage bool

	attributes map[BGPAttrType]bool
}

// IsExtendedMessageSerialization reports whether any element of
// options carries the RFC 8654 ExtendedMessage flag set. Used by
// BGPMessage.Serialize to lift the length cap on the messages where
// the RFC allows it.
func IsExtendedMessageSerialization(options []*MarshallingOption) bool {
	for _, opt := range options {
		if opt == nil {
			continue
		}
		if opt.ExtendedMessage {
			return true
		}
	}
	return false
}

func IsMRTSerialization(options []*MarshallingOption) bool {
	for _, opt := range options {
		if opt == nil {
			continue
		}
		if opt.MRT {
			return true
		}
	}
	return false
}

func IsAddPathEnabled(decode bool, f Family, options []*MarshallingOption) bool {
	for _, opt := range options {
		if opt == nil {
			continue
		}
		if o := opt.AddPath; o != nil {
			if decode && o[f]&BGP_ADD_PATH_RECEIVE > 0 {
				return true
			} else if !decode && o[f]&BGP_ADD_PATH_SEND > 0 {
				return true
			}
		}
	}
	return false
}

func isAttributePresent(attr BGPAttrType, options []*MarshallingOption) bool {
	for _, opt := range options {
		if opt == nil {
			continue
		}
		if o := opt.attributes; o != nil {
			_, ok := o[attr]
			return ok
		}
	}
	return false
}

const (
	AFI_IP     = 1
	AFI_IP6    = 2
	AFI_L2VPN  = 25
	AFI_LS     = 16388
	AFI_OPAQUE = 16397
)

const (
	SAFI_UNICAST                  = 1
	SAFI_MULTICAST                = 2
	SAFI_MPLS_LABEL               = 4
	SAFI_ENCAPSULATION            = 7
	SAFI_VPLS                     = 65
	SAFI_EVPN                     = 70
	SAFI_LS                       = 71
	SAFI_SRPOLICY                 = 73
	SAFI_MUP                      = 85
	SAFI_MPLS_VPN                 = 128
	SAFI_MPLS_VPN_MULTICAST       = 129
	SAFI_ROUTE_TARGET_CONSTRAINTS = 132
	SAFI_FLOW_SPEC_UNICAST        = 133
	SAFI_FLOW_SPEC_VPN            = 134
	SAFI_KEY_VALUE                = 241
)

const (
	BGP_ORIGIN_ATTR_TYPE_IGP        uint8 = 0
	BGP_ORIGIN_ATTR_TYPE_EGP        uint8 = 1
	BGP_ORIGIN_ATTR_TYPE_INCOMPLETE uint8 = 2
)

const (
	BGP_ASPATH_ATTR_TYPE_SET        = 1
	BGP_ASPATH_ATTR_TYPE_SEQ        = 2
	BGP_ASPATH_ATTR_TYPE_CONFED_SEQ = 3
	BGP_ASPATH_ATTR_TYPE_CONFED_SET = 4
)

const (
	BGP_ATTR_NHLEN_VPN_RD             = 8
	BGP_ATTR_NHLEN_IPV4               = 4
	BGP_ATTR_NHLEN_IPV6_GLOBAL        = 16
	BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL = 32
)

// RFC7153 5.1. Registries for the "Type" Field
// RANGE	REGISTRATION PROCEDURES
// 0x00-0x3F	Transitive First Come First Served
// 0x40-0x7F	Non-Transitive First Come First Served
// 0x80-0x8F	Transitive Experimental Use
// 0x90-0xBF	Transitive Standards Action
// 0xC0-0xCF	Non-Transitive Experimental Use
// 0xD0-0xFF	Non-Transitive Standards Action
type ExtendedCommunityAttrType uint8

const (
	EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC      ExtendedCommunityAttrType = 0x00
	EC_TYPE_TRANSITIVE_IP6_SPECIFIC               ExtendedCommunityAttrType = 0x00 // RFC5701
	EC_TYPE_TRANSITIVE_IP4_SPECIFIC               ExtendedCommunityAttrType = 0x01
	EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC     ExtendedCommunityAttrType = 0x02
	EC_TYPE_TRANSITIVE_OPAQUE                     ExtendedCommunityAttrType = 0x03
	EC_TYPE_TRANSITIVE_QOS_MARKING                ExtendedCommunityAttrType = 0x04
	EC_TYPE_COS_CAPABILITY                        ExtendedCommunityAttrType = 0x05
	EC_TYPE_EVPN                                  ExtendedCommunityAttrType = 0x06
	EC_TYPE_FLOWSPEC_REDIRECT_MIRROR              ExtendedCommunityAttrType = 0x08
	EC_TYPE_MUP                                   ExtendedCommunityAttrType = 0x0c
	EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC  ExtendedCommunityAttrType = 0x40
	EC_TYPE_NON_TRANSITIVE_LINK_BANDWIDTH         ExtendedCommunityAttrType = 0x40
	EC_TYPE_NON_TRANSITIVE_IP6_SPECIFIC           ExtendedCommunityAttrType = 0x40 // RFC5701
	EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC           ExtendedCommunityAttrType = 0x41
	EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC ExtendedCommunityAttrType = 0x42
	EC_TYPE_NON_TRANSITIVE_OPAQUE                 ExtendedCommunityAttrType = 0x43
	EC_TYPE_NON_TRANSITIVE_QOS_MARKING            ExtendedCommunityAttrType = 0x44
	EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL       ExtendedCommunityAttrType = 0x80
	EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2      ExtendedCommunityAttrType = 0x81 // RFC7674
	EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3      ExtendedCommunityAttrType = 0x82 // RFC7674
)

// RFC7153 5.2. Registries for the "Sub-Type" Field
// RANGE	REGISTRATION PROCEDURES
// 0x00-0xBF	First Come First Served
// 0xC0-0xFF	IETF Review
type ExtendedCommunityAttrSubType uint8

const (
	EC_SUBTYPE_ROUTE_TARGET            ExtendedCommunityAttrSubType = 0x02 // EC_TYPE: 0x00, 0x01, 0x02
	EC_SUBTYPE_ROUTE_ORIGIN            ExtendedCommunityAttrSubType = 0x03 // EC_TYPE: 0x00, 0x01, 0x02
	EC_SUBTYPE_LINK_BANDWIDTH          ExtendedCommunityAttrSubType = 0x04 // EC_TYPE: 0x40
	EC_SUBTYPE_GENERIC                 ExtendedCommunityAttrSubType = 0x04 // EC_TYPE: 0x02, 0x42
	EC_SUBTYPE_OSPF_DOMAIN_ID          ExtendedCommunityAttrSubType = 0x05 // EC_TYPE: 0x00, 0x01, 0x02
	EC_SUBTYPE_OSPF_ROUTE_ID           ExtendedCommunityAttrSubType = 0x07 // EC_TYPE: 0x01
	EC_SUBTYPE_BGP_DATA_COLLECTION     ExtendedCommunityAttrSubType = 0x08 // EC_TYPE: 0x00, 0x02
	EC_SUBTYPE_SOURCE_AS               ExtendedCommunityAttrSubType = 0x09 // EC_TYPE: 0x00, 0x02
	EC_SUBTYPE_L2VPN_ID                ExtendedCommunityAttrSubType = 0x0A // EC_TYPE: 0x00, 0x01
	EC_SUBTYPE_VRF_ROUTE_IMPORT        ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x01
	EC_SUBTYPE_CISCO_VPN_DISTINGUISHER ExtendedCommunityAttrSubType = 0x10 // EC_TYPE: 0x00, 0x01, 0x02

	EC_SUBTYPE_OSPF_ROUTE_TYPE ExtendedCommunityAttrSubType = 0x06 // EC_TYPE: 0x03
	EC_SUBTYPE_COLOR           ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x03
	EC_SUBTYPE_ENCAPSULATION   ExtendedCommunityAttrSubType = 0x0C // EC_TYPE: 0x03
	EC_SUBTYPE_DEFAULT_GATEWAY ExtendedCommunityAttrSubType = 0x0D // EC_TYPE: 0x03

	EC_SUBTYPE_ORIGIN_VALIDATION ExtendedCommunityAttrSubType = 0x00 // EC_TYPE: 0x43

	EC_SUBTYPE_MUP_DIRECT_SEG ExtendedCommunityAttrSubType = 0x00 // EC_TYPE: 0x0c

	EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE   ExtendedCommunityAttrSubType = 0x06 // EC_TYPE: 0x80
	EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION ExtendedCommunityAttrSubType = 0x07 // EC_TYPE: 0x80
	EC_SUBTYPE_FLOWSPEC_REDIRECT       ExtendedCommunityAttrSubType = 0x08 // EC_TYPE: 0x80
	EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK ExtendedCommunityAttrSubType = 0x09 // EC_TYPE: 0x80
	EC_SUBTYPE_L2_INFO                 ExtendedCommunityAttrSubType = 0x0A // EC_TYPE: 0x80
	EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6   ExtendedCommunityAttrSubType = 0x0B // EC_TYPE: 0x80

	EC_SUBTYPE_MAC_MOBILITY    ExtendedCommunityAttrSubType = 0x00 // EC_TYPE: 0x06
	EC_SUBTYPE_ESI_LABEL       ExtendedCommunityAttrSubType = 0x01 // EC_TYPE: 0x06
	EC_SUBTYPE_ES_IMPORT       ExtendedCommunityAttrSubType = 0x02 // EC_TYPE: 0x06
	EC_SUBTYPE_ROUTER_MAC      ExtendedCommunityAttrSubType = 0x03 // EC_TYPE: 0x06
	EC_SUBTYPE_L2_ATTRIBUTES   ExtendedCommunityAttrSubType = 0x04 // EC_TYPE: 0x06
	EC_SUBTYPE_ETREE           ExtendedCommunityAttrSubType = 0x05 // EC_TYPE: 0x06
	EC_SUBTYPE_MULTICAST_FLAGS ExtendedCommunityAttrSubType = 0x09 // EC_TYPE: 0x06

	EC_SUBTYPE_UUID_BASED_RT ExtendedCommunityAttrSubType = 0x11
)

// RFC6624
type Layer2EncapsulationType uint8

const (
	LAYER2ENCAPSULATION_TYPE_FRAMERELAY      Layer2EncapsulationType = 1
	LAYER2ENCAPSULATION_TYPE_ATM_AAL5        Layer2EncapsulationType = 2
	LAYER2ENCAPSULATION_TYPE_ATM_TRANSPARENT Layer2EncapsulationType = 3
	LAYER2ENCAPSULATION_TYPE_ETHERNET_VLAN   Layer2EncapsulationType = 4
	LAYER2ENCAPSULATION_TYPE_ETHERNET_RAW    Layer2EncapsulationType = 5
	LAYER2ENCAPSULATION_TYPE_CISCO_HDLC      Layer2EncapsulationType = 6
	LAYER2ENCAPSULATION_TYPE_PPP             Layer2EncapsulationType = 7
	LAYER2ENCAPSULATION_TYPE_SONET           Layer2EncapsulationType = 8
	LAYER2ENCAPSULATION_TYPE_ATM_VCC         Layer2EncapsulationType = 9
	LAYER2ENCAPSULATION_TYPE_ATM_VPC         Layer2EncapsulationType = 10
	LAYER2ENCAPSULATION_TYPE_IP_LAYER2       Layer2EncapsulationType = 11
	LAYER2ENCAPSULATION_TYPE_VPLS            Layer2EncapsulationType = 19
)

func (l Layer2EncapsulationType) String() string {
	switch l {
	case LAYER2ENCAPSULATION_TYPE_FRAMERELAY:
		return "framerelay"
	case LAYER2ENCAPSULATION_TYPE_ATM_AAL5:
		return "atm-aal5"
	case LAYER2ENCAPSULATION_TYPE_ATM_TRANSPARENT:
		return "atm-transparent"
	case LAYER2ENCAPSULATION_TYPE_ETHERNET_VLAN:
		return "ethernet-vlan"
	case LAYER2ENCAPSULATION_TYPE_ETHERNET_RAW:
		return "ethernet-raw"
	case LAYER2ENCAPSULATION_TYPE_CISCO_HDLC:
		return "cisco-hdlc"
	case LAYER2ENCAPSULATION_TYPE_PPP:
		return "ppp"
	case LAYER2ENCAPSULATION_TYPE_SONET:
		return "sonet"
	case LAYER2ENCAPSULATION_TYPE_ATM_VCC:
		return "atm-vcc"
	case LAYER2ENCAPSULATION_TYPE_ATM_VPC:
		return "atm-vpc"
	case LAYER2ENCAPSULATION_TYPE_IP_LAYER2:
		return "ip-layer2"
	case LAYER2ENCAPSULATION_TYPE_VPLS:
		return "vpls"
	default:
		return fmt.Sprintf("Layer2EncapsulationType(%d)", uint8(l))
	}
}

type TunnelType uint16

const (
	TUNNEL_TYPE_L2TP3       TunnelType = 1
	TUNNEL_TYPE_GRE         TunnelType = 2
	TUNNEL_TYPE_IP_IN_IP    TunnelType = 7
	TUNNEL_TYPE_VXLAN       TunnelType = 8
	TUNNEL_TYPE_NVGRE       TunnelType = 9
	TUNNEL_TYPE_MPLS        TunnelType = 10
	TUNNEL_TYPE_MPLS_IN_GRE TunnelType = 11
	TUNNEL_TYPE_VXLAN_GRE   TunnelType = 12
	TUNNEL_TYPE_MPLS_IN_UDP TunnelType = 13
	TUNNEL_TYPE_SR_POLICY   TunnelType = 15
	TUNNEL_TYPE_GENEVE      TunnelType = 19
)

func (p TunnelType) String() string {
	switch p {
	case TUNNEL_TYPE_L2TP3:
		return "l2tp3"
	case TUNNEL_TYPE_GRE:
		return "gre"
	case TUNNEL_TYPE_IP_IN_IP:
		return "ip-in-ip"
	case TUNNEL_TYPE_VXLAN:
		return "vxlan"
	case TUNNEL_TYPE_NVGRE:
		return "nvgre"
	case TUNNEL_TYPE_MPLS:
		return "mpls"
	case TUNNEL_TYPE_MPLS_IN_GRE:
		return "mpls-in-gre"
	case TUNNEL_TYPE_VXLAN_GRE:
		return "vxlan-gre"
	case TUNNEL_TYPE_MPLS_IN_UDP:
		return "mpls-in-udp"
	case TUNNEL_TYPE_SR_POLICY:
		return "sr-policy"
	case TUNNEL_TYPE_GENEVE:
		return "geneve"
	default:
		return fmt.Sprintf("TunnelType(%d)", uint8(p))
	}
}

type PmsiTunnelType uint8

const (
	PMSI_TUNNEL_TYPE_NO_TUNNEL      PmsiTunnelType = 0
	PMSI_TUNNEL_TYPE_RSVP_TE_P2MP   PmsiTunnelType = 1
	PMSI_TUNNEL_TYPE_MLDP_P2MP      PmsiTunnelType = 2
	PMSI_TUNNEL_TYPE_PIM_SSM_TREE   PmsiTunnelType = 3
	PMSI_TUNNEL_TYPE_PIM_SM_TREE    PmsiTunnelType = 4
	PMSI_TUNNEL_TYPE_BIDIR_PIM_TREE PmsiTunnelType = 5
	PMSI_TUNNEL_TYPE_INGRESS_REPL   PmsiTunnelType = 6
	PMSI_TUNNEL_TYPE_MLDP_MP2MP     PmsiTunnelType = 7
)

func (p PmsiTunnelType) String() string {
	switch p {
	case PMSI_TUNNEL_TYPE_NO_TUNNEL:
		return "no-tunnel"
	case PMSI_TUNNEL_TYPE_RSVP_TE_P2MP:
		return "rsvp-te-p2mp"
	case PMSI_TUNNEL_TYPE_MLDP_P2MP:
		return "mldp-p2mp"
	case PMSI_TUNNEL_TYPE_PIM_SSM_TREE:
		return "pim-ssm-tree"
	case PMSI_TUNNEL_TYPE_PIM_SM_TREE:
		return "pim-sm-tree"
	case PMSI_TUNNEL_TYPE_BIDIR_PIM_TREE:
		return "bidir-pim-tree"
	case PMSI_TUNNEL_TYPE_INGRESS_REPL:
		return "ingress-repl"
	case PMSI_TUNNEL_TYPE_MLDP_MP2MP:
		return "mldp-mp2mp"
	default:
		return fmt.Sprintf("PmsiTunnelType(%d)", uint8(p))
	}
}

type EncapSubTLVType uint8

const (
	ENCAP_SUBTLV_TYPE_ENCAPSULATION         EncapSubTLVType = 1
	ENCAP_SUBTLV_TYPE_PROTOCOL              EncapSubTLVType = 2
	ENCAP_SUBTLV_TYPE_COLOR                 EncapSubTLVType = 4
	ENCAP_SUBTLV_TYPE_EGRESS_ENDPOINT       EncapSubTLVType = 6
	ENCAP_SUBTLV_TYPE_UDP_DEST_PORT         EncapSubTLVType = 8
	ENCAP_SUBTLV_TYPE_SRPREFERENCE          EncapSubTLVType = 12
	ENCAP_SUBTLV_TYPE_SRBINDING_SID         EncapSubTLVType = 13
	ENCAP_SUBTLV_TYPE_SRENLP                EncapSubTLVType = 14
	ENCAP_SUBTLV_TYPE_SRPRIORITY            EncapSubTLVType = 15
	ENCAP_SUBTLV_TYPE_SRSEGMENT_LIST        EncapSubTLVType = 128
	ENCAP_SUBTLV_TYPE_SRCANDIDATE_PATH_NAME EncapSubTLVType = 129
)

const (
	_ = iota
	BGP_MSG_OPEN
	BGP_MSG_UPDATE
	BGP_MSG_NOTIFICATION
	BGP_MSG_KEEPALIVE
	BGP_MSG_ROUTE_REFRESH
)

const (
	BGP_OPT_CAPABILITY = 2
)

type BGPCapabilityCode uint8

const (
	BGP_CAP_MULTIPROTOCOL               BGPCapabilityCode = 1
	BGP_CAP_ROUTE_REFRESH               BGPCapabilityCode = 2
	BGP_CAP_CARRYING_LABEL_INFO         BGPCapabilityCode = 4
	BGP_CAP_EXTENDED_NEXTHOP            BGPCapabilityCode = 5
	BGP_CAP_EXTENDED_MESSAGE            BGPCapabilityCode = 6
	BGP_CAP_GRACEFUL_RESTART            BGPCapabilityCode = 64
	BGP_CAP_FOUR_OCTET_AS_NUMBER        BGPCapabilityCode = 65
	BGP_CAP_ADD_PATH                    BGPCapabilityCode = 69
	BGP_CAP_ENHANCED_ROUTE_REFRESH      BGPCapabilityCode = 70
	BGP_CAP_LONG_LIVED_GRACEFUL_RESTART BGPCapabilityCode = 71
	BGP_CAP_FQDN                        BGPCapabilityCode = 73
	BGP_CAP_SOFT_VERSION                BGPCapabilityCode = 75
	BGP_CAP_ROUTE_REFRESH_CISCO         BGPCapabilityCode = 128
)

var CapNameMap = map[BGPCapabilityCode]string{
	BGP_CAP_MULTIPROTOCOL:               "multiprotocol",
	BGP_CAP_ROUTE_REFRESH:               "route-refresh",
	BGP_CAP_CARRYING_LABEL_INFO:         "carrying-label-info",
	BGP_CAP_GRACEFUL_RESTART:            "graceful-restart",
	BGP_CAP_EXTENDED_NEXTHOP:            "extended-nexthop",
	BGP_CAP_EXTENDED_MESSAGE:            "extended-message",
	BGP_CAP_FOUR_OCTET_AS_NUMBER:        "4-octet-as",
	BGP_CAP_ADD_PATH:                    "add-path",
	BGP_CAP_ENHANCED_ROUTE_REFRESH:      "enhanced-route-refresh",
	BGP_CAP_ROUTE_REFRESH_CISCO:         "cisco-route-refresh",
	BGP_CAP_LONG_LIVED_GRACEFUL_RESTART: "long-lived-graceful-restart",
	BGP_CAP_FQDN:                        "fqdn",
	BGP_CAP_SOFT_VERSION:                "software-version",
}

func (c BGPCapabilityCode) String() string {
	if n, y := CapNameMap[c]; y {
		return n
	}
	return fmt.Sprintf("UnknownCapability(%d)", c)
}

var (
	// Used parsing RouteDistinguisher
	_regexpRouteDistinguisher = regexp.MustCompile(`^((\d+)\.(\d+)\.(\d+)\.(\d+)|((\d+)\.)?(\d+)|([\w]+:[\w:]*:[\w]+)):(\d+)$`)

	// Used for operator and value for the FlowSpec numeric type
	// Example:
	// re.FindStringSubmatch("&==80")
	// >>> ["&==80" "&" "==" "80"]
	_regexpFlowSpecNumericType = regexp.MustCompile(`(&?)(==|=|>|>=|<|<=|!|!=|=!)?(\d+|-\d|true|false)`)

	// - "=!" is used in the old style format of "tcp-flags" and "fragment".
	// - The value field should be one of the followings:
	//     * Decimal value (e.g., 80)
	//     * Combination of the small letters, decimals, "-" and "+"
	//       (e.g., tcp, ipv4, is-fragment+first-fragment)
	//     * Capital letters (e.g., SA)
	_regexpFlowSpecOperator      = regexp.MustCompile(`&|=|>|<|!|[\w\-+]+`)
	_regexpFlowSpecOperatorValue = regexp.MustCompile(`[\w\-+]+`)

	// Note: "(-*)" and "(.*)" catch the invalid flags
	// Example: In this case, "Z" is unsupported flag type.
	// re.FindStringSubmatch("&==-SZU")
	// >>> ["&==-SZU" "&" "==" "-" "S" "ZU"]
	_regexpFlowSpecTCPFlag = regexp.MustCompile("(&?)(==|=|!|!=|=!)?(-*)([FSRPAUCE]+)(.*)")

	// Note: "(.*)" catches the invalid flags
	// re.FindStringSubmatch("&!=+first-fragment+last-fragment+invalid-fragment")
	// >>> ["&!=+first-fragment+last-fragment+invalid-fragment" "&" "!=" "+first-fragment+last-fragment" "+last-fragment" "+" "last" "+invalid-fragment"]
	_regexpFlowSpecFragment = regexp.MustCompile(`(&?)(==|=|!|!=|=!)?(((\+)?(dont|is|first|last|not-a)-fragment)+)(.*)`)

	// re.FindStringSubmatch("192.168.0.0/24")
	// >>> ["192.168.0.0/24" "192.168.0.0" "/24" "24"]
	// re.FindStringSubmatch("192.168.0.1")
	// >>> ["192.168.0.1" "192.168.0.1" "" ""]
	_regexpFindIPv4Prefix = regexp.MustCompile(`^([\d.]+)(/(\d{1,2}))?`)

	// re.FindStringSubmatch("2001:dB8::/64")
	// >>> ["2001:dB8::/64" "2001:dB8::" "/64" "64" "" ""]
	// re.FindStringSubmatch("2001:dB8::/64/8")
	// >>> ["2001:dB8::/64/8" "2001:dB8::" "/64" "64" "/8" "8"]
	// re.FindStringSubmatch("2001:dB8::1")
	// >>> ["2001:dB8::1" "2001:dB8::1" "" "" "" ""]
	_regexpFindIPv6Prefix = regexp.MustCompile(`^([a-fA-F\d:.]+)(/(\d{1,3}))?(/(\d{1,3}))?`)
)

type ParameterCapabilityInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	Len() int
	Code() BGPCapabilityCode
}

type DefaultParameterCapability struct {
	CapCode  BGPCapabilityCode `json:"code"`
	CapLen   uint8             `json:"-"`
	CapValue []byte            `json:"value,omitempty"`
}

func (c *DefaultParameterCapability) Code() BGPCapabilityCode {
	return c.CapCode
}

func (c *DefaultParameterCapability) DecodeFromBytes(data []byte) error {
	if len(data) < 2 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not enough DefaultParameterCapability bytes available")
	}
	c.CapCode = BGPCapabilityCode(data[0])
	c.CapLen = data[1]
	if len(data) < 2+int(c.CapLen) {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all DefaultParameterCapability bytes available")
	}
	if c.CapLen > 0 {
		c.CapValue = data[2 : 2+int(c.CapLen)]
	}
	return nil
}

func (c *DefaultParameterCapability) Serialize() ([]byte, error) {
	c.CapLen = uint8(len(c.CapValue))
	buf := make([]byte, 2+len(c.CapValue))
	buf[0] = uint8(c.CapCode)
	buf[1] = c.CapLen
	copy(buf[2:], c.CapValue)
	return buf, nil
}

func (c *DefaultParameterCapability) Len() int {
	return int(c.CapLen) + 2
}

type CapMultiProtocol struct {
	DefaultParameterCapability
	CapValue Family
}

func (c *CapMultiProtocol) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	if c.CapLen != 4 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "CapMultiProtocol requires exactly 4 bytes")
	}
	v := c.DefaultParameterCapability.CapValue
	c.CapValue = NewFamily(binary.BigEndian.Uint16(v[:2]), v[3])
	return nil
}

func (c *CapMultiProtocol) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint16(buf[0:], c.CapValue.Afi())
	buf[3] = c.CapValue.Safi()
	c.DefaultParameterCapability.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapMultiProtocol) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code  BGPCapabilityCode `json:"code"`
		Value Family            `json:"value"`
	}{
		Code:  c.Code(),
		Value: c.CapValue,
	})
}

func NewCapMultiProtocol(rf Family) *CapMultiProtocol {
	return &CapMultiProtocol{
		DefaultParameterCapability{
			CapCode: BGP_CAP_MULTIPROTOCOL,
		},
		rf,
	}
}

type CapRouteRefresh struct {
	DefaultParameterCapability
}

func NewCapRouteRefresh() *CapRouteRefresh {
	return &CapRouteRefresh{
		DefaultParameterCapability{
			CapCode: BGP_CAP_ROUTE_REFRESH,
		},
	}
}

// CapExtendedMessage advertises the BGP Extended Message capability
// from RFC 8654. The capability TLV is empty (Capability Code 6,
// Capability Length 0). When both peers advertise it the maximum BGP
// message size for UPDATE, NOTIFICATION and ROUTE-REFRESH grows from
// 4096 to 65535 octets; OPEN and KEEPALIVE remain capped at 4096
// (RFC 8654 Section 6).
type CapExtendedMessage struct {
	DefaultParameterCapability
}

func NewCapExtendedMessage() *CapExtendedMessage {
	return &CapExtendedMessage{
		DefaultParameterCapability{
			CapCode: BGP_CAP_EXTENDED_MESSAGE,
		},
	}
}

type CapCarryingLabelInfo struct {
	DefaultParameterCapability
}

func NewCapCarryingLabelInfo() *CapCarryingLabelInfo {
	return &CapCarryingLabelInfo{
		DefaultParameterCapability{
			CapCode: BGP_CAP_CARRYING_LABEL_INFO,
		},
	}
}

type CapExtendedNexthopTuple struct {
	NLRIAFI    uint16
	NLRISAFI   uint16
	NexthopAFI uint16
}

func (c *CapExtendedNexthopTuple) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		NLRIAddressFamily    Family `json:"nlri_address_family"`
		NexthopAddressFamily uint16 `json:"nexthop_address_family"`
	}{
		NLRIAddressFamily:    NewFamily(c.NLRIAFI, uint8(c.NLRISAFI)),
		NexthopAddressFamily: c.NexthopAFI,
	})
}

func NewCapExtendedNexthopTuple(af Family, nexthop uint16) *CapExtendedNexthopTuple {
	return &CapExtendedNexthopTuple{
		NLRIAFI:    af.Afi(),
		NLRISAFI:   uint16(af.Safi()),
		NexthopAFI: nexthop,
	}
}

type CapExtendedNexthop struct {
	DefaultParameterCapability
	Tuples []*CapExtendedNexthopTuple
}

func (c *CapExtendedNexthop) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	data = data[2:]
	capLen := int(c.CapLen)
	if capLen%6 != 0 || capLen < 6 || len(data) < capLen {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityExtendedNexthop bytes available")
	}

	c.Tuples = []*CapExtendedNexthopTuple{}
	for capLen >= 6 {
		t := &CapExtendedNexthopTuple{
			binary.BigEndian.Uint16(data[:2]),
			binary.BigEndian.Uint16(data[2:4]),
			binary.BigEndian.Uint16(data[4:6]),
		}
		c.Tuples = append(c.Tuples, t)
		data = data[6:]
		capLen -= 6
	}
	return nil
}

func (c *CapExtendedNexthop) Serialize() ([]byte, error) {
	buf := make([]byte, len(c.Tuples)*6)
	for i, t := range c.Tuples {
		binary.BigEndian.PutUint16(buf[i*6:i*6+2], t.NLRIAFI)
		binary.BigEndian.PutUint16(buf[i*6+2:i*6+4], t.NLRISAFI)
		binary.BigEndian.PutUint16(buf[i*6+4:i*6+6], t.NexthopAFI)
	}
	c.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapExtendedNexthop) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code   BGPCapabilityCode          `json:"code"`
		Tuples []*CapExtendedNexthopTuple `json:"tuples"`
	}{
		Code:   c.Code(),
		Tuples: c.Tuples,
	})
}

func NewCapExtendedNexthop(tuples []*CapExtendedNexthopTuple) *CapExtendedNexthop {
	return &CapExtendedNexthop{
		DefaultParameterCapability{
			CapCode: BGP_CAP_EXTENDED_NEXTHOP,
		},
		tuples,
	}
}

type CapGracefulRestartTuple struct {
	AFI   uint16
	SAFI  uint8
	Flags uint8
}

func (c *CapGracefulRestartTuple) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Family Family `json:"route_family"`
		Flags  uint8  `json:"flags"`
	}{
		Family: NewFamily(c.AFI, c.SAFI),
		Flags:  c.Flags,
	})
}

func NewCapGracefulRestartTuple(rf Family, forward bool) *CapGracefulRestartTuple {
	flags := 0
	if forward {
		flags = 0x80
	}
	return &CapGracefulRestartTuple{
		AFI:   rf.Afi(),
		SAFI:  rf.Safi(),
		Flags: uint8(flags),
	}
}

type CapGracefulRestart struct {
	DefaultParameterCapability
	Flags  uint8
	Time   uint16
	Tuples []*CapGracefulRestartTuple
}

func (c *CapGracefulRestart) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	if c.CapLen < 2 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityGracefulRestart bytes available")
	}
	v := c.CapValue
	restart := binary.BigEndian.Uint16(v[:2])
	c.Flags = uint8(restart >> 12)
	c.Time = restart & 0xfff
	v = v[2:]

	valueLen := int(c.CapLen) - 2

	if valueLen >= 4 && len(v) >= valueLen {
		c.Tuples = make([]*CapGracefulRestartTuple, 0, valueLen/4)

		for i := valueLen; i >= 4; i -= 4 {
			t := &CapGracefulRestartTuple{
				binary.BigEndian.Uint16(v[:2]),
				v[2], v[3],
			}
			c.Tuples = append(c.Tuples, t)
			v = v[4:]
		}
	}
	return nil
}

func (c *CapGracefulRestart) Serialize() ([]byte, error) {
	buf := make([]byte, 2, 2+4*len(c.Tuples))
	binary.BigEndian.PutUint16(buf[0:], uint16(c.Flags)<<12|c.Time)
	var tbuf [4]byte
	for _, t := range c.Tuples {
		binary.BigEndian.PutUint16(tbuf[:2], t.AFI)
		tbuf[2] = t.SAFI
		tbuf[3] = t.Flags
		buf = append(buf, tbuf[:]...)
	}
	c.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapGracefulRestart) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code   BGPCapabilityCode          `json:"code"`
		Flags  uint8                      `json:"flags"`
		Time   uint16                     `json:"time"`
		Tuples []*CapGracefulRestartTuple `json:"tuples"`
	}{
		Code:   c.Code(),
		Flags:  c.Flags,
		Time:   c.Time,
		Tuples: c.Tuples,
	})
}

func NewCapGracefulRestart(restarting, notification bool, time uint16, tuples []*CapGracefulRestartTuple) *CapGracefulRestart {
	flags := 0
	if restarting {
		flags = 0x08
	}
	if notification {
		flags |= 0x04
	}
	return &CapGracefulRestart{
		DefaultParameterCapability: DefaultParameterCapability{
			CapCode: BGP_CAP_GRACEFUL_RESTART,
		},
		Flags:  uint8(flags),
		Time:   time,
		Tuples: tuples,
	}
}

type CapFourOctetASNumber struct {
	DefaultParameterCapability
	CapValue uint32
}

func (c *CapFourOctetASNumber) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	if c.CapLen != 4 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "CapFourOctetASNumber requires exactly 4 bytes")
	}
	c.CapValue = binary.BigEndian.Uint32(c.DefaultParameterCapability.CapValue)
	return nil
}

func (c *CapFourOctetASNumber) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, c.CapValue)
	c.DefaultParameterCapability.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapFourOctetASNumber) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code  BGPCapabilityCode `json:"code"`
		Value uint32            `json:"value"`
	}{
		Code:  c.Code(),
		Value: c.CapValue,
	})
}

func NewCapFourOctetASNumber(asnum uint32) *CapFourOctetASNumber {
	return &CapFourOctetASNumber{
		DefaultParameterCapability{
			CapCode: BGP_CAP_FOUR_OCTET_AS_NUMBER,
		},
		asnum,
	}
}

type BGPAddPathMode uint8

const (
	BGP_ADD_PATH_NONE BGPAddPathMode = iota
	BGP_ADD_PATH_RECEIVE
	BGP_ADD_PATH_SEND
	BGP_ADD_PATH_BOTH
)

func (m BGPAddPathMode) String() string {
	switch m {
	case BGP_ADD_PATH_NONE:
		return "none"
	case BGP_ADD_PATH_RECEIVE:
		return "receive"
	case BGP_ADD_PATH_SEND:
		return "send"
	case BGP_ADD_PATH_BOTH:
		return "receive/send"
	default:
		return fmt.Sprintf("unknown(%d)", m)
	}
}

type CapAddPathTuple struct {
	Family Family
	Mode   BGPAddPathMode
}

func (t *CapAddPathTuple) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Family Family `json:"family"`
		Mode   uint8  `json:"mode"`
	}{
		Family: t.Family,
		Mode:   uint8(t.Mode),
	})
}

func NewCapAddPathTuple(family Family, mode BGPAddPathMode) *CapAddPathTuple {
	return &CapAddPathTuple{
		Family: family,
		Mode:   mode,
	}
}

type CapAddPath struct {
	DefaultParameterCapability
	Tuples []*CapAddPathTuple
}

func (c *CapAddPath) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	data = data[2:]
	capLen := int(c.CapLen)
	if capLen%4 != 0 || capLen < 4 || len(data) < capLen {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityAddPath bytes available")
	}

	c.Tuples = []*CapAddPathTuple{}
	for capLen >= 4 {
		t := &CapAddPathTuple{
			Family: NewFamily(binary.BigEndian.Uint16(data[:2]), data[2]),
			Mode:   BGPAddPathMode(data[3]),
		}
		c.Tuples = append(c.Tuples, t)
		data = data[4:]
		capLen -= 4
	}
	return nil
}

func (c *CapAddPath) Serialize() ([]byte, error) {
	buf := make([]byte, len(c.Tuples)*4)
	for i, t := range c.Tuples {
		binary.BigEndian.PutUint16(buf[i*4:i*4+2], t.Family.Afi())
		buf[i*4+2] = t.Family.Safi()
		buf[i*4+3] = byte(t.Mode)
	}
	c.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapAddPath) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code   BGPCapabilityCode  `json:"code"`
		Tuples []*CapAddPathTuple `json:"tuples"`
	}{
		Code:   c.Code(),
		Tuples: c.Tuples,
	})
}

func NewCapAddPath(tuples []*CapAddPathTuple) *CapAddPath {
	return &CapAddPath{
		DefaultParameterCapability: DefaultParameterCapability{
			CapCode: BGP_CAP_ADD_PATH,
		},
		Tuples: tuples,
	}
}

type CapEnhancedRouteRefresh struct {
	DefaultParameterCapability
}

func NewCapEnhancedRouteRefresh() *CapEnhancedRouteRefresh {
	return &CapEnhancedRouteRefresh{
		DefaultParameterCapability{
			CapCode: BGP_CAP_ENHANCED_ROUTE_REFRESH,
		},
	}
}

type CapRouteRefreshCisco struct {
	DefaultParameterCapability
}

func NewCapRouteRefreshCisco() *CapRouteRefreshCisco {
	return &CapRouteRefreshCisco{
		DefaultParameterCapability{
			CapCode: BGP_CAP_ROUTE_REFRESH_CISCO,
		},
	}
}

type CapLongLivedGracefulRestartTuple struct {
	AFI         uint16
	SAFI        uint8
	Flags       uint8
	RestartTime uint32
}

func (c *CapLongLivedGracefulRestartTuple) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Family      Family `json:"route_family"`
		Flags       uint8  `json:"flags"`
		RestartTime uint32 `json:"restart_time"`
	}{
		Family:      NewFamily(c.AFI, c.SAFI),
		Flags:       c.Flags,
		RestartTime: c.RestartTime,
	})
}

func NewCapLongLivedGracefulRestartTuple(rf Family, forward bool, restartTime uint32) *CapLongLivedGracefulRestartTuple {
	flags := 0
	if forward {
		flags = 0x80
	}
	return &CapLongLivedGracefulRestartTuple{
		AFI:         rf.Afi(),
		SAFI:        rf.Safi(),
		Flags:       uint8(flags),
		RestartTime: restartTime,
	}
}

type CapLongLivedGracefulRestart struct {
	DefaultParameterCapability
	Tuples []*CapLongLivedGracefulRestartTuple
}

func (c *CapLongLivedGracefulRestart) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	data = data[2:]

	valueLen := int(c.CapLen)
	if valueLen%7 != 0 || len(data) < valueLen {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "invalid length of long lived graceful restart capablity")
	}
	for i := valueLen; i >= 7; i -= 7 {
		t := &CapLongLivedGracefulRestartTuple{
			binary.BigEndian.Uint16(data),
			data[2],
			data[3],
			uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6]),
		}
		c.Tuples = append(c.Tuples, t)
		data = data[7:]
	}
	return nil
}

func (c *CapLongLivedGracefulRestart) Serialize() ([]byte, error) {
	buf := make([]byte, 7*len(c.Tuples))
	for idx, t := range c.Tuples {
		binary.BigEndian.PutUint16(buf[idx*7:], t.AFI)
		buf[idx*7+2] = t.SAFI
		buf[idx*7+3] = t.Flags
		buf[idx*7+4] = uint8(t.RestartTime >> 16 & 0xff)
		buf[idx*7+5] = uint8(t.RestartTime >> 8 & 0xff)
		buf[idx*7+6] = uint8(t.RestartTime & 0xff)
	}
	c.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapLongLivedGracefulRestart) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Code   BGPCapabilityCode                   `json:"code"`
		Tuples []*CapLongLivedGracefulRestartTuple `json:"tuples"`
	}{
		Code:   c.Code(),
		Tuples: c.Tuples,
	})
}

func NewCapLongLivedGracefulRestart(tuples []*CapLongLivedGracefulRestartTuple) *CapLongLivedGracefulRestart {
	return &CapLongLivedGracefulRestart{
		DefaultParameterCapability: DefaultParameterCapability{
			CapCode: BGP_CAP_LONG_LIVED_GRACEFUL_RESTART,
		},
		Tuples: tuples,
	}
}

type CapFQDN struct {
	DefaultParameterCapability
	HostNameLen   uint8
	HostName      string
	DomainNameLen uint8
	DomainName    string
}

func (c *CapFQDN) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	v := c.CapValue
	if len(v) < 1 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
	}
	c.HostNameLen = v[0]
	hostNameLen := int(c.HostNameLen)
	if len(v) < 1+hostNameLen+1 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
	}
	c.HostName = string(v[1 : hostNameLen+1])
	domainNameLen := v[hostNameLen+1]
	if len(v) < 1+hostNameLen+1+int(domainNameLen) {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
	}
	c.DomainNameLen = domainNameLen
	c.DomainName = string(v[hostNameLen+2 : hostNameLen+2+int(domainNameLen)])
	return nil
}

func (c *CapFQDN) Serialize() ([]byte, error) {
	buf := make([]byte, c.HostNameLen+c.DomainNameLen+2)
	buf[0] = c.HostNameLen
	copy(buf[1:c.HostNameLen+1], c.HostName)
	buf[c.HostNameLen+1] = c.DomainNameLen
	copy(buf[c.HostNameLen+2:], c.DomainName)
	c.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapFQDN) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		HostNameLen   uint8  `json:"hostname_len"`
		HostName      string `json:"hostname"`
		DomainNameLen uint8  `json:"domainname_len"`
		DomainName    string `json:"domainname"`
	}{
		HostNameLen:   c.HostNameLen,
		HostName:      c.HostName,
		DomainNameLen: c.DomainNameLen,
		DomainName:    c.DomainName,
	})
}

func NewCapFQDN(hostname string, domainname string) *CapFQDN {
	if len(hostname) > 64 {
		hostname = hostname[:64]
	}
	if len(domainname) > 64 {
		domainname = domainname[:64]
	}
	return &CapFQDN{
		DefaultParameterCapability{
			CapCode: BGP_CAP_FQDN,
		},
		uint8(len(hostname)),
		hostname,
		uint8(len(domainname)),
		domainname,
	}
}

type CapSoftwareVersion struct {
	DefaultParameterCapability
	SoftwareVersionLen uint8
	SoftwareVersion    string
}

func (c *CapSoftwareVersion) DecodeFromBytes(data []byte) error {
	if err := c.DefaultParameterCapability.DecodeFromBytes(data); err != nil {
		return err
	}
	v := c.CapValue
	if len(v) < 2 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilitySoftwareVersion bytes allowed")
	}
	softwareVersionLen := v[0]
	if len(v[1:]) < int(softwareVersionLen) || softwareVersionLen > 64 || softwareVersionLen == 0 {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "invalid length of software version capablity")
	}
	c.SoftwareVersionLen = softwareVersionLen
	c.SoftwareVersion = string(v[1 : 1+c.SoftwareVersionLen])
	return nil
}

func (c *CapSoftwareVersion) Serialize() ([]byte, error) {
	buf := make([]byte, c.SoftwareVersionLen+1)
	buf[0] = c.SoftwareVersionLen
	copy(buf[1:], []byte(c.SoftwareVersion))
	c.CapValue = buf
	return c.DefaultParameterCapability.Serialize()
}

func (c *CapSoftwareVersion) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		SoftwareVersionLen uint8  `json:"software_version_len"`
		SoftwareVersion    string `json:"software_version"`
	}{
		SoftwareVersionLen: c.SoftwareVersionLen,
		SoftwareVersion:    c.SoftwareVersion,
	})
}

func NewCapSoftwareVersion(version string) *CapSoftwareVersion {
	if len(version) > 64 {
		version = version[:64]
	}

	return &CapSoftwareVersion{
		DefaultParameterCapability{
			CapCode: BGP_CAP_SOFT_VERSION,
		},
		uint8(len(version)),
		version,
	}
}

type CapUnknown struct {
	DefaultParameterCapability
}

func NewCapUnknown(code BGPCapabilityCode, value []byte) *CapUnknown {
	return &CapUnknown{
		DefaultParameterCapability{
			CapCode:  code,
			CapValue: value,
		},
	}
}

func DecodeCapability(data []byte) (ParameterCapabilityInterface, error) {
	if len(data) < 2 {
		return nil, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all ParameterCapability bytes available")
	}
	var c ParameterCapabilityInterface
	switch BGPCapabilityCode(data[0]) {
	case BGP_CAP_MULTIPROTOCOL:
		c = &CapMultiProtocol{}
	case BGP_CAP_ROUTE_REFRESH:
		c = &CapRouteRefresh{}
	case BGP_CAP_CARRYING_LABEL_INFO:
		c = &CapCarryingLabelInfo{}
	case BGP_CAP_EXTENDED_NEXTHOP:
		c = &CapExtendedNexthop{}
	case BGP_CAP_EXTENDED_MESSAGE:
		c = &CapExtendedMessage{}
	case BGP_CAP_GRACEFUL_RESTART:
		c = &CapGracefulRestart{}
	case BGP_CAP_FOUR_OCTET_AS_NUMBER:
		c = &CapFourOctetASNumber{}
	case BGP_CAP_ADD_PATH:
		c = &CapAddPath{}
	case BGP_CAP_ENHANCED_ROUTE_REFRESH:
		c = &CapEnhancedRouteRefresh{}
	case BGP_CAP_ROUTE_REFRESH_CISCO:
		c = &CapRouteRefreshCisco{}
	case BGP_CAP_LONG_LIVED_GRACEFUL_RESTART:
		c = &CapLongLivedGracefulRestart{}
	case BGP_CAP_FQDN:
		c = &CapFQDN{}
	case BGP_CAP_SOFT_VERSION:
		c = &CapSoftwareVersion{}
	default:
		c = &CapUnknown{}
	}
	if err := c.DecodeFromBytes(data); err != nil {
		return nil, err
	}
	return c, nil
}

type OptionParameterInterface interface {
	Serialize() ([]byte, error)
}

type OptionParameterCapability struct {
	ParamType  uint8
	ParamLen   uint8
	Capability []ParameterCapabilityInterface
}

func (o *OptionParameterCapability) DecodeFromBytes(data []byte) error {
	if uint8(len(data)) < o.ParamLen {
		return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER, nil, "Not all OptionParameterCapability bytes available")
	}
	for len(data) >= 2 {
		c, err := DecodeCapability(data)
		if err != nil {
			return err
		}
		o.Capability = append(o.Capability, c)
		if c.Len() == 0 || len(data) < c.Len() {
			return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Bad capability length")
		}
		data = data[c.Len():]
	}
	return nil
}

func (o *OptionParameterCapability) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	buf[0] = o.ParamType
	for _, p := range o.Capability {
		pbuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, pbuf...)
	}
	o.ParamLen = uint8(len(buf) - 2)
	buf[1] = o.ParamLen
	return buf, nil
}

func NewOptionParameterCapability(capability []ParameterCapabilityInterface) *OptionParameterCapability {
	return &OptionParameterCapability{
		ParamType:  BGP_OPT_CAPABILITY,
		Capability: capability,
	}
}

type OptionParameterUnknown struct {
	ParamType uint8
	ParamLen  uint8
	Value     []byte
}

func (o *OptionParameterUnknown) Serialize() ([]byte, error) {
	buf := make([]byte, 2+len(o.Value))
	buf[0] = o.ParamType
	if o.ParamLen == 0 {
		o.ParamLen = uint8(len(o.Value))
	}
	buf[1] = o.ParamLen
	copy(buf[2:], o.Value)
	return buf, nil
}

type BGPOpen struct {
	Version     uint8
	MyAS        uint16
	HoldTime    uint16
	ID          netip.Addr
	OptParamLen uint8
	OptParams   []OptionParameterInterface
}

func (msg *BGPOpen) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 10 {
		return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Not all BGP Open message bytes available")
	}
	msg.Version = data[0]
	msg.MyAS = binary.BigEndian.Uint16(data[1:3])
	msg.HoldTime = binary.BigEndian.Uint16(data[3:5])
	msg.ID, _ = netip.AddrFromSlice(data[5:9])
	msg.OptParamLen = data[9]
	data = data[10:]
	if len(data) < int(msg.OptParamLen) {
		return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Not all BGP Open message bytes available")
	}

	msg.OptParams = []OptionParameterInterface{}
	for rest := msg.OptParamLen; rest > 0; {
		if rest < 2 {
			return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Malformed BGP Open message")
		}
		paramtype := data[0]
		paramlen := data[1]
		if paramlen >= 254 || rest < paramlen+2 {
			return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Malformed BGP Open message")
		}
		rest -= paramlen + 2

		if paramtype == BGP_OPT_CAPABILITY {
			p := &OptionParameterCapability{}
			p.ParamType = paramtype
			p.ParamLen = paramlen
			if err := p.DecodeFromBytes(data[2 : 2+paramlen]); err != nil {
				return err
			}
			msg.OptParams = append(msg.OptParams, p)
		} else {
			p := &OptionParameterUnknown{}
			p.ParamType = paramtype
			p.ParamLen = paramlen
			p.Value = data[2 : 2+paramlen]
			msg.OptParams = append(msg.OptParams, p)
		}
		data = data[2+paramlen:]
	}
	return nil
}

func (msg *BGPOpen) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 10)
	buf[0] = msg.Version
	binary.BigEndian.PutUint16(buf[1:3], msg.MyAS)
	binary.BigEndian.PutUint16(buf[3:5], msg.HoldTime)
	copy(buf[5:9], msg.ID.AsSlice())
	pbuf := make([]byte, 0)
	for _, p := range msg.OptParams {
		onepbuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		pbuf = append(pbuf, onepbuf...)
	}
	msg.OptParamLen = uint8(len(pbuf))
	buf[9] = msg.OptParamLen
	return append(buf, pbuf...), nil
}

func NewBGPOpenMessage(myas uint16, holdtime uint16, id netip.Addr, optparams []OptionParameterInterface) (*BGPMessage, error) {
	if !id.Is4() {
		return nil, fmt.Errorf("invalid address")
	}
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_OPEN},
		Body:   &BGPOpen{4, myas, holdtime, id, 0, optparams},
	}, nil
}

type NLRI interface {
	Serialize(...*MarshallingOption) ([]byte, error)
	Len(...*MarshallingOption) int
	String() string
	MarshalJSON() ([]byte, error)
	// Create a flat map to describe attributes and their
	// values. This can be used to create structured outputs.
	Flat() map[string]string
}

func LabelString(nlri NLRI) string {
	label := ""
	switch n := nlri.(type) {
	case *LabeledIPAddrPrefix:
		label = n.Labels.String()
	case *LabeledVPNIPAddrPrefix:
		label = n.Labels.String()
	case *EVPNNLRI:
		switch route := n.RouteTypeData.(type) {
		case *EVPNEthernetAutoDiscoveryRoute:
			label = fmt.Sprintf("[%d]", route.Label)
		case *EVPNMacIPAdvertisementRoute:
			ls := make([]string, len(route.Labels))
			for i, l := range route.Labels {
				ls[i] = strconv.Itoa(int(l))
			}
			label = fmt.Sprintf("[%s]", strings.Join(ls, ","))
		case *EVPNIPPrefixRoute:
			label = fmt.Sprintf("[%d]", route.Label)
		}
	}
	return label
}

type IPAddrPrefixDefault struct {
	Prefix netip.Prefix
}

func (r *IPAddrPrefixDefault) decodePrefix(data []byte, bitlen uint8, addrlen int) error {
	if addrlen != 4 && addrlen != 16 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "invalid address length")
	}

	bytelen := (int(bitlen) + 7) / 8
	if len(data) < bytelen {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "network bytes is short")
	}
	if int(bitlen) > addrlen*8 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "network bit length is too long")
	}

	var b [16]byte
	copy(b[:], data[:bytelen])
	// clear trailing bits in the last byte. rfc doesn't require
	// this but some bgp implementations need this...
	rem := bitlen % 8
	if rem != 0 {
		mask := 0xff00 >> rem
		lastByte := b[bytelen-1] & byte(mask)
		b[bytelen-1] = lastByte
	}
	addr, _ := netip.AddrFromSlice(b[:addrlen])
	r.Prefix = netip.PrefixFrom(addr, int(bitlen))
	return nil
}

func (r *IPAddrPrefixDefault) serializePrefix() []byte {
	byteLen := (r.Prefix.Bits() + 7) / 8
	buf := make([]byte, byteLen)
	copy(buf, r.Prefix.Addr().AsSlice()[:byteLen])

	return buf
}

type IPAddrPrefix struct {
	IPAddrPrefixDefault
}

func (r *IPAddrPrefix) decodeFromBytes(data []byte, addrlen int, options ...*MarshallingOption) error {
	if len(data) < 1 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "prefix misses length field")
	}

	return r.decodePrefix(data[1:], data[0], addrlen)
}

func (r *IPAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := []byte{uint8(r.Prefix.Bits())}
	return append(buf, r.serializePrefix()...), nil
}

func (r *IPAddrPrefix) Len(options ...*MarshallingOption) int {
	return 1 + (r.Prefix.Bits()+7)/8
}

func (r *IPAddrPrefix) String() string {
	return r.Prefix.String()
}

func (p *IPAddrPrefix) Flat() map[string]string {
	return map[string]string{
		"Prefix":    p.Prefix.Addr().String(),
		"PrefixLen": strconv.Itoa(p.Prefix.Bits()),
	}
}

func (r *IPAddrPrefix) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Prefix string `json:"prefix"`
	}{
		Prefix: r.Prefix.String(),
	})
}

func NewIPAddrPrefix(prefix netip.Prefix) (*IPAddrPrefix, error) {
	if !prefix.IsValid() {
		return nil, fmt.Errorf("invalid prefix")
	}
	return &IPAddrPrefix{
		IPAddrPrefixDefault: IPAddrPrefixDefault{
			Prefix: prefix.Masked(),
		},
	}, nil
}

const (
	BGP_RD_TWO_OCTET_AS = iota
	BGP_RD_IPV4_ADDRESS
	BGP_RD_FOUR_OCTET_AS
	BGP_RD_EOR
)

type RouteDistinguisherInterface interface {
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	Len() int
	String() string
	MarshalJSON() ([]byte, error)
}

type DefaultRouteDistinguisher struct {
	Type uint16
}

func (rd *DefaultRouteDistinguisher) serialize(value []byte) ([]byte, error) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint16(buf, rd.Type)
	copy(buf[2:], value)
	return buf, nil
}

func (rd *DefaultRouteDistinguisher) Len() int {
	return 8
}

type RouteDistinguisherTwoOctetAS struct {
	DefaultRouteDistinguisher
	Admin    uint16
	Assigned uint32
}

func (rd *RouteDistinguisherTwoOctetAS) DecodeFromBytes(data []byte) error {
	if len(data) < 6 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not enough RouteDistinguisherTwoOctetAS bytes available")
	}
	rd.Admin = binary.BigEndian.Uint16(data[:2])
	rd.Assigned = binary.BigEndian.Uint32(data[2:6])
	return nil
}

func (rd *RouteDistinguisherTwoOctetAS) Serialize() ([]byte, error) {
	buf := make([]byte, 6)
	binary.BigEndian.PutUint16(buf[:2], rd.Admin)
	binary.BigEndian.PutUint32(buf[2:6], rd.Assigned)
	return rd.serialize(buf)
}

func (rd *RouteDistinguisherTwoOctetAS) String() string {
	return fmt.Sprintf("%d:%d", rd.Admin, rd.Assigned)
}

func (rd *RouteDistinguisherTwoOctetAS) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     uint16 `json:"type"`
		Admin    uint16 `json:"admin"`
		Assigned uint32 `json:"assigned"`
	}{
		Type:     rd.Type,
		Admin:    rd.Admin,
		Assigned: rd.Assigned,
	})
}

func NewRouteDistinguisherTwoOctetAS(admin uint16, assigned uint32) *RouteDistinguisherTwoOctetAS {
	return &RouteDistinguisherTwoOctetAS{
		DefaultRouteDistinguisher: DefaultRouteDistinguisher{
			Type: BGP_RD_TWO_OCTET_AS,
		},
		Admin:    admin,
		Assigned: assigned,
	}
}

type RouteDistinguisherIPAddressAS struct {
	DefaultRouteDistinguisher
	Admin    netip.Addr
	Assigned uint16
}

func (rd *RouteDistinguisherIPAddressAS) DecodeFromBytes(data []byte) error {
	if len(data) < 6 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not enough RouteDistinguisherIPAddressAS bytes available")
	}
	rd.Admin, _ = netip.AddrFromSlice(data[:4])
	rd.Assigned = binary.BigEndian.Uint16(data[4:6])
	return nil
}

func (rd *RouteDistinguisherIPAddressAS) Serialize() ([]byte, error) {
	buf := make([]byte, 6)
	copy(buf[:4], rd.Admin.AsSlice())
	binary.BigEndian.PutUint16(buf[4:6], rd.Assigned)
	return rd.serialize(buf)
}

func (rd *RouteDistinguisherIPAddressAS) String() string {
	return fmt.Sprintf("%s:%d", rd.Admin.String(), rd.Assigned)
}

func (rd *RouteDistinguisherIPAddressAS) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     uint16 `json:"type"`
		Admin    string `json:"admin"`
		Assigned uint16 `json:"assigned"`
	}{
		Type:     rd.Type,
		Admin:    rd.Admin.String(),
		Assigned: rd.Assigned,
	})
}

func NewRouteDistinguisherIPAddressAS(admin netip.Addr, assigned uint16) (*RouteDistinguisherIPAddressAS, error) {
	if !admin.Is4() {
		return nil, fmt.Errorf("invalid address")
	}
	return &RouteDistinguisherIPAddressAS{
		DefaultRouteDistinguisher: DefaultRouteDistinguisher{
			Type: BGP_RD_IPV4_ADDRESS,
		},
		Admin:    admin,
		Assigned: assigned,
	}, nil
}

type RouteDistinguisherFourOctetAS struct {
	DefaultRouteDistinguisher
	Admin    uint32
	Assigned uint16
}

func (rd *RouteDistinguisherFourOctetAS) DecodeFromBytes(data []byte) error {
	if len(data) < 6 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not enough RouteDistinguisherFourOctetAS bytes available")
	}
	rd.Admin = binary.BigEndian.Uint32(data[:4])
	rd.Assigned = binary.BigEndian.Uint16(data[4:6])
	return nil
}

func (rd *RouteDistinguisherFourOctetAS) Serialize() ([]byte, error) {
	buf := make([]byte, 6)
	binary.BigEndian.PutUint32(buf[:4], rd.Admin)
	binary.BigEndian.PutUint16(buf[4:6], rd.Assigned)
	return rd.serialize(buf)
}

func (rd *RouteDistinguisherFourOctetAS) String() string {
	fst := rd.Admin >> 16 & 0xffff
	snd := rd.Admin & 0xffff
	return fmt.Sprintf("%d.%d:%d", fst, snd, rd.Assigned)
}

func (rd *RouteDistinguisherFourOctetAS) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     uint16 `json:"type"`
		Admin    uint32 `json:"admin"`
		Assigned uint16 `json:"assigned"`
	}{
		Type:     rd.Type,
		Admin:    rd.Admin,
		Assigned: rd.Assigned,
	})
}

func NewRouteDistinguisherFourOctetAS(admin uint32, assigned uint16) *RouteDistinguisherFourOctetAS {
	return &RouteDistinguisherFourOctetAS{
		DefaultRouteDistinguisher: DefaultRouteDistinguisher{
			Type: BGP_RD_FOUR_OCTET_AS,
		},
		Admin:    admin,
		Assigned: assigned,
	}
}

type RouteDistinguisherUnknown struct {
	DefaultRouteDistinguisher
	Value []byte
}

func (rd *RouteDistinguisherUnknown) DecodeFromBytes(data []byte) error {
	if len(data) < 6 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not enough RouteDistinguisherUnknown bytes available")
	}
	rd.Value = data[:6]
	return nil
}

func (rd *RouteDistinguisherUnknown) Serialize() ([]byte, error) {
	return rd.serialize(rd.Value)
}

func (rd *RouteDistinguisherUnknown) String() string {
	return fmt.Sprintf("%v", rd.Value)
}

func (rd *RouteDistinguisherUnknown) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  uint16 `json:"type"`
		Value []byte `json:"value"`
	}{
		Type:  rd.Type,
		Value: rd.Value,
	})
}

func GetRouteDistinguisher(data []byte) RouteDistinguisherInterface {
	typ := binary.BigEndian.Uint16(data[:2])
	switch typ {
	case BGP_RD_TWO_OCTET_AS:
		return NewRouteDistinguisherTwoOctetAS(binary.BigEndian.Uint16(data[2:4]), binary.BigEndian.Uint32(data[4:8]))
	case BGP_RD_IPV4_ADDRESS:
		addr, _ := netip.AddrFromSlice(data[2:6])
		rd, _ := NewRouteDistinguisherIPAddressAS(addr, binary.BigEndian.Uint16(data[6:8]))
		return rd
	case BGP_RD_FOUR_OCTET_AS:
		return NewRouteDistinguisherFourOctetAS(binary.BigEndian.Uint32(data[2:6]), binary.BigEndian.Uint16(data[6:8]))
	}
	rd := &RouteDistinguisherUnknown{
		DefaultRouteDistinguisher: DefaultRouteDistinguisher{
			Type: typ,
		},
	}
	return rd
}

func parseRdAndRt(input string) ([]string, error) {
	elems := _regexpRouteDistinguisher.FindStringSubmatch(input)
	if len(elems) != 11 {
		return nil, fmt.Errorf("failed to parse RD %q", input)
	}
	return elems, nil
}

func ParseRouteDistinguisher(rd string) (RouteDistinguisherInterface, error) {
	elems, err := parseRdAndRt(rd)
	if err != nil {
		return nil, err
	}
	assigned, _ := strconv.ParseUint(elems[10], 10, 32)
	ip, _ := netip.ParseAddr(elems[1])
	switch {
	case ip.Is4():
		return NewRouteDistinguisherIPAddressAS(ip, uint16(assigned))
	case elems[6] == "" && elems[7] == "":
		asn, _ := strconv.ParseUint(elems[8], 10, 16)
		return NewRouteDistinguisherTwoOctetAS(uint16(asn), uint32(assigned)), nil
	default:
		fst, _ := strconv.ParseUint(elems[7], 10, 16)
		snd, _ := strconv.ParseUint(elems[8], 10, 16)
		asn := fst<<16 | snd
		return NewRouteDistinguisherFourOctetAS(uint32(asn), uint16(assigned)), nil
	}
}

// ParseVPNPrefix parses VPNv4/VPNv6 prefix.
func ParseVPNPrefix(prefix string) (RouteDistinguisherInterface, netip.Prefix, error) {
	elems := strings.SplitN(prefix, ":", 3)
	if len(elems) < 3 {
		return nil, netip.Prefix{}, fmt.Errorf("invalid VPN prefix format: %q", prefix)
	}

	rd, err := ParseRouteDistinguisher(elems[0] + ":" + elems[1])
	if err != nil {
		return nil, netip.Prefix{}, err
	}

	p, err := netip.ParsePrefix(elems[2])
	if err != nil {
		return rd, netip.Prefix{}, err
	}
	return rd, p, err
}

//
// RFC3107 Carrying Label Information in BGP-4
//
// 3. Carrying Label Mapping Information
//
// b) Label:
//
// The Label field carries one or more labels (that corresponds to
// the stack of labels [MPLS-ENCAPS(RFC3032)]). Each label is encoded as
// 4 octets, where the high-order 20 bits contain the label value, and
// the low order bit contains "Bottom of Stack"
//
// RFC3032 MPLS Label Stack Encoding
//
// 2.1. Encoding the Label Stack
//
//  0       1       2               3
//  0 ... 9 0 ... 9 0 1 2 3 4 ... 9 0 1
// +-----+-+-+---+-+-+-+-+-+-----+-+-+-+
// |     Label     | Exp |S|    TTL    |
// +-----+-+-+---+-+-+-+-+-+-----+-+-+-+
//

// RFC3107 Carrying Label Information in BGP-4
//
// 3. Carrying Label Mapping Information
//
// The label information carried (as part of NLRI) in the Withdrawn
// Routes field should be set to 0x800000.
const (
	WITHDRAW_LABEL = uint32(0x800000)
	ZERO_LABEL     = uint32(0) // some platform uses this as withdraw label
)

type MPLSLabelStack struct {
	Labels []uint32
}

func (l *MPLSLabelStack) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	labels := []uint32{}
	foundBottom := false
	bottomExpected := !isAttributePresent(BGP_ATTR_TYPE_PREFIX_SID, options)

	for len(data) >= 3 {
		label := uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2])
		if label == WITHDRAW_LABEL || label == ZERO_LABEL {
			l.Labels = []uint32{label}
			return nil
		}
		data = data[3:]
		labels = append(labels, label>>4)
		if !bottomExpected {
			// Faking found bottom.
			foundBottom = true
			break
		}
		if label&1 == 1 {
			foundBottom = true
			break
		}
	}

	if !foundBottom {
		if len(labels) > 0 {
			return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "MPLS label stack missing bottom-of-stack bit")
		}
		l.Labels = []uint32{}
		return nil
	}
	l.Labels = labels
	return nil
}

func (l *MPLSLabelStack) Serialize(options ...*MarshallingOption) ([]byte, error) {
	if len(l.Labels) == 0 {
		return nil, fmt.Errorf("empty MPLS label stack")
	}
	buf := make([]byte, len(l.Labels)*3)
	for i, label := range l.Labels {
		if label == WITHDRAW_LABEL {
			return []byte{128, 0, 0}, nil
		}
		label = label << 4
		buf[i*3] = byte(label >> 16 & 0xff)
		buf[i*3+1] = byte(label >> 8 & 0xff)
		buf[i*3+2] = byte(label & 0xff)
	}
	buf[len(buf)-1] |= 1
	return buf, nil
}

func (l *MPLSLabelStack) Len() int { return 3 * len(l.Labels) }

func (l *MPLSLabelStack) String() string {
	if len(l.Labels) == 0 {
		return ""
	}
	s := bytes.NewBuffer(make([]byte, 0, 64))
	s.WriteString("[")
	ss := make([]string, 0, len(l.Labels))
	for _, label := range l.Labels {
		ss = append(ss, fmt.Sprintf("%d", label))
	}
	s.WriteString(strings.Join(ss, ", "))
	s.WriteString("]")
	return s.String()
}

func NewMPLSLabelStack(labels ...uint32) *MPLSLabelStack {
	if len(labels) == 0 {
		labels = []uint32{0}
	}
	return &MPLSLabelStack{labels}
}

func ParseMPLSLabelStack(buf string) (*MPLSLabelStack, error) {
	elems := strings.Split(buf, "/")
	labels := make([]uint32, 0, len(elems))
	if len(elems) == 0 {
		goto ERR
	}
	for _, elem := range elems {
		i, err := strconv.ParseUint(elem, 10, 32)
		if err != nil {
			goto ERR
		}
		if i > 1<<20-1 {
			goto ERR
		}
		labels = append(labels, uint32(i))
	}
	return NewMPLSLabelStack(labels...), nil
ERR:
	return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "invalid mpls label stack format")
}

//
// RFC3107 Carrying Label Information in BGP-4
//
// 3. Carrying Label Mapping Information
//
// +----------------------+
// |   Length (1 octet)   |
// +----------------------+
// |   Label (3 octets)   |
// +----------------------+
// .......................
// +----------------------+
// |   Prefix (variable)  |
// +----------------------+
//
// RFC4364 BGP/MPLS IP VPNs
//
// 4.3.4. How VPN-IPv4 NLRI Is Carried in BGP
//
// The labeled VPN-IPv4 NLRI itself is encoded as specified in
// [MPLS-BGP(RFC3107)], where the prefix consists of an 8-byte RD
// followed by an IPv4 prefix.
//

type LabeledVPNIPAddrPrefix struct {
	IPAddrPrefixDefault
	Labels MPLSLabelStack
	RD     RouteDistinguisherInterface
}

func (l *LabeledVPNIPAddrPrefix) decodeFromBytes(data []byte, addrlen int, options ...*MarshallingOption) error {
	if len(data) < 1 {
		return NewMessageError(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST), nil, "LabeledVPNIPAddrPrefix not enough data")
	}
	bits := int(data[0])
	data = data[1:]
	if err := l.Labels.DecodeFromBytes(data, options...); err != nil {
		return err
	}
	if bits-8*l.Labels.Len() < 0 {
		return NewMessageError(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST), nil, "LabeledVPNIPAddrPrefix declared length too short for label stack")
	}
	if len(data) < l.Labels.Len()+8 {
		return NewMessageError(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST), nil, "LabeledVPNIPAddrPrefix not enough data")
	}
	data = data[l.Labels.Len():]
	l.RD = GetRouteDistinguisher(data)
	rdLen := l.RD.Len()
	if len(data) < rdLen {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "bad labeled VPN-IPv4 NLRI length")
	}
	data = data[l.RD.Len():]
	restbits := bits - 8*(l.Labels.Len()+l.RD.Len())
	return l.decodePrefix(data, uint8(restbits), addrlen)
}

func (l *LabeledVPNIPAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf []byte
	bits := 8*(l.Labels.Len()+l.RD.Len()) + l.Prefix.Bits()
	buf = append(buf, uint8(bits))
	lbuf, err := l.Labels.Serialize(options...)
	if err != nil {
		return nil, err
	}
	buf = append(buf, lbuf...)
	rbuf, err := l.RD.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, rbuf...)
	buf = append(buf, l.serializePrefix()...)
	return buf, nil
}

func (l *LabeledVPNIPAddrPrefix) IPPrefixLen() uint8 {
	return uint8(l.Prefix.Bits())
}

func (l *LabeledVPNIPAddrPrefix) Len(options ...*MarshallingOption) int {
	return 1 + l.Labels.Len() + l.RD.Len() + (int(l.IPPrefixLen())+7)/8
}

func (l *LabeledVPNIPAddrPrefix) String() string {
	return l.RD.String() + ":" + l.IPPrefix()
}

func (l *LabeledVPNIPAddrPrefix) IPPrefix() string {
	return l.Prefix.String()
}

func (l *LabeledVPNIPAddrPrefix) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Prefix string                      `json:"prefix"`
		Labels []uint32                    `json:"labels"`
		RD     RouteDistinguisherInterface `json:"rd"`
	}{
		Prefix: l.Prefix.String(),
		Labels: l.Labels.Labels,
		RD:     l.RD,
	})
}

func NewLabeledVPNIPAddrPrefix(prefix netip.Prefix, label MPLSLabelStack, rd RouteDistinguisherInterface) (*LabeledVPNIPAddrPrefix, error) {
	if !prefix.IsValid() {
		return nil, fmt.Errorf("invalid prefix")
	}
	return &LabeledVPNIPAddrPrefix{
		IPAddrPrefixDefault: IPAddrPrefixDefault{
			Prefix: prefix,
		},
		Labels: label,
		RD:     rd,
	}, nil
}

type LabeledIPAddrPrefix struct {
	IPAddrPrefixDefault
	Labels MPLSLabelStack
}

func (l *LabeledIPAddrPrefix) IPPrefixLen() uint8 {
	return uint8(l.Prefix.Bits())
}

func (l *LabeledIPAddrPrefix) Len(options ...*MarshallingOption) int {
	return 1 + l.Labels.Len() + int((l.IPPrefixLen()+7)/8)
}

func (l *LabeledIPAddrPrefix) decodeFromBytes(data []byte, addrlen int, options ...*MarshallingOption) error {
	if len(data) < 1 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "LabeledIPAddrPrefix not enough data")
	}
	bits := int(data[0])
	data = data[1:]
	if err := l.Labels.DecodeFromBytes(data, options...); err != nil {
		return err
	}

	if bits-8*l.Labels.Len() < 0 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "LabeledIPAddrPrefix declared length too short for label stack")
	}
	if len(data) < l.Labels.Len() {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "LabeledIPAddrPrefix not enough data")
	}
	data = data[l.Labels.Len():]
	restbits := bits - 8*l.Labels.Len()
	return l.decodePrefix(data, uint8(restbits), addrlen)
}

func (l *LabeledIPAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf []byte
	bits := 8*l.Labels.Len() + l.Prefix.Bits()
	buf = append(buf, uint8(bits))
	lbuf, err := l.Labels.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, lbuf...)
	buf = append(buf, l.serializePrefix()...)
	return buf, nil
}

func (l *LabeledIPAddrPrefix) String() string {
	return l.Prefix.String()
}

func (l *LabeledIPAddrPrefix) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Prefix string   `json:"prefix"`
		Labels []uint32 `json:"labels"`
	}{
		Prefix: l.String(),
		Labels: l.Labels.Labels,
	})
}

func (l *LabeledIPAddrPrefix) Flat() map[string]string {
	return map[string]string{
		"Prefix":    l.Prefix.Addr().String(),
		"PrefixLen": fmt.Sprintf("%d", l.Prefix.Bits()),
		"NLRI":      l.String(),
		"Label":     l.Labels.String(),
	}
}

func NewLabeledIPAddrPrefix(prefix netip.Prefix, label MPLSLabelStack) (*LabeledIPAddrPrefix, error) {
	if !prefix.IsValid() {
		return nil, fmt.Errorf("invalid prefix")
	}
	return &LabeledIPAddrPrefix{
		IPAddrPrefixDefault: IPAddrPrefixDefault{
			Prefix: prefix,
		},
		Labels: label,
	}, nil
}

type RouteTargetMembershipNLRI struct {
	Length      uint8
	AS          uint32
	RouteTarget ExtendedCommunityInterface
}

func (n *RouteTargetMembershipNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 1 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "prefix misses length field")
	}
	n.Length = data[0]
	if n.Length == 0 {
		return nil
	}
	data = data[1:]
	if n.Length < 32 || len(data)*8 < int(n.Length) {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "bad RouteTargetMembershipNLRI length")
	}
	n.AS = binary.BigEndian.Uint32(data[:4])
	if n.Length < 96 {
		return nil
	}
	rt, err := ParseExtended(data[4:])
	if err != nil {
		return err
	}
	n.RouteTarget = rt
	return nil
}

func (n *RouteTargetMembershipNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf []byte
	if n.Length == 0 {
		return append(buf, 0), nil
	}
	offset := len(buf)
	buf = append(buf, make([]byte, 5)...)
	buf[offset] = n.Length
	binary.BigEndian.PutUint32(buf[offset+1:], n.AS)
	if n.RouteTarget == nil {
		return buf, nil
	}
	ebuf, err := n.RouteTarget.Serialize()
	if err != nil {
		return nil, err
	}
	return append(buf, ebuf...), nil
}

func (n *RouteTargetMembershipNLRI) Len(options ...*MarshallingOption) int {
	return 1 + (int(n.Length)+7)/8
}

func (n *RouteTargetMembershipNLRI) String() string {
	if n.Length == 0 {
		return "default"
	}
	target := "0:0"
	if n.RouteTarget != nil {
		target = n.RouteTarget.String()
	}
	return strconv.FormatUint(uint64(n.AS), 10) + ":" + target
}

func (n *RouteTargetMembershipNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Prefix string `json:"prefix"`
	}{
		Prefix: n.String(),
	})
}

func NewRouteTargetMembershipNLRI(as uint32, target ExtendedCommunityInterface) *RouteTargetMembershipNLRI {
	l := 12 * 8
	if as == 0 && target == nil {
		l = 0
	} else if target == nil {
		l = 32
	}
	return &RouteTargetMembershipNLRI{
		Length:      uint8(l),
		AS:          as,
		RouteTarget: target,
	}
}

//go:generate stringer -type=ESIType
type ESIType uint8

const (
	ESI_ARBITRARY ESIType = iota
	ESI_LACP
	ESI_MSTP
	ESI_MAC
	ESI_ROUTERID
	ESI_AS
)

type EthernetSegmentIdentifier struct {
	Type  ESIType
	Value []byte
}

func (esi *EthernetSegmentIdentifier) DecodeFromBytes(data []byte) error {
	if len(data) < 10 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("invalid %s length", esi.Type.String()))
	}
	esi.Type = ESIType(data[0])
	esi.Value = data[1:10]
	switch esi.Type {
	case ESI_LACP, ESI_MSTP, ESI_ROUTERID, ESI_AS:
		if esi.Value[8] != 0x00 {
			return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("invalid %s. last octet must be 0x00 (0x%02x)", esi.Type.String(), esi.Value[8]))
		}
	}
	return nil
}

func (esi *EthernetSegmentIdentifier) Serialize() ([]byte, error) {
	buf := make([]byte, 10)
	buf[0] = uint8(esi.Type)
	copy(buf[1:], esi.Value)
	return buf, nil
}

func isZeroBuf(buf []byte) bool {
	for _, b := range buf {
		if b != 0 {
			return false
		}
	}
	return true
}

func (esi *EthernetSegmentIdentifier) String() string {
	toHexArray := func(data []byte) string {
		// Converts byte slice into the colon separated hex values and the
		// number of elements are 9 at most (excluding Type field).
		values := make([]string, 0, 9)
		for _, v := range data {
			values = append(values, fmt.Sprintf("%02x", v))
		}
		return strings.Join(values, ":")
	}

	s := bytes.NewBuffer(make([]byte, 0, 64))
	fmt.Fprintf(s, "%s | ", esi.Type.String())
	switch esi.Type {
	case ESI_LACP:
		fmt.Fprintf(s, "system mac %s, ", net.HardwareAddr(esi.Value[:6]).String())
		fmt.Fprintf(s, "port key %d", binary.BigEndian.Uint16(esi.Value[6:8]))
	case ESI_MSTP:
		fmt.Fprintf(s, "bridge mac %s, ", net.HardwareAddr(esi.Value[:6]).String())
		fmt.Fprintf(s, "priority %d", binary.BigEndian.Uint16(esi.Value[6:8]))
	case ESI_MAC:
		fmt.Fprintf(s, "system mac %s, ", net.HardwareAddr(esi.Value[:6]).String())
		fmt.Fprintf(s, "local discriminator %d", uint32(esi.Value[6])<<16|uint32(esi.Value[7])<<8|uint32(esi.Value[8]))
	case ESI_ROUTERID:
		fmt.Fprintf(s, "router id %s, ", net.IP(esi.Value[:4]))
		fmt.Fprintf(s, "local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8]))
	case ESI_AS:
		fmt.Fprintf(s, "as %d, ", binary.BigEndian.Uint32(esi.Value[:4]))
		fmt.Fprintf(s, "local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8]))
	case ESI_ARBITRARY:
		if isZeroBuf(esi.Value) {
			return "single-homed"
		}
		fallthrough
	default:
		s.WriteString(toHexArray(esi.Value))
	}
	return s.String()
}

// Decode Ethernet Segment Identifier (ESI) from string slice.
//
// The first element of args should be the Type field (e.g., "ARBITRARY",
// "arbitrary", "ESI_ARBITRARY" or "esi_arbitrary") and "single-homed" is
// the special keyword for all zeroed ESI.
// For the "ARBITRARY" Value field (Type 0), it should be the colon separated
// hex values and the number of elements should be 9 at most.
//
//	e.g.) args := []string{"ARBITRARY", "11:22:33:44:55:66:77:88:99"}
//
// For the other types, the Value field format is the similar to the string
// format of ESI.
//
//	e.g.) args := []string{"lacp", "aa:bb:cc:dd:ee:ff", "100"}
func ParseEthernetSegmentIdentifier(args []string) (EthernetSegmentIdentifier, error) {
	esi := EthernetSegmentIdentifier{}
	argLen := len(args)
	if argLen == 0 || args[0] == "single-homed" {
		return esi, nil
	}

	typeStr := strings.TrimPrefix(strings.ToUpper(args[0]), "ESI_")
	switch typeStr {
	case "ARBITRARY":
		esi.Type = ESI_ARBITRARY
	case "LACP":
		esi.Type = ESI_LACP
	case "MSTP":
		esi.Type = ESI_MSTP
	case "MAC":
		esi.Type = ESI_MAC
	case "ROUTERID":
		esi.Type = ESI_ROUTERID
	case "AS":
		esi.Type = ESI_AS
	default:
		typ, err := strconv.ParseUint(args[0], 10, 8)
		if err != nil {
			return esi, fmt.Errorf("invalid esi type: %s", args[0])
		}
		esi.Type = ESIType(typ)
	}

	invalidEsiValuesError := fmt.Errorf("invalid esi values for type %s: %s", esi.Type.String(), args[1:])
	esi.Value = make([]byte, 9)
	switch esi.Type {
	case ESI_LACP:
		fallthrough
	case ESI_MSTP:
		if argLen < 3 {
			return esi, invalidEsiValuesError
		}
		// MAC
		mac, err := net.ParseMAC(args[1])
		if err != nil {
			return esi, invalidEsiValuesError
		}
		copy(esi.Value[:6], mac)
		// Port Key or Bridge Priority
		i, err := strconv.ParseUint(args[2], 10, 16)
		if err != nil {
			return esi, invalidEsiValuesError
		}
		binary.BigEndian.PutUint16(esi.Value[6:8], uint16(i))
	case ESI_MAC:
		if argLen < 3 {
			return esi, invalidEsiValuesError
		}
		// MAC
		mac, err := net.ParseMAC(args[1])
		if err != nil {
			return esi, invalidEsiValuesError
		}
		copy(esi.Value[:6], mac)
		// Local Discriminator
		i, err := strconv.ParseUint(args[2], 10, 32)
		if err != nil {
			return esi, invalidEsiValuesError
		}
		iBuf := make([]byte, 4)
		binary.BigEndian.PutUint32(iBuf, uint32(i))
		copy(esi.Value[6:9], iBuf[1:4])
	case ESI_ROUTERID:
		if argLen < 3 {
			return esi, invalidEsiValuesError
		}
		// Router ID
		ip := net.ParseIP(args[1])
		if ip == nil || ip.To4() == nil {
			return esi, invalidEsiValuesError
		}
		copy(esi.Value[:4], ip.To4())
		// Local Discriminator
		i, err := strconv.ParseUint(args[2], 10, 32)
		if err != nil {
			return esi, invalidEsiValuesError
		}
		binary.BigEndian.PutUint32(esi.Value[4:8], uint32(i))
	case ESI_AS:
		if argLen < 3 {
			return esi, invalidEsiValuesError
		}
		// AS
		as, err := strconv.ParseUint(args[1], 10, 32)
		if err != nil {
			return esi, invalidEsiValuesError
		}
		binary.BigEndian.PutUint32(esi.Value[:4], uint32(as))
		// Local Discriminator
		i, err := strconv.ParseUint(args[2], 10, 32)
		if err != nil {
			return esi, invalidEsiValuesError
		}
		binary.BigEndian.PutUint32(esi.Value[4:8], uint32(i))
	case ESI_ARBITRARY:
		fallthrough
	default:
		if argLen < 2 {
			// Assumes the Value field is omitted
			break
		}
		values := make([]byte, 0, 9)
		for _, e := range strings.SplitN(args[1], ":", 9) {
			v, err := strconv.ParseUint(e, 16, 16)
			if err != nil {
				return esi, invalidEsiValuesError
			}
			values = append(values, byte(v))
		}
		copy(esi.Value, values)
	}

	return esi, nil
}

//
// I-D bess-evpn-overlay-01
//
// 5.1.3 Constructing EVPN BGP Routes
//
// For the balance of this memo, the MPLS label field will be
// referred to as the VNI/VSID field. The VNI/VSID field is used for
// both local and global VNIs/VSIDs, and for either case the entire 24-
// bit field is used to encode the VNI/VSID value.
//
// We can't use type MPLSLabelStack for EVPN NLRI, because EVPN NLRI's MPLS
// field can be filled with VXLAN VNI. In that case, we must avoid modifying
// bottom of stack bit.
//

func labelDecode(data []byte) (uint32, error) {
	if len(data) < 3 {
		return 0, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all Label bytes available")
	}
	return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2]), nil
}

func labelSerialize(label uint32) ([]byte, error) {
	if label > 0xffffff {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Out of range Label: %d", label))
	}
	buf := make([]byte, 3)
	buf[0] = byte(label >> 16 & 0xff)
	buf[1] = byte(label >> 8 & 0xff)
	buf[2] = byte(label & 0xff)
	return buf, nil
}

type EVPNEthernetAutoDiscoveryRoute struct {
	RD    RouteDistinguisherInterface
	ESI   EthernetSegmentIdentifier
	ETag  uint32
	Label uint32
}

func (er *EVPNEthernetAutoDiscoveryRoute) Len() int {
	// RD(8) + ESI(10) + ETag(4) + Label(3)
	return 25
}

func (er *EVPNEthernetAutoDiscoveryRoute) DecodeFromBytes(data []byte) error {
	if len(data) < 8 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "bad Ethernet Auto-discovery Route length")
	}
	er.RD = GetRouteDistinguisher(data)
	rdLen := er.RD.Len()
	if len(data) < rdLen+14 { // 14 is 10 for
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "bad Ethernet Auto-discovery Route length")
	}
	data = data[er.RD.Len():]
	err := er.ESI.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if len(data) < 10+4 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "bad Ethernet Auto-discovery Route length")
	}
	data = data[10:]
	er.ETag = binary.BigEndian.Uint32(data[:4])
	data = data[4:]
	if er.Label, err = labelDecode(data); err != nil {
		return err
	}
	return nil
}

func (er *EVPNEthernetAutoDiscoveryRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}
	tbuf, err := er.ESI.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)

	var tagBuf [4]byte
	binary.BigEndian.PutUint32(tagBuf[:4], er.ETag)
	buf = append(buf, tagBuf[:4]...)

	tbuf, err = labelSerialize(er.Label)
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)

	return buf, nil
}

func (er *EVPNEthernetAutoDiscoveryRoute) String() string {
	// RFC7432: BGP MPLS-Based Ethernet VPN
	// 7.1. Ethernet Auto-discovery Route
	// For the purpose of BGP route key processing, only the Ethernet
	// Segment Identifier and the Ethernet Tag ID are considered to be part
	// of the prefix in the NLRI.  The MPLS Label field is to be treated as
	// a route attribute as opposed to being part of the route.
	return fmt.Sprintf("[type:A-D][rd:%s][esi:%s][etag:%d]", er.RD, er.ESI.String(), er.ETag)
}

func (er *EVPNEthernetAutoDiscoveryRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD    RouteDistinguisherInterface `json:"rd"`
		ESI   string                      `json:"esi"`
		Etag  uint32                      `json:"etag"`
		Label uint32                      `json:"label"`
	}{
		RD:    er.RD,
		ESI:   er.ESI.String(),
		Etag:  er.ETag,
		Label: er.Label,
	})
}

func (er *EVPNEthernetAutoDiscoveryRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

func NewEVPNEthernetAutoDiscoveryRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, etag uint32, label uint32) *EVPNNLRI {
	return NewEVPNNLRI(EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY, &EVPNEthernetAutoDiscoveryRoute{
		RD:    rd,
		ESI:   esi,
		ETag:  etag,
		Label: label,
	})
}

type EVPNMacIPAdvertisementRoute struct {
	RD               RouteDistinguisherInterface
	ESI              EthernetSegmentIdentifier
	ETag             uint32
	MacAddressLength uint8
	MacAddress       net.HardwareAddr
	IPAddressLength  uint8
	IPAddress        netip.Addr
	Labels           []uint32
}

func (er *EVPNMacIPAdvertisementRoute) Len() int {
	// RD(8) + ESI(10) + ETag(4) + MacAddressLength(1) + MacAddress(6)
	// + IPAddressLength(1) + IPAddress(0, 4 or 16) + Labels(3 or 6)
	return 30 + int(er.IPAddressLength)/8 + len(er.Labels)*3
}

func (er *EVPNMacIPAdvertisementRoute) DecodeFromBytes(data []byte) error {
	if len(data) < 8 {
		return malformedAttrListErr("bad length of MAC/IP Advertisement Route")
	}
	er.RD = GetRouteDistinguisher(data)
	rdLen := er.RD.Len()
	if len(data) < rdLen+10+4+8 {
		return malformedAttrListErr("bad length of MAC/IP Advertisement Route")
	}
	data = data[rdLen:]
	err := er.ESI.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	data = data[10:]
	er.ETag = binary.BigEndian.Uint32(data[:4])
	data = data[4:]
	er.MacAddressLength = data[0]
	er.MacAddress = net.HardwareAddr(data[1:7])
	er.IPAddressLength = data[7]
	data = data[8:]
	switch er.IPAddressLength {
	case 32, 128:
		if len(data) < int(er.IPAddressLength/8) {
			return malformedAttrListErr("bad length of MAC/IP Advertisement Route")
		}
		er.IPAddress, _ = netip.AddrFromSlice(data[:er.IPAddressLength/8])
	case 0:
		// IP address omitted
	default:
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid IP address length: %d", er.IPAddressLength))
	}
	data = data[er.IPAddressLength/8:]
	var label uint32
	if label, err = labelDecode(data); err != nil {
		return err
	}
	er.Labels = append(er.Labels, label)
	if len(data) < 3 {
		return malformedAttrListErr("bad length of MAC/IP Advertisement Route")
	}
	data = data[3:]
	if len(data) == 3 {
		if label, err = labelDecode(data); err != nil {
			return err
		}
		er.Labels = append(er.Labels, label)
	}
	return nil
}

func (er *EVPNMacIPAdvertisementRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}

	esi, err := er.ESI.Serialize()
	if err != nil {
		return nil, err
	}

	buf = append(buf, esi...)
	var tbuf [7]byte
	binary.BigEndian.PutUint32(tbuf[:4], er.ETag)
	buf = append(buf, tbuf[:4]...)
	tbuf[0] = er.MacAddressLength
	copy(tbuf[1:], er.MacAddress)
	buf = append(buf, tbuf[:7]...)

	buf = append(buf, er.IPAddressLength)
	switch er.IPAddressLength {
	case 0:
		// IP address omitted
	case 32:
		addr := er.IPAddress.As4()
		buf = append(buf, addr[:]...)
	case 128:
		addr := er.IPAddress.As16()
		buf = append(buf, addr[:]...)
	default:
		return nil, fmt.Errorf("invalid IP address length: %d", er.IPAddressLength)
	}

	for _, l := range er.Labels {
		label, err := labelSerialize(l)
		if err != nil {
			return nil, err
		}
		buf = append(buf, label...)
	}
	return buf, nil
}

func (er *EVPNMacIPAdvertisementRoute) String() string {
	// RFC7432: BGP MPLS-Based Ethernet VPN
	// 7.2. MAC/IP Advertisement Route
	// For the purpose of BGP route key processing, only the Ethernet Tag
	// ID, MAC Address Length, MAC Address, IP Address Length, and IP
	// Address fields are considered to be part of the prefix in the NLRI.
	// The Ethernet Segment Identifier, MPLS Label1, and MPLS Label2 fields
	// are to be treated as route attributes as opposed to being part of the
	// "route".
	if er.IPAddressLength == 0 {
		return fmt.Sprintf("[type:macadv][rd:%s][etag:%d][mac:%s]", er.RD, er.ETag, er.MacAddress)
	}
	return fmt.Sprintf("[type:macadv][rd:%s][etag:%d][mac:%s][ip:%s]", er.RD, er.ETag, er.MacAddress, er.IPAddress)
}

func (er *EVPNMacIPAdvertisementRoute) MarshalJSON() ([]byte, error) {
	ipAddr := ""
	if er.IPAddressLength != 0 {
		ipAddr = er.IPAddress.String()
	}
	return json.Marshal(struct {
		RD         RouteDistinguisherInterface `json:"rd"`
		ESI        string                      `json:"esi"`
		Etag       uint32                      `json:"etag"`
		MacAddress string                      `json:"mac"`
		IPAddress  string                      `json:"ip,omitempty"`
		Labels     []uint32                    `json:"labels"`
	}{
		RD:         er.RD,
		ESI:        er.ESI.String(),
		Etag:       er.ETag,
		MacAddress: er.MacAddress.String(),
		IPAddress:  ipAddr,
		Labels:     er.Labels,
	})
}

func (er *EVPNMacIPAdvertisementRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

func NewEVPNMacIPAdvertisementRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, etag uint32, macAddress string, ipAddress netip.Addr, labels []uint32) (*EVPNNLRI, error) {
	mac, _ := net.ParseMAC(macAddress)
	return NewEVPNNLRI(EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, &EVPNMacIPAdvertisementRoute{
		RD:               rd,
		ESI:              esi,
		ETag:             etag,
		MacAddressLength: 48,
		MacAddress:       mac,
		IPAddressLength:  uint8(ipAddress.BitLen()),
		IPAddress:        ipAddress,
		Labels:           labels,
	}), nil
}

type EVPNMulticastEthernetTagRoute struct {
	RD              RouteDistinguisherInterface
	ETag            uint32
	IPAddressLength uint8
	IPAddress       netip.Addr
}

func (er *EVPNMulticastEthernetTagRoute) Len() int {
	// RD(8) + ETag(4) + IPAddressLength(1) + IPAddress(4 or 16)
	return 13 + int(er.IPAddressLength)/8
}

func (er *EVPNMulticastEthernetTagRoute) DecodeFromBytes(data []byte) error {
	if len(data) < 8 {
		return malformedAttrListErr("invalid length of multicast ethernet tag route")
	}
	er.RD = GetRouteDistinguisher(data)
	rdLen := er.RD.Len()
	if len(data) < rdLen+4+1 {
		return malformedAttrListErr("invalid length of multicast ethernet tag route")
	}
	data = data[rdLen:]
	er.ETag = binary.BigEndian.Uint32(data[:4])
	er.IPAddressLength = data[4]
	data = data[5:]
	if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		if len(data) < int(er.IPAddressLength/8) {
			return malformedAttrListErr("invalid length of multicast ethernet tag route")
		}
		er.IPAddress, _ = netip.AddrFromSlice(data[:er.IPAddressLength/8])
	} else {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid IP address length: %d", er.IPAddressLength))
	}
	return nil
}

func (er *EVPNMulticastEthernetTagRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}
	var tbuf [4]byte
	binary.BigEndian.PutUint32(tbuf[:4], er.ETag)
	buf = append(buf, tbuf[:4]...)
	buf = append(buf, er.IPAddressLength)
	if !er.IPAddress.IsValid() {
		return nil, fmt.Errorf("invalid IP address")
	}
	switch er.IPAddressLength {
	case 32, 128:
		buf = append(buf, er.IPAddress.AsSlice()...)
	default:
		return nil, fmt.Errorf("invalid IP address length: %d", er.IPAddressLength)
	}
	return buf, nil
}

func (er *EVPNMulticastEthernetTagRoute) String() string {
	// RFC7432: BGP MPLS-Based Ethernet VPN
	// 7.3. Inclusive Multicast Ethernet Tag Route
	// ...(snip)... For the purpose of BGP route key
	// processing, only the Ethernet Tag ID, IP Address Length, and
	// Originating Router's IP Address fields are considered to be part of
	// the prefix in the NLRI.
	return fmt.Sprintf("[type:multicast][rd:%s][etag:%d][ip:%s]", er.RD, er.ETag, er.IPAddress)
}

func (er *EVPNMulticastEthernetTagRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD        RouteDistinguisherInterface `json:"rd"`
		Etag      uint32                      `json:"etag"`
		IPAddress string                      `json:"ip"`
	}{
		RD:        er.RD,
		Etag:      er.ETag,
		IPAddress: er.IPAddress.String(),
	})
}

func (er *EVPNMulticastEthernetTagRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

func NewEVPNMulticastEthernetTagRoute(rd RouteDistinguisherInterface, etag uint32, ipAddress netip.Addr) (*EVPNNLRI, error) {
	if !ipAddress.IsValid() {
		return nil, fmt.Errorf("invalid IP address: %v", ipAddress)
	}

	return NewEVPNNLRI(EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, &EVPNMulticastEthernetTagRoute{
		RD:              rd,
		ETag:            etag,
		IPAddressLength: uint8(ipAddress.BitLen()),
		IPAddress:       ipAddress,
	}), nil
}

type EVPNEthernetSegmentRoute struct {
	RD              RouteDistinguisherInterface
	ESI             EthernetSegmentIdentifier
	IPAddressLength uint8
	IPAddress       netip.Addr
}

func (er *EVPNEthernetSegmentRoute) Len() int {
	// RD(8) + ESI(10) + IPAddressLength(1) + IPAddress(4 or 16)
	return 19 + int(er.IPAddressLength)/8
}

func (er *EVPNEthernetSegmentRoute) DecodeFromBytes(data []byte) error {
	if len(data) < 8 {
		return malformedAttrListErr("invalid Ethernet Segment Route length")
	}
	er.RD = GetRouteDistinguisher(data)
	rdLen := er.RD.Len()
	if len(data) < rdLen+10+1 {
		return malformedAttrListErr("invalid Ethernet Segment Route length")
	}
	data = data[rdLen:]
	if err := er.ESI.DecodeFromBytes(data); err != nil {
		return err
	}
	data = data[10:]
	er.IPAddressLength = data[0]
	data = data[1:]
	if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
		if len(data) < int(er.IPAddressLength/8) {
			return malformedAttrListErr("invalid Ethernet Segment Route length")
		}
		er.IPAddress, _ = netip.AddrFromSlice(data[:er.IPAddressLength/8])
	} else {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid IP address length: %d", er.IPAddressLength))
	}
	return nil
}

func (er *EVPNEthernetSegmentRoute) Serialize() ([]byte, error) {
	var buf []byte
	var err error
	if er.RD != nil {
		buf, err = er.RD.Serialize()
		if err != nil {
			return nil, err
		}
	} else {
		buf = make([]byte, 8)
	}
	tbuf, err := er.ESI.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)
	buf = append(buf, er.IPAddressLength)
	if !er.IPAddress.IsValid() {
		return nil, fmt.Errorf("invalid IP address")
	}
	switch er.IPAddressLength {
	case 32, 128:
		buf = append(buf, er.IPAddress.AsSlice()...)
	default:
		return nil, fmt.Errorf("invalid IP address length: %d", er.IPAddressLength)
	}
	return buf, nil
}

func (er *EVPNEthernetSegmentRoute) String() string {
	// RFC7432: BGP MPLS-Based Ethernet VPN
	// 7.4. Ethernet Segment Route
	// For the purpose of BGP route key processing, only the Ethernet
	// Segment ID, IP Address Length, and Originating Router's IP Address
	// fields are considered to be part of the prefix in the NLRI.
	return fmt.Sprintf("[type:esi][rd:%s][esi:%s][ip:%s]", er.RD, er.ESI.String(), er.IPAddress)
}

func (er *EVPNEthernetSegmentRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD        RouteDistinguisherInterface `json:"rd"`
		ESI       string                      `json:"esi"`
		IPAddress string                      `json:"ip"`
	}{
		RD:        er.RD,
		ESI:       er.ESI.String(),
		IPAddress: er.IPAddress.String(),
	})
}

func (er *EVPNEthernetSegmentRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

func NewEVPNEthernetSegmentRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, ipAddress netip.Addr) (*EVPNNLRI, error) {
	if !ipAddress.IsValid() {
		return nil, fmt.Errorf("invalid IP address")
	}
	return NewEVPNNLRI(EVPN_ETHERNET_SEGMENT_ROUTE, &EVPNEthernetSegmentRoute{
		RD:              rd,
		ESI:             esi,
		IPAddressLength: uint8(ipAddress.BitLen()),
		IPAddress:       ipAddress,
	}), nil
}

type EVPNIPPrefixRoute struct {
	RD             RouteDistinguisherInterface
	ESI            EthernetSegmentIdentifier
	ETag           uint32
	IPPrefixLength uint8
	IPPrefix       netip.Addr
	GWIPAddress    netip.Addr
	Label          uint32
}

func (er *EVPNIPPrefixRoute) Len() int {
	if er.IPPrefix.Is4() {
		return 34
	}
	return 58
}

func (er *EVPNIPPrefixRoute) DecodeFromBytes(data []byte) error {
	addrLen := net.IPv4len
	switch len(data) {
	case 34:
		// RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1) + IPv4 Prefix(4) + GW IPv4(4) + Label(3)
	case 58:
		// RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1) + IPv6 Prefix(16) + GW IPv6(16) + Label(3)
		addrLen = net.IPv6len
	default:
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPN IP Prefix Route bytes available")
	}

	er.RD = GetRouteDistinguisher(data[:8])

	err := er.ESI.DecodeFromBytes(data[8:18])
	if err != nil {
		return err
	}

	er.ETag = binary.BigEndian.Uint32(data[18:22])

	er.IPPrefixLength = data[22]

	offset := 23 // RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1)
	er.IPPrefix, _ = netip.AddrFromSlice(data[offset : offset+addrLen])
	offset += addrLen

	er.GWIPAddress, _ = netip.AddrFromSlice(data[offset : offset+addrLen])
	offset += addrLen

	if er.Label, err = labelDecode(data[offset : offset+3]); err != nil {
		return err
	}
	// offset += 3

	return nil
}

func (er *EVPNIPPrefixRoute) Serialize() ([]byte, error) {
	buf := make([]byte, 23) // RD(8) + ESI(10) + ETag(4) + IPPrefixLength(1)

	if er.RD != nil {
		tbuf, err := er.RD.Serialize()
		if err != nil {
			return nil, err
		}
		copy(buf[:8], tbuf)
	}

	tbuf, err := er.ESI.Serialize()
	if err != nil {
		return nil, err
	}
	copy(buf[8:18], tbuf)

	binary.BigEndian.PutUint32(buf[18:22], er.ETag)

	buf[22] = er.IPPrefixLength

	if !er.IPPrefix.IsValid() {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "IP Prefix is nil")
	} else if er.IPPrefix.Is4() {
		buf = append(buf, er.IPPrefix.AsSlice()...)
		if !er.GWIPAddress.IsValid() {
			// draft-ietf-bess-evpn-prefix-advertisement: IP Prefix Advertisement in EVPN
			// The GW IP field SHOULD be zero if it is not used as an Overlay Index.
			er.GWIPAddress = netip.IPv4Unspecified()
		}
		buf = append(buf, er.GWIPAddress.AsSlice()...)
	} else {
		buf = append(buf, er.IPPrefix.AsSlice()...)
		if !er.GWIPAddress.IsValid() {
			er.GWIPAddress, _ = netip.AddrFromSlice(net.IPv6zero)
		}
		buf = append(buf, er.GWIPAddress.AsSlice()...)
	}

	tbuf, err = labelSerialize(er.Label)
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)

	return buf, nil
}

func (er *EVPNIPPrefixRoute) String() string {
	// draft-ietf-bess-evpn-prefix-advertisement: IP Prefix Advertisement in EVPN
	// 3.1 IP Prefix Route Encoding
	// The RD, Eth-Tag ID, IP Prefix Length and IP Prefix will be part of
	// the route key used by BGP to compare routes. The rest of the fields
	// will not be part of the route key.
	return fmt.Sprintf("[type:Prefix][rd:%s][etag:%d][prefix:%s/%d]", er.RD, er.ETag, er.IPPrefix, er.IPPrefixLength)
}

func (er *EVPNIPPrefixRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD      RouteDistinguisherInterface `json:"rd"`
		ESI     string                      `json:"esi"`
		Etag    uint32                      `json:"etag"`
		Prefix  string                      `json:"prefix"`
		Gateway string                      `json:"gateway"`
		Label   uint32                      `json:"label"`
	}{
		RD:      er.RD,
		ESI:     er.ESI.String(),
		Etag:    er.ETag,
		Prefix:  fmt.Sprintf("%s/%d", er.IPPrefix, er.IPPrefixLength),
		Gateway: er.GWIPAddress.String(),
		Label:   er.Label,
	})
}

func (er *EVPNIPPrefixRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

func NewEVPNIPPrefixRoute(rd RouteDistinguisherInterface, esi EthernetSegmentIdentifier, etag uint32, ipPrefixLength uint8, ipPrefix netip.Addr, gateway netip.Addr, label uint32) (*EVPNNLRI, error) {
	if !ipPrefix.IsValid() || !gateway.IsValid() {
		return nil, fmt.Errorf("invalid IP address")
	}
	return NewEVPNNLRI(EVPN_IP_PREFIX, &EVPNIPPrefixRoute{
		RD:             rd,
		ESI:            esi,
		ETag:           etag,
		IPPrefixLength: ipPrefixLength,
		IPPrefix:       ipPrefix,
		GWIPAddress:    gateway,
		Label:          label,
	}), nil
}

type EVPNIPMSIRoute struct {
	RD   RouteDistinguisherInterface
	ETag uint32
	EC   ExtendedCommunityInterface
}

func (er *EVPNIPMSIRoute) Len() int {
	// RD(8) + ETag(4) + EC(8)
	return 20
}

func (er *EVPNIPMSIRoute) DecodeFromBytes(data []byte) error {
	if len(data) < 8 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Parse extended community interface failed")
	}
	er.RD = GetRouteDistinguisher(data[:8])

	if len(data) < er.RD.Len() {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Parse extended community interface failed")
	}
	data = data[er.RD.Len():]
	if len(data) < 4 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Parse extended community interface failed")
	}
	er.ETag = binary.BigEndian.Uint32(data[:4])

	data = data[4:]
	if len(data) < 8 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Parse extended community interface failed")
	}
	ec, err := ParseExtended(data[:8])
	if err != nil {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Parse extended community interface failed")
	}
	er.EC = ec
	return nil
}

func (er *EVPNIPMSIRoute) Serialize() ([]byte, error) {
	buf := make([]byte, 20)

	if er.RD != nil {
		tbuf, err := er.RD.Serialize()
		if err != nil {
			return nil, err
		}
		copy(buf[:8], tbuf)
	}

	binary.BigEndian.PutUint32(buf[8:12], er.ETag)

	ec, err := er.EC.Serialize()
	if err != nil {
		return nil, err
	}

	return append(buf, ec...), nil
}

func (er *EVPNIPMSIRoute) String() string {
	ec := "default"
	if er.EC != nil {
		ec = er.EC.String()
	}
	return fmt.Sprintf("[type:I-PMSI][rd:%s][etag:%d][EC:%s]", er.RD, er.ETag, ec)
}

func (er *EVPNIPMSIRoute) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		RD   RouteDistinguisherInterface `json:"rd"`
		ETag uint32                      `json:"etag"`
		EC   string                      `json:"ec"`
	}{
		RD:   er.RD,
		ETag: er.ETag,
		EC:   er.EC.String(),
	})
}

func (er *EVPNIPMSIRoute) rd() RouteDistinguisherInterface {
	return er.RD
}

func NewEVPNIPMSIRoute(rd RouteDistinguisherInterface, etag uint32, ec ExtendedCommunityInterface) *EVPNNLRI {
	return NewEVPNNLRI(EVPN_I_PMSI, &EVPNIPMSIRoute{
		RD:   rd,
		ETag: etag,
		EC:   ec,
	})
}

type EVPNRouteTypeInterface interface {
	Len() int
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	String() string
	rd() RouteDistinguisherInterface
	MarshalJSON() ([]byte, error)
}

func getEVPNRouteType(t uint8) (EVPNRouteTypeInterface, error) {
	switch t {
	case EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY:
		return &EVPNEthernetAutoDiscoveryRoute{}, nil
	case EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT:
		return &EVPNMacIPAdvertisementRoute{}, nil
	case EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG:
		return &EVPNMulticastEthernetTagRoute{}, nil
	case EVPN_ETHERNET_SEGMENT_ROUTE:
		return &EVPNEthernetSegmentRoute{}, nil
	case EVPN_IP_PREFIX:
		return &EVPNIPPrefixRoute{}, nil
	}
	return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Unknown EVPN Route type: %d", t))
}

const (
	EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY = 1
	EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT    = 2
	EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG   = 3
	EVPN_ETHERNET_SEGMENT_ROUTE             = 4
	EVPN_IP_PREFIX                          = 5
	EVPN_I_PMSI                             = 9
)

type EVPNNLRI struct {
	RouteType     uint8
	Length        uint8
	RouteTypeData EVPNRouteTypeInterface
}

func (n *EVPNNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 2 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPNNLRI bytes available")
	}
	n.RouteType = data[0]
	n.Length = data[1]
	data = data[2:]
	if len(data) < int(n.Length) {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all EVPNNLRI Route type bytes available")
	}
	r, err := getEVPNRouteType(n.RouteType)
	if err != nil {
		return err
	}
	n.RouteTypeData = r
	return n.RouteTypeData.DecodeFromBytes(data[:n.Length])
}

func (n *EVPNNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf []byte
	offset := len(buf)
	buf = append(buf, make([]byte, 2)...)
	buf[offset] = n.RouteType
	tbuf, err := n.RouteTypeData.Serialize()
	buf[offset+1] = n.Length
	if err != nil {
		return nil, err
	}
	return append(buf, tbuf...), nil
}

func (n *EVPNNLRI) Len(options ...*MarshallingOption) int {
	return int(n.Length) + 2
}

func (n *EVPNNLRI) String() string {
	if n.RouteTypeData != nil {
		return n.RouteTypeData.String()
	}
	return strconv.FormatUint(uint64(n.RouteType), 10) + ":" + strconv.FormatUint(uint64(n.Length), 10)
}

func (n *EVPNNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  uint8                  `json:"type"`
		Value EVPNRouteTypeInterface `json:"value"`
	}{
		Type:  n.RouteType,
		Value: n.RouteTypeData,
	})
}

func (n *EVPNNLRI) RD() RouteDistinguisherInterface {
	return n.RouteTypeData.rd()
}

func NewEVPNNLRI(routeType uint8, routeTypeData EVPNRouteTypeInterface) *EVPNNLRI {
	var l uint8
	if routeTypeData != nil {
		l = uint8(routeTypeData.Len())
	}
	return &EVPNNLRI{
		RouteType:     routeType,
		Length:        l,
		RouteTypeData: routeTypeData,
	}
}

type EncapNLRI struct {
	Endpoint netip.Addr
}

func (n *EncapNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 1 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "prefix misses length field")
	}
	switch data[0] {
	case net.IPv4len * 8, net.IPv6len * 8:
	default:
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "nlri length isn't valid")
	}
	addr, _ := netip.AddrFromSlice(data[1:])
	n.Endpoint = addr
	return nil
}

func (n *EncapNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf []byte
	if n.Endpoint.Is4() {
		buf = append(buf, net.IPv4len*8)
	} else {
		buf = append(buf, net.IPv6len*8)
	}
	buf = append(buf, n.Endpoint.AsSlice()...)
	return buf, nil
}

func (n *EncapNLRI) String() string {
	return n.Endpoint.String()
}

func (n *EncapNLRI) Len(options ...*MarshallingOption) int {
	return 1 + len(n.Endpoint.AsSlice())
}

func (n *EncapNLRI) Flat() map[string]string {
	return map[string]string{
		"Endpoint": n.Endpoint.String(),
	}
}

func (n *EncapNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Endpoint string `json:"endpoint"`
	}{
		Endpoint: n.Endpoint.String(),
	})
}

func NewEncapNLRI(endpoint netip.Addr) (*EncapNLRI, error) {
	if !endpoint.IsValid() {
		return nil, fmt.Errorf("invalid address")
	}
	return &EncapNLRI{
		Endpoint: endpoint,
	}, nil
}

type BGPFlowSpecType uint8

const (
	FLOW_SPEC_TYPE_UNKNOWN BGPFlowSpecType = iota
	FLOW_SPEC_TYPE_DST_PREFIX
	FLOW_SPEC_TYPE_SRC_PREFIX
	FLOW_SPEC_TYPE_IP_PROTO
	FLOW_SPEC_TYPE_PORT
	FLOW_SPEC_TYPE_DST_PORT
	FLOW_SPEC_TYPE_SRC_PORT
	FLOW_SPEC_TYPE_ICMP_TYPE
	FLOW_SPEC_TYPE_ICMP_CODE
	FLOW_SPEC_TYPE_TCP_FLAG
	FLOW_SPEC_TYPE_PKT_LEN
	FLOW_SPEC_TYPE_DSCP
	FLOW_SPEC_TYPE_FRAGMENT
	FLOW_SPEC_TYPE_LABEL
	FLOW_SPEC_TYPE_ETHERNET_TYPE // 14
	FLOW_SPEC_TYPE_SRC_MAC
	FLOW_SPEC_TYPE_DST_MAC
	FLOW_SPEC_TYPE_LLC_DSAP
	FLOW_SPEC_TYPE_LLC_SSAP
	FLOW_SPEC_TYPE_LLC_CONTROL
	FLOW_SPEC_TYPE_SNAP
	FLOW_SPEC_TYPE_VID
	FLOW_SPEC_TYPE_COS
	FLOW_SPEC_TYPE_INNER_VID
	FLOW_SPEC_TYPE_INNER_COS
)

var FlowSpecNameMap = map[BGPFlowSpecType]string{
	FLOW_SPEC_TYPE_UNKNOWN:       "unknown",
	FLOW_SPEC_TYPE_DST_PREFIX:    "destination",
	FLOW_SPEC_TYPE_SRC_PREFIX:    "source",
	FLOW_SPEC_TYPE_IP_PROTO:      "protocol",
	FLOW_SPEC_TYPE_PORT:          "port",
	FLOW_SPEC_TYPE_DST_PORT:      "destination-port",
	FLOW_SPEC_TYPE_SRC_PORT:      "source-port",
	FLOW_SPEC_TYPE_ICMP_TYPE:     "icmp-type",
	FLOW_SPEC_TYPE_ICMP_CODE:     "icmp-code",
	FLOW_SPEC_TYPE_TCP_FLAG:      "tcp-flags",
	FLOW_SPEC_TYPE_PKT_LEN:       "packet-length",
	FLOW_SPEC_TYPE_DSCP:          "dscp",
	FLOW_SPEC_TYPE_FRAGMENT:      "fragment",
	FLOW_SPEC_TYPE_LABEL:         "label",
	FLOW_SPEC_TYPE_ETHERNET_TYPE: "ether-type",
	FLOW_SPEC_TYPE_SRC_MAC:       "source-mac",
	FLOW_SPEC_TYPE_DST_MAC:       "destination-mac",
	FLOW_SPEC_TYPE_LLC_DSAP:      "llc-dsap",
	FLOW_SPEC_TYPE_LLC_SSAP:      "llc-ssap",
	FLOW_SPEC_TYPE_LLC_CONTROL:   "llc-control",
	FLOW_SPEC_TYPE_SNAP:          "snap",
	FLOW_SPEC_TYPE_VID:           "vid",
	FLOW_SPEC_TYPE_COS:           "cos",
	FLOW_SPEC_TYPE_INNER_VID:     "inner-vid",
	FLOW_SPEC_TYPE_INNER_COS:     "inner-cos",
}

var FlowSpecValueMap = map[string]BGPFlowSpecType{
	FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]:    FLOW_SPEC_TYPE_DST_PREFIX,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]:    FLOW_SPEC_TYPE_SRC_PREFIX,
	FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO]:      FLOW_SPEC_TYPE_IP_PROTO,
	FlowSpecNameMap[FLOW_SPEC_TYPE_PORT]:          FLOW_SPEC_TYPE_PORT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PORT]:      FLOW_SPEC_TYPE_DST_PORT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PORT]:      FLOW_SPEC_TYPE_SRC_PORT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_TYPE]:     FLOW_SPEC_TYPE_ICMP_TYPE,
	FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_CODE]:     FLOW_SPEC_TYPE_ICMP_CODE,
	FlowSpecNameMap[FLOW_SPEC_TYPE_TCP_FLAG]:      FLOW_SPEC_TYPE_TCP_FLAG,
	FlowSpecNameMap[FLOW_SPEC_TYPE_PKT_LEN]:       FLOW_SPEC_TYPE_PKT_LEN,
	FlowSpecNameMap[FLOW_SPEC_TYPE_DSCP]:          FLOW_SPEC_TYPE_DSCP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_FRAGMENT]:      FLOW_SPEC_TYPE_FRAGMENT,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LABEL]:         FLOW_SPEC_TYPE_LABEL,
	FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE]: FLOW_SPEC_TYPE_ETHERNET_TYPE,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]:       FLOW_SPEC_TYPE_SRC_MAC,
	FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]:       FLOW_SPEC_TYPE_DST_MAC,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_DSAP]:      FLOW_SPEC_TYPE_LLC_DSAP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_SSAP]:      FLOW_SPEC_TYPE_LLC_SSAP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_CONTROL]:   FLOW_SPEC_TYPE_LLC_CONTROL,
	FlowSpecNameMap[FLOW_SPEC_TYPE_SNAP]:          FLOW_SPEC_TYPE_SNAP,
	FlowSpecNameMap[FLOW_SPEC_TYPE_VID]:           FLOW_SPEC_TYPE_VID,
	FlowSpecNameMap[FLOW_SPEC_TYPE_COS]:           FLOW_SPEC_TYPE_COS,
	FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_VID]:     FLOW_SPEC_TYPE_INNER_VID,
	FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_COS]:     FLOW_SPEC_TYPE_INNER_COS,
}

// Joins the given and args into a single string and normalize it.
// Example:
// args := []string{"  &  <=80", " tcp  != udp ", " =!   SA   & =U!  F", " =  is-fragment+last-fragment"}
// fmt.Printf("%q", normalizeFlowSpecOpValues(args))
// >>> ["<=80" "tcp" "!=udp" "=!SA" "&=U" "!F" "=is-fragment+last-fragment"]
func normalizeFlowSpecOpValues(args []string) []string {
	// Extracts keywords from the given args.
	sub := ""
	subs := make([]string, 0)
	for _, s := range _regexpFlowSpecOperator.FindAllString(strings.Join(args, " "), -1) {
		sub += s
		if _regexpFlowSpecOperatorValue.MatchString(s) {
			subs = append(subs, sub)
			sub = ""
		}
	}

	// RFC5575 says "It should be unset in the first operator byte of a
	// sequence".
	if len(subs) > 0 {
		subs[0] = strings.TrimPrefix(subs[0], "&")
	}

	return subs
}

// Parses the FlowSpec numeric operator using the given submatch which should be
// the return value of func (*Regexp) FindStringSubmatch.
func parseFlowSpecNumericOperator(submatch []string) (operator uint8, err error) {
	if submatch[1] == "&" {
		operator = DEC_NUM_OP_AND
	}
	value, ok := DECNumOpValueMap[submatch[2]]
	if !ok {
		return 0, fmt.Errorf("invalid numeric operator: %s%s", submatch[1], submatch[2])
	}
	operator |= uint8(value)
	return operator, nil
}

// Parses the pairs of operator and value for the FlowSpec numeric type. The
// given validationFunc is applied to evaluate whether the parsed value is
// valid or not (e.g., if exceeds range or not).
// Note: Each of the args should be formatted in single pair of operator and
// value before calling this function.
// e.g.) "&==100", ">=200" or "&<300"
func parseFlowSpecNumericOpValues(typ BGPFlowSpecType, args []string, validationFunc func(uint64) error) (FlowSpecComponentInterface, error) {
	argsLen := len(args)
	if argsLen == 0 {
		return nil, fmt.Errorf("invalid argument for %s: %q", typ.String(), args)
	}
	items := make([]*FlowSpecComponentItem, 0, argsLen)
	for idx, arg := range args {
		m := _regexpFlowSpecNumericType.FindStringSubmatch(arg)
		if len(m) < 4 {
			return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args)
		}
		operator, err := parseFlowSpecNumericOperator(m)
		if err != nil {
			return nil, err
		}
		// "true" and "false" is operator, but here handles them as value.
		var value uint64
		switch m[3] {
		case "true", "false":
			if idx != argsLen-1 {
				return nil, fmt.Errorf("%s should be the last of each rule", m[3])
			}
			operator = uint8(DECNumOpValueMap[m[3]])
		default:
			if value, err = strconv.ParseUint(m[3], 10, 64); err != nil {
				return nil, fmt.Errorf("invalid numeric value: %s", m[3])
			}
			if err = validationFunc(value); err != nil {
				return nil, err
			}
		}
		items = append(items, NewFlowSpecComponentItem(operator, value))
	}

	// Marks end-of-list bit
	items[argsLen-1].Op |= uint8(DEC_NUM_OP_END)

	return NewFlowSpecComponent(typ, items), nil
}

func flowSpecNumeric1ByteParser(_ Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	args = normalizeFlowSpecOpValues(args)

	f := func(i uint64) error {
		if i <= 0xff { // 1 byte
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

func flowSpecNumeric2BytesParser(_ Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	args = normalizeFlowSpecOpValues(args)

	f := func(i uint64) error {
		if i <= 0xffff { // 2 bytes
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

// Parses the FlowSpec bitmask operand using the given submatch which should be
// the return value of func (*Regexp) FindStringSubmatch.
func parseFlowSpecBitmaskOperand(submatch []string) (operand uint8, err error) {
	if submatch[1] == "&" {
		operand = BITMASK_FLAG_OP_AND
	}
	value, ok := BitmaskFlagOpValueMap[submatch[2]]
	if !ok {
		return 0, fmt.Errorf("invalid bitmask operand: %s%s", submatch[1], submatch[2])
	}
	operand |= uint8(value)
	return operand, nil
}

func flowSpecPrefixParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	// args[0]: IP Prefix or IP Address (suppose prefix length is 32)
	// args[1]: Offset in bit (IPv6 only)
	//
	// Example:
	// - IPv4 Prefix
	//   args := []string{"192.168.0.0/24"}
	// - IPv4 Address
	//   args := []string{"192.168.0.1"}
	// - IPv6 Prefix
	//   args := []string{"2001:db8:1::/64"}
	// - IPv6 Prefix with offset
	//   args := []string{"0:db8:1::/64/16"}
	//   args := []string{"0:db8:1::/64", "16"}
	// - IPv6 Address
	//   args := []string{"2001:db8:1::1"}
	// - IPv6 Address with offset
	//   args := []string{"0:db8:1::1", "16"}
	switch rf.Afi() {
	case AFI_IP:
		if len(args) > 1 {
			return nil, errors.New("cannot specify offset for ipv4 prefix")
		}
		invalidIPv4PrefixError := fmt.Errorf("invalid ipv4 prefix: %s", args[0])
		m := _regexpFindIPv4Prefix.FindStringSubmatch(args[0])
		if len(m) < 4 {
			return nil, invalidIPv4PrefixError
		}
		prefix := net.ParseIP(m[1])
		if prefix.To4() == nil {
			return nil, invalidIPv4PrefixError
		}
		var prefixLen uint64 = 32
		if m[3] != "" {
			var err error
			prefixLen, err = strconv.ParseUint(m[3], 10, 8)
			if err != nil || prefixLen > 32 {
				return nil, invalidIPv4PrefixError
			}
		}
		switch typ {
		case FLOW_SPEC_TYPE_DST_PREFIX:
			ipPrefix, _ := NewIPAddrPrefix(netip.MustParsePrefix(fmt.Sprintf("%s/%d", prefix.String(), prefixLen)))
			return NewFlowSpecDestinationPrefix(ipPrefix), nil
		case FLOW_SPEC_TYPE_SRC_PREFIX:
			ipPrefix, _ := NewIPAddrPrefix(netip.MustParsePrefix(fmt.Sprintf("%s/%d", prefix.String(), prefixLen)))
			return NewFlowSpecSourcePrefix(ipPrefix), nil
		}
		return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String())
	case AFI_IP6:
		if len(args) > 2 {
			return nil, fmt.Errorf("invalid arguments for ipv6 prefix: %q", args)
		}
		invalidIPv6PrefixError := fmt.Errorf("invalid ipv6 prefix: %s", args[0])
		m := _regexpFindIPv6Prefix.FindStringSubmatch(args[0])
		if len(m) < 4 {
			return nil, invalidIPv6PrefixError
		}
		prefix := net.ParseIP(m[1])
		if prefix.To16() == nil {
			return nil, invalidIPv6PrefixError
		}
		var prefixLen uint64 = 128
		if m[3] != "" {
			var err error
			prefixLen, err = strconv.ParseUint(m[3], 10, 8)
			if err != nil || prefixLen > 128 {
				return nil, invalidIPv6PrefixError
			}
		}
		var offset uint64
		if len(args) == 1 && m[5] != "" {
			var err error
			offset, err = strconv.ParseUint(m[5], 10, 8)
			if err != nil || offset > 128 {
				return nil, fmt.Errorf("invalid ipv6 prefix offset: %s", m[5])
			}
		} else if len(args) == 2 {
			if m[5] != "" {
				return nil, fmt.Errorf("multiple ipv6 prefix offset arguments detected: %q", args)
			}
			var err error
			offset, err = strconv.ParseUint(args[1], 10, 8)
			if err != nil || offset > 128 {
				return nil, fmt.Errorf("invalid ipv6 prefix offset: %s", args[1])
			}
		}
		// validated above
		nlri, _ := NewIPAddrPrefix(netip.MustParsePrefix(fmt.Sprintf("%s/%d", prefix.String(), prefixLen)))
		switch typ {
		case FLOW_SPEC_TYPE_DST_PREFIX:
			return NewFlowSpecDestinationPrefix6(nlri, uint8(offset)), nil
		case FLOW_SPEC_TYPE_SRC_PREFIX:
			return NewFlowSpecSourcePrefix6(nlri, uint8(offset)), nil
		}
		return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String())
	}
	return nil, fmt.Errorf("invalid address family: %s", rf.String())
}

func flowSpecIpProtoParser(_ Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	// args: List of pairs of Operator and IP protocol type
	//
	// Example:
	// - TCP or UDP
	//   args := []string{"tcp", "==udp"}
	// - Not TCP and not UDP
	//   args := []string{"!=tcp", "&!=udp"}
	args = normalizeFlowSpecOpValues(args)
	s := strings.Join(args, " ")
	for i, name := range ProtocolNameMap {
		s = strings.ReplaceAll(s, name, fmt.Sprintf("%d", i))
	}
	args = strings.Split(s, " ")

	f := func(i uint64) error {
		if i <= 0xff { // 1 byte
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

func flowSpecTcpFlagParser(_ Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	// args: List of pairs of Operand and TCP Flags
	//
	// Example:
	// - SYN or SYN/ACK
	//   args := []string{"==S", "==SA"}
	// - Not FIN and not URG
	//   args := []string{"!=F", "&!=U"}
	args = normalizeFlowSpecOpValues(args)
	if len(args) == 0 {
		return nil, fmt.Errorf("invalid argument for %s: %q", typ.String(), args)
	}

	argsLen := len(args)
	items := make([]*FlowSpecComponentItem, 0, argsLen)

	for _, arg := range args {
		m := _regexpFlowSpecTCPFlag.FindStringSubmatch(arg)
		if len(m) < 6 {
			return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args)
		} else if mLast := m[len(m)-1]; mLast != "" || m[3] != "" {
			return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args)
		}
		operand, err := parseFlowSpecBitmaskOperand(m)
		if err != nil {
			return nil, err
		}
		var value uint64
		for flag, name := range TCPFlagNameMap {
			if strings.Contains(m[4], name) {
				value |= uint64(flag)
			}
		}
		items = append(items, NewFlowSpecComponentItem(operand, value))
	}

	// Marks end-of-list bit
	items[argsLen-1].Op |= BITMASK_FLAG_OP_END

	return NewFlowSpecComponent(typ, items), nil
}

func flowSpecDscpParser(_ Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	args = normalizeFlowSpecOpValues(args)

	f := func(i uint64) error {
		if i < 64 { // 6 bits
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

func flowSpecFragmentParser(_ Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	// args: List of pairs of Operator and Fragment flags
	//
	// Example:
	// - is-fragment or last-fragment
	//   args := []string{"==is-fragment", "==last-fragment"}
	// - is-fragment and last-fragment (exact match)
	//   args := []string{"==is-fragment+last-fragment"}
	args = normalizeFlowSpecOpValues(args)
	if len(args) == 0 {
		return nil, fmt.Errorf("invalid argument for %s: %q", typ.String(), args)
	}

	argsLen := len(args)
	items := make([]*FlowSpecComponentItem, 0, argsLen)

	for _, arg := range args {
		m := _regexpFlowSpecFragment.FindStringSubmatch(arg)
		if len(m) < 4 {
			return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args)
		} else if mLast := m[len(m)-1]; mLast != "" {
			return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args)
		}
		operand, err := parseFlowSpecBitmaskOperand(m)
		if err != nil {
			return nil, err
		}
		var value uint64
		// Example:
		// m[3] = "first-fragment+last-fragment"
		for flag, name := range FragmentFlagNameMap {
			if strings.Contains(m[3], name) {
				value |= uint64(flag)
			}
		}
		items = append(items, NewFlowSpecComponentItem(operand, value))
	}

	// Marks end-of-list bit
	items[argsLen-1].Op |= BITMASK_FLAG_OP_END

	return NewFlowSpecComponent(typ, items), nil
}

func flowSpecLabelParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	if rf.Afi() == AFI_IP {
		return nil, fmt.Errorf("%s is not supported for ipv4", typ.String())
	}

	args = normalizeFlowSpecOpValues(args)

	f := func(i uint64) error {
		if i <= 0xfffff { // 20 bits
			return nil
		}
		return errors.New("flow label range exceeded")
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

func flowSpecEtherTypeParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	// args: List of pairs of Operator and Ether Types
	//
	// Example:
	// - ARP or IPv4
	//   args := []string{"==arp", "==ipv4"}
	// - Not IPv4 and not IPv6
	//   args := []string{"!=ipv4", "&!=ipv6"}
	if rf != RF_FS_L2_VPN {
		return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String())
	}

	args = normalizeFlowSpecOpValues(args)
	s := strings.Join(args, " ")
	for i, name := range EthernetTypeNameMap {
		s = strings.ReplaceAll(s, name, fmt.Sprintf("%d", i))
	}
	args = strings.Split(s, " ")

	f := func(i uint64) error {
		if i <= 0xffff { // 2 bytes
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

func flowSpecMacParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	// args[0]: MAC address
	if rf != RF_FS_L2_VPN {
		return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String())
	}

	mac, err := net.ParseMAC(args[0])
	if err != nil {
		return nil, fmt.Errorf("invalid mac address: %s", args[0])
	}

	switch typ {
	case FLOW_SPEC_TYPE_DST_MAC:
		return NewFlowSpecDestinationMac(mac), nil
	case FLOW_SPEC_TYPE_SRC_MAC:
		return NewFlowSpecSourceMac(mac), nil
	}
	return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String())
}

func flowSpecLlcParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	if rf != RF_FS_L2_VPN {
		return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String())
	}

	return flowSpecNumeric1ByteParser(rf, typ, args)
}

func flowSpecSnapParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	if rf != RF_FS_L2_VPN {
		return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String())
	}

	args = normalizeFlowSpecOpValues(args)

	f := func(i uint64) error {
		if i <= 0xffffffffff { // 5 bytes
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

func flowSpecVlanIDParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	if rf != RF_FS_L2_VPN {
		return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String())
	}

	args = normalizeFlowSpecOpValues(args)
	s := strings.Join(args, " ")
	for i, name := range EthernetTypeNameMap {
		s = strings.ReplaceAll(s, name, fmt.Sprintf("%d", i))
	}
	args = strings.Split(s, " ")

	f := func(i uint64) error {
		if i <= 4095 { // 12 bits
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

func flowSpecVlanCosParser(rf Family, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) {
	if rf != RF_FS_L2_VPN {
		return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String())
	}

	args = normalizeFlowSpecOpValues(args)
	s := strings.Join(args, " ")
	for i, name := range EthernetTypeNameMap {
		s = strings.ReplaceAll(s, name, fmt.Sprintf("%d", i))
	}
	args = strings.Split(s, " ")

	f := func(i uint64) error {
		if i <= 7 { // 3 bits
			return nil
		}
		return fmt.Errorf("%s range exceeded", typ.String())
	}

	return parseFlowSpecNumericOpValues(typ, args, f)
}

var flowSpecParserMap = map[BGPFlowSpecType]func(Family, BGPFlowSpecType, []string) (FlowSpecComponentInterface, error){
	FLOW_SPEC_TYPE_DST_PREFIX:    flowSpecPrefixParser,
	FLOW_SPEC_TYPE_SRC_PREFIX:    flowSpecPrefixParser,
	FLOW_SPEC_TYPE_IP_PROTO:      flowSpecIpProtoParser,
	FLOW_SPEC_TYPE_PORT:          flowSpecNumeric2BytesParser,
	FLOW_SPEC_TYPE_DST_PORT:      flowSpecNumeric2BytesParser,
	FLOW_SPEC_TYPE_SRC_PORT:      flowSpecNumeric2BytesParser,
	FLOW_SPEC_TYPE_ICMP_TYPE:     flowSpecNumeric1ByteParser,
	FLOW_SPEC_TYPE_ICMP_CODE:     flowSpecNumeric1ByteParser,
	FLOW_SPEC_TYPE_TCP_FLAG:      flowSpecTcpFlagParser,
	FLOW_SPEC_TYPE_PKT_LEN:       flowSpecNumeric2BytesParser,
	FLOW_SPEC_TYPE_DSCP:          flowSpecDscpParser,
	FLOW_SPEC_TYPE_FRAGMENT:      flowSpecFragmentParser,
	FLOW_SPEC_TYPE_LABEL:         flowSpecLabelParser,
	FLOW_SPEC_TYPE_ETHERNET_TYPE: flowSpecEtherTypeParser,
	FLOW_SPEC_TYPE_DST_MAC:       flowSpecMacParser,
	FLOW_SPEC_TYPE_SRC_MAC:       flowSpecMacParser,
	FLOW_SPEC_TYPE_LLC_DSAP:      flowSpecLlcParser,
	FLOW_SPEC_TYPE_LLC_SSAP:      flowSpecLlcParser,
	FLOW_SPEC_TYPE_LLC_CONTROL:   flowSpecLlcParser,
	FLOW_SPEC_TYPE_SNAP:          flowSpecSnapParser,
	FLOW_SPEC_TYPE_VID:           flowSpecVlanIDParser,
	FLOW_SPEC_TYPE_COS:           flowSpecVlanCosParser,
	FLOW_SPEC_TYPE_INNER_VID:     flowSpecVlanIDParser,
	FLOW_SPEC_TYPE_INNER_COS:     flowSpecVlanCosParser,
}

func extractFlowSpecArgs(args []string) map[BGPFlowSpecType][]string {
	m := make(map[BGPFlowSpecType][]string, len(FlowSpecValueMap))
	var typ BGPFlowSpecType
	for _, arg := range args {
		if t, ok := FlowSpecValueMap[arg]; ok {
			typ = t
			m[typ] = make([]string, 0)
		} else {
			m[typ] = append(m[typ], arg)
		}
	}
	return m
}

func ParseFlowSpecComponents(rf Family, arg string) ([]FlowSpecComponentInterface, error) {
	switch rf.Safi() {
	case SAFI_FLOW_SPEC_UNICAST, SAFI_FLOW_SPEC_VPN:
		// Valid
	default:
		return nil, fmt.Errorf("invalid address family: %s", rf.String())
	}

	typeArgs := extractFlowSpecArgs(strings.Split(arg, " "))
	rules := make([]FlowSpecComponentInterface, 0, len(typeArgs))
	for typ, args := range typeArgs {
		parser, ok := flowSpecParserMap[typ]
		if !ok {
			return nil, fmt.Errorf("unsupported traffic filtering rule type: %s", typ.String())
		}
		if len(args) == 0 {
			return nil, fmt.Errorf("specify traffic filtering rules for %s", typ.String())
		}
		rule, err := parser(rf, typ, args)
		if err != nil {
			return nil, err
		}
		rules = append(rules, rule)
	}
	return rules, nil
}

func (t BGPFlowSpecType) String() string {
	name, ok := FlowSpecNameMap[t]
	if !ok {
		return fmt.Sprintf("%s(%d)", FlowSpecNameMap[FLOW_SPEC_TYPE_UNKNOWN], t)
	}
	return name
}

type FlowSpecComponentInterface interface {
	DecodeFromBytes([]byte, ...*MarshallingOption) error
	Serialize(...*MarshallingOption) ([]byte, error)
	Len(...*MarshallingOption) int
	Type() BGPFlowSpecType
	String() string
}

type flowSpecPrefix struct {
	Prefix *IPAddrPrefix
	typ    BGPFlowSpecType
}

func (p *flowSpecPrefix) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 1 {
		return malformedAttrListErr("Parse flowSpecPrefix failed")
	}
	p.typ = BGPFlowSpecType(data[0])
	if p.Prefix == nil {
		return malformedAttrListErr("flowSpecPrefix: Prefix is nil")
	}
	return p.Prefix.decodeFromBytes(data[1:], net.IPv4len, options...)
}

func (p *flowSpecPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) {
	if p.Prefix == nil {
		return nil, malformedAttrListErr("flowSpecPrefix: Prefix is nil")
	}
	bbuf, err := p.Prefix.Serialize(options...)
	if err != nil {
		return nil, err
	}
	buf := make([]byte, 1+len(bbuf))
	buf[0] = byte(p.Type())
	copy(buf[1:], bbuf)
	return buf, nil
}

func (p *flowSpecPrefix) Len(options ...*MarshallingOption) int {
	buf, _ := p.Serialize(options...)
	return len(buf)
}

func (p *flowSpecPrefix) Type() BGPFlowSpecType {
	return p.typ
}

func (p *flowSpecPrefix) String() string {
	return fmt.Sprintf("[%s: %s]", p.Type(), p.Prefix.String())
}

func (p *flowSpecPrefix) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPFlowSpecType `json:"type"`
		Value *IPAddrPrefix   `json:"value"`
	}{
		Type:  p.Type(),
		Value: p.Prefix,
	})
}

type flowSpecPrefix6 struct {
	Prefix *IPAddrPrefix
	Offset uint8
	typ    BGPFlowSpecType
}

// draft-ietf-idr-flow-spec-v6-06
// <type (1 octet), prefix length (1 octet), prefix offset(1 octet), prefix>
func (p *flowSpecPrefix6) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 3 {
		return malformedAttrListErr("not all flowSpecPrefix6 bits available")
	}
	p.typ = BGPFlowSpecType(data[0])
	p.Offset = data[2]
	prefix := append([]byte{data[1]}, data[3:]...)
	if p.Prefix == nil {
		return malformedAttrListErr("flowSpecPrefix6: Prefix is nil")
	}
	return p.Prefix.decodeFromBytes(prefix, net.IPv6len, options...)
}

func (p *flowSpecPrefix6) Serialize(options ...*MarshallingOption) ([]byte, error) {
	if p.Prefix == nil {
		return nil, malformedAttrListErr("flowSpecPrefix6: Prefix is nil")
	}
	bbuf, err := p.Prefix.Serialize(options...)
	if err != nil {
		return nil, err
	}
	buf := []byte{byte(p.Type())}
	buf = append(buf, bbuf[0])
	buf = append(buf, p.Offset)
	return append(buf, bbuf[1:]...), nil
}

func (p *flowSpecPrefix6) Len(options ...*MarshallingOption) int {
	buf, _ := p.Serialize(options...)
	return len(buf)
}

func (p *flowSpecPrefix6) Type() BGPFlowSpecType {
	return p.typ
}

func (p *flowSpecPrefix6) String() string {
	return fmt.Sprintf("[%s: %s/%d]", p.Type(), p.Prefix.String(), p.Offset)
}

func (p *flowSpecPrefix6) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type   BGPFlowSpecType `json:"type"`
		Value  *IPAddrPrefix   `json:"value"`
		Offset uint8           `json:"offset"`
	}{
		Type:   p.Type(),
		Value:  p.Prefix,
		Offset: p.Offset,
	})
}

type FlowSpecDestinationPrefix struct {
	flowSpecPrefix
}

func NewFlowSpecDestinationPrefix(prefix *IPAddrPrefix) *FlowSpecDestinationPrefix {
	return &FlowSpecDestinationPrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_DST_PREFIX}}
}

type FlowSpecSourcePrefix struct {
	flowSpecPrefix
}

func NewFlowSpecSourcePrefix(prefix *IPAddrPrefix) *FlowSpecSourcePrefix {
	return &FlowSpecSourcePrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_SRC_PREFIX}}
}

type FlowSpecDestinationPrefix6 struct {
	flowSpecPrefix6
}

func NewFlowSpecDestinationPrefix6(prefix *IPAddrPrefix, offset uint8) *FlowSpecDestinationPrefix6 {
	return &FlowSpecDestinationPrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_DST_PREFIX}}
}

type FlowSpecSourcePrefix6 struct {
	flowSpecPrefix6
}

func NewFlowSpecSourcePrefix6(prefix *IPAddrPrefix, offset uint8) *FlowSpecSourcePrefix6 {
	return &FlowSpecSourcePrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_SRC_PREFIX}}
}

type flowSpecMac struct {
	Mac net.HardwareAddr
	typ BGPFlowSpecType
}

func (p *flowSpecMac) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 2 || len(data) < 2+int(data[1]) {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all mac bits available")
	}
	p.typ = BGPFlowSpecType(data[0])
	p.Mac = net.HardwareAddr(data[2 : 2+int(data[1])])
	return nil
}

func (p *flowSpecMac) Serialize(options ...*MarshallingOption) ([]byte, error) {
	if len(p.Mac) == 0 {
		return nil, errors.New("mac unset")
	}
	buf := make([]byte, 2+len(p.Mac))
	buf[0] = byte(p.Type())
	buf[1] = byte(len(p.Mac))
	copy(buf[2:], p.Mac)
	return buf, nil
}

func (p *flowSpecMac) Len(options ...*MarshallingOption) int {
	return 2 + len(p.Mac)
}

func (p *flowSpecMac) Type() BGPFlowSpecType {
	return p.typ
}

func (p *flowSpecMac) String() string {
	return fmt.Sprintf("[%s: %s]", p.Type(), p.Mac.String())
}

func (p *flowSpecMac) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPFlowSpecType `json:"type"`
		Value string          `json:"value"`
	}{
		Type:  p.Type(),
		Value: p.Mac.String(),
	})
}

type FlowSpecSourceMac struct {
	flowSpecMac
}

func NewFlowSpecSourceMac(mac net.HardwareAddr) *FlowSpecSourceMac {
	return &FlowSpecSourceMac{flowSpecMac{Mac: mac, typ: FLOW_SPEC_TYPE_SRC_MAC}}
}

type FlowSpecDestinationMac struct {
	flowSpecMac
}

func NewFlowSpecDestinationMac(mac net.HardwareAddr) *FlowSpecDestinationMac {
	return &FlowSpecDestinationMac{flowSpecMac{Mac: mac, typ: FLOW_SPEC_TYPE_DST_MAC}}
}

type FlowSpecComponentItem struct {
	Op    uint8  `json:"op"`
	Value uint64 `json:"value"`
}

func (v *FlowSpecComponentItem) Len() int {
	return 1 << (uint32(v.Op) >> 4 & 0x3)
}

func (v *FlowSpecComponentItem) Serialize() ([]byte, error) {
	order := uint32(math.Log2(float64(v.Len())))
	buf := make([]byte, 1+1<<order)
	buf[0] = byte(uint32(v.Op) | order<<4)
	switch order {
	case 0:
		buf[1] = byte(v.Value)
	case 1:
		binary.BigEndian.PutUint16(buf[1:], uint16(v.Value))
	case 2:
		binary.BigEndian.PutUint32(buf[1:], uint32(v.Value))
	case 3:
		binary.BigEndian.PutUint64(buf[1:], v.Value)
	default:
		return nil, fmt.Errorf("invalid value size(too big): %d", v.Value)
	}
	return buf, nil
}

func NewFlowSpecComponentItem(op uint8, value uint64) *FlowSpecComponentItem {
	v := &FlowSpecComponentItem{op, value}
	order := uint32(math.Log2(float64(v.Len())))
	// we don't know if not initialized properly or initialized to
	// zero...
	if order == 0 {
		order = func() uint32 {
			for i := range 3 {
				if v.Value < 1<<(1<<uint(i)*8) {
					return uint32(i)
				}
			}
			// Return 8 octet order
			return 3
		}()
	}
	v.Op = uint8(uint32(v.Op) | order<<4)
	return v
}

type FlowSpecComponent struct {
	Items []*FlowSpecComponentItem
	typ   BGPFlowSpecType
}

func (p *FlowSpecComponent) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 1 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all flowspec component bytes available")
	}
	p.typ = BGPFlowSpecType(data[0])
	data = data[1:]
	p.Items = make([]*FlowSpecComponentItem, 0)
	for {
		if len(data) < 2 {
			return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all flowspec component bytes available")
		}
		op := data[0]
		end := op & 0x80
		l := 1 << (op >> 4 & 0x3) // (min, max) = (1, 8)
		v := make([]byte, 8)
		if len(data) < 1+l {
			return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all flowspec component bytes available")
		}
		copy(v[8-l:], data[1:1+l])
		i := binary.BigEndian.Uint64(v)
		item := &FlowSpecComponentItem{op, i}
		p.Items = append(p.Items, item)
		if end > 0 {
			break
		}
		data = data[1+l:]
	}
	return nil
}

func (p *FlowSpecComponent) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := []byte{byte(p.Type())}
	for _, v := range p.Items {
		bbuf, err := v.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	return buf, nil
}

func (p *FlowSpecComponent) Len(options ...*MarshallingOption) int {
	l := 1
	for _, item := range p.Items {
		l += item.Len() + 1
	}
	return l
}

func (p *FlowSpecComponent) Type() BGPFlowSpecType {
	return p.typ
}

func formatRaw(op uint8, value uint64) string {
	return fmt.Sprintf("op:%b,value:%d", op, value)
}

func formatNumeric(op uint8, value uint64) string {
	cmpFlag := DECNumOp(op & 0x7) // lower 3 bits
	if cmpFlag == DEC_NUM_OP_TRUE || cmpFlag == DEC_NUM_OP_FALSE {
		// Omit value field
		return DECNumOp(op).String()
	}
	return DECNumOp(op).String() + strconv.FormatUint(value, 10)
}

func formatProto(op uint8, value uint64) string {
	cmpFlag := DECNumOp(op & 0x7) // lower 3 bits
	if cmpFlag == DEC_NUM_OP_TRUE || cmpFlag == DEC_NUM_OP_FALSE {
		// Omit value field
		return DECNumOp(op).String()
	}
	return DECNumOp(op).String() + Protocol(value).String()
}

func formatTCPFlag(op uint8, value uint64) string {
	return BitmaskFlagOp(op).String() + TCPFlag(value).String()
}

func formatFragment(op uint8, value uint64) string {
	return BitmaskFlagOp(op).String() + FragmentFlag(value).String()
}

func formatEtherType(op uint8, value uint64) string {
	cmpFlag := DECNumOp(op & 0x7) // lower 3 bits
	if cmpFlag == DEC_NUM_OP_TRUE || cmpFlag == DEC_NUM_OP_FALSE {
		// Omit value field
		return DECNumOp(op).String()
	}
	return DECNumOp(op).String() + EthernetType(value).String()
}

var flowSpecFormatMap = map[BGPFlowSpecType]func(op uint8, value uint64) string{
	FLOW_SPEC_TYPE_UNKNOWN:       formatRaw,
	FLOW_SPEC_TYPE_IP_PROTO:      formatProto,
	FLOW_SPEC_TYPE_PORT:          formatNumeric,
	FLOW_SPEC_TYPE_DST_PORT:      formatNumeric,
	FLOW_SPEC_TYPE_SRC_PORT:      formatNumeric,
	FLOW_SPEC_TYPE_ICMP_TYPE:     formatNumeric,
	FLOW_SPEC_TYPE_ICMP_CODE:     formatNumeric,
	FLOW_SPEC_TYPE_TCP_FLAG:      formatTCPFlag,
	FLOW_SPEC_TYPE_PKT_LEN:       formatNumeric,
	FLOW_SPEC_TYPE_DSCP:          formatNumeric,
	FLOW_SPEC_TYPE_FRAGMENT:      formatFragment,
	FLOW_SPEC_TYPE_LABEL:         formatNumeric,
	FLOW_SPEC_TYPE_ETHERNET_TYPE: formatEtherType,
	FLOW_SPEC_TYPE_LLC_DSAP:      formatNumeric,
	FLOW_SPEC_TYPE_LLC_SSAP:      formatNumeric,
	FLOW_SPEC_TYPE_LLC_CONTROL:   formatNumeric,
	FLOW_SPEC_TYPE_SNAP:          formatNumeric,
	FLOW_SPEC_TYPE_VID:           formatNumeric,
	FLOW_SPEC_TYPE_COS:           formatNumeric,
	FLOW_SPEC_TYPE_INNER_VID:     formatNumeric,
	FLOW_SPEC_TYPE_INNER_COS:     formatNumeric,
}

func (p *FlowSpecComponent) String() string {
	f := flowSpecFormatMap[FLOW_SPEC_TYPE_UNKNOWN]
	if _, ok := flowSpecFormatMap[p.typ]; ok {
		f = flowSpecFormatMap[p.typ]
	}

	items := make([]string, 0, len(p.Items))
	for _, i := range p.Items {
		items = append(items, f(i.Op, i.Value))
	}
	// Removes leading and tailing spaces
	value := strings.TrimSpace(strings.Join(items, ""))

	return fmt.Sprintf("[%s: %s]", p.typ, value)
}

func (p *FlowSpecComponent) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPFlowSpecType          `json:"type"`
		Value []*FlowSpecComponentItem `json:"value"`
	}{
		Type:  p.Type(),
		Value: p.Items,
	})
}

func NewFlowSpecComponent(typ BGPFlowSpecType, items []*FlowSpecComponentItem) *FlowSpecComponent {
	// Set end-of-list bit on the last item and unset them on the others.
	for i, v := range items {
		if i == len(items)-1 {
			v.Op |= 0x80
		} else {
			v.Op &^= 0x80
		}
	}
	return &FlowSpecComponent{
		Items: items,
		typ:   typ,
	}
}

type FlowSpecUnknown struct {
	Value []byte
}

func (p *FlowSpecUnknown) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	p.Value = data
	return nil
}

func (p *FlowSpecUnknown) Serialize(options ...*MarshallingOption) ([]byte, error) {
	return p.Value, nil
}

func (p *FlowSpecUnknown) Len(options ...*MarshallingOption) int {
	return len(p.Value)
}

func (p *FlowSpecUnknown) Type() BGPFlowSpecType {
	if len(p.Value) > 0 {
		return BGPFlowSpecType(p.Value[0])
	}
	return FLOW_SPEC_TYPE_UNKNOWN
}

func (p *FlowSpecUnknown) String() string {
	return fmt.Sprintf("[unknown:%v]", p.Value)
}

func (p *FlowSpecUnknown) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPFlowSpecType `json:"type"`
		Value string          `json:"value"`
	}{
		Type:  p.Type(),
		Value: string(p.Value),
	})
}

type FlowSpecNLRI struct {
	Value []FlowSpecComponentInterface
	rf    Family
	rd    RouteDistinguisherInterface
}

func (n *FlowSpecNLRI) Flat() map[string]string {
	return map[string]string{}
}

func (n *FlowSpecNLRI) RD() RouteDistinguisherInterface {
	return n.rd
}

func (n *FlowSpecNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 1 {
		return malformedAttrListErr("not all flowspec component bytes available")
	}
	var length int
	if data[0]>>4 == 0xf && len(data) > 2 {
		length = int(binary.BigEndian.Uint16(data[:2]))
		data = data[2:]
	} else if len(data) > 1 {
		length = int(data[0])
		data = data[1:]
	} else {
		return malformedAttrListErr("not all flowspec component bytes available")
	}
	if len(data) < length {
		return malformedAttrListErr("not all flowspec component bytes available")
	}

	if n.rf.Safi() == SAFI_FLOW_SPEC_VPN {
		if length < 8 {
			return malformedAttrListErr("not all flowspec component bytes available")
		}
		n.rd = GetRouteDistinguisher(data[:8])
		data = data[8:]
		length -= 8
	}

	for l := length; l > 0; {
		if len(data) == 0 {
			return malformedAttrListErr("not all flowspec component bytes available")
		}
		t := BGPFlowSpecType(data[0])
		var i FlowSpecComponentInterface
		switch t {
		case FLOW_SPEC_TYPE_DST_PREFIX:
			switch n.rf >> 16 {
			case AFI_IP:
				ipPrefix, _ := NewIPAddrPrefix(netip.MustParsePrefix("0.0.0.0/0"))
				i = NewFlowSpecDestinationPrefix(ipPrefix)
			case AFI_IP6:
				i = NewFlowSpecDestinationPrefix6(&IPAddrPrefix{}, 0)
			default:
				return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", n.rf))
			}
		case FLOW_SPEC_TYPE_SRC_PREFIX:
			switch n.rf >> 16 {
			case AFI_IP:
				ipPrefix, _ := NewIPAddrPrefix(netip.MustParsePrefix("0.0.0.0/0"))
				i = NewFlowSpecSourcePrefix(ipPrefix)
			case AFI_IP6:
				i = NewFlowSpecSourcePrefix6(&IPAddrPrefix{}, 0)
			default:
				return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", n.rf))
			}
		case FLOW_SPEC_TYPE_SRC_MAC:
			switch n.rf {
			case RF_FS_L2_VPN:
				i = NewFlowSpecSourceMac(nil)
			default:
				return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", n.rf))
			}
		case FLOW_SPEC_TYPE_DST_MAC:
			switch n.rf {
			case RF_FS_L2_VPN:
				i = NewFlowSpecDestinationMac(nil)
			default:
				return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid address family: %v", n.rf))
			}
		case FLOW_SPEC_TYPE_IP_PROTO, FLOW_SPEC_TYPE_PORT, FLOW_SPEC_TYPE_DST_PORT, FLOW_SPEC_TYPE_SRC_PORT,
			FLOW_SPEC_TYPE_ICMP_TYPE, FLOW_SPEC_TYPE_ICMP_CODE, FLOW_SPEC_TYPE_TCP_FLAG, FLOW_SPEC_TYPE_PKT_LEN,
			FLOW_SPEC_TYPE_DSCP, FLOW_SPEC_TYPE_FRAGMENT, FLOW_SPEC_TYPE_LABEL, FLOW_SPEC_TYPE_ETHERNET_TYPE,
			FLOW_SPEC_TYPE_LLC_DSAP, FLOW_SPEC_TYPE_LLC_SSAP, FLOW_SPEC_TYPE_LLC_CONTROL, FLOW_SPEC_TYPE_SNAP,
			FLOW_SPEC_TYPE_VID, FLOW_SPEC_TYPE_COS, FLOW_SPEC_TYPE_INNER_VID, FLOW_SPEC_TYPE_INNER_COS:
			i = NewFlowSpecComponent(t, nil)
		default:
			i = &FlowSpecUnknown{}
		}

		err := i.DecodeFromBytes(data, options...)
		if err != nil {
			i = &FlowSpecUnknown{data}
		}
		l -= i.Len(options...)
		data = data[i.Len(options...):]
		n.Value = append(n.Value, i)
	}

	// Sort Traffic Filtering Rules in types order to avoid the unordered rules
	// are determined different.
	sort.SliceStable(n.Value, func(i, j int) bool { return n.Value[i].Type() < n.Value[j].Type() })

	return nil
}

func (n *FlowSpecNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0, 32)
	if n.rf.Safi() == SAFI_FLOW_SPEC_VPN {
		if n.rd == nil {
			return nil, errors.New("RD is nil")
		}
		b, err := n.rd.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, b...)
	}
	for _, v := range n.Value {
		b, err := v.Serialize(options...)
		if err != nil {
			return nil, err
		}
		buf = append(buf, b...)
	}
	length := n.Len(options...)
	if length > 0xfff {
		return nil, fmt.Errorf("too large: %d", length)
	} else if length < 0xf0 {
		length -= 1
		buf = append([]byte{byte(length)}, buf...)
	} else {
		length -= 2
		b := make([]byte, 2)
		binary.BigEndian.PutUint16(buf, uint16(length))
		buf = append(b, buf...)
	}
	return buf, nil
}

func (n *FlowSpecNLRI) Len(options ...*MarshallingOption) int {
	l := 0
	if n.rf.Safi() == SAFI_FLOW_SPEC_VPN {
		l += n.RD().Len()
	}
	for _, v := range n.Value {
		l += v.Len(options...)
	}
	if l < 0xf0 {
		return l + 1
	} else {
		return l + 2
	}
}

func (n *FlowSpecNLRI) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	if n.rf.Safi() == SAFI_FLOW_SPEC_VPN {
		buf.WriteString("[rd: ")
		buf.WriteString(n.rd.String())
		buf.WriteString("]")
	}
	for _, v := range n.Value {
		buf.WriteString(v.String())
	}
	return buf.String()
}

func (n *FlowSpecNLRI) MarshalJSON() ([]byte, error) {
	if n.rd != nil {
		return json.Marshal(struct {
			RD    RouteDistinguisherInterface  `json:"rd"`
			Value []FlowSpecComponentInterface `json:"value"`
		}{
			RD:    n.rd,
			Value: n.Value,
		})
	}
	return json.Marshal(struct {
		Value []FlowSpecComponentInterface `json:"value"`
	}{
		Value: n.Value,
	})
}

// CompareFlowSpecNLRI(n, m) returns
// -1 when m has precedence
//
//	0 when n and m have same precedence
//	1 when n has precedence
func CompareFlowSpecNLRI(n, m *FlowSpecNLRI) (int, error) {
	if n.rf != m.rf {
		return 0, errors.New("address family mismatch")
	}
	longer := n.Value
	shorter := m.Value
	invert := 1
	if n.rf.Safi() == SAFI_FLOW_SPEC_VPN {
		k, _ := n.Serialize()
		l, _ := m.Serialize()
		if result := bytes.Compare(k, l); result != 0 {
			return result, nil
		}
	}
	if len(n.Value) < len(m.Value) {
		longer = m.Value
		shorter = n.Value
		invert = -1
	}
	for idx, v := range longer {
		if len(shorter) < idx+1 {
			return invert, nil
		}
		w := shorter[idx]
		if v.Type() < w.Type() {
			return invert, nil
		} else if v.Type() > w.Type() {
			return invert * -1, nil
		} else if v.Type() == FLOW_SPEC_TYPE_DST_PREFIX || v.Type() == FLOW_SPEC_TYPE_SRC_PREFIX {
			// RFC5575 5.1
			//
			// For IP prefix values (IP destination and source prefix) precedence is
			// given to the lowest IP value of the common prefix length; if the
			// common prefix is equal, then the most specific prefix has precedence.
			var p, q *netip.Prefix
			var pCommon, qCommon uint64
			if n.rf.Afi() == AFI_IP {
				if v.Type() == FLOW_SPEC_TYPE_DST_PREFIX {
					p = &v.(*FlowSpecDestinationPrefix).Prefix.Prefix
					q = &w.(*FlowSpecDestinationPrefix).Prefix.Prefix
				} else {
					p = &v.(*FlowSpecSourcePrefix).Prefix.Prefix
					q = &w.(*FlowSpecSourcePrefix).Prefix.Prefix
				}
				min := p.Bits()
				if q.Bits() < p.Bits() {
					min = q.Bits()
				}
				pCommon = uint64(binary.BigEndian.Uint32(p.Addr().AsSlice()) >> (32 - min))
				qCommon = uint64(binary.BigEndian.Uint32(q.Addr().AsSlice()) >> (32 - min))
			} else if n.rf.Afi() == AFI_IP6 {
				if v.Type() == FLOW_SPEC_TYPE_DST_PREFIX {
					p = &v.(*FlowSpecDestinationPrefix6).Prefix.Prefix
					q = &w.(*FlowSpecDestinationPrefix6).Prefix.Prefix
				} else {
					p = &v.(*FlowSpecSourcePrefix6).Prefix.Prefix
					q = &w.(*FlowSpecSourcePrefix6).Prefix.Prefix
				}
				min := uint(p.Bits())
				if q.Bits() < p.Bits() {
					min = uint(q.Bits())
				}
				var mask uint
				if min-64 > 0 {
					mask = min - 64
				}
				pCommon = binary.BigEndian.Uint64(p.Addr().AsSlice()[:8]) >> mask
				qCommon = binary.BigEndian.Uint64(q.Addr().AsSlice()[:8]) >> mask
				if pCommon == qCommon && mask == 0 {
					mask = 64 - min
					pCommon = binary.BigEndian.Uint64(p.Addr().AsSlice()[8:]) >> mask
					qCommon = binary.BigEndian.Uint64(q.Addr().AsSlice()[8:]) >> mask
				}
			}

			if pCommon < qCommon {
				return invert, nil
			} else if pCommon > qCommon {
				return invert * -1, nil
			} else if p.Bits() > q.Bits() {
				return invert, nil
			} else if p.Bits() < q.Bits() {
				return invert * -1, nil
			}
		} else {
			// RFC5575 5.1
			//
			// For all other component types, unless otherwise specified, the
			// comparison is performed by comparing the component data as a binary
			// string using the memcmp() function as defined by the ISO C standard.
			// For strings of different lengths, the common prefix is compared.  If
			// equal, the longest string is considered to have higher precedence
			// than the shorter one.
			p, _ := v.Serialize()
			q, _ := w.Serialize()
			min := len(p)
			if len(q) < len(p) {
				min = len(q)
			}
			if result := bytes.Compare(p[:min], q[:min]); result < 0 {
				return invert, nil
			} else if result > 0 {
				return invert * -1, nil
			} else if len(p) > len(q) {
				return invert, nil
			} else if len(q) > len(p) {
				return invert * -1, nil
			}
		}
	}
	return 0, nil
}

func NewFlowSpecUnicast(family Family, value []FlowSpecComponentInterface) (*FlowSpecNLRI, error) {
	switch family {
	case RF_FS_IPv4_UC, RF_FS_IPv6_UC:
		// ok
	default:
		return nil, fmt.Errorf("invalid family: %v", family)
	}
	sort.SliceStable(value, func(i, j int) bool { return value[i].Type() < value[j].Type() })
	return &FlowSpecNLRI{
		Value: value,
		rf:    family,
	}, nil
}

func NewFlowSpecVPN(family Family, rd RouteDistinguisherInterface, value []FlowSpecComponentInterface) (*FlowSpecNLRI, error) {
	switch family {
	case RF_FS_IPv4_VPN, RF_FS_IPv6_VPN, RF_FS_L2_VPN:
		// ok
	default:
		return nil, fmt.Errorf("invalid family: %v", rd)
	}
	sort.SliceStable(value, func(i, j int) bool { return value[i].Type() < value[j].Type() })
	return &FlowSpecNLRI{
		Value: value,
		rf:    family,
		rd:    rd,
	}, nil
}

type OpaqueNLRI struct {
	Length uint16
	Key    []byte
	Value  []byte
}

func (n *OpaqueNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 2 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all OpaqueNLRI bytes available")
	}
	n.Length = binary.BigEndian.Uint16(data[:2])
	if len(data) < 2+int(n.Length) {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all OpaqueNLRI bytes available")
	}
	n.Key = data[2 : 2+int(n.Length)]
	n.Value = data[2+int(n.Length):]
	return nil
}

func (n *OpaqueNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
	keyLen := len(n.Key)
	if keyLen > math.MaxUint16 {
		return nil, errors.New("key length too big")
	}
	buf := make([]byte, 2, 2+keyLen+len(n.Value))
	binary.BigEndian.PutUint16(buf[:2], uint16(keyLen))
	buf = append(buf, n.Key...)
	buf = append(buf, n.Value...)
	return buf, nil
}

func (n *OpaqueNLRI) Len(options ...*MarshallingOption) int {
	return 2 + len(n.Key) + len(n.Value)
}

func (n *OpaqueNLRI) String() string {
	return string(n.Key)
}

func (n *OpaqueNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Key   string `json:"key"`
		Value string `json:"value"`
	}{
		Key:   string(n.Key),
		Value: string(n.Value),
	})
}

func NewOpaqueNLRI(key, value []byte) *OpaqueNLRI {
	return &OpaqueNLRI{
		Key:   key,
		Value: value,
	}
}

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
	LS_NLRI_TYPE_SR_POLICY_CANDIDATE_PATH
	LS_NLRI_TYPE_SRV6_SID
)

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
	LS_PROTOCOL_BGP
)

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"
	case LS_PROTOCOL_BGP:
		return "BGP"
	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 err
	}

	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, errors.New("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 *netip.Addr
	NeighborAddrIPv4  *netip.Addr
	InterfaceAddrIPv6 *netip.Addr
	NeighborAddrIPv6  *netip.Addr
	MultiTopoIDs      map[uint16]struct{}
}

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

		case *LsTLVMultiTopoID:
			if l.MultiTopoIDs == nil {
				l.MultiTopoIDs = make(map[uint16]struct{}, len(v.MultiTopoIDs))
			}

			for _, id := range v.MultiTopoIDs {
				l.MultiTopoIDs[id] = struct{}{}
			}
		}
	}
}

func (l *LsLinkDescriptor) String() string {
	var base string
	switch {
	case l.InterfaceAddrIPv4 != nil && l.NeighborAddrIPv4 != nil:
		base = fmt.Sprintf("%v->%v", l.InterfaceAddrIPv4, l.NeighborAddrIPv4)

	case l.InterfaceAddrIPv6 != nil && l.NeighborAddrIPv6 != nil:
		base = fmt.Sprintf("%v->%v", l.InterfaceAddrIPv6, l.NeighborAddrIPv6)

	case l.LinkLocalID != nil && l.LinkRemoteID != nil:
		base = fmt.Sprintf("%v->%v", *l.LinkLocalID, *l.LinkRemoteID)

	case l.InterfaceAddrIPv4 != nil:
		base = fmt.Sprintf("%v->UNKNOWN", l.InterfaceAddrIPv4)
	case l.NeighborAddrIPv4 != nil:
		base = fmt.Sprintf("UNKNOWN->%v", l.NeighborAddrIPv4)

	case l.InterfaceAddrIPv6 != nil:
		base = fmt.Sprintf("%v->UNKNOWN", l.InterfaceAddrIPv6)
	case l.NeighborAddrIPv6 != nil:
		base = fmt.Sprintf("UNKNOWN->%v", l.NeighborAddrIPv6)

	case l.LinkLocalID != nil:
		base = fmt.Sprintf("%v->UNKNOWN", *l.LinkLocalID)
	case l.LinkRemoteID != nil:
		base = fmt.Sprintf("UNKNOWN->%v", *l.LinkRemoteID)

	default:
		base = "UNKNOWN"
	}

	// The Link Local/Remote Identifier sub-TLV (Type 258, RFC 7752
	// Table 5) coexists with the interface/neighbor address sub-TLVs
	// in some NLRI shapes. RFC 9086 Section 5.2 mandates Type 258 on
	// PeerAdj-SID Link NLRIs and lets the producer also include the
	// IPv4 or IPv6 address sub-TLVs alongside. PeerNode-SID Link
	// NLRIs for the same eBGP session may share the addresses but
	// omit Type 258. The two NLRIs are distinct prefixes in
	// adj-RIB-in because of Type 258's presence, so the String form
	// must show the identifier whenever it is set or two distinct
	// prefixes collide in any caller keyed on this output.
	if l.LinkLocalID != nil && l.LinkRemoteID != nil &&
		(l.InterfaceAddrIPv4 != nil || l.NeighborAddrIPv4 != nil ||
			l.InterfaceAddrIPv6 != nil || l.NeighborAddrIPv6 != nil) {
		base += fmt.Sprintf("(L:%d,R:%d)", *l.LinkLocalID, *l.LinkRemoteID)
	}

	// MT-ID participates in the destination key (RFC 7752 Section
	// 3.2.1.5). Two ISIS-MT advertisements of the same physical link
	// must not collide in the RIB.
	return base + multiTopoIDsToString(l.MultiTopoIDs)
}

func NewLsLinkTLVs(ld *LsLinkDescriptor) []LsTLVInterface {
	tlvs := []LsTLVInterface{}

	if ld.LinkLocalID != nil && ld.LinkRemoteID != nil {
		tlvs = append(tlvs, &LsTLVLinkID{
			// https://tools.ietf.org/html/rfc5307#section-1.1
			LsTLV: LsTLV{
				Type:   LS_TLV_LINK_ID,
				Length: 8,
			},
			Local:  *ld.LinkLocalID,
			Remote: *ld.LinkRemoteID,
		})
	}

	if ld.InterfaceAddrIPv4 != nil {
		tlvs = append(tlvs, &LsTLVIPv4InterfaceAddr{
			LsTLV: LsTLV{
				Type:   LS_TLV_IPV4_INTERFACE_ADDR,
				Length: net.IPv4len,
			},
			IP: *ld.InterfaceAddrIPv4,
		})
	}
	if ld.NeighborAddrIPv4 != nil {
		tlvs = append(tlvs, &LsTLVIPv4NeighborAddr{
			LsTLV: LsTLV{
				Type:   LS_TLV_IPV4_NEIGHBOR_ADDR,
				Length: net.IPv4len,
			},
			IP: *ld.NeighborAddrIPv4,
		})
	}
	if ld.InterfaceAddrIPv6 != nil {
		tlvs = append(tlvs, &LsTLVIPv6InterfaceAddr{
			LsTLV: LsTLV{
				Type:   LS_TLV_IPV6_INTERFACE_ADDR,
				Length: net.IPv6len,
			},
			IP: *ld.InterfaceAddrIPv6,
		})
	}
	if ld.NeighborAddrIPv6 != nil {
		tlvs = append(tlvs, &LsTLVIPv6NeighborAddr{
			LsTLV: LsTLV{
				Type:   LS_TLV_IPV6_NEIGHBOR_ADDR,
				Length: net.IPv6len,
			},
			IP: *ld.NeighborAddrIPv6,
		})
	}

	return tlvs
}

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 }"
	}
	var local string
	var remote string
	if l.ProtocolID == LS_PROTOCOL_BGP {
		local = l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract().BGPRouterID.String()
		remote = l.RemoteNodeDesc.(*LsTLVNodeDescriptor).Extract().BGPRouterID.String()
	} else {
		local = l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract().IGPRouterID
		remote = l.RemoteNodeDesc.(*LsTLVNodeDescriptor).Extract().IGPRouterID
	}

	link := &LsLinkDescriptor{}
	link.ParseTLVs(l.LinkDesc)

	return fmt.Sprintf("LINK { LOCAL_NODE: %v REMOTE_NODE: %v LINK: %v}", local, remote, link)
}

func (l *LsLinkNLRI) DecodeFromBytes(data []byte) error {
	if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
		return err
	}

	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{}
		case LS_TLV_MULTI_TOPO_ID:
			subTLV = &LsTLVMultiTopoID{}

		default:
			if sub.Len() > len(tlv) {
				return malformedAttrListErr("sub-TLV length exceeds parent TLV length")
			}
			tlv = tlv[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, errors.New("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 []netip.Prefix
	OSPFRouteType  LsOspfRouteType
	MultiTopoIDs   map[uint16]struct{}
}

func (l *LsPrefixDescriptor) ParseTLVs(tlvs []LsTLVInterface, ipv6 bool) {
	for _, tlv := range tlvs {
		switch v := tlv.(type) {
		case *LsTLVIPReachability:
			l.IPReachability = append(l.IPReachability, v.toPrefix(ipv6))

		case *LsTLVOspfRouteType:
			l.OSPFRouteType = v.RouteType

		case *LsTLVMultiTopoID:
			if l.MultiTopoIDs == nil {
				l.MultiTopoIDs = make(map[uint16]struct{}, len(v.MultiTopoIDs))
			}

			for _, id := range v.MultiTopoIDs {
				l.MultiTopoIDs[id] = struct{}{}
			}
		}
	}
}

// MultiTopoIDsToString renders an MT-ID list for use in NLRI String() output.
// The empty case returns an empty string so callers can append it
// unconditionally without affecting the existing format.
func multiTopoIDsToString(ids map[uint16]struct{}) string {
	if len(ids) == 0 {
		return ""
	}

	mtIDs := make([]uint16, 0, len(ids))
	for id := range ids {
		mtIDs = append(mtIDs, id)
	}

	slices.Sort(mtIDs)

	return fmt.Sprintf(" MT: %v", mtIDs)
}

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 := make([]string, len(prefix.IPReachability))
	for i, ip := range prefix.IPReachability {
		ips[i] = 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: %s PREFIX: %v %s%s}", local.IGPRouterID, ips, ospf, multiTopoIDsToString(prefix.MultiTopoIDs))
}

func (l *LsPrefixV4NLRI) DecodeFromBytes(data []byte) error {
	if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
		return err
	}

	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{}
		case LS_TLV_MULTI_TOPO_ID:
			subTLV = &LsTLVMultiTopoID{}

		default:
			if sub.Len() > len(tlv) {
				return malformedAttrListErr("sub-TLV length exceeds parent TLV length")
			}
			tlv = tlv[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, errors.New("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,
	})
}

func NewLsPrefixTLVs(pd *LsPrefixDescriptor) []LsTLVInterface {
	lsTLVs := []LsTLVInterface{}
	for _, ipReach := range pd.IPReachability {
		prefixSize := ipReach.Bits()
		lenIpPrefix := (prefixSize-1)/8 + 1
		lenIpReach := uint16(lenIpPrefix + 1)
		var tlv *LsTLVIPReachability

		if ipReach.Addr().Is4() {
			ip := ipReach.Addr().AsSlice()
			tlv = &LsTLVIPReachability{
				LsTLV: LsTLV{
					Type:   LS_TLV_IP_REACH_INFO,
					Length: lenIpReach,
				},
				PrefixLength: uint8(prefixSize),
				Prefix:       ip[:lenIpPrefix],
			}
		} else if ipReach.Addr().Is6() {
			ip := ipReach.Addr().AsSlice()
			tlv = &LsTLVIPReachability{
				LsTLV: LsTLV{
					Type:   LS_TLV_IP_REACH_INFO,
					Length: lenIpReach,
				},
				PrefixLength: uint8(prefixSize),
				Prefix:       ip[:lenIpPrefix],
			}
		}
		lsTLVs = append(lsTLVs, tlv)
	}

	if pd.OSPFRouteType != 0 {
		lsTLVs = append(lsTLVs,
			&LsTLVOspfRouteType{
				LsTLV: LsTLV{
					Type:   LS_TLV_OSPF_ROUTE_TYPE,
					Length: 1,
				},
				RouteType: pd.OSPFRouteType,
			})
	}
	return lsTLVs
}

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%s}", local.IGPRouterID, ips, ospf, multiTopoIDsToString(prefix.MultiTopoIDs))
}

func (l *LsPrefixV6NLRI) DecodeFromBytes(data []byte) error {
	if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
		return err
	}

	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{}
		case LS_TLV_MULTI_TOPO_ID:
			subTLV = &LsTLVMultiTopoID{}

		default:
			if sub.Len() > len(tlv) {
				return malformedAttrListErr("sub-TLV length exceeds parent TLV length")
			}
			tlv = tlv[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, errors.New("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,
	})
}

// TODO: LsSrPolicyiCandidatePathNLRI

type LsTLVSrv6SIDInfo struct {
	LsTLV
	SIDs []netip.Addr
}

func (l *LsTLVSrv6SIDInfo) DecodeFromBytes(data []byte) error {
	sid, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	for i := range len(sid) / 16 {
		addr, _ := netip.AddrFromSlice(sid[i*16 : i*16+16])
		l.SIDs = append(l.SIDs, addr)
	}

	return nil
}

func (l *LsTLVSrv6SIDInfo) Serialize() ([]byte, error) {
	buf := []byte{}

	for _, sid := range l.SIDs {
		buf = append(buf, sid.AsSlice()...)
	}

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVSrv6SIDInfo) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type LsTLVType `json:"type"`
		SIDs []string  `json:"sids"`
	}{
		l.Type,
		l.Strings(),
	})
}

func (l *LsTLVSrv6SIDInfo) Strings() []string {
	sidStrings := []string{}
	for _, sid := range l.SIDs {
		sidStrings = append(sidStrings, sid.String())
	}

	return sidStrings
}

func (l *LsTLVSrv6SIDInfo) String() string {
	return fmt.Sprintf("{SIDs: %s}", strings.Join(l.Strings(), ", "))
}

func (l *LsTLVSrv6SIDInfo) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVMultiTopoID struct {
	LsTLV
	MultiTopoIDs []uint16
}

func (l *LsTLVMultiTopoID) DecodeFromBytes(data []byte) error {
	tlv, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	for i := 0; i < len(tlv); i += 2 {
		if i+2 > len(tlv) {
			return errors.New("TLV length mismatch")
		}

		multiTopoID := binary.BigEndian.Uint16(tlv[i:i+2]) & 0x0FFF
		l.MultiTopoIDs = append(l.MultiTopoIDs, multiTopoID)
	}
	return nil
}

func (l *LsTLVMultiTopoID) Serialize() ([]byte, error) {
	buf := []byte{}

	for _, multiTopoID := range l.MultiTopoIDs {
		bytes := make([]byte, 2)
		binary.BigEndian.PutUint16(bytes, multiTopoID)
		buf = append(buf, bytes...)
	}

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVMultiTopoID) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type         LsTLVType `json:"type"`
		MultiTopoIDs []uint16  `json:"multi_topo_ids"`
	}{
		l.Type,
		l.MultiTopoIDs,
	})
}

func (l *LsTLVMultiTopoID) Strings() []string {
	multiTopoIDs := []string{}
	for _, multiTopoID := range l.MultiTopoIDs {
		multiTopoIDs = append(multiTopoIDs, fmt.Sprintf("%d", multiTopoID))
	}

	return multiTopoIDs
}

func (l *LsTLVMultiTopoID) String() string {
	return fmt.Sprintf("{MultiTopoIDs: %s}", strings.Join(l.Strings(), ", "))
}

func (l *LsTLVMultiTopoID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsSrv6SIDNLRI struct {
	LsNLRI
	LocalNodeDesc LsTLVInterface
	MultiTopoID   LsTLVInterface
	Srv6SIDInfo   LsTLVInterface
}

func (l *LsSrv6SIDNLRI) String() string {
	local := l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract()
	srv6SID := l.Srv6SIDInfo.(*LsTLVSrv6SIDInfo)
	multiTopo := l.MultiTopoID.(*LsTLVMultiTopoID)

	return fmt.Sprintf("SRv6SID { LOCAL_NODE: %s SRv6_SID: %v MULTI_TOPO_IDs: %v}", local, srv6SID.String(), multiTopo.String())
}

func (l *LsSrv6SIDNLRI) DecodeFromBytes(data []byte) error {
	if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
		return err
	}

	tlvs := data[lsNLRIHdrLen:]
	m := make(map[LsTLVType]bool)

	for len(tlvs) >= tlvHdrLen {
		tlv := &LsTLV{}
		_, err := tlv.DecodeFromBytes(tlvs)
		if err != nil {
			return err
		}
		m[tlv.Type] = true

		var tLVInterface LsTLVInterface
		switch tlv.Type {
		case LS_TLV_LOCAL_NODE_DESC:
			tLVInterface = &LsTLVNodeDescriptor{}
		case LS_TLV_SRV6_SID_INFO:
			tLVInterface = &LsTLVSrv6SIDInfo{}
		case LS_TLV_MULTI_TOPO_ID:
			tLVInterface = &LsTLVMultiTopoID{}
		default:
			tlvs = tlvs[tlv.Len():]
			l.Length -= uint16(tlv.Len())
			continue
		}

		if err := tLVInterface.DecodeFromBytes(tlvs); err != nil {
			return err
		}
		tlvs = tlvs[tLVInterface.Len():]

		switch tlv.Type {
		case LS_TLV_LOCAL_NODE_DESC:
			l.LocalNodeDesc = tLVInterface
		case LS_TLV_SRV6_SID_INFO:
			l.Srv6SIDInfo = tLVInterface
		case LS_TLV_MULTI_TOPO_ID:
			l.MultiTopoID = tLVInterface
		}
	}

	required := []LsTLVType{LS_TLV_LOCAL_NODE_DESC, LS_TLV_SRV6_SID_INFO}
	for _, tlv := range required {
		if _, ok := m[tlv]; !ok {
			return malformedAttrListErr("Required TLV missing")
		}
	}

	return nil
}

func (l *LsSrv6SIDNLRI) Serialize() ([]byte, error) {
	if l.LocalNodeDesc == nil || l.Srv6SIDInfo == nil {
		return nil, errors.New("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.Srv6SIDInfo.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, s...)

	s, err = l.MultiTopoID.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, s...)

	return l.LsNLRI.Serialize(buf)
}

func (l *LsSrv6SIDNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type        LsNLRIType       `json:"type"`
		LocalNode   LsNodeDescriptor `json:"local_node_desc"`
		Srv6SID     LsTLVSrv6SIDInfo `json:"srv6_sid_info"`
		MultiTopoID LsTLVMultiTopoID `json:"multi_topo"`
	}{
		Type:        l.Type(),
		LocalNode:   *l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract(),
		Srv6SID:     *l.Srv6SIDInfo.(*LsTLVSrv6SIDInfo),
		MultiTopoID: *l.MultiTopoID.(*LsTLVMultiTopoID),
	})
}

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 // RFC9086
	LS_TLV_BGP_CONFEDERATION_MEMBER = 517 // RFC9086
	LS_TLV_SRV6_SID_INFO            = 518 // RFC9514

	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

	// Flexible Algorithm Definition (RFC9351 Section 3, IS-IS source RFC9350).
	// TLV 1039 is a Node Attribute TLV. 1040, 1041, 1042, 1043, 1045 and 1046
	// are sub-TLVs nested inside TLV 1039. TLV 1044 (FAPM) is a top-level
	// Prefix Attribute TLV; see LS_TLV_FAD_PREFIX_METRIC below.
	LS_TLV_FLEX_ALGO_DEF            = 1039 // RFC9351 Section 3
	LS_TLV_FAD_EXCLUDE_ANY_AFFINITY = 1040 // RFC9351 Section 3.1
	LS_TLV_FAD_INCLUDE_ANY_AFFINITY = 1041 // RFC9351 Section 3.2
	LS_TLV_FAD_INCLUDE_ALL_AFFINITY = 1042 // RFC9351 Section 3.3
	LS_TLV_FAD_DEFINITION_FLAGS     = 1043 // RFC9351 Section 3.4
	LS_TLV_FAD_PREFIX_METRIC        = 1044 // RFC9351 Section 4 (FAPM, Prefix-side)
	LS_TLV_FAD_EXCLUDE_SRLG         = 1045 // RFC9351 Section 3.5
	LS_TLV_FAD_UNSUPPORTED          = 1046 // RFC9351 Section 3.6

	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
	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 // RFC9086
	LS_TLV_PEER_ADJACENCY_SID       = 1102 // RFC9086
	LS_TLV_PEER_SET_SID             = 1103 // RFC9086

	LS_TLV_RTM_CAPABILITY = 1105 // RFC8169, TODO
	LS_TLV_SRV6_END_X_SID = 1106 // RFC9514

	LS_TLV_UNIDIRECTIONAL_LINK_DELAY         = 1114 // RFC8571
	LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY = 1115 // RFC8571
	LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION    = 1116 // RFC8571

	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

	LS_TLV_SRV6_ENDPOINT_BEHAVIOR = 1250 // RFC9514
	LS_TLV_SRV6_BGP_PEER_NODE_SID = 1251 // RFC9514
	LS_TLV_SRV6_SID_STRUCTURE     = 1252 // RFC9514
)

type LsTLVInterface interface {
	Len() int
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	String() string
	MarshalJSON() ([]byte, error)
	GetLsTLV() LsTLV
}

func NewLsAttributeTLVs(lsAttr *LsAttribute) []LsTLVInterface {
	tlvs := []LsTLVInterface{}

	if lsAttr.Node.Flags != nil {
		tlvs = append(tlvs, NewLsTLVNodeFlagbits(lsAttr.Node.Flags))
	}
	if lsAttr.Node.Opaque != nil {
		tlvs = append(tlvs, NewLsTLVOpaqueNodeAttr(lsAttr.Node.Opaque))
	}
	if lsAttr.Node.Name != nil {
		tlvs = append(tlvs, NewLsTLVNodeName(lsAttr.Node.Name))
	}
	if lsAttr.Node.IsisArea != nil {
		tlvs = append(tlvs, NewLsTLVIsisArea(lsAttr.Node.IsisArea))
	}
	if lsAttr.Node.LocalRouterID != nil {
		tlvs = append(tlvs, NewLsTLVLocalIPv4RouterID(lsAttr.Node.LocalRouterID))
	}
	if lsAttr.Node.LocalRouterIDv6 != nil {
		tlvs = append(tlvs, NewLsTLVLocalIPv6RouterID(lsAttr.Node.LocalRouterIDv6))
	}
	if lsAttr.Node.SrCapabilties != nil {
		tlvs = append(tlvs, NewLsTLVSrCapabilities(lsAttr.Node.SrCapabilties))
	}
	if lsAttr.Node.SrAlgorithms != nil {
		tlvs = append(tlvs, NewLsTLVSrAlgorithm(lsAttr.Node.SrAlgorithms))
	}
	if lsAttr.Node.SrLocalBlock != nil {
		tlvs = append(tlvs, NewLsTLVSrLocalBlock(lsAttr.Node.SrLocalBlock))
	}

	if lsAttr.Link.Name != nil {
		tlvs = append(tlvs, NewLsTLVLinkName(lsAttr.Link.Name))
	}
	if lsAttr.Link.LocalRouterID != nil {
		tlvs = append(tlvs, NewLsTLVLocalIPv4RouterID(lsAttr.Link.LocalRouterID))
	}
	if lsAttr.Link.LocalRouterIDv6 != nil {
		tlvs = append(tlvs, NewLsTLVLocalIPv6RouterID(lsAttr.Link.LocalRouterIDv6))
	}
	if lsAttr.Link.RemoteRouterID != nil {
		tlvs = append(tlvs, NewLsTLVRemoteIPv4RouterID(lsAttr.Link.RemoteRouterID))
	}
	if lsAttr.Link.RemoteRouterIDv6 != nil {
		tlvs = append(tlvs, NewLsTLVRemoteIPv6RouterID(lsAttr.Link.RemoteRouterIDv6))
	}
	if lsAttr.Link.AdminGroup != nil {
		tlvs = append(tlvs, NewLsTLVAdminGroup(lsAttr.Link.AdminGroup))
	}
	if lsAttr.Link.DefaultTEMetric != nil {
		tlvs = append(tlvs, NewLsTLVTEDefaultMetric(lsAttr.Link.DefaultTEMetric))
	}
	if lsAttr.Link.UnidirectionalLinkDelay != nil {
		tlvs = append(tlvs, NewLsTLVUnidirectionalLinkDelay(lsAttr.Link.UnidirectionalLinkDelay))
	}
	if lsAttr.Link.MinMaxUnidirectionalLinkDelay != nil {
		tlvs = append(tlvs, NewLsTLVMinMaxUnidirectionalLinkDelay(lsAttr.Link.MinMaxUnidirectionalLinkDelay))
	}
	if lsAttr.Link.UnidirectionalDelayVariation != nil {
		tlvs = append(tlvs, NewLsTLVUnidirectionalDelayVariation(lsAttr.Link.UnidirectionalDelayVariation))
	}
	if lsAttr.Link.IGPMetric != nil {
		tlvs = append(tlvs, NewLsTLVIGPMetric(lsAttr.Link.IGPMetric))
	}
	if lsAttr.Link.Opaque != nil {
		tlvs = append(tlvs, NewLsTLVOpaqueLinkAttr(lsAttr.Link.Opaque))
	}
	if lsAttr.Link.Bandwidth != nil {
		tlvs = append(tlvs, NewLsTLVMaxLinkBw(lsAttr.Link.Bandwidth))
	}
	if lsAttr.Link.ReservableBandwidth != nil {
		tlvs = append(tlvs, NewLsTLVMaxReservableLinkBw(lsAttr.Link.ReservableBandwidth))
	}
	if lsAttr.Link.UnreservedBandwidth != nil && *lsAttr.Link.UnreservedBandwidth != [8]float32{0, 0, 0, 0, 0, 0, 0, 0} {
		tlvs = append(tlvs, NewLsTLVUnreservedBw(lsAttr.Link.UnreservedBandwidth))
	}
	if lsAttr.Link.Srlgs != nil {
		tlvs = append(tlvs, NewLsTLVSrlg(lsAttr.Link.Srlgs))
	}
	if lsAttr.Link.SrAdjacencySID != nil {
		tlvs = append(tlvs, NewLsTLVAdjacencySID(lsAttr.Link.SrAdjacencySID))
	}
	if lsAttr.Link.Srv6EndXSID != nil && len(lsAttr.Link.Srv6EndXSID.SIDs) > 0 {
		tlvs = append(tlvs, NewLsTLVSrv6EndXSID(lsAttr.Link.Srv6EndXSID))
	}

	if lsAttr.Prefix.IGPFlags != nil {
		tlvs = append(tlvs, NewLsTLVIGPFlags(lsAttr.Prefix.IGPFlags))
	}
	if lsAttr.Prefix.Opaque != nil {
		tlvs = append(tlvs, NewLsTLVOpaquePrefixAttr(lsAttr.Prefix.Opaque))
	}
	if lsAttr.Prefix.SrPrefixSID != nil {
		tlvs = append(tlvs, NewLsTLVPrefixSID(lsAttr.Prefix.SrPrefixSID))
	}

	if lsAttr.BgpPeerSegment.BgpPeerNodeSid != nil {
		tlvs = append(tlvs, NewLsTLVPeerNodeSID(lsAttr.BgpPeerSegment.BgpPeerNodeSid))
	}
	if lsAttr.BgpPeerSegment.BgpPeerAdjacencySid != nil {
		tlvs = append(tlvs, NewLsTLVPeerAdjacencySID(lsAttr.BgpPeerSegment.BgpPeerAdjacencySid))
	}
	if lsAttr.BgpPeerSegment.BgpPeerSetSid != nil {
		tlvs = append(tlvs, NewLsTLVPeerSetSID(lsAttr.BgpPeerSegment.BgpPeerSetSid))
	}

	if lsAttr.Srv6SID.Srv6SIDStructure != nil {
		tlvs = append(tlvs, NewLsTLVSrv6SIDStructure(lsAttr.Srv6SID.Srv6SIDStructure))
	}

	if lsAttr.Srv6SID.Srv6BgpPeerNodeSID != nil {
		tlvs = append(tlvs, NewLsTLVSrv6BgpPeerNodeSID(lsAttr.Srv6SID.Srv6BgpPeerNodeSID))
	}

	if lsAttr.Srv6SID.Srv6EndpointBehavior != nil {
		tlvs = append(tlvs, NewLsTLVSrv6EndpointBehavior(lsAttr.Srv6SID.Srv6EndpointBehavior))
	}

	return tlvs
}

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+len(value))
	binary.BigEndian.PutUint16(buf[:2], uint16(l.Type))
	binary.BigEndian.PutUint16(buf[2:4], l.Length)
	copy(buf[4:], 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,
	})
}

func (l *LsTLVLinkID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIPv4InterfaceAddr struct {
	LsTLV
	IP netip.Addr
}

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")
	}

	ip, ok := netip.AddrFromSlice(value)
	if !ok {
		return malformedAttrListErr("Failed to parse IPv4 interface address")
	}
	l.IP = ip

	return nil
}

func (l *LsTLVIPv4InterfaceAddr) Serialize() ([]byte, error) {
	return l.LsTLV.Serialize(l.IP.AsSlice())
}

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),
	})
}

func (l *LsTLVIPv4InterfaceAddr) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIPv4NeighborAddr struct {
	LsTLV
	IP netip.Addr
}

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")
	}

	ip, ok := netip.AddrFromSlice(value)
	if !ok {
		return malformedAttrListErr("Failed to parse IPv4 neighbor address")
	}
	l.IP = ip

	return nil
}

func (l *LsTLVIPv4NeighborAddr) Serialize() ([]byte, error) {
	return l.LsTLV.Serialize(l.IP.AsSlice())
}

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),
	})
}

func (l *LsTLVIPv4NeighborAddr) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIPv6InterfaceAddr struct {
	LsTLV
	IP netip.Addr
}

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, _ = netip.AddrFromSlice(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.AsSlice())
}

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),
	})
}

func (l *LsTLVIPv6InterfaceAddr) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIPv6NeighborAddr struct {
	LsTLV
	IP netip.Addr
}

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, _ = netip.AddrFromSlice(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.AsSlice())
}

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),
	})
}

func (l *LsTLVIPv6NeighborAddr) GetLsTLV() LsTLV {
	return l.LsTLV
}

// 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 NewLsTLVNodeFlagbits(l *LsNodeFlags) *LsTLVNodeFlagBits {
	var flags uint8
	if l.Overload {
		flags = flags | 1<<7
	}
	if l.Attached {
		flags = flags | 1<<6
	}
	if l.External {
		flags = flags | 1<<5
	}
	if l.ABR {
		flags = flags | 1<<4
	}
	if l.Router {
		flags = flags | 1<<3
	}
	if l.V6 {
		flags = flags | 1<<2
	}
	return &LsTLVNodeFlagBits{
		LsTLV: LsTLV{
			Type:   LS_TLV_NODE_FLAG_BITS,
			Length: 1,
		},
		Flags: flags,
	}
}

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 := range len(flags) {
		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(),
	})
}

func (l *LsTLVNodeFlagBits) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVNodeName struct {
	LsTLV
	Name string
}

func NewLsTLVNodeName(l *string) *LsTLVNodeName {
	return &LsTLVNodeName{
		LsTLV: LsTLV{
			Type:   LS_TLV_NODE_NAME,
			Length: uint16(len(*l)),
		},
		Name: *l,
	}
}

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,
	})
}

func (l *LsTLVNodeName) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIsisArea struct {
	LsTLV
	Area []byte
}

func NewLsTLVIsisArea(l *[]byte) *LsTLVIsisArea {
	return &LsTLVIsisArea{
		LsTLV: LsTLV{
			Type:   LS_TLV_ISIS_AREA,
			Length: uint16(len(*l)),
		},
		Area: *l,
	}
}

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),
	})
}

func (l *LsTLVIsisArea) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVLocalIPv4RouterID struct {
	LsTLV
	IP netip.Addr
}

func NewLsTLVLocalIPv4RouterID(l *netip.Addr) *LsTLVLocalIPv4RouterID {
	return &LsTLVLocalIPv4RouterID{
		LsTLV: LsTLV{
			Type:   LS_TLV_IPV4_LOCAL_ROUTER_ID,
			Length: 4,
		},
		IP: *l,
	}
}

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, _ = netip.AddrFromSlice(value)

	return nil
}

func (l *LsTLVLocalIPv4RouterID) Serialize() ([]byte, error) {
	return l.LsTLV.Serialize(l.IP.AsSlice())
}

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),
	})
}

func (l *LsTLVLocalIPv4RouterID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVRemoteIPv4RouterID struct {
	LsTLV
	IP netip.Addr
}

func NewLsTLVRemoteIPv4RouterID(l *netip.Addr) *LsTLVRemoteIPv4RouterID {
	return &LsTLVRemoteIPv4RouterID{
		LsTLV: LsTLV{
			Type:   LS_TLV_IPV4_REMOTE_ROUTER_ID,
			Length: 4,
		},
		IP: *l,
	}
}

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, _ = netip.AddrFromSlice(value)

	return nil
}

func (l *LsTLVRemoteIPv4RouterID) Serialize() ([]byte, error) {
	return l.LsTLV.Serialize(l.IP.AsSlice())
}

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),
	})
}

func (l *LsTLVRemoteIPv4RouterID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVLocalIPv6RouterID struct {
	LsTLV
	IP netip.Addr
}

func NewLsTLVLocalIPv6RouterID(l *netip.Addr) *LsTLVLocalIPv6RouterID {
	return &LsTLVLocalIPv6RouterID{
		LsTLV: LsTLV{
			Type:   LS_TLV_IPV6_LOCAL_ROUTER_ID,
			Length: 0,
		},
		IP: *l,
	}
}

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, _ = netip.AddrFromSlice(value)

	return nil
}

func (l *LsTLVLocalIPv6RouterID) Serialize() ([]byte, error) {
	return l.LsTLV.Serialize(l.IP.AsSlice())
}

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),
	})
}

func (l *LsTLVLocalIPv6RouterID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVRemoteIPv6RouterID struct {
	LsTLV
	IP netip.Addr
}

func NewLsTLVRemoteIPv6RouterID(l *netip.Addr) *LsTLVRemoteIPv6RouterID {
	return &LsTLVRemoteIPv6RouterID{
		LsTLV: LsTLV{
			Type:   LS_TLV_IPV6_REMOTE_ROUTER_ID,
			Length: 4,
		},
		IP: *l,
	}
}

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, _ = netip.AddrFromSlice(value)

	return nil
}

func (l *LsTLVRemoteIPv6RouterID) Serialize() ([]byte, error) {
	return l.LsTLV.Serialize(l.IP.AsSlice())
}

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),
	})
}

func (l *LsTLVRemoteIPv6RouterID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVOpaqueNodeAttr struct {
	LsTLV
	Attr []byte
}

func NewLsTLVOpaqueNodeAttr(l *[]byte) *LsTLVOpaqueNodeAttr {
	return &LsTLVOpaqueNodeAttr{
		LsTLV: LsTLV{
			Type:   LS_TLV_OPAQUE_NODE_ATTR,
			Length: uint16(len(*l)),
		},
		Attr: *l,
	}
}

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),
	})
}

func (l *LsTLVOpaqueNodeAttr) GetLsTLV() LsTLV {
	return l.LsTLV
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], 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,
	})
}

func (l *LsTLVAutonomousSystem) GetLsTLV() LsTLV {
	return l.LsTLV
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], l.BGPLsID)

	return l.LsTLV.Serialize(buf[:4])
}

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,
	})
}

func (l *LsTLVBgpLsID) GetLsTLV() LsTLV {
	return l.LsTLV
}

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
	// 4, 6, 7, and 8 are the only valid values.
	switch len(value) {
	case 4, 6, 7, 8:
		break
	default:
		return malformedAttrListErr(fmt.Sprintf("Incorrect IGP Router ID length: %d", len(value)))
	}

	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),
	})
}

func (l *LsTLVIgpRouterID) GetLsTLV() LsTLV {
	return l.LsTLV
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], l.AreaID)

	return l.LsTLV.Serialize(buf[:4])
}

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,
	})
}

func (l *LsTLVOspfAreaID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVBgpRouterID struct {
	LsTLV
	RouterID netip.Addr
}

func (l *LsTLVBgpRouterID) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_BGP_ROUTER_ID {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://tools.ietf.org/html/rfc9086#section-4.1
	// 4 is the only valid value.
	if len(value) != 4 {
		return malformedAttrListErr(fmt.Sprintf("Incorrect BGP Router ID length: %d", len(value)))
	}

	l.RouterID = netip.AddrFrom4([4]byte(value))

	return nil
}

func (l *LsTLVBgpRouterID) Serialize() ([]byte, error) {
	tmpaddr := l.RouterID
	if tmpaddr.Is4() {
		var buf [4]byte
		copy(buf[:], l.RouterID.AsSlice())
		return l.LsTLV.Serialize(buf[:])
	}
	var buf [16]byte
	copy(buf[:], l.RouterID.AsSlice())
	return l.LsTLV.Serialize(buf[:])
}

func (l *LsTLVBgpRouterID) String() string {
	return fmt.Sprintf("{BGP Router ID: %v}", l.RouterID)
}

func (l *LsTLVBgpRouterID) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     LsTLVType `json:"type"`
		RouterID string    `json:"bgp_router_id"`
	}{
		Type:     l.Type,
		RouterID: fmt.Sprintf("%v", l.RouterID),
	})
}

func (l *LsTLVBgpRouterID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVBgpConfederationMember struct {
	LsTLV
	BgpConfederationMember uint32
}

func (l *LsTLVBgpConfederationMember) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_BGP_CONFEDERATION_MEMBER {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://tools.ietf.org/html/rfc9086#section-4.3
	// 4 is the only valid value.
	if len(value) != 4 {
		return malformedAttrListErr(fmt.Sprintf("Incorrect BGP Confederation Member length: %d", len(value)))
	}

	l.BgpConfederationMember = binary.BigEndian.Uint32(value)

	return nil
}

func (l *LsTLVBgpConfederationMember) Serialize() ([]byte, error) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], l.BgpConfederationMember)

	return l.LsTLV.Serialize(buf[:4])
}

func (l *LsTLVBgpConfederationMember) String() string {
	return fmt.Sprintf("{BGP Confederation Member: %d}", l.BgpConfederationMember)
}

func (l *LsTLVBgpConfederationMember) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type                   LsTLVType `json:"type"`
		BgpConfederationMember uint32    `json:"bgp_confederation_member"`
	}{
		Type:                   l.Type,
		BgpConfederationMember: l.BgpConfederationMember,
	})
}

func (l *LsTLVBgpConfederationMember) GetLsTLV() LsTLV {
	return l.LsTLV
}

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) {
	var buf [1]byte
	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(),
	})
}

func (l *LsTLVOspfRouteType) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIPReachability struct {
	LsTLV
	PrefixLength uint8
	Prefix       []byte
}

func (l *LsTLVIPReachability) toPrefix(ipv6 bool) netip.Prefix {
	b := make([]byte, 16)
	bytes := 0
	if l.PrefixLength > 0 {
		bytes = (int(l.PrefixLength)-1)/8 + 1
	}
	for i := range bytes {
		if i >= len(l.Prefix) {
			break
		}
		b[i] = l.Prefix[i]
	}

	ip := net.IPv4(b[0], b[1], b[2], b[3]).To4()
	if ipv6 {
		ip = net.IP(b).To16()
	}

	p, err := netip.ParsePrefix(fmt.Sprintf("%v/%v", ip, l.PrefixLength))
	if err != nil {
		return netip.Prefix{}
	}
	// backwards compatibility
	p = p.Masked()

	return p
}

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) < 1 {
		return malformedAttrListErr("Incorrect IP reachability Info length")
	}

	// https://tools.ietf.org/html/rfc7752#section-3.2.3.2
	// PrefixLength 0 is valid (default route 0.0.0.0/0 or ::/0).
	if value[0] > 128 {
		return malformedAttrListErr("Incorrect IP prefix length")
	}

	ll := 0
	if value[0] > 0 {
		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),
	})
}

func (l *LsTLVIPReachability) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVAdminGroup struct {
	LsTLV
	AdminGroup uint32
}

func NewLsTLVAdminGroup(l *uint32) *LsTLVAdminGroup {
	return &LsTLVAdminGroup{
		LsTLV: LsTLV{
			Type:   LS_TLV_ADMIN_GROUP,
			Length: 4,
		},
		AdminGroup: *l,
	}
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], 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),
	})
}

func (l *LsTLVAdminGroup) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVMaxLinkBw struct {
	LsTLV
	Bandwidth float32
}

func NewLsTLVMaxLinkBw(l *float32) *LsTLVMaxLinkBw {
	return &LsTLVMaxLinkBw{
		LsTLV: LsTLV{
			Type:   LS_TLV_MAX_LINK_BANDWIDTH,
			Length: 4,
		},
		Bandwidth: *l,
	}
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], 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,
	})
}

func (l *LsTLVMaxLinkBw) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVMaxReservableLinkBw struct {
	LsTLV
	Bandwidth float32
}

func NewLsTLVMaxReservableLinkBw(l *float32) *LsTLVMaxReservableLinkBw {
	return &LsTLVMaxReservableLinkBw{
		LsTLV: LsTLV{
			Type:   LS_TLV_MAX_RESERVABLE_BANDWIDTH,
			Length: 4,
		},
		Bandwidth: *l,
	}
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], 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,
	})
}

func (l *LsTLVMaxReservableLinkBw) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVUnreservedBw struct {
	LsTLV
	Bandwidth [8]float32
}

func NewLsTLVUnreservedBw(l *[8]float32) *LsTLVUnreservedBw {
	return &LsTLVUnreservedBw{
		LsTLV: LsTLV{
			Type:   LS_TLV_UNRESERVED_BANDWIDTH,
			Length: 32,
		},
		Bandwidth: *l,
	}
}

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 := range len(l.Bandwidth) {
		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, 4*len(l.Bandwidth))

	var b [4]byte
	for i := range len(l.Bandwidth) {
		binary.BigEndian.PutUint32(b[:4], 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,
	})
}

func (l *LsTLVUnreservedBw) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVTEDefaultMetric struct {
	LsTLV
	Metric uint32
}

func NewLsTLVTEDefaultMetric(l *uint32) *LsTLVTEDefaultMetric {
	return &LsTLVTEDefaultMetric{
		LsTLV: LsTLV{
			Type:   LS_TLV_TE_DEFAULT_METRIC,
			Length: 4,
		},
		Metric: *l,
	}
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], 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,
	})
}

func (l *LsTLVTEDefaultMetric) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsDelayMetricFlags struct {
	Anomalous bool `json:"anomalous"`
}

func (l *LsDelayMetricFlags) FlagBits() uint8 {
	var flags uint8
	if l.Anomalous {
		flags = flags | 1<<7
	}
	return flags
}

func NewLsDelayMetricFlags(v uint8) LsDelayMetricFlags {
	return LsDelayMetricFlags{
		Anomalous: v&(1<<7) > 0,
	}
}

type LsUnidirectionalLinkDelay struct {
	Flags LsDelayMetricFlags `json:"flags"`
	Delay uint32             `json:"delay"`
}

type LsTLVUnidirectionalLinkDelay struct {
	LsTLV
	Flags uint8
	Delay uint32
}

func NewLsTLVUnidirectionalLinkDelay(l *LsUnidirectionalLinkDelay) *LsTLVUnidirectionalLinkDelay {
	return &LsTLVUnidirectionalLinkDelay{
		LsTLV: LsTLV{
			Type:   LS_TLV_UNIDIRECTIONAL_LINK_DELAY,
			Length: 4,
		},
		Flags: l.Flags.FlagBits(),
		Delay: l.Delay,
	}
}

func (l *LsTLVUnidirectionalLinkDelay) Extract() *LsUnidirectionalLinkDelay {
	return &LsUnidirectionalLinkDelay{
		Flags: NewLsDelayMetricFlags(l.Flags),
		Delay: l.Delay,
	}
}

func (l *LsTLVUnidirectionalLinkDelay) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_UNIDIRECTIONAL_LINK_DELAY {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://www.rfc-editor.org/rfc/rfc8571#section-2.1
	if len(value) != 4 {
		return malformedAttrListErr("Incorrect unidirectional link delay length")
	}

	l.Flags = value[0]
	l.Delay = uint32(value[1])<<16 | uint32(value[2])<<8 | uint32(value[3])

	return nil
}

func (l *LsTLVUnidirectionalLinkDelay) Serialize() ([]byte, error) {
	if l.Delay > 0xFFFFFF {
		return nil, malformedAttrListErr("Incorrect unidirectional link delay value")
	}

	// Only the A-flag bit is defined by RFC8571; clear all other bits on transmit.
	const lsDelayMetricAFlagMask uint8 = 0x80

	buf := make([]byte, 4)
	buf[0] = l.Flags & lsDelayMetricAFlagMask
	buf[1] = byte(l.Delay >> 16)
	buf[2] = byte(l.Delay >> 8)
	buf[3] = byte(l.Delay)

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVUnidirectionalLinkDelay) String() string {
	return fmt.Sprintf("{Unidirectional Link Delay: Flags:%02x Delay:%d}", l.Flags, l.Delay)
}

func (l *LsTLVUnidirectionalLinkDelay) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  LsTLVType `json:"type"`
		Flags uint8     `json:"flags"`
		Delay uint32    `json:"unidirectional_link_delay"`
	}{
		Type:  l.Type,
		Flags: l.Flags,
		Delay: l.Delay,
	})
}

func (l *LsTLVUnidirectionalLinkDelay) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsMinMaxUnidirectionalLinkDelay struct {
	Flags    LsDelayMetricFlags `json:"flags"`
	MinDelay uint32             `json:"min_delay"`
	MaxDelay uint32             `json:"max_delay"`
}

type LsTLVMinMaxUnidirectionalLinkDelay struct {
	LsTLV
	Flags    uint8
	MinDelay uint32
	Reserved uint8
	MaxDelay uint32
}

func NewLsTLVMinMaxUnidirectionalLinkDelay(l *LsMinMaxUnidirectionalLinkDelay) *LsTLVMinMaxUnidirectionalLinkDelay {
	return &LsTLVMinMaxUnidirectionalLinkDelay{
		LsTLV: LsTLV{
			Type:   LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY,
			Length: 8,
		},
		Flags:    l.Flags.FlagBits(),
		Reserved: 0,
		MinDelay: l.MinDelay,
		MaxDelay: l.MaxDelay,
	}
}

func (l *LsTLVMinMaxUnidirectionalLinkDelay) Extract() *LsMinMaxUnidirectionalLinkDelay {
	return &LsMinMaxUnidirectionalLinkDelay{
		Flags:    NewLsDelayMetricFlags(l.Flags),
		MinDelay: l.MinDelay,
		MaxDelay: l.MaxDelay,
	}
}

func (l *LsTLVMinMaxUnidirectionalLinkDelay) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://www.rfc-editor.org/rfc/rfc8571#section-2.2
	if len(value) != 8 {
		return malformedAttrListErr("Incorrect min/max unidirectional link delay length")
	}

	l.Flags = value[0]
	l.MinDelay = uint32(value[1])<<16 | uint32(value[2])<<8 | uint32(value[3])
	l.Reserved = value[4]
	l.MaxDelay = uint32(value[5])<<16 | uint32(value[6])<<8 | uint32(value[7])

	return nil
}

func (l *LsTLVMinMaxUnidirectionalLinkDelay) Serialize() ([]byte, error) {
	if l.MinDelay > 0xFFFFFF || l.MaxDelay > 0xFFFFFF {
		return nil, malformedAttrListErr("Incorrect min/max unidirectional link delay value")
	}

	// Only the A-flag bit is defined by RFC8571; clear all other bits on transmit.
	const lsDelayMetricAFlagMask uint8 = 0x80

	buf := make([]byte, 8)
	buf[0] = l.Flags & lsDelayMetricAFlagMask
	buf[1] = byte(l.MinDelay >> 16)
	buf[2] = byte(l.MinDelay >> 8)
	buf[3] = byte(l.MinDelay)
	// Reserved byte must be zero on the wire per RFC8571.
	buf[4] = 0
	buf[5] = byte(l.MaxDelay >> 16)
	buf[6] = byte(l.MaxDelay >> 8)
	buf[7] = byte(l.MaxDelay)

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVMinMaxUnidirectionalLinkDelay) String() string {
	return fmt.Sprintf("{Min/Max Unidirectional Link Delay: Flags:%02x Min:%d Max:%d}", l.Flags, l.MinDelay, l.MaxDelay)
}

func (l *LsTLVMinMaxUnidirectionalLinkDelay) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     LsTLVType `json:"type"`
		Flags    uint8     `json:"flags"`
		MinDelay uint32    `json:"min_unidirectional_link_delay"`
		MaxDelay uint32    `json:"max_unidirectional_link_delay"`
	}{
		Type:     l.Type,
		Flags:    l.Flags,
		MinDelay: l.MinDelay,
		MaxDelay: l.MaxDelay,
	})
}

func (l *LsTLVMinMaxUnidirectionalLinkDelay) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVUnidirectionalDelayVariation struct {
	LsTLV
	Reserved       uint8
	DelayVariation uint32
}

func NewLsTLVUnidirectionalDelayVariation(l *uint32) *LsTLVUnidirectionalDelayVariation {
	return &LsTLVUnidirectionalDelayVariation{
		LsTLV: LsTLV{
			Type:   LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION,
			Length: 4,
		},
		Reserved:       0,
		DelayVariation: *l,
	}
}

func (l *LsTLVUnidirectionalDelayVariation) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://www.rfc-editor.org/rfc/rfc8571#section-2.3
	if len(value) != 4 {
		return malformedAttrListErr("Incorrect unidirectional delay variation length")
	}

	l.Reserved = value[0]
	l.DelayVariation = uint32(value[1])<<16 | uint32(value[2])<<8 | uint32(value[3])

	return nil
}

func (l *LsTLVUnidirectionalDelayVariation) Serialize() ([]byte, error) {
	if l.DelayVariation > 0xFFFFFF {
		return nil, malformedAttrListErr("Incorrect unidirectional delay variation value")
	}

	buf := make([]byte, 4)
	buf[0] = 0
	buf[1] = byte(l.DelayVariation >> 16)
	buf[2] = byte(l.DelayVariation >> 8)
	buf[3] = byte(l.DelayVariation)

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVUnidirectionalDelayVariation) String() string {
	return fmt.Sprintf("{Unidirectional Delay Variation: %d}", l.DelayVariation)
}

func (l *LsTLVUnidirectionalDelayVariation) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type           LsTLVType `json:"type"`
		DelayVariation uint32    `json:"unidirectional_delay_variation"`
	}{
		Type:           l.Type,
		DelayVariation: l.DelayVariation,
	})
}

func (l *LsTLVUnidirectionalDelayVariation) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIGPMetric struct {
	LsTLV
	Metric uint32
}

func NewLsTLVIGPMetric(l *uint32) *LsTLVIGPMetric {
	return &LsTLVIGPMetric{
		LsTLV: LsTLV{
			Type:   LS_TLV_IGP_METRIC,
			Length: 3, // TODO: implementation for IS-IS small metrics and OSPF link metrics.
		},
		Metric: *l,
	}
}

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:
		var buf [2]byte
		binary.BigEndian.PutUint16(buf[:2], uint16(l.Metric))
		return l.LsTLV.Serialize(buf[:])

	case 3:
		var buf [4]byte
		binary.BigEndian.PutUint32(buf[:4], 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,
	})
}

func (l *LsTLVIGPMetric) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVLinkName struct {
	LsTLV
	Name string
}

func NewLsTLVLinkName(l *string) *LsTLVLinkName {
	return &LsTLVLinkName{
		LsTLV: LsTLV{
			Type:   LS_TLV_LINK_NAME,
			Length: uint16(len(*l)),
		},
		Name: *l,
	}
}

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,
	})
}

func (l *LsTLVLinkName) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVSrAlgorithm struct {
	LsTLV
	Algorithm []byte
}

func NewLsTLVSrAlgorithm(l *[]byte) *LsTLVSrAlgorithm {
	return &LsTLVSrAlgorithm{
		LsTLV: LsTLV{
			Type:   LS_TLV_SR_ALGORITHM,
			Length: uint16(len(*l)),
		},
		Algorithm: *l,
	}
}

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),
	})
}

func (l *LsTLVSrAlgorithm) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsSrLabelRange struct {
	Range      uint32
	FirstLabel LsTLVSIDLabel
}

type LsTLVSrCapabilities struct {
	LsTLV
	Flags  uint8
	Ranges []LsSrLabelRange
}

func NewLsTLVSrCapabilities(l *LsSrCapabilities) *LsTLVSrCapabilities {
	var flags uint8
	if l.IPv4Supported {
		flags = flags | 1<<7
	}
	if l.IPv6Supported {
		flags = flags | 1<<6
	}
	ranges := []LsSrLabelRange{}
	var length uint16
	for _, r := range l.Ranges {
		ranges = append(ranges, LsSrLabelRange{
			Range: r.End - r.Begin,
			FirstLabel: LsTLVSIDLabel{
				LsTLV: LsTLV{
					Type:   LS_TLV_SID_LABEL_TLV,
					Length: 4,
				},
				SID: r.Begin,
			},
		})
		length += 4
	}
	return &LsTLVSrCapabilities{
		LsTLV: LsTLV{
			Type:   LS_TLV_SR_CAPABILITIES,
			Length: length,
		},
		Flags:  flags,
		Ranges: ranges,
	}
}

func (l *LsTLVSrCapabilities) GetLsTLV() LsTLV {
	return l.LsTLV
}

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<<7) > 0,
		IPv6Supported: l.Flags&(1<<6) > 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)
	var b [4]byte

	for _, r := range l.Ranges {
		binary.BigEndian.PutUint32(b[:4], 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 {
		fmt.Fprintf(&buf, "%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 NewLsTLVSrLocalBlock(l *LsSrLocalBlock) *LsTLVSrLocalBlock {
	var flags uint8 //
	ranges := []LsSrLabelRange{}
	var length uint16
	for _, r := range l.Ranges {
		ranges = append(ranges, LsSrLabelRange{
			Range: r.End - r.Begin,
			FirstLabel: LsTLVSIDLabel{
				LsTLV: LsTLV{
					Type:   LS_TLV_SID_LABEL_TLV,
					Length: 4,
				},
				SID: r.Begin,
			},
		})
		length += 4
	}
	return &LsTLVSrLocalBlock{
		LsTLV: LsTLV{
			Type:   LS_TLV_SR_LOCAL_BLOCK,
			Length: length,
		},
		Flags:  flags, // MUST be set 0. (RFC9085 2.1.4)
		Ranges: 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)
	var b [4]byte

	for _, r := range l.Ranges {
		binary.BigEndian.PutUint32(b[:4], 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 {
		fmt.Fprintf(&buf, "%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,
	})
}

func (l *LsTLVSrLocalBlock) GetLsTLV() LsTLV {
	return l.LsTLV
}

// LsTLVFlexAlgoDef wire format (RFC 9351 Section 3):
//
//	byte 0     Flex Algo ID (128..255 per RFC 9350 Section 4)
//	byte 1     Metric-Type (0=IGP, 1=Min-Delay, 2=TE per RFC 9350
//	           Section 5.1)
//	byte 2     Calc-Type (0=SPF per RFC 9350 Section 5.2)
//	byte 3     Priority (RFC 9350 Section 5.3)
//	bytes 4+   zero or more nested sub-TLVs in BGP-LS sub-TLV format
//	           (Type 2B + Length 2B + Value), codepoints 1040, 1041,
//	           1042, 1043, 1045, 1046.
//
// Sub-TLVs are decoded into typed fields below; unknown sub-TLV types
// are preserved in the Unknown slice so a future caller can inspect
// them without re-parsing the value bytes. Multiple FADs MAY be
// advertised for the same Node NLRI (one per algorithm), so the
// containing PathAttributeLs Extract aggregates them into a slice.

// LsTLVFlexAlgoSubTLVRaw preserves a sub-TLV whose type is not one of
// the codepoints RFC 9351 currently defines. Keeping the raw bytes
// lets callers inspect or re-serialise the FAD without losing data
// and matches the forward-compatibility requirement in RFC 9351
// Section 3.
type LsTLVFlexAlgoSubTLVRaw struct {
	Type  LsTLVType
	Value []byte
}

type LsTLVFlexAlgoDef struct {
	LsTLV
	Algorithm   uint8
	MetricType  uint8
	CalcType    uint8
	Priority    uint8
	ExcludeAny  []uint32                 // RFC9351 Section 3.1 (sub-TLV 1040)
	IncludeAny  []uint32                 // RFC9351 Section 3.2 (sub-TLV 1041)
	IncludeAll  []uint32                 // RFC9351 Section 3.3 (sub-TLV 1042)
	Flags       []byte                   // RFC9351 Section 3.4 (sub-TLV 1043; bit ordering per RFC9350 Section 6.4)
	ExcludeSRLG []uint32                 // RFC9351 Section 3.5 (sub-TLV 1045)
	Unsupported *LsTLVFADUnsupported     // RFC9351 Section 3.6 (sub-TLV 1046)
	Unknown     []LsTLVFlexAlgoSubTLVRaw // forward-compat for sub-TLVs not defined by RFC 9351
}

// LsTLVFADUnsupported records the Flex-Algorithm Unsupported sub-TLV
// (RFC 9351 Section 3.6): a Protocol-ID byte plus the list of sub-TLV
// type codes the originating node could not honour.
type LsTLVFADUnsupported struct {
	ProtocolID  uint8
	SubTLVTypes []uint16
}

func (l *LsTLVFlexAlgoDef) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if l.Type != LS_TLV_FLEX_ALGO_DEF {
		return malformedAttrListErr("Unexpected TLV type")
	}
	if len(value) < 4 {
		return malformedAttrListErr("FAD TLV too short")
	}
	l.Algorithm = value[0]
	l.MetricType = value[1]
	l.CalcType = value[2]
	l.Priority = value[3]
	if l.Algorithm < 128 {
		return malformedAttrListErr("FAD Algorithm reserved (must be 128..255)")
	}

	rem := value[4:]
	for len(rem) >= 4 {
		stype := binary.BigEndian.Uint16(rem[:2])
		slen := int(binary.BigEndian.Uint16(rem[2:4]))
		if 4+slen > len(rem) {
			return malformedAttrListErr("FAD sub-TLV truncated")
		}
		sv := rem[4 : 4+slen]
		switch LsTLVType(stype) {
		case LS_TLV_FAD_EXCLUDE_ANY_AFFINITY:
			if slen == 0 || slen%4 != 0 {
				return malformedAttrListErr("FAD Exclude-Any length must be non-zero multiple of 4")
			}
			for i := 0; i+4 <= slen; i += 4 {
				l.ExcludeAny = append(l.ExcludeAny, binary.BigEndian.Uint32(sv[i:i+4]))
			}
		case LS_TLV_FAD_INCLUDE_ANY_AFFINITY:
			if slen == 0 || slen%4 != 0 {
				return malformedAttrListErr("FAD Include-Any length must be non-zero multiple of 4")
			}
			for i := 0; i+4 <= slen; i += 4 {
				l.IncludeAny = append(l.IncludeAny, binary.BigEndian.Uint32(sv[i:i+4]))
			}
		case LS_TLV_FAD_INCLUDE_ALL_AFFINITY:
			if slen == 0 || slen%4 != 0 {
				return malformedAttrListErr("FAD Include-All length must be non-zero multiple of 4")
			}
			for i := 0; i+4 <= slen; i += 4 {
				l.IncludeAll = append(l.IncludeAll, binary.BigEndian.Uint32(sv[i:i+4]))
			}
		case LS_TLV_FAD_DEFINITION_FLAGS:
			if slen == 0 || slen%4 != 0 {
				return malformedAttrListErr("FAD Flags length must be non-zero multiple of 4")
			}
			l.Flags = append(l.Flags[:0], sv...)
		case LS_TLV_FAD_EXCLUDE_SRLG:
			if slen == 0 || slen%4 != 0 {
				return malformedAttrListErr("FAD Exclude SRLG length must be non-zero multiple of 4")
			}
			for i := 0; i+4 <= slen; i += 4 {
				l.ExcludeSRLG = append(l.ExcludeSRLG, binary.BigEndian.Uint32(sv[i:i+4]))
			}
		case LS_TLV_FAD_UNSUPPORTED:
			if slen < 1 {
				return malformedAttrListErr("FAD Unsupported sub-TLV must be at least 1 byte")
			}
			u := &LsTLVFADUnsupported{ProtocolID: sv[0]}
			body := sv[1:]
			if len(body)%2 != 0 {
				return malformedAttrListErr("FAD Unsupported sub-TLV type list must be a multiple of 2")
			}
			for i := 0; i+2 <= len(body); i += 2 {
				u.SubTLVTypes = append(u.SubTLVTypes, binary.BigEndian.Uint16(body[i:i+2]))
			}
			l.Unsupported = u
		default:
			cp := make([]byte, slen)
			copy(cp, sv)
			l.Unknown = append(l.Unknown, LsTLVFlexAlgoSubTLVRaw{Type: LsTLVType(stype), Value: cp})
		}
		rem = rem[4+slen:]
	}
	return nil
}

func (l *LsTLVFlexAlgoDef) Serialize() ([]byte, error) {
	body := make([]byte, 0, 4)
	body = append(body, l.Algorithm, l.MetricType, l.CalcType, l.Priority)

	appendSub := func(t LsTLVType, v []byte) {
		var hdr [4]byte
		binary.BigEndian.PutUint16(hdr[:2], uint16(t))
		binary.BigEndian.PutUint16(hdr[2:4], uint16(len(v)))
		body = append(body, hdr[:]...)
		body = append(body, v...)
	}
	u32slice := func(in []uint32) []byte {
		out := make([]byte, 4*len(in))
		for i, v := range in {
			binary.BigEndian.PutUint32(out[4*i:4*i+4], v)
		}
		return out
	}
	if len(l.ExcludeAny) > 0 {
		appendSub(LS_TLV_FAD_EXCLUDE_ANY_AFFINITY, u32slice(l.ExcludeAny))
	}
	if len(l.IncludeAny) > 0 {
		appendSub(LS_TLV_FAD_INCLUDE_ANY_AFFINITY, u32slice(l.IncludeAny))
	}
	if len(l.IncludeAll) > 0 {
		appendSub(LS_TLV_FAD_INCLUDE_ALL_AFFINITY, u32slice(l.IncludeAll))
	}
	if len(l.Flags) > 0 {
		appendSub(LS_TLV_FAD_DEFINITION_FLAGS, l.Flags)
	}
	if len(l.ExcludeSRLG) > 0 {
		appendSub(LS_TLV_FAD_EXCLUDE_SRLG, u32slice(l.ExcludeSRLG))
	}
	if l.Unsupported != nil {
		buf := make([]byte, 1+2*len(l.Unsupported.SubTLVTypes))
		buf[0] = l.Unsupported.ProtocolID
		for i, t := range l.Unsupported.SubTLVTypes {
			binary.BigEndian.PutUint16(buf[1+2*i:1+2*i+2], t)
		}
		appendSub(LS_TLV_FAD_UNSUPPORTED, buf)
	}
	for _, raw := range l.Unknown {
		appendSub(raw.Type, raw.Value)
	}
	l.Length = uint16(len(body))
	return l.LsTLV.Serialize(body)
}

func (l *LsTLVFlexAlgoDef) String() string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "{Flex-Algo Def: %d metric:%d calc:%d prio:%d",
		l.Algorithm, l.MetricType, l.CalcType, l.Priority)
	if len(l.ExcludeAny) > 0 {
		fmt.Fprintf(&buf, " exclude-any:%s", flexAlgoUint32SliceHex(l.ExcludeAny))
	}
	if len(l.IncludeAny) > 0 {
		fmt.Fprintf(&buf, " include-any:%s", flexAlgoUint32SliceHex(l.IncludeAny))
	}
	if len(l.IncludeAll) > 0 {
		fmt.Fprintf(&buf, " include-all:%s", flexAlgoUint32SliceHex(l.IncludeAll))
	}
	if len(l.Flags) > 0 {
		fmt.Fprintf(&buf, " flags:%s", flexAlgoByteSliceHex(l.Flags))
	}
	if len(l.ExcludeSRLG) > 0 {
		fmt.Fprintf(&buf, " exclude-srlg:%v", l.ExcludeSRLG)
	}
	if l.Unsupported != nil {
		fmt.Fprintf(&buf, " unsupported:{proto:%d types:%v}",
			l.Unsupported.ProtocolID, l.Unsupported.SubTLVTypes)
	}
	buf.WriteString("}")
	return buf.String()
}

// flexAlgoUint32SliceHex formats a slice of 32-bit affinity bitmaps
// as a comma-separated hex list, e.g. [0x0000000F 0x000000F0]. Used
// by LsTLVFlexAlgoDef.String to keep the CLI render compact and easy
// to compare against vendor "show isis flex-algorithm" output.
func flexAlgoUint32SliceHex(in []uint32) string {
	var buf bytes.Buffer
	buf.WriteString("[")
	for i, v := range in {
		if i > 0 {
			buf.WriteString(" ")
		}
		fmt.Fprintf(&buf, "0x%08x", v)
	}
	buf.WriteString("]")
	return buf.String()
}

func flexAlgoByteSliceHex(in []byte) string {
	var buf bytes.Buffer
	buf.WriteString("[")
	for i, v := range in {
		if i > 0 {
			buf.WriteString(" ")
		}
		fmt.Fprintf(&buf, "0x%02x", v)
	}
	buf.WriteString("]")
	return buf.String()
}

func (l *LsTLVFlexAlgoDef) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type        LsTLVType `json:"type"`
		Algorithm   uint8     `json:"algorithm"`
		MetricType  uint8     `json:"metric_type"`
		CalcType    uint8     `json:"calc_type"`
		Priority    uint8     `json:"priority"`
		ExcludeAny  []uint32  `json:"exclude_any_affinity,omitempty"`
		IncludeAny  []uint32  `json:"include_any_affinity,omitempty"`
		IncludeAll  []uint32  `json:"include_all_affinity,omitempty"`
		Flags       []byte    `json:"definition_flags,omitempty"`
		ExcludeSRLG []uint32  `json:"exclude_srlg,omitempty"`
	}{
		Type:        l.Type,
		Algorithm:   l.Algorithm,
		MetricType:  l.MetricType,
		CalcType:    l.CalcType,
		Priority:    l.Priority,
		ExcludeAny:  l.ExcludeAny,
		IncludeAny:  l.IncludeAny,
		IncludeAll:  l.IncludeAll,
		Flags:       l.Flags,
		ExcludeSRLG: l.ExcludeSRLG,
	})
}

func (l *LsTLVFlexAlgoDef) GetLsTLV() LsTLV {
	return l.LsTLV
}

// LsTLVFADPrefixMetric carries the Flexible Algorithm Prefix Metric
// (FAPM) Prefix Attribute TLV defined by RFC 9351 Section 4. The
// codepoint sits in the same registry as the FAD sub-TLVs but the
// TLV is a top-level Prefix Attribute, not nested inside TLV 1039.
// Length is fixed at 8 octets.
type LsTLVFADPrefixMetric struct {
	LsTLV
	Algorithm uint8
	Flags     uint8 // OSPF-specific per RFC9351 Section 4; MUST be 0 for IS-IS
	Metric    uint32
}

func (l *LsTLVFADPrefixMetric) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if l.Type != LS_TLV_FAD_PREFIX_METRIC {
		return malformedAttrListErr("Unexpected TLV type")
	}
	if len(value) != 8 {
		return malformedAttrListErr("FAPM Prefix Attribute TLV length must be 8")
	}
	l.Algorithm = value[0]
	l.Flags = value[1]
	// value[2:4] is Reserved per RFC9351 Section 4 and MUST be ignored.
	l.Metric = binary.BigEndian.Uint32(value[4:8])
	if l.Algorithm < 128 {
		return malformedAttrListErr("FAPM Algorithm reserved (must be 128..255)")
	}
	return nil
}

func (l *LsTLVFADPrefixMetric) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = l.Algorithm
	buf[1] = l.Flags
	// bytes 2..3 are Reserved (RFC9351 Section 4) and stay zero.
	binary.BigEndian.PutUint32(buf[4:8], l.Metric)
	l.Length = 8
	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVFADPrefixMetric) String() string {
	return fmt.Sprintf("{FAPM: algo %d metric %d}", l.Algorithm, l.Metric)
}

func (l *LsTLVFADPrefixMetric) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type      LsTLVType `json:"type"`
		Algorithm uint8     `json:"algorithm"`
		Flags     uint8     `json:"flags,omitempty"`
		Metric    uint32    `json:"metric"`
	}{
		Type:      l.Type,
		Algorithm: l.Algorithm,
		Flags:     l.Flags,
		Metric:    l.Metric,
	})
}

func (l *LsTLVFADPrefixMetric) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVAdjacencySID struct {
	LsTLV
	Flags  uint8
	Weight uint8
	SID    uint32
}

func NewLsTLVAdjacencySID(l *uint32) *LsTLVAdjacencySID {
	var flags uint8
	return &LsTLVAdjacencySID{
		LsTLV: LsTLV{
			Type:   LS_TLV_ADJACENCY_SID,
			Length: 7, // TODO: Implementation to judge 7 octets or 8 octets
		},
		Flags:  flags,
		Weight: 0,  // TODO: Implementation for IGP
		SID:    *l, // TODO: Implementation for IGP
	}
}

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}...)

	var b [4]byte
	binary.BigEndian.PutUint32(b[:4], 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,
	})
}

func (l *LsTLVAdjacencySID) GetLsTLV() LsTLV {
	return l.LsTLV
}

// https://tools.ietf.org/html/rfc9086#section-5
type LsAttributeBgpPeerSegmentSIDFlags struct {
	Value      bool `json:"value"`
	Local      bool `json:"local"`
	Backup     bool `json:"backup"`
	Persistent bool `json:"persistent"`
}

func (l *LsAttributeBgpPeerSegmentSIDFlags) FlagBits() uint8 {
	var flags uint8
	if l.Value {
		flags = flags | 1<<7
	}
	if l.Local {
		flags = flags | 1<<6
	}
	if l.Backup {
		flags = flags | 1<<5
	}
	if l.Persistent {
		flags = flags | 1<<4
	}
	return flags
}

func (l *LsAttributeBgpPeerSegmentSIDFlags) SidLen() uint16 {
	// https://tools.ietf.org/html/rfc9086#section-5
	if l.Value {
		return 7
	} else {
		return 8
	}
}

func NewLsBgpPeerSegmentSIDFlag(v uint8) LsAttributeBgpPeerSegmentSIDFlags {
	return LsAttributeBgpPeerSegmentSIDFlags{
		Value:      v&(1<<7) > 0,
		Local:      v&(1<<6) > 0,
		Backup:     v&(1<<5) > 0,
		Persistent: v&(1<<4) > 0,
	}
}

type LsBgpPeerSegmentSID struct {
	Flags  LsAttributeBgpPeerSegmentSIDFlags `json:"flags"`
	Weight uint8                             `json:"weight"`
	SID    uint32                            `json:"sid"`
}

type LsTLVPeerNodeSID struct {
	LsTLV
	Flags  uint8
	Weight uint8
	SID    uint32
}

func NewLsTLVPeerNodeSID(l *LsBgpPeerSegmentSID) *LsTLVPeerNodeSID {
	return &LsTLVPeerNodeSID{
		LsTLV: LsTLV{
			Type:   LS_TLV_PEER_NODE_SID,
			Length: l.Flags.SidLen(),
		},
		Flags:  l.Flags.FlagBits(),
		Weight: l.Weight,
		SID:    l.SID,
	}
}

func (l *LsTLVPeerNodeSID) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_PEER_NODE_SID {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://tools.ietf.org/html/rfc9086#section-5
	if len(value) != 7 && len(value) != 8 {
		return malformedAttrListErr("Incorrect Peer Node 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 *LsTLVPeerNodeSID) Extract() *LsBgpPeerSegmentSID {
	return &LsBgpPeerSegmentSID{
		Flags:  NewLsBgpPeerSegmentSIDFlag(l.Flags),
		Weight: l.Weight,
		SID:    l.SID,
	}
}

func (l *LsTLVPeerNodeSID) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	buf = append(buf, l.Flags)
	buf = append(buf, l.Weight)
	// Reserved
	buf = append(buf, []byte{0, 0}...)

	var b [4]byte
	binary.BigEndian.PutUint32(b[:4], l.SID)

	if l.Length == 7 {
		return l.LsTLV.Serialize(append(buf, b[1:]...))
	}

	return l.LsTLV.Serialize(append(buf, b[:]...))
}

func (l *LsTLVPeerNodeSID) String() string {
	return fmt.Sprintf("{Peer Node SID: %v}", l.SID)
}

func (l *LsTLVPeerNodeSID) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type LsTLVType `json:"type"`
		SID  uint32    `json:"peer_node_sid"`
	}{
		Type: l.Type,
		SID:  l.SID,
	})
}

func (l *LsTLVPeerNodeSID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVSrv6EndXSID struct {
	LsTLV
	EndpointBehavior uint16
	Flags            uint8
	Algorithm        uint8
	Weight           uint8
	Reserved         uint8
	SIDs             []netip.Addr
	Srv6SIDStructure LsTLVSrv6SIDStructure
}

func NewLsTLVSrv6EndXSID(l *LsSrv6EndXSID) *LsTLVSrv6EndXSID {
	var length uint16 = 6

	sids := []netip.Addr{}
	for _, sid := range l.SIDs {
		sids = append(sids, sid)
		length += 16 // each SID is 16 bytes
	}

	srv6SIDStructure := LsTLVSrv6SIDStructure{
		LsTLV: LsTLV{
			Type:   LS_TLV_SRV6_SID_STRUCTURE,
			Length: 4,
		},
		LocalBlock: l.Srv6SIDStructure.LocalBlock,
		LocalNode:  l.Srv6SIDStructure.LocalNode,
		LocalFunc:  l.Srv6SIDStructure.LocalFunc,
		LocalArg:   l.Srv6SIDStructure.LocalArg,
	}

	if l.Srv6SIDStructure.LocalBlock != 0 || l.Srv6SIDStructure.LocalNode != 0 ||
		l.Srv6SIDStructure.LocalFunc != 0 || l.Srv6SIDStructure.LocalArg != 0 {
		length += 8 // Sub-TLV header (4 bytes) + payload (4 bytes)
	}

	return &LsTLVSrv6EndXSID{
		LsTLV: LsTLV{
			Type:   LS_TLV_SRV6_END_X_SID,
			Length: length,
		},
		EndpointBehavior: l.EndpointBehavior,
		Flags:            l.Flags,
		Algorithm:        l.Algorithm,
		Weight:           l.Weight,
		Reserved:         l.Reserved,
		SIDs:             sids,
		Srv6SIDStructure: srv6SIDStructure,
	}
}

func (l *LsTLVSrv6EndXSID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsSrv6EndXSID struct {
	EndpointBehavior uint16             `json:"endpoint_behavior"`
	Flags            uint8              `json:"flags"`
	Algorithm        uint8              `json:"algorithm"`
	Weight           uint8              `json:"weight"`
	Reserved         uint8              `json:"reserved"`
	SIDs             []netip.Addr       `json:"sids"`
	Srv6SIDStructure LsSrv6SIDStructure `json:"sid_structure"`
}

func (l *LsTLVSrv6EndXSID) Extract() *LsSrv6EndXSID {
	les := &LsSrv6EndXSID{
		EndpointBehavior: l.EndpointBehavior,
		Flags:            l.Flags,
		Algorithm:        l.Algorithm,
		Weight:           l.Weight,
		Reserved:         l.Reserved,
		SIDs:             l.SIDs,
		Srv6SIDStructure: *l.Srv6SIDStructure.Extract(),
	}

	return les
}

func (l *LsTLVSrv6EndXSID) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_SRV6_END_X_SID {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// RFC 9514: minimum length = 6 bytes header + 16 bytes SID = 22 bytes
	if len(value) < 22 {
		return malformedAttrListErr("Incorrect SRv6 End.X SID length")
	}

	l.EndpointBehavior = binary.BigEndian.Uint16(value[:2])
	l.Flags = value[2]
	l.Algorithm = value[3]
	l.Weight = value[4]
	l.Reserved = value[5]

	value = value[6:]

	// parse variable data: SIDs followed by Sub-TLVs
	return l.parseVariableData(value)
}

func (l *LsTLVSrv6EndXSID) parseVariableData(data []byte) error {
	pos := 0

	// if data length is not multiple of 16, there must be Sub-TLVs at the end
	dataLen := len(data)
	sidDataLen := dataLen / 16 * 16

	for pos < sidDataLen {
		sid := make([]byte, 16)
		copy(sid, data[pos:pos+16])
		addr, _ := netip.AddrFromSlice(sid)
		l.SIDs = append(l.SIDs, addr)
		pos += 16
	}

	if pos < dataLen {
		return l.parseSubTLVs(data[pos:])
	}

	return nil
}

func (l *LsTLVSrv6EndXSID) parseSubTLVs(data []byte) error {
	for len(data) >= 4 {
		tlvType := binary.BigEndian.Uint16(data[:2])
		tlvLen := binary.BigEndian.Uint16(data[2:4])
		need := 4 + int(tlvLen)
		if len(data) < need {
			return malformedAttrListErr("SRv6 End.X SID Sub-TLV is truncated")
		}

		switch tlvType {
		case LS_TLV_SRV6_SID_STRUCTURE:
			if tlvLen != 4 {
				data = data[need:]
				continue
			}
			l.Srv6SIDStructure.LocalBlock = data[4]
			l.Srv6SIDStructure.LocalNode = data[5]
			l.Srv6SIDStructure.LocalFunc = data[6]
			l.Srv6SIDStructure.LocalArg = data[7]
		default:
			// TODO: handle unknown Sub-TLVs
		}

		data = data[need:]
	}

	return nil
}

func (l *LsTLVSrv6EndXSID) Serialize() ([]byte, error) {
	buf := make([]byte, 0)

	temp := make([]byte, 2)
	binary.BigEndian.PutUint16(temp, l.EndpointBehavior)
	buf = append(buf, temp...)
	buf = append(buf, l.Flags)
	buf = append(buf, l.Algorithm)
	buf = append(buf, l.Weight)
	buf = append(buf, l.Reserved)

	for _, sid := range l.SIDs {
		buf = append(buf, sid.AsSlice()...)
	}

	return l.serializeSubTLVs(buf)
}

func (l *LsTLVSrv6EndXSID) serializeSubTLVs(buf []byte) ([]byte, error) {
	if l.Srv6SIDStructure.LocalBlock != 0 || l.Srv6SIDStructure.LocalNode != 0 ||
		l.Srv6SIDStructure.LocalFunc != 0 || l.Srv6SIDStructure.LocalArg != 0 {
		subTLVHeader := make([]byte, 4)
		binary.BigEndian.PutUint16(subTLVHeader[:2], LS_TLV_SRV6_SID_STRUCTURE)
		binary.BigEndian.PutUint16(subTLVHeader[2:4], 4) // Length = 4 bytes
		buf = append(buf, subTLVHeader...)

		buf = append(buf, l.Srv6SIDStructure.LocalBlock)
		buf = append(buf, l.Srv6SIDStructure.LocalNode)
		buf = append(buf, l.Srv6SIDStructure.LocalFunc)
		buf = append(buf, l.Srv6SIDStructure.LocalArg)
	}

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVSrv6EndXSID) String() string {
	var buf bytes.Buffer
	for _, sid := range l.SIDs {
		fmt.Fprintf(&buf, "%s ", sid.String())
	}
	return fmt.Sprintf("{SRv6 End.X SID: EndpointBehavior:%d SIDs: %s LocalBlock:%d LocalNode:%d LocalFunc:%d LocalArg:%d}", l.EndpointBehavior, buf.String(), l.Srv6SIDStructure.LocalBlock, l.Srv6SIDStructure.LocalNode, l.Srv6SIDStructure.LocalFunc, l.Srv6SIDStructure.LocalArg)
}

func (l *LsTLVSrv6EndXSID) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type             LsTLVType             `json:"type"`
		EndpointBehavior uint16                `json:"endpoint_behavior"`
		SIDs             []netip.Addr          `json:"sids"`
		Srv6SIDStructure LsTLVSrv6SIDStructure `json:"sid_structure"`
	}{
		Type:             l.Type,
		EndpointBehavior: l.EndpointBehavior,
		SIDs:             l.SIDs,
		Srv6SIDStructure: l.Srv6SIDStructure,
	})
}

type LsTLVSrv6SIDStructure struct {
	LsTLV
	LocalBlock uint8
	LocalNode  uint8
	LocalFunc  uint8
	LocalArg   uint8
}

func NewLsTLVSrv6SIDStructure(l *LsSrv6SIDStructure) *LsTLVSrv6SIDStructure {
	return &LsTLVSrv6SIDStructure{
		LsTLV: LsTLV{
			Type:   LS_TLV_SRV6_SID_STRUCTURE,
			Length: 4,
		},
		LocalBlock: l.LocalBlock,
		LocalNode:  l.LocalNode,
		LocalFunc:  l.LocalFunc,
		LocalArg:   l.LocalArg,
	}
}

func (l *LsTLVSrv6SIDStructure) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsSrv6SIDStructure struct {
	LocalBlock uint8 `json:"local_block"`
	LocalNode  uint8 `json:"local_node"`
	LocalFunc  uint8 `json:"local_func"`
	LocalArg   uint8 `json:"local_arg"`
}

func (l *LsTLVSrv6SIDStructure) Extract() *LsSrv6SIDStructure {
	lsss := &LsSrv6SIDStructure{
		LocalBlock: l.LocalBlock,
		LocalNode:  l.LocalNode,
		LocalFunc:  l.LocalFunc,
		LocalArg:   l.LocalArg,
	}
	return lsss
}

func (l *LsTLVSrv6SIDStructure) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_SRV6_SID_STRUCTURE {
		return malformedAttrListErr("Unexpected TLV type")
	}

	if len(value) != 4 {
		return malformedAttrListErr("Incorrect SRv6 SID Structure length")
	}

	l.LocalBlock = value[0]
	l.LocalNode = value[1]
	l.LocalFunc = value[2]
	l.LocalArg = value[3]

	sum := uint16(l.LocalBlock) + uint16(l.LocalNode) + uint16(l.LocalFunc) + uint16(l.LocalArg)
	if sum > 128 {
		return malformedAttrListErr("SRv6 SID Structure length sum exceeds 128 bits")
	}

	return nil
}

func (l *LsTLVSrv6SIDStructure) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	buf = append(buf, l.LocalBlock)
	buf = append(buf, l.LocalNode)
	buf = append(buf, l.LocalFunc)
	buf = append(buf, l.LocalArg)

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVSrv6SIDStructure) String() string {
	return fmt.Sprintf("{SRv6 SID Structure: LocalBlock:%v LocalNode:%v LocalFunc:%v LocalArg:%v}", l.LocalBlock, l.LocalNode, l.LocalFunc, l.LocalArg)
}

func (l *LsTLVSrv6SIDStructure) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type       LsTLVType `json:"type"`
		LocalBlock uint8     `json:"local_block"`
		LocalNode  uint8     `json:"local_node"`
		LocalFunc  uint8     `json:"local_func"`
		LocalArg   uint8     `json:"local_arg"`
	}{
		Type:       l.Type,
		LocalBlock: l.LocalBlock,
		LocalNode:  l.LocalNode,
		LocalFunc:  l.LocalFunc,
		LocalArg:   l.LocalArg,
	})
}

type LsTLVSrv6BgpPeerNodeSID struct {
	LsTLV
	Flags        uint8
	Weight       uint8
	Reserved     uint16
	PeerASNumber uint32
	PeerBgpID    uint32
}

type LsSrv6BgpPeerNodeSID struct {
	Flags     uint8  `json:"flags"`
	Weight    uint8  `json:"weight"`
	PeerAS    uint32 `json:"peer_as"`
	PeerBgpID string `json:"peer_bgp_id"`
}

func NewLsTLVSrv6BgpPeerNodeSID(l *LsSrv6BgpPeerNodeSID) *LsTLVSrv6BgpPeerNodeSID {
	addr, err := netip.ParseAddr(l.PeerBgpID)
	if err != nil || !addr.Is4() {
		return nil
	}
	peerBgpID := addr.As4()

	return &LsTLVSrv6BgpPeerNodeSID{
		LsTLV: LsTLV{
			Type:   LS_TLV_SRV6_BGP_PEER_NODE_SID,
			Length: 12,
		},
		Flags:        l.Flags,
		Weight:       l.Weight,
		Reserved:     0,
		PeerASNumber: l.PeerAS,
		PeerBgpID:    binary.BigEndian.Uint32(peerBgpID[:]),
	}
}

func (l *LsTLVSrv6BgpPeerNodeSID) GetLsTLV() LsTLV {
	return l.LsTLV
}

func (l *LsTLVSrv6BgpPeerNodeSID) Extract() *LsSrv6BgpPeerNodeSID {
	peerBgpIP := make(net.IP, 4)
	binary.BigEndian.PutUint32(peerBgpIP, l.PeerBgpID)

	return &LsSrv6BgpPeerNodeSID{
		Flags:     l.Flags,
		Weight:    l.Weight,
		PeerAS:    l.PeerASNumber,
		PeerBgpID: peerBgpIP.String(),
	}
}

func (l *LsTLVSrv6BgpPeerNodeSID) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_SRV6_BGP_PEER_NODE_SID {
		return malformedAttrListErr("Incorrect SRv6 BGP PeerNode SID TLV type")
	}

	if l.Length != 12 {
		return malformedAttrListErr("Incorrect SRv6 BGP PeerNode SID TLV length")
	}
	l.Flags = value[0]
	l.Weight = value[1]
	l.Reserved = binary.BigEndian.Uint16(value[2:4])
	l.PeerASNumber = binary.BigEndian.Uint32(value[4:8])
	l.PeerBgpID = binary.BigEndian.Uint32(value[8:12])

	return nil
}

func (l *LsTLVSrv6BgpPeerNodeSID) Serialize() ([]byte, error) {
	buf := make([]byte, 12)
	buf[0] = l.Flags
	buf[1] = l.Weight
	binary.BigEndian.PutUint16(buf[2:4], l.Reserved)
	binary.BigEndian.PutUint32(buf[4:8], l.PeerASNumber)
	binary.BigEndian.PutUint32(buf[8:12], l.PeerBgpID)

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVSrv6BgpPeerNodeSID) String() string {
	peerBgpIP := make(net.IP, 4)
	binary.BigEndian.PutUint32(peerBgpIP, l.PeerBgpID)
	return fmt.Sprintf("{SRv6 BGP PeerNode SID: Flags:%d Weight:%d PeerAS:%d PeerBgpID:%s}",
		l.Flags, l.Weight, l.PeerASNumber, peerBgpIP.String())
}

func (l *LsTLVSrv6BgpPeerNodeSID) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type      LsTLVType `json:"type"`
		Flags     uint8     `json:"flags"`
		Weight    uint8     `json:"weight"`
		PeerAS    uint32    `json:"peer_as"`
		PeerBgpID string    `json:"peer_bgp_id"`
	}{
		Type:      l.Type,
		Flags:     l.Flags,
		Weight:    l.Weight,
		PeerAS:    l.PeerASNumber,
		PeerBgpID: l.Extract().PeerBgpID,
	})
}

type LsTLVSrv6EndpointBehavior struct {
	LsTLV
	EndpointBehavior uint16
	Flags            uint8
	Algorithm        uint8
}

func NewLsTLVSrv6EndpointBehavior(l *LsSrv6EndpointBehavior) *LsTLVSrv6EndpointBehavior {
	return &LsTLVSrv6EndpointBehavior{
		LsTLV: LsTLV{
			Type:   LS_TLV_SRV6_ENDPOINT_BEHAVIOR,
			Length: 4,
		},
		EndpointBehavior: l.EndpointBehavior,
		Flags:            l.Flags,
		Algorithm:        l.Algorithm,
	}
}

func (l *LsTLVSrv6EndpointBehavior) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsSrv6EndpointBehavior struct {
	EndpointBehavior uint16 `json:"endpoint_behavior"`
	Flags            uint8  `json:"flags"`
	Algorithm        uint8  `json:"algorithm"`
}

func (l *LsTLVSrv6EndpointBehavior) Extract() *LsSrv6EndpointBehavior {
	lseb := &LsSrv6EndpointBehavior{
		EndpointBehavior: l.EndpointBehavior,
		Flags:            l.Flags,
		Algorithm:        l.Algorithm,
	}
	return lseb
}

func (l *LsTLVSrv6EndpointBehavior) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_SRV6_ENDPOINT_BEHAVIOR {
		return malformedAttrListErr("Unexpected TLV type")
	}

	if len(value) != 4 {
		return malformedAttrListErr("Incorrect SRv6 Endpoint Behavior length")
	}

	l.EndpointBehavior = binary.BigEndian.Uint16(value[:2])
	l.Flags = value[2]
	l.Algorithm = value[3]

	return nil
}

func (l *LsTLVSrv6EndpointBehavior) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	binary.BigEndian.PutUint16(buf, l.EndpointBehavior)
	buf = append(buf, l.Flags)
	buf = append(buf, l.Algorithm)
	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVSrv6EndpointBehavior) String() string {
	return fmt.Sprintf("{SRv6 Endpoint Behavior: EndpointBehavior:%v Flags:%v Algorithm:%v}", l.EndpointBehavior, l.Flags, l.Algorithm)
}

func (l *LsTLVSrv6EndpointBehavior) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type             LsTLVType `json:"type"`
		EndpointBehavior uint16    `json:"endpoint_behavior"`
		Flags            uint8     `json:"flags"`
		Algorithm        uint8     `json:"algorithm"`
	}{
		Type:             l.Type,
		EndpointBehavior: l.EndpointBehavior,
		Flags:            l.Flags,
		Algorithm:        l.Algorithm,
	})
}

type LsTLVPeerAdjacencySID struct {
	LsTLV
	Flags  uint8
	Weight uint8
	SID    uint32
}

func NewLsTLVPeerAdjacencySID(l *LsBgpPeerSegmentSID) *LsTLVPeerAdjacencySID {
	return &LsTLVPeerAdjacencySID{
		LsTLV: LsTLV{
			Type:   LS_TLV_ADJACENCY_SID,
			Length: l.Flags.SidLen(),
		},
		Flags:  l.Flags.FlagBits(),
		Weight: l.Weight,
		SID:    l.SID,
	}
}

func (l *LsTLVPeerAdjacencySID) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_PEER_ADJACENCY_SID {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://tools.ietf.org/html/rfc9086#section-5
	if len(value) != 7 && len(value) != 8 {
		return malformedAttrListErr("Incorrect Peer 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 *LsTLVPeerAdjacencySID) Extract() *LsBgpPeerSegmentSID {
	return &LsBgpPeerSegmentSID{
		Flags:  NewLsBgpPeerSegmentSIDFlag(l.Flags),
		Weight: l.Weight,
		SID:    l.SID,
	}
}

func (l *LsTLVPeerAdjacencySID) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	buf = append(buf, l.Flags)
	buf = append(buf, l.Weight)
	// Reserved
	buf = append(buf, []byte{0, 0}...)

	var b [4]byte
	binary.BigEndian.PutUint32(b[:4], l.SID)

	if l.Length == 7 {
		return l.LsTLV.Serialize(append(buf, b[1:]...))
	}

	return l.LsTLV.Serialize(append(buf, b[:]...))
}

func (l *LsTLVPeerAdjacencySID) String() string {
	return fmt.Sprintf("{Peer Adjacency SID: %v}", l.SID)
}

func (l *LsTLVPeerAdjacencySID) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type LsTLVType `json:"type"`
		SID  uint32    `json:"peer_adjacency_sid"`
	}{
		Type: l.Type,
		SID:  l.SID,
	})
}

func (l *LsTLVPeerAdjacencySID) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVPeerSetSID struct {
	LsTLV
	Flags  uint8
	Weight uint8
	SID    uint32
}

func NewLsTLVPeerSetSID(l *LsBgpPeerSegmentSID) *LsTLVPeerSetSID {
	return &LsTLVPeerSetSID{
		LsTLV: LsTLV{
			Type:   LS_TLV_PEER_SET_SID,
			Length: l.Flags.SidLen(),
		},
		Flags:  l.Flags.FlagBits(),
		Weight: l.Weight,
		SID:    l.SID,
	}
}

func (l *LsTLVPeerSetSID) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_PEER_SET_SID {
		return malformedAttrListErr("Unexpected TLV type")
	}

	// https://tools.ietf.org/html/rfc9086#section-5
	if len(value) != 7 && len(value) != 8 {
		return malformedAttrListErr("Incorrect Peer Set 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 *LsTLVPeerSetSID) Extract() *LsBgpPeerSegmentSID {
	return &LsBgpPeerSegmentSID{
		Flags:  NewLsBgpPeerSegmentSIDFlag(l.Flags),
		Weight: l.Weight,
		SID:    l.SID,
	}
}

func (l *LsTLVPeerSetSID) Serialize() ([]byte, error) {
	buf := make([]byte, 0)
	buf = append(buf, l.Flags)
	buf = append(buf, l.Weight)
	// Reserved
	buf = append(buf, []byte{0, 0}...)

	var b [4]byte
	binary.BigEndian.PutUint32(b[:4], l.SID)

	if l.Length == 7 {
		return l.LsTLV.Serialize(append(buf, b[1:]...))
	}

	return l.LsTLV.Serialize(append(buf, b[:]...))
}

func (l *LsTLVPeerSetSID) String() string {
	return fmt.Sprintf("{Peer Set SID: %v}", l.SID)
}

func (l *LsTLVPeerSetSID) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type LsTLVType `json:"type"`
		SID  uint32    `json:"peer_set_sid"`
	}{
		Type: l.Type,
		SID:  l.SID,
	})
}

func (l *LsTLVPeerSetSID) GetLsTLV() LsTLV {
	return l.LsTLV
}

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) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], 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 NewLsTLVPrefixSID(l *uint32) *LsTLVPrefixSID {
	var flags uint8
	return &LsTLVPrefixSID{
		LsTLV: LsTLV{
			Type:   LS_TLV_PREFIX_SID,
			Length: 0,
		},
		Flags:     flags, // TODO: Implementation for IGP
		Algorithm: 0,     // TODO: Implementation for IGP
		SID:       *l,
	}
}

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}...)

	var b [4]byte
	binary.BigEndian.PutUint32(b[:4], 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,
	})
}

func (l *LsTLVPrefixSID) GetLsTLV() LsTLV {
	return l.LsTLV
}

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 NewLsTLVOpaqueLinkAttr(l *[]byte) *LsTLVOpaqueLinkAttr {
	return &LsTLVOpaqueLinkAttr{
		LsTLV: LsTLV{
			Type:   LS_TLV_OPAQUE_LINK_ATTR,
			Length: uint16(len(*l)),
		},
		Attr: *l,
	}
}

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),
	})
}

func (l LsTLVOpaqueLinkAttr) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVSrlg struct {
	LsTLV
	Srlgs []uint32
}

func NewLsTLVSrlg(l *[]uint32) *LsTLVSrlg {
	return &LsTLVSrlg{
		LsTLV: LsTLV{
			Type:   LS_TLV_SRLG,
			Length: uint16(4 * len(*l)),
		},
		Srlgs: *l,
	}
}

func (l *LsTLVSrlg) DecodeFromBytes(data []byte) error {
	value, err := l.LsTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}

	if l.Type != LS_TLV_SRLG {
		return malformedAttrListErr("Unexpected TLV type")
	}

	if len(value)%4 != 0 {
		return malformedAttrListErr("Incorrect SRLG length")
	}

	for len(value) > 0 {
		l.Srlgs = append(l.Srlgs, binary.BigEndian.Uint32(value[:4]))
		value = value[4:]
	}

	return nil
}

func (l *LsTLVSrlg) Serialize() ([]byte, error) {
	buf := make([]byte, 0, 4*len(l.Srlgs))

	var b [4]byte
	for i := range len(l.Srlgs) {
		binary.BigEndian.PutUint32(b[:4], l.Srlgs[i])
		buf = append(buf, b[:]...)
	}

	return l.LsTLV.Serialize(buf)
}

func (l *LsTLVSrlg) String() string {
	return fmt.Sprintf("{SRLG link attribute: %d}", l.Srlgs)
}

func (l *LsTLVSrlg) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  LsTLVType `json:"type"`
		Value []uint32  `json:"link_srlg_attribute"`
	}{
		Type:  l.Type,
		Value: l.Srlgs,
	})
}

func (l *LsTLVSrlg) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVIGPFlags struct {
	LsTLV
	Flags uint8
}

func NewLsTLVIGPFlags(l *LsIGPFlags) *LsTLVIGPFlags {
	var flags uint8
	if l.Down {
		flags = flags | 1<<7
	}
	if l.NoUnicast {
		flags = flags | 1<<6
	}
	if l.LocalAddress {
		flags = flags | 1<<5
	}
	if l.PropagateNSSA {
		flags = flags | 1<<4
	}
	return &LsTLVIGPFlags{
		LsTLV: LsTLV{
			Type:   LS_TLV_IGP_FLAGS,
			Length: 1,
		},
		Flags: flags,
	}
}

// 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<<7) > 0,
		NoUnicast:     l.Flags&(1<<6) > 0,
		LocalAddress:  l.Flags&(1<<5) > 0,
		PropagateNSSA: l.Flags&(1<<4) > 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 := range len(flags) {
		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(),
	})
}

func (l *LsTLVIGPFlags) GetLsTLV() LsTLV {
	return l.LsTLV
}

type LsTLVOpaquePrefixAttr struct {
	LsTLV
	Attr []byte
}

func NewLsTLVOpaquePrefixAttr(l *[]byte) *LsTLVOpaquePrefixAttr {
	return &LsTLVOpaquePrefixAttr{
		LsTLV: LsTLV{
			Type:   LS_TLV_OPAQUE_PREFIX_ATTR,
			Length: 0,
		},
		Attr: *l,
	}
}

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),
	})
}

func (l *LsTLVOpaquePrefixAttr) GetLsTLV() LsTLV {
	return l.LsTLV
}

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{}
		case LS_TLV_BGP_ROUTER_ID:
			subTLV = &LsTLVBgpRouterID{}
		case LS_TLV_BGP_CONFEDERATION_MEMBER:
			subTLV = &LsTLVBgpConfederationMember{}

		default:
			if sub.Len() > len(tlv) {
				return malformedAttrListErr("sub-TLV length exceeds parent TLV length")
			}
			tlv = tlv[sub.Len():]
			continue
		}

		if err := subTLV.DecodeFromBytes(tlv); err != nil {
			return err
		}
		l.SubTLVs = append(l.SubTLVs, subTLV)
		tlv = tlv[subTLV.Len():]
	}

	_, lsTLVIgpRouterIDExists := m[LS_TLV_IGP_ROUTER_ID]
	_, lsTLVBgpRouterIDExists := m[LS_TLV_BGP_ROUTER_ID]
	_, lsTLVAutonomousSystemExists := m[LS_TLV_AS]

	if !lsTLVIgpRouterIDExists && (!lsTLVBgpRouterIDExists || !lsTLVAutonomousSystemExists) {
		return malformedAttrListErr("Required TLV missing")
	}

	return nil
}

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)
		case *LsTLVBgpRouterID:
			nd.BGPRouterID = v.RouterID
		case *LsTLVBgpConfederationMember:
			nd.BGPConfederationMember = v.BgpConfederationMember
		}
	}

	return nd
}

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) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type LsTLVType `json:"type"`
		LsNodeDescriptor
	}{
		l.Type,
		*l.Extract(),
	})
}

func (l *LsTLVNodeDescriptor) String() string {
	nd := l.Extract()

	return nd.String()
}

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"`
	BGPRouterID            netip.Addr `json:"bgp_router_id"`
	BGPConfederationMember uint32     `json:"bgp_confederation_member"`
}

func (l *LsTLVNodeDescriptor) GetLsTLV() LsTLV {
	return l.LsTLV
}

func (l *LsNodeDescriptor) String() string {
	if !l.BGPRouterID.IsValid() || !l.BGPRouterID.Is4() {
		return fmt.Sprintf("{ASN: %v, BGP LS ID: %v, OSPF AREA: %v, IGP ROUTER ID: %v}", l.Asn, l.BGPLsID, l.OspfAreaID, l.IGPRouterID)
	}

	return fmt.Sprintf("{ASN: %v, BGP LS ID: %v, BGP ROUTER ID: %v}", l.Asn, l.BGPLsID, l.BGPRouterID)
}

func igpRouterIDStringToBytes(routerID string) []byte {
	if len(routerID) == 0 {
		return nil
	}
	// ISIS non-pseudonode format: 0000.0000.0001
	if len(routerID) == 14 && strings.Count(routerID, ".") == 2 {
		parts := strings.Split(routerID, ".")
		if len(parts) == 3 && len(parts[0]) == 4 && len(parts[1]) == 4 && len(parts[2]) == 4 {
			var result []byte
			for _, part := range parts {
				if bytes, err := hex.DecodeString(part); err == nil {
					result = append(result, bytes...)
				} else {
					return []byte(routerID)
				}
			}
			if len(result) == 6 {
				return result
			}
		}
	}
	// IPv4 format
	if ip := net.ParseIP(routerID); ip != nil && ip.To4() != nil {
		return ip.To4()
	}
	// ISIS pseudonode format: 0000.0000.0001-01
	if strings.Contains(routerID, "-") && len(routerID) == 17 {
		parts := strings.Split(routerID, "-")
		if len(parts) == 2 && len(parts[1]) == 2 {
			idParts := strings.Split(parts[0], ".")
			if len(idParts) == 3 && len(idParts[0]) == 4 && len(idParts[1]) == 4 && len(idParts[2]) == 4 {
				var result []byte
				for _, part := range idParts {
					if bytes, err := hex.DecodeString(part); err == nil {
						result = append(result, bytes...)
					} else {
						return []byte(routerID)
					}
				}
				if pseudoByte, err := hex.DecodeString(parts[1]); err == nil && len(pseudoByte) == 1 {
					result = append(result, pseudoByte[0])
				}
				if len(result) == 7 {
					return result
				}
			}
		}
	}
	// OSPF pseudonode format: IP:IP
	if strings.Contains(routerID, ":") {
		parts := strings.Split(routerID, ":")
		if len(parts) == 2 {
			ip1 := net.ParseIP(parts[0])
			ip2 := net.ParseIP(parts[1])
			if ip1 != nil && ip2 != nil && ip1.To4() != nil && ip2.To4() != nil {
				result := make([]byte, 8)
				copy(result[:4], ip1.To4())
				copy(result[4:8], ip2.To4())
				return result
			}
		}
	}

	return []byte(routerID)
}

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
	}
}

// Generate LsTLVNodeDescriptor from LsNodeDescriptor
func NewLsTLVNodeDescriptor(nd *LsNodeDescriptor, tlvType LsTLVType) LsTLVNodeDescriptor {
	// The sub-TLVs within a Node Descriptor MUST be arranged in ascending order by sub-TLV type.
	subTLVs := []LsTLVInterface{}

	// TLV Code Point 512 Autonomous System
	// ASN 0 is invalid.
	if nd.Asn != 0 {
		subTLVs = append(subTLVs,
			&LsTLVAutonomousSystem{
				LsTLV: LsTLV{
					Type:   LS_TLV_AS,
					Length: 4, // 4 is the only valid value.
				},
				ASN: nd.Asn,
			})
	}

	// For IGP
	if nd.IGPRouterID != "" {
		routerIdBytes := igpRouterIDStringToBytes(nd.IGPRouterID)
		routerIdLength := len(routerIdBytes)
		isOspf := false
		// OSPF/OSPFv3 non-pseudonode or pseudonode
		if routerIdLength == 4 || routerIdLength == 8 {
			isOspf = true
		}
		if isOspf {
			// TLV Code Point 514 OSPF Area-ID
			subTLVs = append(subTLVs,
				&LsTLVOspfAreaID{
					LsTLV: LsTLV{
						Type:   LS_TLV_OSPF_AREA,
						Length: 4, // 4 is the only valid value.
					},
					AreaID: nd.OspfAreaID,
				})
		}
		// TLV Code Point 515 IGP Router-ID
		subTLVs = append(subTLVs,
			&LsTLVIgpRouterID{
				LsTLV: LsTLV{
					Type:   LS_TLV_IGP_ROUTER_ID,
					Length: uint16(routerIdLength),
				},
				RouterID: routerIdBytes,
			})
	}

	// For BGP
	// TLV Code Point 516 BGP Router-ID
	if nd.BGPRouterID.IsValid() && nd.BGPRouterID.Is4() {
		subTLVs = append(subTLVs,
			&LsTLVBgpRouterID{
				LsTLV: LsTLV{
					Type:   LS_TLV_BGP_ROUTER_ID,
					Length: 4, // 4 is the only valid value.
				},
				RouterID: nd.BGPRouterID,
			})
		if nd.BGPConfederationMember != 0 {
			subTLVs = append(subTLVs,
				&LsTLVBgpConfederationMember{
					LsTLV: LsTLV{
						Type:   LS_TLV_BGP_CONFEDERATION_MEMBER,
						Length: 4, // 4 is the only valid value.
					},
					BgpConfederationMember: nd.BGPConfederationMember,
				})
		}
	}

	subTLVs = append(subTLVs,
		&LsTLVBgpLsID{
			LsTLV: LsTLV{
				Type:   LS_TLV_BGP_LS_ID,
				Length: 4, // 4 is the only valid value.
			},
			BGPLsID: nd.BGPLsID,
		})

	sort.Slice(subTLVs, func(i, j int) bool {
		return subTLVs[i].GetLsTLV().Type < subTLVs[j].GetLsTLV().Type
	})

	ndLength := 0
	for _, val := range subTLVs {
		ndLength += val.Len()
	}

	return LsTLVNodeDescriptor{
		LsTLV: LsTLV{
			Type:   tlvType, // LocalNodeDesc
			Length: uint16(ndLength),
		},
		SubTLVs: subTLVs,
	}
}

type LsAddrPrefix struct {
	Type   LsNLRIType
	Length uint16
	NLRI   LsNLRIInterface
}

func (l *LsAddrPrefix) Len(...*MarshallingOption) int {
	return 4 + int(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_IPV6
		l.NLRI = prefixv6

	// TODO: LS_NLRI_TYPE_SR_POLICY_CANDIDATE_PATH

	case LS_NLRI_TYPE_SRV6_SID:
		srv6sid := &LsSrv6SIDNLRI{}
		srv6sid.Length = l.Length
		srv6sid.NLRIType = LS_NLRI_TYPE_SRV6_SID
		l.NLRI = srv6sid

	default:
		return malformedAttrListErr("Unsupported BGP-LS NLRI")
	}

	if l.NLRI != nil {
		if len(data) < 4+int(l.Length) {
			return malformedAttrListErr("Malformed BGP-LS NLRI: declared length exceeds available data")
		}
		return l.NLRI.DecodeFromBytes(data[4 : 4+int(l.Length)])
	}

	return nil
}

func (l *LsAddrPrefix) Serialize(options ...*MarshallingOption) ([]byte, error) {
	if l.NLRI == nil {
		return nil, errors.New("empty NLRI")
	}

	ser, err := l.NLRI.Serialize()
	if err != nil {
		return nil, err
	}

	buf := make([]byte, 4+len(ser))
	binary.BigEndian.PutUint16(buf[:2], uint16(l.Type))
	binary.BigEndian.PutUint16(buf[2:4], uint16(len(ser)))
	copy(buf[4:], ser)

	return buf, nil
}

func (l *LsAddrPrefix) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type   LsNLRIType `json:"type"`
		Length uint16     `json:"length"`
		NLRI   string     `json:"nlri"`
	}{
		l.Type,
		l.Length,
		l.String(),
	})
}

func (l *LsAddrPrefix) String() string {
	if l.NLRI == nil {
		return "NLRI: (nil)"
	}

	return "NLRI { " + 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   *netip.Addr  `json:"local_router_id_ipv4,omitempty"`
	LocalRouterIDv6 *netip.Addr  `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"`

	// Flexible Algorithm (RFC9351 Section 3 / RFC9350 source).
	// A node MAY advertise more than one FAD, one per algorithm in
	// [128, 255]; the order is the order in which the TLVs appeared.
	FlexAlgoDefs []LsAttributeFlexAlgoDef `json:"flex_algo_defs,omitempty"`
}

// LsAttributeFlexAlgoDef is the structured projection of a single
// FAD TLV (TLV 1039, RFC 9351 Section 3) into the LsAttribute
// surface. Per RFC 9350 Section 5.1 reserved Metric-Type values
// (3..127) are kept verbatim with MetricTypeKnown=false so callers
// can render them as "advertised but unknown" instead of guessing
// semantics.
type LsAttributeFlexAlgoDef struct {
	Algorithm       uint8    `json:"algorithm"`
	MetricType      uint8    `json:"metric_type"`
	MetricTypeKnown bool     `json:"metric_type_known"`
	CalcType        uint8    `json:"calc_type"`
	Priority        uint8    `json:"priority"`
	ExcludeAny      []uint32 `json:"exclude_any_affinity,omitempty"`
	IncludeAny      []uint32 `json:"include_any_affinity,omitempty"`
	IncludeAll      []uint32 `json:"include_all_affinity,omitempty"`
	Flags           []byte   `json:"definition_flags,omitempty"`
	ExcludeSRLG     []uint32 `json:"exclude_srlg,omitempty"`
}

type LsAttributeLink struct {
	Name                          *string                          `json:"name,omitempty"`
	LocalRouterID                 *netip.Addr                      `json:"local_router_id_ipv4,omitempty"`
	LocalRouterIDv6               *netip.Addr                      `json:"local_router_id_ipv6,omitempty"`
	RemoteRouterID                *netip.Addr                      `json:"remote_router_id_ipv4,omitempty"`
	RemoteRouterIDv6              *netip.Addr                      `json:"remote_router_id_ipv6,omitempty"`
	AdminGroup                    *uint32                          `json:"admin_group,omitempty"`
	DefaultTEMetric               *uint32                          `json:"default_te_metric,omitempty"`
	UnidirectionalLinkDelay       *LsUnidirectionalLinkDelay       `json:"unidirectional_link_delay,omitempty"`
	MinMaxUnidirectionalLinkDelay *LsMinMaxUnidirectionalLinkDelay `json:"min_max_unidirectional_link_delay,omitempty"`
	UnidirectionalDelayVariation  *uint32                          `json:"unidirectional_delay_variation,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"`
	Srlgs               *[]uint32   `json:"srlgs,omitempty"`

	// TODO flag
	SrAdjacencySID *uint32 `json:"adjacency_sid,omitempty"`

	Srv6EndXSID *LsSrv6EndXSID `json:"srv6_end_x_sid,omitempty"`
}

type LsAttributePrefix struct {
	IGPFlags *LsIGPFlags `json:"igp_flags,omitempty"`
	Opaque   *[]byte     `json:"opaque,omitempty"`

	// SrPrefixSID holds the SR-MPLS Prefix-SID of the prefix when the
	// originator advertised it with Algorithm = 0 (default SPF). RFC
	// 9085 Section 2.1.1 permits several Prefix-SID TLVs on the same
	// Prefix NLRI, one per algorithm, so SrPrefixSIDs below carries
	// the full list (Algorithm + Flags + SID) without losing entries.
	// SrPrefixSID stays for backward compatibility with the singular
	// Algorithm-0 lookup callers already perform.
	SrPrefixSID  *uint32                `json:"sr_prefix_sid,omitempty"`
	SrPrefixSIDs []LsAttributePrefixSID `json:"sr_prefix_sids,omitempty"`

	// FadPrefixMetrics carries any FAPM (Flexible Algorithm Prefix
	// Metric) Prefix Attribute TLVs (TLV 1044, RFC 9351 Section 4)
	// attached to the prefix. One entry per Flex-Algorithm announced.
	FadPrefixMetrics []LsAttributeFADPrefixMetric `json:"fad_prefix_metrics,omitempty"`
}

// LsAttributePrefixSID projects one Prefix-SID TLV (RFC 9085
// Section 2.1.1) into the LsAttribute surface so callers can read
// Algorithm + Flags without re-parsing the wire bytes.
type LsAttributePrefixSID struct {
	Algorithm uint8  `json:"algorithm"`
	Flags     uint8  `json:"flags"`
	SID       uint32 `json:"sid"`
}

// LsAttributeFADPrefixMetric projects one FAPM TLV (RFC 9351
// Section 4) into the LsAttribute surface.
type LsAttributeFADPrefixMetric struct {
	Algorithm uint8  `json:"algorithm"`
	Flags     uint8  `json:"flags,omitempty"`
	Metric    uint32 `json:"metric"`
}

type LsAttributeBgpPeerSegment struct {
	BgpPeerNodeSid      *LsBgpPeerSegmentSID `json:"bgp_peer_node_sid,omitempty"`
	BgpPeerAdjacencySid *LsBgpPeerSegmentSID `json:"bgp_peer_adjacency_sid,omitempty"`
	BgpPeerSetSid       *LsBgpPeerSegmentSID `json:"bgp_peer_set_sid,omitempty"`
}

type LsAttributeSrv6SID struct {
	Srv6SIDStructure     *LsSrv6SIDStructure     `json:"srv6_sid_structure,omitempty"`
	Srv6BgpPeerNodeSID   *LsSrv6BgpPeerNodeSID   `json:"srv6_bgp_peer_node_sid,omitempty"`
	Srv6EndpointBehavior *LsSrv6EndpointBehavior `json:"srv6_endpoint_behavior,omitempty"`
}

type LsAttribute struct {
	Node           LsAttributeNode           `json:"node"`
	Link           LsAttributeLink           `json:"link"`
	Prefix         LsAttributePrefix         `json:"prefix"`
	BgpPeerSegment LsAttributeBgpPeerSegment `json:"bgp_peer_segment"`
	Srv6SID        LsAttributeSrv6SID        `json:"srv6_sid"`
}

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 *LsTLVSrlg:
			l.Link.Srlgs = &v.Srlgs

		case *LsTLVTEDefaultMetric:
			l.Link.DefaultTEMetric = &v.Metric

		case *LsTLVUnidirectionalLinkDelay:
			l.Link.UnidirectionalLinkDelay = v.Extract()

		case *LsTLVMinMaxUnidirectionalLinkDelay:
			l.Link.MinMaxUnidirectionalLinkDelay = v.Extract()

		case *LsTLVUnidirectionalDelayVariation:
			l.Link.UnidirectionalDelayVariation = &v.DelayVariation

		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 *LsTLVSrv6EndXSID:
			l.Link.Srv6EndXSID = v.Extract()

		case *LsTLVIGPFlags:
			l.Prefix.IGPFlags = v.Extract()

		case *LsTLVOpaquePrefixAttr:
			l.Prefix.Opaque = &v.Attr

		case *LsTLVPrefixSID:
			// RFC 9085 Section 2.1.1 allows multiple Prefix-SID TLVs
			// on the same Prefix NLRI (one per SR algorithm). Append
			// every observation here so Algorithm 128..255 Flex-Algo
			// SIDs survive; mirror the Algorithm-0 SID into
			// SrPrefixSID for the singular legacy field.
			l.Prefix.SrPrefixSIDs = append(l.Prefix.SrPrefixSIDs, LsAttributePrefixSID{
				Algorithm: v.Algorithm,
				Flags:     v.Flags,
				SID:       v.SID,
			})
			if v.Algorithm == 0 && l.Prefix.SrPrefixSID == nil {
				sid := v.SID
				l.Prefix.SrPrefixSID = &sid
			}

		case *LsTLVFlexAlgoDef:
			// RFC 9351 Section 3: one FAD per algorithm; aggregate
			// every observation into the slice in the order they
			// arrived on the wire.
			l.Node.FlexAlgoDefs = append(l.Node.FlexAlgoDefs, LsAttributeFlexAlgoDef{
				Algorithm:       v.Algorithm,
				MetricType:      v.MetricType,
				MetricTypeKnown: v.MetricType <= 2, // RFC 9350 Section 5.1 known set: 0=IGP, 1=MinDelay, 2=TE
				CalcType:        v.CalcType,
				Priority:        v.Priority,
				ExcludeAny:      v.ExcludeAny,
				IncludeAny:      v.IncludeAny,
				IncludeAll:      v.IncludeAll,
				Flags:           v.Flags,
				ExcludeSRLG:     v.ExcludeSRLG,
			})

		case *LsTLVFADPrefixMetric:
			l.Prefix.FadPrefixMetrics = append(l.Prefix.FadPrefixMetrics, LsAttributeFADPrefixMetric{
				Algorithm: v.Algorithm,
				Flags:     v.Flags,
				Metric:    v.Metric,
			})

		case *LsTLVPeerNodeSID:
			l.BgpPeerSegment.BgpPeerNodeSid = v.Extract()

		case *LsTLVPeerAdjacencySID:
			l.BgpPeerSegment.BgpPeerAdjacencySid = v.Extract()

		case *LsTLVPeerSetSID:
			l.BgpPeerSegment.BgpPeerSetSid = v.Extract()

		case *LsTLVSrv6SIDStructure:
			l.Srv6SID.Srv6SIDStructure = v.Extract()

		case *LsTLVSrv6BgpPeerNodeSID:
			l.Srv6SID.Srv6BgpPeerNodeSID = v.Extract()

		case *LsTLVSrv6EndpointBehavior:
			l.Srv6SID.Srv6EndpointBehavior = v.Extract()
		}
	}

	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{}

		// Flex-Algorithm Definition (RFC 9351 Section 3).
		case LS_TLV_FLEX_ALGO_DEF:
			tlv = &LsTLVFlexAlgoDef{}

		// Flex-Algorithm Prefix Metric (RFC 9351 Section 4).
		// Top-level Prefix Attribute, not nested inside TLV 1039.
		case LS_TLV_FAD_PREFIX_METRIC:
			tlv = &LsTLVFADPrefixMetric{}

		// 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_SRLG:
			tlv = &LsTLVSrlg{}

		case LS_TLV_TE_DEFAULT_METRIC:
			tlv = &LsTLVTEDefaultMetric{}

		case LS_TLV_UNIDIRECTIONAL_LINK_DELAY:
			tlv = &LsTLVUnidirectionalLinkDelay{}

		case LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY:
			tlv = &LsTLVMinMaxUnidirectionalLinkDelay{}

		case LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION:
			tlv = &LsTLVUnidirectionalDelayVariation{}

		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{}

		// BGP-EPE related TLVs (https://tools.ietf.org/html/rfc9086)
		case LS_TLV_PEER_NODE_SID:
			tlv = &LsTLVPeerNodeSID{}

		case LS_TLV_PEER_ADJACENCY_SID:
			tlv = &LsTLVPeerAdjacencySID{}

		case LS_TLV_PEER_SET_SID:
			tlv = &LsTLVPeerSetSID{}

		case LS_TLV_SRV6_END_X_SID:
			tlv = &LsTLVSrv6EndXSID{}

		case LS_TLV_SRV6_SID_STRUCTURE:
			tlv = &LsTLVSrv6SIDStructure{}

		case LS_TLV_SRV6_BGP_PEER_NODE_SID:
			tlv = &LsTLVSrv6BgpPeerNodeSID{}

		case LS_TLV_SRV6_ENDPOINT_BEHAVIOR:
			tlv = &LsTLVSrv6EndpointBehavior{}

		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...)
	}

	result, err := p.PathAttribute.Serialize(buf, options...)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (p *PathAttributeLs) String() string {
	var buf bytes.Buffer

	for _, tlv := range p.TLVs {
		buf.WriteString(tlv.String() + " ")
	}
	if buf.String() != "" {
		return "{LsAttributes: " + buf.String() + "}"
	}
	return ""
}

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(),
	})
}

type Family uint32

func NewFamily(afi uint16, safi uint8) Family {
	return Family(uint32(afi)<<16 | uint32(safi))
}

func (f Family) Afi() uint16 {
	return uint16(f >> 16)
}

func (f Family) Safi() uint8 {
	return uint8(f & 0xff)
}

func (f Family) String() string {
	if n, y := AddressFamilyNameMap[f]; y {
		return n
	}
	return fmt.Sprintf("UnknownFamily(%d)", f)
}

const (
	RF_IPv4_UC        Family = AFI_IP<<16 | SAFI_UNICAST
	RF_IPv6_UC        Family = AFI_IP6<<16 | SAFI_UNICAST
	RF_IPv4_MC        Family = AFI_IP<<16 | SAFI_MULTICAST
	RF_IPv6_MC        Family = AFI_IP6<<16 | SAFI_MULTICAST
	RF_IPv4_VPN       Family = AFI_IP<<16 | SAFI_MPLS_VPN
	RF_IPv6_VPN       Family = AFI_IP6<<16 | SAFI_MPLS_VPN
	RF_IPv4_VPN_MC    Family = AFI_IP<<16 | SAFI_MPLS_VPN_MULTICAST
	RF_IPv6_VPN_MC    Family = AFI_IP6<<16 | SAFI_MPLS_VPN_MULTICAST
	RF_IPv4_MPLS      Family = AFI_IP<<16 | SAFI_MPLS_LABEL
	RF_IPv6_MPLS      Family = AFI_IP6<<16 | SAFI_MPLS_LABEL
	RF_VPLS           Family = AFI_L2VPN<<16 | SAFI_VPLS
	RF_EVPN           Family = AFI_L2VPN<<16 | SAFI_EVPN
	RF_RTC_UC         Family = AFI_IP<<16 | SAFI_ROUTE_TARGET_CONSTRAINTS
	RF_IPv4_ENCAP     Family = AFI_IP<<16 | SAFI_ENCAPSULATION
	RF_IPv6_ENCAP     Family = AFI_IP6<<16 | SAFI_ENCAPSULATION
	RF_FS_IPv4_UC     Family = AFI_IP<<16 | SAFI_FLOW_SPEC_UNICAST
	RF_FS_IPv4_VPN    Family = AFI_IP<<16 | SAFI_FLOW_SPEC_VPN
	RF_FS_IPv6_UC     Family = AFI_IP6<<16 | SAFI_FLOW_SPEC_UNICAST
	RF_FS_IPv6_VPN    Family = AFI_IP6<<16 | SAFI_FLOW_SPEC_VPN
	RF_FS_L2_VPN      Family = AFI_L2VPN<<16 | SAFI_FLOW_SPEC_VPN
	RF_OPAQUE         Family = AFI_OPAQUE<<16 | SAFI_KEY_VALUE
	RF_LS             Family = AFI_LS<<16 | SAFI_LS
	RF_SR_POLICY_IPv4 Family = AFI_IP<<16 | SAFI_SRPOLICY
	RF_SR_POLICY_IPv6 Family = AFI_IP6<<16 | SAFI_SRPOLICY
	RF_MUP_IPv4       Family = AFI_IP<<16 | SAFI_MUP
	RF_MUP_IPv6       Family = AFI_IP6<<16 | SAFI_MUP
)

var AddressFamilyNameMap = map[Family]string{
	RF_IPv4_UC:        "ipv4-unicast",
	RF_IPv6_UC:        "ipv6-unicast",
	RF_IPv4_MC:        "ipv4-multicast",
	RF_IPv6_MC:        "ipv6-multicast",
	RF_IPv4_MPLS:      "ipv4-labelled-unicast",
	RF_IPv6_MPLS:      "ipv6-labelled-unicast",
	RF_IPv4_VPN:       "l3vpn-ipv4-unicast",
	RF_IPv6_VPN:       "l3vpn-ipv6-unicast",
	RF_IPv4_VPN_MC:    "l3vpn-ipv4-multicast",
	RF_IPv6_VPN_MC:    "l3vpn-ipv6-multicast",
	RF_VPLS:           "l2vpn-vpls",
	RF_EVPN:           "l2vpn-evpn",
	RF_RTC_UC:         "rtc",
	RF_IPv4_ENCAP:     "ipv4-encap",
	RF_IPv6_ENCAP:     "ipv6-encap",
	RF_FS_IPv4_UC:     "ipv4-flowspec",
	RF_FS_IPv4_VPN:    "l3vpn-ipv4-flowspec",
	RF_FS_IPv6_UC:     "ipv6-flowspec",
	RF_FS_IPv6_VPN:    "l3vpn-ipv6-flowspec",
	RF_FS_L2_VPN:      "l2vpn-flowspec",
	RF_OPAQUE:         "opaque",
	RF_LS:             "ls",
	RF_SR_POLICY_IPv4: "ipv4-srpolicy",
	RF_SR_POLICY_IPv6: "ipv6-srpolicy",
	RF_MUP_IPv4:       "ipv4-mup",
	RF_MUP_IPv6:       "ipv6-mup",
}

var AddressFamilyValueMap = map[string]Family{
	AddressFamilyNameMap[RF_IPv4_UC]:        RF_IPv4_UC,
	AddressFamilyNameMap[RF_IPv6_UC]:        RF_IPv6_UC,
	AddressFamilyNameMap[RF_IPv4_MC]:        RF_IPv4_MC,
	AddressFamilyNameMap[RF_IPv6_MC]:        RF_IPv6_MC,
	AddressFamilyNameMap[RF_IPv4_MPLS]:      RF_IPv4_MPLS,
	AddressFamilyNameMap[RF_IPv6_MPLS]:      RF_IPv6_MPLS,
	AddressFamilyNameMap[RF_IPv4_VPN]:       RF_IPv4_VPN,
	AddressFamilyNameMap[RF_IPv6_VPN]:       RF_IPv6_VPN,
	AddressFamilyNameMap[RF_IPv4_VPN_MC]:    RF_IPv4_VPN_MC,
	AddressFamilyNameMap[RF_IPv6_VPN_MC]:    RF_IPv6_VPN_MC,
	AddressFamilyNameMap[RF_VPLS]:           RF_VPLS,
	AddressFamilyNameMap[RF_EVPN]:           RF_EVPN,
	AddressFamilyNameMap[RF_RTC_UC]:         RF_RTC_UC,
	AddressFamilyNameMap[RF_IPv4_ENCAP]:     RF_IPv4_ENCAP,
	AddressFamilyNameMap[RF_IPv6_ENCAP]:     RF_IPv6_ENCAP,
	AddressFamilyNameMap[RF_FS_IPv4_UC]:     RF_FS_IPv4_UC,
	AddressFamilyNameMap[RF_FS_IPv4_VPN]:    RF_FS_IPv4_VPN,
	AddressFamilyNameMap[RF_FS_IPv6_UC]:     RF_FS_IPv6_UC,
	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,
	AddressFamilyNameMap[RF_SR_POLICY_IPv4]: RF_SR_POLICY_IPv4,
	AddressFamilyNameMap[RF_SR_POLICY_IPv6]: RF_SR_POLICY_IPv6,
	AddressFamilyNameMap[RF_MUP_IPv4]:       RF_MUP_IPv4,
	AddressFamilyNameMap[RF_MUP_IPv6]:       RF_MUP_IPv6,
}

func GetFamily(name string) (Family, error) {
	if v, ok := AddressFamilyValueMap[name]; ok {
		return v, nil
	}
	return Family(0), fmt.Errorf("%s isn't a valid route family name", name)
}

func NLRIFromSlice(family Family, buf []byte, options ...*MarshallingOption) (nlri NLRI, err error) {
	switch family {
	case RF_IPv4_UC, RF_IPv4_MC, RF_IPv6_UC, RF_IPv6_MC:
		addrlen := net.IPv4len
		if family.Afi() == AFI_IP6 {
			addrlen = net.IPv6len
		}
		nlri := &IPAddrPrefix{}
		err := nlri.decodeFromBytes(buf, addrlen, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_IPv4_VPN, RF_IPv6_VPN, RF_IPv4_VPN_MC, RF_IPv6_VPN_MC:
		addrlen := net.IPv4len
		if family.Afi() == AFI_IP6 {
			addrlen = net.IPv6len
		}
		nlri := &LabeledVPNIPAddrPrefix{}
		err := nlri.decodeFromBytes(buf, addrlen, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_IPv4_MPLS, RF_IPv6_MPLS:
		addrlen := net.IPv4len
		if family.Afi() == AFI_IP6 {
			addrlen = net.IPv6len
		}
		nlri := &LabeledIPAddrPrefix{}
		err := nlri.decodeFromBytes(buf, addrlen, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_EVPN:
		nlri := &EVPNNLRI{}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_VPLS:
		nlri := &VPLSNLRI{}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_SR_POLICY_IPv4, RF_SR_POLICY_IPv6:
		// TODO (sbezverk) Add processing SR Policy NLRI
		nlri := &SRPolicyNLRI{
			rf: family,
		}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_RTC_UC:
		nlri := &RouteTargetMembershipNLRI{}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_IPv4_ENCAP, RF_IPv6_ENCAP:
		nlri := &EncapNLRI{}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_FS_IPv4_UC, RF_FS_IPv6_UC, RF_FS_IPv4_VPN, RF_FS_IPv6_VPN, RF_FS_L2_VPN:
		nlri := &FlowSpecNLRI{rf: family}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_OPAQUE:
		nlri := &OpaqueNLRI{}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_LS:
		nlri := &LsAddrPrefix{}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	case RF_MUP_IPv4, RF_MUP_IPv6:
		nlri := &MUPNLRI{
			Afi: family.Afi(),
		}
		err := nlri.decodeFromBytes(buf, options...)
		if err != nil {
			return nil, err
		}
		return nlri, nil
	default:
		err = fmt.Errorf("unknown route family. AFI: %d, SAFI: %d", family.Afi(), family.Safi())
	}
	return nlri, err
}

type BGPAttrFlag uint8

const (
	BGP_ATTR_FLAG_EXTENDED_LENGTH BGPAttrFlag = 1 << 4
	BGP_ATTR_FLAG_PARTIAL         BGPAttrFlag = 1 << 5
	BGP_ATTR_FLAG_TRANSITIVE      BGPAttrFlag = 1 << 6
	BGP_ATTR_FLAG_OPTIONAL        BGPAttrFlag = 1 << 7
)

func (f BGPAttrFlag) String() string {
	strs := make([]string, 0, 4)
	if f&BGP_ATTR_FLAG_EXTENDED_LENGTH > 0 {
		strs = append(strs, "EXTENDED_LENGTH")
	}
	if f&BGP_ATTR_FLAG_PARTIAL > 0 {
		strs = append(strs, "PARTIAL")
	}
	if f&BGP_ATTR_FLAG_TRANSITIVE > 0 {
		strs = append(strs, "TRANSITIVE")
	}
	if f&BGP_ATTR_FLAG_OPTIONAL > 0 {
		strs = append(strs, "OPTIONAL")
	}
	return strings.Join(strs, "|")
}

//go:generate stringer -type=BGPAttrType
type BGPAttrType uint8

const (
	_ BGPAttrType = iota
	BGP_ATTR_TYPE_ORIGIN
	BGP_ATTR_TYPE_AS_PATH
	BGP_ATTR_TYPE_NEXT_HOP
	BGP_ATTR_TYPE_MULTI_EXIT_DISC
	BGP_ATTR_TYPE_LOCAL_PREF
	BGP_ATTR_TYPE_ATOMIC_AGGREGATE
	BGP_ATTR_TYPE_AGGREGATOR
	BGP_ATTR_TYPE_COMMUNITIES
	BGP_ATTR_TYPE_ORIGINATOR_ID
	BGP_ATTR_TYPE_CLUSTER_LIST
	_
	_
	_
	BGP_ATTR_TYPE_MP_REACH_NLRI // = 14
	BGP_ATTR_TYPE_MP_UNREACH_NLRI
	BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
	BGP_ATTR_TYPE_AS4_PATH
	BGP_ATTR_TYPE_AS4_AGGREGATOR
	_
	_
	_
	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_LS                          // = 29
	BGP_ATTR_TYPE_LARGE_COMMUNITY BGPAttrType = 32
	BGP_ATTR_TYPE_PREFIX_SID      BGPAttrType = 40
)

// NOTIFICATION Error Code  RFC 4271 4.5.
const (
	_ = iota
	BGP_ERROR_MESSAGE_HEADER_ERROR
	BGP_ERROR_OPEN_MESSAGE_ERROR
	BGP_ERROR_UPDATE_MESSAGE_ERROR
	BGP_ERROR_HOLD_TIMER_EXPIRED
	BGP_ERROR_FSM_ERROR
	BGP_ERROR_CEASE
	BGP_ERROR_ROUTE_REFRESH_MESSAGE_ERROR
)

// NOTIFICATION Error Subcode for BGP_ERROR_MESSAGE_HEADER_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED
	BGP_ERROR_SUB_BAD_MESSAGE_LENGTH
	BGP_ERROR_SUB_BAD_MESSAGE_TYPE
)

// NOTIFICATION Error Subcode for BGP_ERROR_OPEN_MESSAGE_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER
	BGP_ERROR_SUB_BAD_PEER_AS
	BGP_ERROR_SUB_BAD_BGP_IDENTIFIER
	BGP_ERROR_SUB_UNSUPPORTED_OPTIONAL_PARAMETER
	BGP_ERROR_SUB_DEPRECATED_AUTHENTICATION_FAILURE
	BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME
	BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY
)

// NOTIFICATION Error Subcode for BGP_ERROR_UPDATE_MESSAGE_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST
	BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
	BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE
	BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR
	BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR
	BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE
	BGP_ERROR_SUB_DEPRECATED_ROUTING_LOOP
	BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE
	BGP_ERROR_SUB_OPTIONAL_ATTRIBUTE_ERROR
	BGP_ERROR_SUB_INVALID_NETWORK_FIELD
	BGP_ERROR_SUB_MALFORMED_AS_PATH
)

// NOTIFICATION Error Subcode for BGP_ERROR_HOLD_TIMER_EXPIRED
const (
	_ = iota
	BGP_ERROR_SUB_HOLD_TIMER_EXPIRED
)

// NOTIFICATION Error Subcode for BGP_ERROR_FSM_ERROR
const (
	_ = iota
	BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE
	BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE
	BGP_ERROR_SUB_RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE
)

// NOTIFICATION Error Subcode for BGP_ERROR_CEASE  (RFC 4486)
const (
	_ = iota
	BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED
	BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN
	BGP_ERROR_SUB_PEER_DECONFIGURED
	BGP_ERROR_SUB_ADMINISTRATIVE_RESET
	BGP_ERROR_SUB_CONNECTION_REJECTED
	BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
	BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
	BGP_ERROR_SUB_OUT_OF_RESOURCES
	BGP_ERROR_SUB_HARD_RESET // RFC8538
)

// Constants for BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN and BGP_ERROR_SUB_ADMINISTRATIVE_RESET
const (
	BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX = 128
)

// NOTIFICATION Error Subcode for BGP_ERROR_ROUTE_REFRESH
const (
	_ = iota
	BGP_ERROR_SUB_INVALID_MESSAGE_LENGTH
)

type NotificationErrorCode uint16

func (c NotificationErrorCode) String() string {
	code := uint8(uint16(c) >> 8)
	subcode := uint8(uint16(c) & 0xff)
	UNDEFINED := "undefined"
	codeStr := UNDEFINED
	subcodeList := []string{}
	switch code {
	case BGP_ERROR_MESSAGE_HEADER_ERROR:
		codeStr = "header"
		subcodeList = []string{
			UNDEFINED,
			"connection not synchronized",
			"bad message length",
			"bad message type",
		}
	case BGP_ERROR_OPEN_MESSAGE_ERROR:
		codeStr = "open"
		subcodeList = []string{
			UNDEFINED,
			"unsupported version number",
			"bad peer as",
			"bad bgp identifier",
			"unsupported optional parameter",
			"deprecated authentication failure",
			"unacceptable hold time",
			"unsupported capability",
		}
	case BGP_ERROR_UPDATE_MESSAGE_ERROR:
		codeStr = "update"
		subcodeList = []string{
			UNDEFINED,
			"malformed attribute list",
			"unrecognized well known attribute",
			"missing well known attribute",
			"attribute flags error",
			"attribute length error",
			"invalid origin attribute",
			"deprecated routing loop",
			"invalid next hop attribute",
			"optional attribute error",
			"invalid network field",
			"sub malformed as path",
		}
	case BGP_ERROR_HOLD_TIMER_EXPIRED:
		codeStr = "hold timer expired"
		subcodeList = []string{
			UNDEFINED,
			"hold timer expired",
		}
	case BGP_ERROR_FSM_ERROR:
		codeStr = "fsm"
		subcodeList = []string{
			UNDEFINED,
			"receive unexpected message in opensent state",
			"receive unexpected message in openconfirm state",
			"receive unexpected message in established state",
		}
	case BGP_ERROR_CEASE:
		codeStr = "cease"
		subcodeList = []string{
			UNDEFINED,
			"maximum number of prefixes reached",
			"administrative shutdown",
			"peer deconfigured",
			"administrative reset",
			"connection rejected",
			"other configuration change",
			"connection collision resolution",
			"out of resources",
		}
	case BGP_ERROR_ROUTE_REFRESH_MESSAGE_ERROR:
		codeStr = "route refresh"
		subcodeList = []string{"invalid message length"}
	}
	subcodeStr := func(idx uint8, l []string) string {
		if len(l) == 0 || int(idx) > len(l)-1 {
			return UNDEFINED
		}
		return l[idx]
	}(subcode, subcodeList)
	return fmt.Sprintf("code %v(%v) subcode %v(%v)", code, codeStr, subcode, subcodeStr)
}

func NewNotificationErrorCode(code, subcode uint8) NotificationErrorCode {
	return NotificationErrorCode(uint16(code)<<8 | uint16(subcode))
}

var PathAttrFlags map[BGPAttrType]BGPAttrFlag = map[BGPAttrType]BGPAttrFlag{
	BGP_ATTR_TYPE_ORIGIN:                   BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_AS_PATH:                  BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_NEXT_HOP:                 BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_MULTI_EXIT_DISC:          BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_LOCAL_PREF:               BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_ATOMIC_AGGREGATE:         BGP_ATTR_FLAG_TRANSITIVE,
	BGP_ATTR_TYPE_AGGREGATOR:               BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_COMMUNITIES:              BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_ORIGINATOR_ID:            BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_CLUSTER_LIST:             BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_MP_REACH_NLRI:            BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_MP_UNREACH_NLRI:          BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_EXTENDED_COMMUNITIES:     BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_AS4_PATH:                 BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_AS4_AGGREGATOR:           BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_PMSI_TUNNEL:              BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	BGP_ATTR_TYPE_TUNNEL_ENCAP:             BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
	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,
	BGP_ATTR_TYPE_PREFIX_SID:               BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
}

// getPathAttrFlags returns BGP Path Attribute flags value from its type and
// length (byte length of value field).
func getPathAttrFlags(typ BGPAttrType, length int) BGPAttrFlag {
	flags := PathAttrFlags[typ]
	if length > 255 {
		flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
	}
	return flags
}

type PathAttributeInterface interface {
	DecodeFromBytes([]byte, ...*MarshallingOption) error
	Serialize(...*MarshallingOption) ([]byte, error)
	Len(...*MarshallingOption) int
	GetFlags() BGPAttrFlag
	GetType() BGPAttrType
	String() string
	MarshalJSON() ([]byte, error)
	Flat() map[string]string
}

type PathAttribute struct {
	Flags  BGPAttrFlag
	Type   BGPAttrType
	Length uint16 // length of Value
}

func (p *PathAttribute) Len(options ...*MarshallingOption) int {
	if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
		return 4 + int(p.Length)
	}
	return 3 + int(p.Length)
}

func (p *PathAttribute) GetFlags() BGPAttrFlag {
	return p.Flags
}

func (p *PathAttribute) GetType() BGPAttrType {
	return p.Type
}

func (p *PathAttribute) DecodeFromBytes(data []byte, options ...*MarshallingOption) (value []byte, err error) {
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
	if len(data) < 2 {
		return nil, NewMessageError(eCode, eSubCode, data, "attribute header length is short")
	}
	p.Flags = BGPAttrFlag(data[0])
	p.Type = BGPAttrType(data[1])

	if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
		if len(data) < 4 {
			return nil, NewMessageError(eCode, eSubCode, data, "attribute header length is short")
		}
		p.Length = binary.BigEndian.Uint16(data[2:4])
		data = data[4:]
	} else {
		if len(data) < 3 {
			return nil, NewMessageError(eCode, eSubCode, data, "attribute header length is short")
		}
		p.Length = uint16(data[2])
		data = data[3:]
	}
	if len(data) < int(p.Length) {
		return nil, NewMessageError(eCode, eSubCode, data, "attribute value length is short")
	}

	if eMsg := validatePathAttributeFlags(p.Type, p.Flags); eMsg != "" {
		return nil, NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data, eMsg)
	}

	return data[:p.Length], nil
}

func (p *PathAttribute) Serialize(value []byte, options ...*MarshallingOption) ([]byte, error) {
	// Note: Do not update "p.Flags" and "p.Length" to avoid data race.
	flags := p.Flags
	length := uint16(len(value))
	if flags&BGP_ATTR_FLAG_EXTENDED_LENGTH == 0 && length > 255 {
		flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
	}
	var buf []byte
	if flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
		buf = append(make([]byte, 4), value...)
		binary.BigEndian.PutUint16(buf[2:4], length)
	} else {
		buf = append(make([]byte, 3), value...)
		buf[2] = byte(length)
	}
	buf[0] = uint8(flags)
	buf[1] = uint8(p.Type)
	return buf, nil
}

type PathAttributeOrigin struct {
	PathAttribute
	Value uint8
}

func (p *PathAttributeOrigin) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length != 1 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "Origin attribute length is incorrect")
	}
	p.Value = value[0]
	return nil
}

func (p *PathAttributeOrigin) Serialize(options ...*MarshallingOption) ([]byte, error) {
	return p.PathAttribute.Serialize([]byte{p.Value}, options...)
}

func (p *PathAttributeOrigin) String() string {
	typ := "-"
	switch p.Value {
	case BGP_ORIGIN_ATTR_TYPE_IGP:
		typ = "i"
	case BGP_ORIGIN_ATTR_TYPE_EGP:
		typ = "e"
	case BGP_ORIGIN_ATTR_TYPE_INCOMPLETE:
		typ = "?"
	}
	return "{Origin: " + typ + "}"
}

func (p *PathAttributeOrigin) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value uint8       `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeOrigin(value uint8) *PathAttributeOrigin {
	t := BGP_ATTR_TYPE_ORIGIN
	return &PathAttributeOrigin{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: 1,
		},
		Value: value,
	}
}

type AsPathParamFormat struct {
	start     string
	end       string
	separator string
}

var asPathParamFormatMap = map[uint8]*AsPathParamFormat{
	BGP_ASPATH_ATTR_TYPE_SET:        {"{", "}", ","},
	BGP_ASPATH_ATTR_TYPE_SEQ:        {"", "", " "},
	BGP_ASPATH_ATTR_TYPE_CONFED_SET: {"(", ")", " "},
	BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: {"[", "]", ","},
}

type AsPathParamInterface interface {
	GetType() uint8
	GetAS() []uint32
	Serialize() ([]byte, error)
	DecodeFromBytes([]byte) error
	Len() int
	ASLen() int
	MarshalJSON() ([]byte, error)
	String() string
}

func AsPathString(aspath *PathAttributeAsPath) string {
	s := bytes.NewBuffer(make([]byte, 0, 64))
	for i, param := range aspath.Value {
		segType := param.GetType()
		asList := param.GetAS()
		if i != 0 {
			s.WriteString(" ")
		}

		sep := " "
		switch segType {
		case BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
			s.WriteString("(")
		case BGP_ASPATH_ATTR_TYPE_CONFED_SET:
			s.WriteString("[")
			sep = ","
		case BGP_ASPATH_ATTR_TYPE_SET:
			s.WriteString("{")
			sep = ","
		}
		for j, as := range asList {
			fmt.Fprintf(s, "%d", as)
			if j != len(asList)-1 {
				s.WriteString(sep)
			}
		}
		switch segType {
		case BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
			s.WriteString(")")
		case BGP_ASPATH_ATTR_TYPE_CONFED_SET:
			s.WriteString("]")
		case BGP_ASPATH_ATTR_TYPE_SET:
			s.WriteString("}")
		}
	}
	return s.String()
}

type AsPathParam struct {
	Type uint8
	Num  uint8
	AS   []uint16
}

func (a *AsPathParam) GetType() uint8 {
	return a.Type
}

func (a *AsPathParam) GetAS() []uint32 {
	nums := make([]uint32, 0, len(a.AS))
	for _, as := range a.AS {
		nums = append(nums, uint32(as))
	}
	return nums
}

func (a *AsPathParam) Serialize() ([]byte, error) {
	buf := make([]byte, 2+len(a.AS)*2)
	buf[0] = a.Type
	buf[1] = a.Num
	for j, as := range a.AS {
		binary.BigEndian.PutUint16(buf[2+j*2:], as)
	}
	return buf, nil
}

func (a *AsPathParam) DecodeFromBytes(data []byte) error {
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH)
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "AS param header length is short")
	}
	a.Type = data[0]
	a.Num = data[1]
	data = data[2:]
	if len(data) < int(a.Num)*2 {
		return NewMessageError(eCode, eSubCode, nil, "AS param data length is short")
	}
	for range a.Num {
		a.AS = append(a.AS, binary.BigEndian.Uint16(data))
		data = data[2:]
	}
	return nil
}

func (a *AsPathParam) Len() int {
	return 2 + len(a.AS)*2
}

func (a *AsPathParam) ASLen() int {
	switch a.Type {
	case BGP_ASPATH_ATTR_TYPE_SEQ:
		return len(a.AS)
	case BGP_ASPATH_ATTR_TYPE_SET:
		return 1
	case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
		return 0
	}
	return 0
}

func (a *AsPathParam) String() string {
	format, ok := asPathParamFormatMap[a.Type]
	if !ok {
		return fmt.Sprintf("%v", a.AS)
	}
	aspath := make([]string, 0, len(a.AS))
	for _, asn := range a.AS {
		aspath = append(aspath, strconv.FormatUint(uint64(asn), 10))
	}
	s := bytes.NewBuffer(make([]byte, 0, 32))
	s.WriteString(format.start)
	s.WriteString(strings.Join(aspath, format.separator))
	s.WriteString(format.end)
	return s.String()
}

func (a *AsPathParam) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type uint8    `json:"segment_type"`
		Num  uint8    `json:"num"`
		AS   []uint16 `json:"asns"`
	}{
		Type: a.Type,
		Num:  a.Num,
		AS:   a.AS,
	})
}

func NewAsPathParam(segType uint8, as []uint16) *AsPathParam {
	return &AsPathParam{
		Type: segType,
		Num:  uint8(len(as)),
		AS:   as,
	}
}

type As4PathParam struct {
	Type uint8
	Num  uint8
	AS   []uint32
}

func (a *As4PathParam) GetType() uint8 {
	return a.Type
}

func (a *As4PathParam) GetAS() []uint32 {
	return a.AS
}

func (a *As4PathParam) Serialize() ([]byte, error) {
	buf := make([]byte, 2+len(a.AS)*4)
	buf[0] = a.Type
	buf[1] = a.Num
	for j, as := range a.AS {
		binary.BigEndian.PutUint32(buf[2+j*4:], as)
	}
	return buf, nil
}

func (a *As4PathParam) DecodeFromBytes(data []byte) error {
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH)
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "AS4 param header length is short")
	}
	a.Type = data[0]
	a.Num = data[1]
	data = data[2:]
	if len(data) < int(a.Num)*4 {
		return NewMessageError(eCode, eSubCode, nil, "AS4 param data length is short")
	}
	for range a.Num {
		a.AS = append(a.AS, binary.BigEndian.Uint32(data))
		data = data[4:]
	}
	return nil
}

func (a *As4PathParam) Len() int {
	return 2 + len(a.AS)*4
}

func (a *As4PathParam) ASLen() int {
	switch a.Type {
	case BGP_ASPATH_ATTR_TYPE_SEQ:
		return len(a.AS)
	case BGP_ASPATH_ATTR_TYPE_SET:
		return 1
	case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
		return 0
	}
	return 0
}

func (a *As4PathParam) String() string {
	format, ok := asPathParamFormatMap[a.Type]
	if !ok {
		return fmt.Sprintf("%v", a.AS)
	}
	aspath := make([]string, 0, len(a.AS))
	for _, asn := range a.AS {
		aspath = append(aspath, strconv.FormatUint(uint64(asn), 10))
	}
	s := bytes.NewBuffer(make([]byte, 0, 32))
	s.WriteString(format.start)
	s.WriteString(strings.Join(aspath, format.separator))
	s.WriteString(format.end)
	return s.String()
}

func (a *As4PathParam) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type uint8    `json:"segment_type"`
		Num  uint8    `json:"num"`
		AS   []uint32 `json:"asns"`
	}{
		Type: a.Type,
		Num:  a.Num,
		AS:   a.AS,
	})
}

func NewAs4PathParam(segType uint8, as []uint32) *As4PathParam {
	return &As4PathParam{
		Type: segType,
		Num:  uint8(len(as)),
		AS:   as,
	}
}

type PathAttributeAsPath struct {
	PathAttribute
	Value []AsPathParamInterface
}

func (p *PathAttributeAsPath) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length == 0 {
		// ibgp or something
		return nil
	}
	isAs4, err := validateAsPathValueBytes(value, options...)
	if err != nil {
		err.(*MessageError).Data, _ = p.PathAttribute.Serialize(value, options...)
		return err
	}
	for len(value) > 0 {
		var tuple AsPathParamInterface
		if isAs4 {
			tuple = &As4PathParam{}
		} else {
			tuple = &AsPathParam{}
		}
		err := tuple.DecodeFromBytes(value)
		if err != nil {
			return err
		}
		p.Value = append(p.Value, tuple)
		value = value[tuple.Len():]
	}
	return nil
}

func (p *PathAttributeAsPath) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0)
	for _, v := range p.Value {
		vbuf, err := v.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, vbuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeAsPath) String() string {
	params := make([]string, 0, len(p.Value))
	for _, param := range p.Value {
		params = append(params, param.String())
	}
	return "{AsPath: " + strings.Join(params, " ") + "}"
}

func (p *PathAttributeAsPath) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType            `json:"type"`
		Value []AsPathParamInterface `json:"as_paths"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeAsPath(value []AsPathParamInterface) *PathAttributeAsPath {
	var l int
	for _, v := range value {
		l += v.Len()
	}
	t := BGP_ATTR_TYPE_AS_PATH
	return &PathAttributeAsPath{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Value: value,
	}
}

type PathAttributeNextHop struct {
	PathAttribute
	Value netip.Addr
}

func (p *PathAttributeNextHop) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length != 4 && p.Length != 16 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "nexthop length isn't correct")
	}
	var ok bool
	p.Value, ok = netip.AddrFromSlice(value)
	if !ok {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE)
		return NewMessageError(eCode, eSubCode, nil, "invalid nexthop address")
	}
	return nil
}

func (p *PathAttributeNextHop) Serialize(options ...*MarshallingOption) ([]byte, error) {
	return p.PathAttribute.Serialize(p.Value.AsSlice(), options...)
}

func (p *PathAttributeNextHop) String() string {
	return "{Nexthop: " + p.Value.String() + "}"
}

func (p *PathAttributeNextHop) MarshalJSON() ([]byte, error) {
	value := "0.0.0.0"
	if p.Value.IsValid() {
		value = p.Value.String()
	}
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value string      `json:"nexthop"`
	}{
		Type:  p.GetType(),
		Value: value,
	})
}

func NewPathAttributeNextHop(addr netip.Addr) (*PathAttributeNextHop, error) {
	if !addr.IsValid() {
		return nil, errors.New("invalid address")
	}
	t := BGP_ATTR_TYPE_NEXT_HOP
	l := net.IPv4len
	if addr.Is6() {
		l = net.IPv6len
	}
	return &PathAttributeNextHop{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: uint16(l),
		},
		Value: addr,
	}, nil
}

type PathAttributeMultiExitDisc struct {
	PathAttribute
	Value uint32
}

func (p *PathAttributeMultiExitDisc) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length != 4 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "med length isn't correct")
	}
	p.Value = binary.BigEndian.Uint32(value)
	return nil
}

func (p *PathAttributeMultiExitDisc) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:], p.Value)
	return p.PathAttribute.Serialize(buf[:], options...)
}

func (p *PathAttributeMultiExitDisc) String() string {
	return "{Med: " + strconv.FormatUint(uint64(p.Value), 10) + "}"
}

func (p *PathAttributeMultiExitDisc) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value uint32      `json:"metric"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeMultiExitDisc(value uint32) *PathAttributeMultiExitDisc {
	t := BGP_ATTR_TYPE_MULTI_EXIT_DISC
	return &PathAttributeMultiExitDisc{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: 4,
		},
		Value: value,
	}
}

type PathAttributeLocalPref struct {
	PathAttribute
	Value uint32
}

func (p *PathAttributeLocalPref) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length != 4 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "local pref length isn't correct")
	}
	p.Value = binary.BigEndian.Uint32(value)
	return nil
}

func (p *PathAttributeLocalPref) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:], p.Value)
	return p.PathAttribute.Serialize(buf[:], options...)
}

func (p *PathAttributeLocalPref) String() string {
	return "{LocalPref: " + strconv.FormatUint(uint64(p.Value), 10) + "}"
}

func (p *PathAttributeLocalPref) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value uint32      `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeLocalPref(value uint32) *PathAttributeLocalPref {
	t := BGP_ATTR_TYPE_LOCAL_PREF
	return &PathAttributeLocalPref{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: 4,
		},
		Value: value,
	}
}

type PathAttributeAtomicAggregate struct {
	PathAttribute
}

func (p *PathAttributeAtomicAggregate) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	_, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "atomic aggregate should have no value")
	}
	return nil
}

func (p *PathAttributeAtomicAggregate) Serialize(options ...*MarshallingOption) ([]byte, error) {
	return p.PathAttribute.Serialize(nil, options...)
}

func (p *PathAttributeAtomicAggregate) String() string {
	return "{AtomicAggregate}"
}

func (p *PathAttributeAtomicAggregate) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type BGPAttrType `json:"type"`
	}{
		Type: p.GetType(),
	})
}

func NewPathAttributeAtomicAggregate() *PathAttributeAtomicAggregate {
	t := BGP_ATTR_TYPE_ATOMIC_AGGREGATE
	return &PathAttributeAtomicAggregate{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: 0,
		},
	}
}

type PathAttributeAggregatorParam struct {
	AS      uint32
	Askind  reflect.Kind
	Address netip.Addr
}

type PathAttributeAggregator struct {
	PathAttribute
	Value PathAttributeAggregatorParam
}

func (p *PathAttributeAggregator) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	switch p.Length {
	case 6:
		p.Value.Askind = reflect.Uint16
		p.Value.AS = uint32(binary.BigEndian.Uint16(value[:2]))
		p.Value.Address, _ = netip.AddrFromSlice(value[2:])
	case 8:
		p.Value.Askind = reflect.Uint32
		p.Value.AS = binary.BigEndian.Uint32(value[:4])
		p.Value.Address, _ = netip.AddrFromSlice(value[4:])
	default:
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "aggregator length isn't correct")
	}
	return nil
}

func (p *PathAttributeAggregator) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf []byte
	switch p.Value.Askind {
	case reflect.Uint16:
		buf = make([]byte, 6)
		binary.BigEndian.PutUint16(buf, uint16(p.Value.AS))
		copy(buf[2:], p.Value.Address.AsSlice())
	case reflect.Uint32:
		buf = make([]byte, 8)
		binary.BigEndian.PutUint32(buf, p.Value.AS)
		copy(buf[4:], p.Value.Address.AsSlice())
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeAggregator) String() string {
	return "{Aggregate: {AS: " + strconv.FormatUint(uint64(p.Value.AS), 10) +
		", Address: " + p.Value.Address.String() + "}}"
}

func (p *PathAttributeAggregator) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type    BGPAttrType `json:"type"`
		AS      uint32      `json:"as"`
		Address string      `json:"address"`
	}{
		Type:    p.GetType(),
		AS:      p.Value.AS,
		Address: p.Value.Address.String(),
	})
}

func NewPathAttributeAggregator(as any, address netip.Addr) (*PathAttributeAggregator, error) {
	v := reflect.ValueOf(as)
	asKind := v.Kind()
	var l uint16
	switch asKind {
	case reflect.Uint16:
		l = 6
	case reflect.Uint32:
		l = 8
	default:
		// Invalid type
		return nil, fmt.Errorf("invalid as type: %v", asKind)
	}
	if !address.Is4() {
		return nil, fmt.Errorf("invalid address: %v", address)
	}
	t := BGP_ATTR_TYPE_AGGREGATOR
	return &PathAttributeAggregator{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: l,
		},
		Value: PathAttributeAggregatorParam{
			AS:      uint32(v.Uint()),
			Askind:  asKind,
			Address: address,
		},
	}, nil
}

type PathAttributeCommunities struct {
	PathAttribute
	Value []uint32
}

func (p *PathAttributeCommunities) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length%4 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "communities length isn't correct")
	}
	for len(value) >= 4 {
		p.Value = append(p.Value, binary.BigEndian.Uint32(value))
		value = value[4:]
	}
	return nil
}

func (p *PathAttributeCommunities) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, len(p.Value)*4)
	for i, v := range p.Value {
		binary.BigEndian.PutUint32(buf[i*4:], v)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

type WellKnownCommunity uint32

const (
	COMMUNITY_INTERNET                   WellKnownCommunity = 0x00000000
	COMMUNITY_PLANNED_SHUT               WellKnownCommunity = 0xffff0000
	COMMUNITY_ACCEPT_OWN                 WellKnownCommunity = 0xffff0001
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v4 WellKnownCommunity = 0xffff0002
	COMMUNITY_ROUTE_FILTER_v4            WellKnownCommunity = 0xffff0003
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v6 WellKnownCommunity = 0xffff0004
	COMMUNITY_ROUTE_FILTER_v6            WellKnownCommunity = 0xffff0005
	COMMUNITY_LLGR_STALE                 WellKnownCommunity = 0xffff0006
	COMMUNITY_NO_LLGR                    WellKnownCommunity = 0xffff0007
	COMMUNITY_BLACKHOLE                  WellKnownCommunity = 0xffff029a
	COMMUNITY_NO_EXPORT                  WellKnownCommunity = 0xffffff01
	COMMUNITY_NO_ADVERTISE               WellKnownCommunity = 0xffffff02
	COMMUNITY_NO_EXPORT_SUBCONFED        WellKnownCommunity = 0xffffff03
	COMMUNITY_NO_PEER                    WellKnownCommunity = 0xffffff04
)

var WellKnownCommunityNameMap = map[WellKnownCommunity]string{
	COMMUNITY_INTERNET:                   "internet",
	COMMUNITY_PLANNED_SHUT:               "planned-shut",
	COMMUNITY_ACCEPT_OWN:                 "accept-own",
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: "route-filter-translated-v4",
	COMMUNITY_ROUTE_FILTER_v4:            "route-filter-v4",
	COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: "route-filter-translated-v6",
	COMMUNITY_ROUTE_FILTER_v6:            "route-filter-v6",
	COMMUNITY_LLGR_STALE:                 "llgr-stale",
	COMMUNITY_NO_LLGR:                    "no-llgr",
	COMMUNITY_BLACKHOLE:                  "blackhole",
	COMMUNITY_NO_EXPORT:                  "no-export",
	COMMUNITY_NO_ADVERTISE:               "no-advertise",
	COMMUNITY_NO_EXPORT_SUBCONFED:        "no-export-subconfed",
	COMMUNITY_NO_PEER:                    "no-peer",
}

var WellKnownCommunityValueMap = map[string]WellKnownCommunity{
	WellKnownCommunityNameMap[COMMUNITY_INTERNET]:                   COMMUNITY_INTERNET,
	WellKnownCommunityNameMap[COMMUNITY_PLANNED_SHUT]:               COMMUNITY_PLANNED_SHUT,
	WellKnownCommunityNameMap[COMMUNITY_ACCEPT_OWN]:                 COMMUNITY_ACCEPT_OWN,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_TRANSLATED_v4]: COMMUNITY_ROUTE_FILTER_TRANSLATED_v4,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_v4]:            COMMUNITY_ROUTE_FILTER_v4,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_TRANSLATED_v6]: COMMUNITY_ROUTE_FILTER_TRANSLATED_v6,
	WellKnownCommunityNameMap[COMMUNITY_ROUTE_FILTER_v6]:            COMMUNITY_ROUTE_FILTER_v6,
	WellKnownCommunityNameMap[COMMUNITY_LLGR_STALE]:                 COMMUNITY_LLGR_STALE,
	WellKnownCommunityNameMap[COMMUNITY_NO_LLGR]:                    COMMUNITY_NO_LLGR,
	WellKnownCommunityNameMap[COMMUNITY_NO_EXPORT]:                  COMMUNITY_NO_EXPORT,
	WellKnownCommunityNameMap[COMMUNITY_BLACKHOLE]:                  COMMUNITY_BLACKHOLE,
	WellKnownCommunityNameMap[COMMUNITY_NO_ADVERTISE]:               COMMUNITY_NO_ADVERTISE,
	WellKnownCommunityNameMap[COMMUNITY_NO_EXPORT_SUBCONFED]:        COMMUNITY_NO_EXPORT_SUBCONFED,
	WellKnownCommunityNameMap[COMMUNITY_NO_PEER]:                    COMMUNITY_NO_PEER,
}

func (p *PathAttributeCommunities) String() string {
	l := make([]string, 0, len(p.Value))
	for _, v := range p.Value {
		n, ok := WellKnownCommunityNameMap[WellKnownCommunity(v)]
		if ok {
			l = append(l, n)
		} else {
			comm := strconv.FormatUint(uint64(0xffff0000&v>>16), 10) + ":" + strconv.FormatUint(uint64(0xffff&v), 10)
			l = append(l, comm)
		}
	}
	return "{Communities: " + strings.Join(l, ", ") + "}"
}

func (p *PathAttributeCommunities) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value []uint32    `json:"communities"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeCommunities(value []uint32) *PathAttributeCommunities {
	l := len(value) * 4
	t := BGP_ATTR_TYPE_COMMUNITIES
	return &PathAttributeCommunities{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Value: value,
	}
}

type PathAttributeOriginatorId struct {
	PathAttribute
	Value netip.Addr
}

func (p *PathAttributeOriginatorId) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length != 4 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "originator id length isn't correct")
	}
	// It will never fail because the buffer is guaranteed to be 4 bytes long by PathAttribute.DecodeFromBytes().
	p.Value, _ = netip.AddrFromSlice(value)
	return nil
}

func (p *PathAttributeOriginatorId) String() string {
	return "{Originator: " + p.Value.String() + "}"
}

func (p *PathAttributeOriginatorId) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value string      `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value.String(),
	})
}

func (p *PathAttributeOriginatorId) Serialize(options ...*MarshallingOption) ([]byte, error) {
	var buf [4]byte
	copy(buf[:], p.Value.AsSlice())
	return p.PathAttribute.Serialize(buf[:], options...)
}

func NewPathAttributeOriginatorId(addr netip.Addr) (*PathAttributeOriginatorId, error) {
	if !addr.Is4() {
		return nil, errors.New("invalid address")
	}
	t := BGP_ATTR_TYPE_ORIGINATOR_ID
	return &PathAttributeOriginatorId{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: 4,
		},
		Value: addr,
	}, nil
}

type PathAttributeClusterList struct {
	PathAttribute
	Value []netip.Addr
}

func (p *PathAttributeClusterList) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length%4 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "clusterlist length isn't correct")
	}
	for len(value) >= 4 {
		v, _ := netip.AddrFromSlice(value[:4])
		p.Value = append(p.Value, v)
		value = value[4:]
	}
	return nil
}

func (p *PathAttributeClusterList) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, len(p.Value)*4)
	for i, v := range p.Value {
		copy(buf[i*4:], v.AsSlice())
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeClusterList) String() string {
	return fmt.Sprintf("{ClusterList: %v}", p.Value)
}

func (p *PathAttributeClusterList) MarshalJSON() ([]byte, error) {
	value := make([]string, 0, len(p.Value))
	for _, v := range p.Value {
		value = append(value, v.String())
	}
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		Value []string    `json:"value"`
	}{
		Type:  p.GetType(),
		Value: value,
	})
}

func NewPathAttributeClusterList(value []netip.Addr) (*PathAttributeClusterList, error) {
	l := len(value) * 4
	list := make([]netip.Addr, len(value))
	for i, v := range value {
		if !v.Is4() {
			return nil, fmt.Errorf("invalid address")
		}
		list[i] = v
	}
	t := BGP_ATTR_TYPE_CLUSTER_LIST
	return &PathAttributeClusterList{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Value: list,
	}, nil
}

type PathAttributeMpReachNLRI struct {
	PathAttribute
	Nexthop          netip.Addr
	LinkLocalNexthop netip.Addr
	AFI              uint16
	SAFI             uint8
	Value            []PathNLRI
}

func (p *PathAttributeMpReachNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
	eData, _ := p.PathAttribute.Serialize(value, options...)
	if p.Length < 3 {
		return NewMessageError(eCode, eSubCode, eData, "mpreach header length is short")
	}

	var family Family
	// In MRT dumps, AFI+SAFI+NLRI is implicit based on RIB Entry Header, see RFC 6396 4.3.4
	onlyNexthop := IsMRTSerialization(options)
	if !onlyNexthop {
		p.AFI = binary.BigEndian.Uint16(value[:2])
		p.SAFI = value[2]
		family = NewFamily(p.AFI, p.SAFI)
		value = value[3:]
	}

	if len(value) < 1 {
		return NewMessageError(eCode, eSubCode, eData, "mpreach nexthop length is short")
	}
	nexthoplen := int(value[0])
	if len(value) < 1+nexthoplen {
		return NewMessageError(eCode, eSubCode, eData, "mpreach nexthop length is short")
	}
	nexthopbin := value[1 : 1+nexthoplen]
	value = value[1+nexthoplen:]

	switch nexthoplen {
	case 2*BGP_ATTR_NHLEN_VPN_RD + BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: // 8 bytes RD + 16 bytes IPv6 Global + 8 bytes RD + 16 bytes IPv6 Link Local
		// NOTE: Do not use slices.Delete on nexthopbin here because nexthopbin aliases the input buffer.
		// slices.Delete shifts elements and clears tail elements in-place, which would mutate the caller's input.
		// We instead build a new slice without the second RD.
		{
			start := BGP_ATTR_NHLEN_VPN_RD + BGP_ATTR_NHLEN_IPV6_GLOBAL
			end := 2*BGP_ATTR_NHLEN_VPN_RD + BGP_ATTR_NHLEN_IPV6_GLOBAL
			tmp := make([]byte, 0, len(nexthopbin)-(end-start))
			tmp = append(tmp, nexthopbin[:start]...)
			tmp = append(tmp, nexthopbin[end:]...)
			nexthopbin = tmp
		}
		nexthoplen -= BGP_ATTR_NHLEN_VPN_RD
		fallthrough
	case BGP_ATTR_NHLEN_VPN_RD + BGP_ATTR_NHLEN_IPV4, // 8 bytes RD + 4 bytes IPv4
		BGP_ATTR_NHLEN_VPN_RD + BGP_ATTR_NHLEN_IPV6_GLOBAL: // 8 bytes RD + 16 bytes IPv6 Global
		nexthopbin = nexthopbin[BGP_ATTR_NHLEN_VPN_RD:] // skip RD
		nexthoplen -= BGP_ATTR_NHLEN_VPN_RD
	}

	switch nexthoplen {
	case 0: // no nexthop, skip (FlowSpec)
	case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: // 16 bytes IPv6 Global + 16 bytes IPv6 Link Local
		p.LinkLocalNexthop, _ = netip.AddrFromSlice(nexthopbin[BGP_ATTR_NHLEN_IPV6_GLOBAL:BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL])
		fallthrough
	case BGP_ATTR_NHLEN_IPV6_GLOBAL: // 16 bytes IPv6 Global
		p.Nexthop, _ = netip.AddrFromSlice(nexthopbin[:BGP_ATTR_NHLEN_IPV6_GLOBAL])
	case BGP_ATTR_NHLEN_IPV4: // 4 bytes IPv4
		p.Nexthop, _ = netip.AddrFromSlice(nexthopbin[:BGP_ATTR_NHLEN_IPV4])
	default:
		return NewMessageError(eCode, eSubCode, eData, "mpreach nexthop length is incorrect")
	}

	if onlyNexthop {
		return nil
	}

	// skip reserved
	if len(value) == 0 {
		return NewMessageError(eCode, eSubCode, value, "no skip byte")
	}
	value = value[1:]
	addpathLen := 0
	if IsAddPathEnabled(true, family, options) {
		addpathLen = 4
	}
	for len(value) > 0 {
		id := uint32(0)
		if addpathLen != 0 {
			if len(value) < addpathLen {
				return NewMessageError(eCode, eSubCode, value, "addpath length is incorrect")
			}
			id = binary.BigEndian.Uint32(value[:addpathLen])
			value = value[addpathLen:]
		}
		prefix, err := NLRIFromSlice(family, value, options...)
		if err != nil {
			return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, eData, err.Error())
		}
		if len(value) < prefix.Len(options...) {
			return NewMessageError(eCode, eSubCode, value, "prefix length is incorrect")
		}
		value = value[prefix.Len(options...):]
		p.Value = append(p.Value, PathNLRI{NLRI: prefix, ID: id})
	}
	return nil
}

func (p *PathAttributeMpReachNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
	afi := p.AFI
	safi := p.SAFI
	nexthopAddrs := make([]net.IP, 0, 2)
	nexthoplen := 0

	isNexthopIPv6 := p.Nexthop.IsValid() && (afi == AFI_IP6 || p.Nexthop.Is6())
	if isNexthopIPv6 {
		// if nexthop is v4, it needs to be serialized as IPv4-mapped IPv6 address.
		n := p.Nexthop.As16()
		nexthopAddrs = append(nexthopAddrs, n[:])
		nexthoplen = BGP_ATTR_NHLEN_IPV6_GLOBAL
		if p.LinkLocalNexthop.IsValid() && p.LinkLocalNexthop.IsLinkLocalUnicast() {
			nexthopAddrs = append(nexthopAddrs, p.LinkLocalNexthop.AsSlice())
			nexthoplen = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
		}
	} else if p.Nexthop.IsValid() {
		n := p.Nexthop.As4()
		nexthopAddrs = append(nexthopAddrs, n[:])
		nexthoplen = BGP_ATTR_NHLEN_IPV4
	}

	offset := 0
	switch safi {
	case SAFI_MPLS_VPN:
		offset = BGP_ATTR_NHLEN_VPN_RD
		nexthoplen += len(nexthopAddrs) * offset
	case SAFI_FLOW_SPEC_VPN, SAFI_FLOW_SPEC_UNICAST:
		nexthoplen = 0
	}

	var buf []byte
	onlyNexthop := IsMRTSerialization(options)
	if !onlyNexthop {
		family := make([]byte, 3)
		binary.BigEndian.PutUint16(family[0:], afi)
		family[2] = safi

		buf = append(buf, family...)
	}
	buf = append(buf, uint8(nexthoplen))
	if nexthoplen != 0 { // if nexthoplen == 0, no nexthop (FlowSpec)
		nexthop := make([]byte, nexthoplen)

		index := 0
		for _, nh := range nexthopAddrs {
			index += offset
			copy(nexthop[index:], nh)
			index += len(nh)
		}

		buf = append(buf, nexthop...)
	}
	isAddpath := IsAddPathEnabled(false, NewFamily(afi, safi), options)
	if !onlyNexthop {
		buf = append(buf, 0)
		for _, prefix := range p.Value {
			pbuf, err := prefix.toSlice(isAddpath)
			if err != nil {
				return nil, err
			}
			buf = append(buf, pbuf...)
		}
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeMpReachNLRI) MarshalJSON() ([]byte, error) {
	nexthop := p.Nexthop.String()
	if !p.Nexthop.IsValid() {
		switch p.AFI {
		case AFI_IP:
			nexthop = "0.0.0.0"
		case AFI_IP6:
			nexthop = "::"
		default:
			nexthop = "fictitious"
		}
	}
	return json.Marshal(struct {
		Type    BGPAttrType `json:"type"`
		Nexthop string      `json:"nexthop"`
		AFI     uint16      `json:"afi"`
		SAFI    uint8       `json:"safi"`
		Value   []PathNLRI  `json:"value"`
	}{
		Type:    p.GetType(),
		Nexthop: nexthop,
		AFI:     p.AFI,
		SAFI:    p.SAFI,
		Value:   p.Value,
	})
}

func (p *PathAttributeMpReachNLRI) String() string {
	return fmt.Sprintf("{MpReach(%s): {Nexthop: %s, NLRIs: %s}}", NewFamily(p.AFI, p.SAFI), p.Nexthop, p.Value)
}

func NewPathAttributeMpReachNLRI(family Family, nlris []PathNLRI, nextHops ...netip.Addr) (*PathAttributeMpReachNLRI, error) {
	if len(nlris) == 0 {
		return nil, fmt.Errorf("no NLRI provided")
	}
	// AFI(2) + SAFI(1) + NexthopLength(1) + Nexthop(variable)
	// + Reserved(1) + NLRI(variable)
	l := 5
	afi := family.Afi()
	safi := family.Safi()

	nhs := []netip.Addr{}
	nhlen := 0

	if len(nextHops) > 0 {
		isNexthopIPv6 := nextHops[0].IsValid() && (afi == AFI_IP6 || nextHops[0].Is6())
		if isNexthopIPv6 {
			nhs = append(nhs, nextHops[0])
			// if nexthop is v4, it needs to be serialized as IPv4-mapped IPv6 address.
			nhlen = BGP_ATTR_NHLEN_IPV6_GLOBAL
			if len(nextHops) > 1 && nextHops[1].IsValid() && nextHops[1].IsLinkLocalUnicast() {
				nhlen = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
				nhs = append(nhs, nextHops[1])
			}
		} else if nextHops[0].IsValid() {
			nhlen = BGP_ATTR_NHLEN_IPV4
			nhs = append(nhs, nextHops[0])
		}
	}

	switch safi {
	case SAFI_FLOW_SPEC_VPN, SAFI_FLOW_SPEC_UNICAST:
	// Should not have Nexthop
	case SAFI_MPLS_VPN:
		l += BGP_ATTR_NHLEN_VPN_RD
		fallthrough
	default:
		l += nhlen
	}

	for _, n := range nlris {
		l += n.NLRI.Len()
	}
	t := BGP_ATTR_TYPE_MP_REACH_NLRI
	p := &PathAttributeMpReachNLRI{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		AFI:   afi,
		SAFI:  safi,
		Value: nlris,
	}

	switch len(nhs) {
	case 2:
		p.LinkLocalNexthop = nhs[1]
		fallthrough
	case 1:
		p.Nexthop = nhs[0]
	}

	return p, nil
}

type PathAttributeMpUnreachNLRI struct {
	PathAttribute
	AFI   uint16
	SAFI  uint8
	Value []PathNLRI
}

func (p *PathAttributeMpUnreachNLRI) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
	eData, _ := p.PathAttribute.Serialize(value, options...)
	if p.Length < 3 {
		return NewMessageError(eCode, eSubCode, value, "unreach header length is incorrect")
	}
	afi := binary.BigEndian.Uint16(value[:2])
	safi := value[2]
	value = value[3:]
	p.AFI = afi
	p.SAFI = safi
	family := NewFamily(afi, safi)
	addpathLen := 0
	if IsAddPathEnabled(true, family, options) {
		addpathLen = 4
	}
	for len(value) > 0 {
		id := uint32(0)
		if addpathLen != 0 {
			if len(value) < addpathLen {
				return NewMessageError(eCode, eSubCode, eData, "prefix length is incorrect")
			}
			id = binary.BigEndian.Uint32(value[:4])
			value = value[4:]
		}

		prefix, err := NLRIFromSlice(family, value, options...)
		if err != nil {
			return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, eData, err.Error())
		}
		if prefix.Len(options...) > len(value) {
			return NewMessageError(eCode, eSubCode, eData, "prefix length is incorrect")
		}
		value = value[prefix.Len(options...):]
		p.Value = append(p.Value, PathNLRI{NLRI: prefix, ID: id})
	}
	return nil
}

func (p *PathAttributeMpUnreachNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 3)
	binary.BigEndian.PutUint16(buf, p.AFI)
	buf[2] = p.SAFI
	addpath := IsAddPathEnabled(false, NewFamily(p.AFI, p.SAFI), options)

	for _, prefix := range p.Value {
		pbuf, err := prefix.toSlice(addpath)
		if err != nil {
			return nil, err
		}
		buf = append(buf, pbuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeMpUnreachNLRI) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType `json:"type"`
		AFI   uint16      `json:"afi"`
		SAFI  uint8       `json:"safi"`
		Value []PathNLRI  `json:"value"`
	}{
		Type:  p.GetType(),
		AFI:   p.AFI,
		SAFI:  p.SAFI,
		Value: p.Value,
	})
}

func (p *PathAttributeMpUnreachNLRI) String() string {
	if len(p.Value) > 0 {
		return fmt.Sprintf("{MpUnreach(%s): {NLRIs: %s}}", NewFamily(p.AFI, p.SAFI), p.Value)
	}
	return fmt.Sprintf("{MpUnreach(%s): End-of-Rib}", NewFamily(p.AFI, p.SAFI))
}

func NewPathAttributeMpUnreachNLRI(family Family, nlris []PathNLRI) (*PathAttributeMpUnreachNLRI, error) {
	// AFI(2) + SAFI(1) + NLRI(variable)
	l := 3
	for _, n := range nlris {
		l += n.NLRI.Len()
	}
	t := BGP_ATTR_TYPE_MP_UNREACH_NLRI
	return &PathAttributeMpUnreachNLRI{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		AFI:   family.Afi(),
		SAFI:  family.Safi(),
		Value: nlris,
	}, nil
}

type ExtendedCommunityInterface interface {
	Serialize() ([]byte, error)
	String() string
	GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType)
	MarshalJSON() ([]byte, error)
	Flat() map[string]string
}

type TwoOctetAsSpecificExtended struct {
	SubType      ExtendedCommunityAttrSubType
	AS           uint16
	LocalAdmin   uint32
	IsTransitive bool
}

func (e *TwoOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf := [8]byte{}
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC)
	}
	buf[1] = byte(e.SubType)
	binary.BigEndian.PutUint16(buf[2:], e.AS)
	binary.BigEndian.PutUint32(buf[4:], e.LocalAdmin)
	return buf[:], nil
}

func (e *TwoOctetAsSpecificExtended) String() string {
	return strconv.FormatUint(uint64(e.AS), 10) + ":" + strconv.FormatUint(uint64(e.LocalAdmin), 10)
}

func (e *TwoOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.String(),
	})
}

func (e *TwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC
	}
	return t, e.SubType
}

func NewTwoOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint16, localAdmin uint32, isTransitive bool) *TwoOctetAsSpecificExtended {
	return &TwoOctetAsSpecificExtended{
		SubType:      subtype,
		AS:           as,
		LocalAdmin:   localAdmin,
		IsTransitive: isTransitive,
	}
}

type IPv4AddressSpecificExtended struct {
	SubType      ExtendedCommunityAttrSubType
	IPv4         netip.Addr
	LocalAdmin   uint16
	IsTransitive bool
}

func (e *IPv4AddressSpecificExtended) Serialize() ([]byte, error) {
	buf := [8]byte{}
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_IP4_SPECIFIC)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC)
	}
	buf[1] = byte(e.SubType)
	copy(buf[2:6], e.IPv4.AsSlice())
	binary.BigEndian.PutUint16(buf[6:], e.LocalAdmin)
	return buf[:], nil
}

func (e *IPv4AddressSpecificExtended) String() string {
	return e.IPv4.String() + ":" + strconv.FormatUint(uint64(e.LocalAdmin), 10)
}

func (e *IPv4AddressSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.String(),
	})
}

func (e *IPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_IP4_SPECIFIC
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC
	}
	return t, e.SubType
}

func NewIPv4AddressSpecificExtended(subtype ExtendedCommunityAttrSubType, ip netip.Addr, localAdmin uint16, isTransitive bool) (*IPv4AddressSpecificExtended, error) {
	if !ip.Is4() {
		return nil, fmt.Errorf("invalid IPv4 address")
	}
	return &IPv4AddressSpecificExtended{
		SubType:      subtype,
		IPv4:         ip,
		LocalAdmin:   localAdmin,
		IsTransitive: isTransitive,
	}, nil
}

type IPv6AddressSpecificExtended struct {
	SubType      ExtendedCommunityAttrSubType
	IPv6         netip.Addr
	LocalAdmin   uint16
	IsTransitive bool
}

func (e *IPv6AddressSpecificExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 20)
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_IP6_SPECIFIC)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_IP6_SPECIFIC)
	}
	buf[1] = byte(e.SubType)
	copy(buf[2:18], e.IPv6.AsSlice())
	binary.BigEndian.PutUint16(buf[18:], e.LocalAdmin)
	return buf, nil
}

func (e *IPv6AddressSpecificExtended) String() string {
	return e.IPv6.String() + ":" + strconv.FormatUint(uint64(e.LocalAdmin), 10)
}

func (e *IPv6AddressSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.String(),
	})
}

func (e *IPv6AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_IP6_SPECIFIC
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_IP6_SPECIFIC
	}
	return t, e.SubType
}

func NewIPv6AddressSpecificExtended(subtype ExtendedCommunityAttrSubType, ip netip.Addr, localAdmin uint16, isTransitive bool) (*IPv6AddressSpecificExtended, error) {
	if !ip.Is6() {
		return nil, fmt.Errorf("invalid IPv6 address")
	}
	return &IPv6AddressSpecificExtended{
		SubType:      subtype,
		IPv6:         ip,
		LocalAdmin:   localAdmin,
		IsTransitive: isTransitive,
	}, nil
}

type FourOctetAsSpecificExtended struct {
	SubType      ExtendedCommunityAttrSubType
	AS           uint32
	LocalAdmin   uint16
	IsTransitive bool
}

func (e *FourOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf := [8]byte{}
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC)
	}
	buf[1] = byte(e.SubType)
	binary.BigEndian.PutUint32(buf[2:], e.AS)
	binary.BigEndian.PutUint16(buf[6:], e.LocalAdmin)
	return buf[:], nil
}

func (e *FourOctetAsSpecificExtended) String() string {
	var buf [4]byte
	binary.BigEndian.PutUint32(buf[:4], e.AS)
	asUpper := binary.BigEndian.Uint16(buf[:2])
	asLower := binary.BigEndian.Uint16(buf[2:4])
	return strconv.FormatUint(uint64(asUpper), 10) + "." + strconv.FormatUint(uint64(asLower), 10) +
		":" + strconv.FormatUint(uint64(e.LocalAdmin), 10)
}

func (e *FourOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.String(),
	})
}

func (e *FourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	t := EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC
	if !e.IsTransitive {
		t = EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC
	}
	return t, e.SubType
}

func NewFourOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint32, localAdmin uint16, isTransitive bool) *FourOctetAsSpecificExtended {
	return &FourOctetAsSpecificExtended{
		SubType:      subtype,
		AS:           as,
		LocalAdmin:   localAdmin,
		IsTransitive: isTransitive,
	}
}

func ParseExtendedCommunity(subtype ExtendedCommunityAttrSubType, com string) (ExtendedCommunityInterface, error) {
	if subtype == EC_SUBTYPE_ENCAPSULATION {
		var t TunnelType
		switch com {
		case TUNNEL_TYPE_L2TP3.String():
			t = TUNNEL_TYPE_L2TP3
		case TUNNEL_TYPE_GRE.String():
			t = TUNNEL_TYPE_GRE
		case TUNNEL_TYPE_IP_IN_IP.String():
			t = TUNNEL_TYPE_IP_IN_IP
		case TUNNEL_TYPE_VXLAN.String():
			t = TUNNEL_TYPE_VXLAN
		case TUNNEL_TYPE_NVGRE.String():
			t = TUNNEL_TYPE_NVGRE
		case TUNNEL_TYPE_MPLS.String():
			t = TUNNEL_TYPE_MPLS
		case TUNNEL_TYPE_MPLS_IN_GRE.String():
			t = TUNNEL_TYPE_MPLS_IN_GRE
		case TUNNEL_TYPE_VXLAN_GRE.String():
			t = TUNNEL_TYPE_VXLAN_GRE
		case TUNNEL_TYPE_MPLS_IN_UDP.String():
			t = TUNNEL_TYPE_MPLS_IN_UDP
		case TUNNEL_TYPE_GENEVE.String():
			t = TUNNEL_TYPE_GENEVE
		case "L2TPv3 over IP":
			t = TUNNEL_TYPE_L2TP3
		case "GRE":
			t = TUNNEL_TYPE_GRE
		case "IP in IP":
			t = TUNNEL_TYPE_IP_IN_IP
		case "VXLAN":
			t = TUNNEL_TYPE_VXLAN
		case "NVGRE":
			t = TUNNEL_TYPE_NVGRE
		case "MPLS":
			t = TUNNEL_TYPE_MPLS
		case "MPLS in GRE":
			t = TUNNEL_TYPE_MPLS_IN_GRE
		case "VXLAN GRE":
			t = TUNNEL_TYPE_VXLAN_GRE
		case "MPLS in UDP":
			t = TUNNEL_TYPE_MPLS_IN_UDP
		case "GENEVE":
			t = TUNNEL_TYPE_GENEVE
		default:
			return nil, fmt.Errorf("invalid encap type %s", com)
		}
		return NewEncapExtended(t), nil
	}

	if subtype == EC_SUBTYPE_ORIGIN_VALIDATION {
		var state ValidationState
		switch com {
		case VALIDATION_STATE_VALID.String():
			state = VALIDATION_STATE_VALID
		case VALIDATION_STATE_NOT_FOUND.String():
			state = VALIDATION_STATE_NOT_FOUND
		case VALIDATION_STATE_INVALID.String():
			state = VALIDATION_STATE_INVALID
		default:
			return nil, errors.New("invalid validation state")
		}
		return &ValidationExtended{
			State: state,
		}, nil
	}
	elems, err := parseRdAndRt(com)
	if err != nil {
		return nil, err
	}
	localAdmin, _ := strconv.ParseUint(elems[10], 10, 32)
	if subtype == EC_SUBTYPE_SOURCE_AS {
		localAdmin = 0
	}
	addr, _ := netip.ParseAddr(elems[1])
	isTransitive := true
	switch {
	case subtype == EC_SUBTYPE_LINK_BANDWIDTH:
		asn, _ := strconv.ParseUint(elems[8], 10, 16)
		return NewLinkBandwidthExtended(uint16(asn), float32(localAdmin)), nil
	case addr.Is4():
		return NewIPv4AddressSpecificExtended(subtype, addr, uint16(localAdmin), isTransitive)
	case addr.Is6():
		return NewIPv6AddressSpecificExtended(subtype, addr, uint16(localAdmin), isTransitive)
	case elems[6] == "" && elems[7] == "":
		asn, _ := strconv.ParseUint(elems[8], 10, 16)
		return NewTwoOctetAsSpecificExtended(subtype, uint16(asn), uint32(localAdmin), isTransitive), nil
	default:
		fst, _ := strconv.ParseUint(elems[7], 10, 16)
		snd, _ := strconv.ParseUint(elems[8], 10, 16)
		asn := fst<<16 | snd
		return NewFourOctetAsSpecificExtended(subtype, uint32(asn), uint16(localAdmin), isTransitive), nil
	}
}

func ParseRouteTarget(rt string) (ExtendedCommunityInterface, error) {
	return ParseExtendedCommunity(EC_SUBTYPE_ROUTE_TARGET, rt)
}

func SerializeExtendedCommunities(comms []ExtendedCommunityInterface) ([][]byte, error) {
	bufs := make([][]byte, len(comms))
	var err error
	for i, c := range comms {
		bufs[i], err = c.Serialize()
		if err != nil {
			return nil, err
		}
	}
	return bufs, err
}

type ValidationState uint8

const (
	VALIDATION_STATE_VALID     ValidationState = 0
	VALIDATION_STATE_NOT_FOUND ValidationState = 1
	VALIDATION_STATE_INVALID   ValidationState = 2
)

func (s ValidationState) String() string {
	switch s {
	case VALIDATION_STATE_VALID:
		return "valid"
	case VALIDATION_STATE_NOT_FOUND:
		return "not-found"
	case VALIDATION_STATE_INVALID:
		return "invalid"
	}
	return fmt.Sprintf("unknown validation state(%d)", s)
}

type ValidationExtended struct {
	State ValidationState
}

func (e *ValidationExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	typ, subType := e.GetTypes()
	buf[0] = byte(typ)
	buf[1] = byte(subType)
	buf[7] = byte(e.State)
	return buf, nil
}

func (e *ValidationExtended) String() string {
	return e.State.String()
}

func (e *ValidationExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_NON_TRANSITIVE_OPAQUE, EC_SUBTYPE_ORIGIN_VALIDATION
}

func (e *ValidationExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		SubType ExtendedCommunityAttrSubType `json:"subtype"`
		State   ValidationState              `json:"value"`
	}{
		Type:    t,
		SubType: s,
		State:   e.State,
	})
}

func NewValidationExtended(state ValidationState) *ValidationExtended {
	return &ValidationExtended{
		State: state,
	}
}

type LinkBandwidthExtended struct {
	AS        uint16
	Bandwidth float32
}

func (e *LinkBandwidthExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_NON_TRANSITIVE_LINK_BANDWIDTH, EC_SUBTYPE_LINK_BANDWIDTH
}

func (e *LinkBandwidthExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	typ, subType := e.GetTypes()
	buf[0] = byte(typ)
	buf[1] = byte(subType)
	binary.BigEndian.PutUint16(buf[2:4], e.AS)
	binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(e.Bandwidth))
	return buf, nil
}

func (e *LinkBandwidthExtended) String() string {
	return strconv.FormatUint(uint64(e.AS), 10) + ":" + strconv.FormatUint(uint64(e.Bandwidth), 10)
}

func (e *LinkBandwidthExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type      ExtendedCommunityAttrType    `json:"type"`
		SubType   ExtendedCommunityAttrSubType `json:"subtype"`
		AS        uint16                       `json:"asn"`
		Bandwidth float32                      `json:"bandwidth"`
	}{
		Type:      t,
		SubType:   s,
		AS:        e.AS,
		Bandwidth: e.Bandwidth,
	})
}

func NewLinkBandwidthExtended(as uint16, bw float32) *LinkBandwidthExtended {
	return &LinkBandwidthExtended{
		AS:        as,
		Bandwidth: bw,
	}
}

type ColorExtended struct {
	Color uint32
}

func (e *ColorExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	typ, subType := e.GetTypes()
	buf[0] = byte(typ)
	buf[1] = byte(subType)
	binary.BigEndian.PutUint32(buf[4:8], e.Color)
	return buf, nil
}

func (e *ColorExtended) String() string {
	return strconv.FormatUint(uint64(e.Color), 10)
}

func (e *ColorExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_TRANSITIVE_OPAQUE, EC_SUBTYPE_COLOR
}

func (e *ColorExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		SubType ExtendedCommunityAttrSubType `json:"subtype"`
		Color   uint32                       `json:"color"`
	}{
		Type:    t,
		SubType: s,
		Color:   e.Color,
	})
}

func NewColorExtended(color uint32) *ColorExtended {
	return &ColorExtended{
		Color: color,
	}
}

type EncapExtended struct {
	TunnelType TunnelType
}

func (e *EncapExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	typ, subType := e.GetTypes()
	buf[0] = byte(typ)
	buf[1] = byte(subType)
	binary.BigEndian.PutUint16(buf[6:8], uint16(e.TunnelType))
	return buf, nil
}

func (e *EncapExtended) String() string {
	switch e.TunnelType {
	case TUNNEL_TYPE_L2TP3:
		return "L2TPv3 over IP"
	case TUNNEL_TYPE_GRE:
		return "GRE"
	case TUNNEL_TYPE_IP_IN_IP:
		return "IP in IP"
	case TUNNEL_TYPE_VXLAN:
		return "VXLAN"
	case TUNNEL_TYPE_NVGRE:
		return "NVGRE"
	case TUNNEL_TYPE_MPLS:
		return "MPLS"
	case TUNNEL_TYPE_MPLS_IN_GRE:
		return "MPLS in GRE"
	case TUNNEL_TYPE_VXLAN_GRE:
		return "VXLAN GRE"
	case TUNNEL_TYPE_MPLS_IN_UDP:
		return "MPLS in UDP"
	case TUNNEL_TYPE_SR_POLICY:
		return "SR Policy"
	case TUNNEL_TYPE_GENEVE:
		return "GENEVE"
	default:
		return "tunnel: " + strconv.FormatUint(uint64(e.TunnelType), 10)
	}
}

func (e *EncapExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_TRANSITIVE_OPAQUE, EC_SUBTYPE_ENCAPSULATION
}

func (e *EncapExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type       ExtendedCommunityAttrType    `json:"type"`
		SubType    ExtendedCommunityAttrSubType `json:"subtype"`
		TunnelType TunnelType                   `json:"tunnel_type"`
	}{
		Type:       t,
		SubType:    s,
		TunnelType: e.TunnelType,
	})
}

func NewEncapExtended(tunnelType TunnelType) *EncapExtended {
	return &EncapExtended{
		TunnelType: tunnelType,
	}
}

type DefaultGatewayExtended struct{}

func (e *DefaultGatewayExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	typ, subType := e.GetTypes()
	buf[0] = byte(typ)
	buf[1] = byte(subType)
	return buf, nil
}

func (e *DefaultGatewayExtended) String() string {
	return "default-gateway"
}

func (e *DefaultGatewayExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_TRANSITIVE_OPAQUE, EC_SUBTYPE_DEFAULT_GATEWAY
}

func (e *DefaultGatewayExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		SubType ExtendedCommunityAttrSubType `json:"subtype"`
	}{
		Type:    t,
		SubType: s,
	})
}

func NewDefaultGatewayExtended() *DefaultGatewayExtended {
	return &DefaultGatewayExtended{}
}

type OpaqueExtended struct {
	IsTransitive bool
	Value        []byte
}

func (e *OpaqueExtended) Serialize() ([]byte, error) {
	if len(e.Value) != 7 {
		return nil, fmt.Errorf("invalid value length for opaque extended community: %d", len(e.Value))
	}
	buf := make([]byte, 8)
	if e.IsTransitive {
		buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
	} else {
		buf[0] = byte(EC_TYPE_NON_TRANSITIVE_OPAQUE)
	}
	copy(buf[1:], e.Value)
	return buf, nil
}

func (e *OpaqueExtended) String() string {
	var buf [8]byte
	copy(buf[1:], e.Value)
	return strconv.FormatUint(binary.BigEndian.Uint64(buf[:]), 10)
}

func (e *OpaqueExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	var subType ExtendedCommunityAttrSubType
	if len(e.Value) > 0 {
		// Use the first byte of value as the sub type
		subType = ExtendedCommunityAttrSubType(e.Value[0])
	}
	if e.IsTransitive {
		return EC_TYPE_TRANSITIVE_OPAQUE, subType
	}
	return EC_TYPE_NON_TRANSITIVE_OPAQUE, subType
}

func (e *OpaqueExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   []byte                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.Value,
	})
}

func NewOpaqueExtended(isTransitive bool, value []byte) *OpaqueExtended {
	v := make([]byte, 7)
	copy(v, value)
	return &OpaqueExtended{
		IsTransitive: isTransitive,
		Value:        v,
	}
}

func parseOpaqueExtended(isTransitive bool, data []byte) (ExtendedCommunityInterface, error) {
	subType := ExtendedCommunityAttrSubType(data[1])
	if isTransitive {
		switch subType {
		case EC_SUBTYPE_COLOR:
			return &ColorExtended{
				Color: binary.BigEndian.Uint32(data[4:8]),
			}, nil
		case EC_SUBTYPE_ENCAPSULATION:
			return &EncapExtended{
				TunnelType: TunnelType(binary.BigEndian.Uint16(data[6:8])),
			}, nil
		case EC_SUBTYPE_DEFAULT_GATEWAY:
			return &DefaultGatewayExtended{}, nil
		}
	} else {
		switch subType {
		case EC_SUBTYPE_ORIGIN_VALIDATION:
			return &ValidationExtended{
				State: ValidationState(data[7]),
			}, nil
		}
	}
	return NewOpaqueExtended(isTransitive, data[1:8]), nil
}

type ESILabelExtended struct {
	Label          uint32
	IsSingleActive bool
}

func (e *ESILabelExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_ESI_LABEL)
	if e.IsSingleActive {
		buf[2] = byte(1)
	}
	buf[3] = 0
	buf[4] = 0
	buf[5] = byte(e.Label >> 16 & 0xff)
	buf[6] = byte(e.Label >> 8 & 0xff)
	buf[7] = byte(e.Label & 0xff)
	return buf, nil
}

func (e *ESILabelExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString("esi-label: " + strconv.FormatUint(uint64(e.Label), 10))
	if e.IsSingleActive {
		buf.WriteString(", single-active")
	}
	return buf.String()
}

func (e *ESILabelExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type           ExtendedCommunityAttrType    `json:"type"`
		Subtype        ExtendedCommunityAttrSubType `json:"subtype"`
		Label          uint32                       `json:"label"`
		IsSingleActive bool                         `json:"is_single_active"`
	}{
		Type:           t,
		Subtype:        s,
		Label:          e.Label,
		IsSingleActive: e.IsSingleActive,
	})
}

func (e *ESILabelExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_ESI_LABEL
}

func NewESILabelExtended(label uint32, isSingleActive bool) *ESILabelExtended {
	return &ESILabelExtended{
		Label:          label,
		IsSingleActive: isSingleActive,
	}
}

type ESImportRouteTarget struct {
	ESImport net.HardwareAddr
}

func (e *ESImportRouteTarget) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_ES_IMPORT)
	copy(buf[2:], e.ESImport)
	return buf, nil
}

func (e *ESImportRouteTarget) String() string {
	return "es-import rt: " + e.ESImport.String()
}

func (e *ESImportRouteTarget) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.ESImport.String(),
	})
}

func (e *ESImportRouteTarget) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_ES_IMPORT
}

func NewESImportRouteTarget(mac string) *ESImportRouteTarget {
	esImport, err := net.ParseMAC(mac)
	if err != nil {
		return nil
	}
	return &ESImportRouteTarget{
		ESImport: esImport,
	}
}

type MacMobilityExtended struct {
	Sequence uint32
	IsSticky bool
}

func (e *MacMobilityExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_MAC_MOBILITY)
	if e.IsSticky {
		buf[2] = byte(1)
	}
	binary.BigEndian.PutUint32(buf[4:], e.Sequence)
	return buf, nil
}

func (e *MacMobilityExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString("mac-mobility: " + strconv.FormatUint(uint64(e.Sequence), 10))
	if e.IsSticky {
		buf.WriteString(", sticky")
	}
	return buf.String()
}

func (e *MacMobilityExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type     ExtendedCommunityAttrType    `json:"type"`
		Subtype  ExtendedCommunityAttrSubType `json:"subtype"`
		Sequence uint32                       `json:"sequence"`
		IsSticky bool                         `json:"is_sticky"`
	}{
		Type:     t,
		Subtype:  s,
		Sequence: e.Sequence,
		IsSticky: e.IsSticky,
	})
}

func (e *MacMobilityExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_MAC_MOBILITY
}

func NewMacMobilityExtended(seq uint32, isSticky bool) *MacMobilityExtended {
	return &MacMobilityExtended{
		Sequence: seq,
		IsSticky: isSticky,
	}
}

type RouterMacExtended struct {
	Mac net.HardwareAddr
}

func (e *RouterMacExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 2, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_ROUTER_MAC)
	buf = append(buf, e.Mac...)
	return buf, nil
}

func (e *RouterMacExtended) String() string {
	return "router's mac: " + e.Mac.String()
}

func (e *RouterMacExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Mac     string                       `json:"mac"`
	}{
		Type:    t,
		Subtype: s,
		Mac:     e.Mac.String(),
	})
}

func (e *RouterMacExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_ROUTER_MAC
}

func NewRoutersMacExtended(mac string) *RouterMacExtended {
	hw, err := net.ParseMAC(mac)
	if err != nil {
		return nil
	}
	return &RouterMacExtended{
		Mac: hw,
	}
}

type Layer2AttributesExtended struct {
	HasCILabel     bool
	HasFlowLabel   bool
	HasControlWord bool
	IsPrimaryPe    bool
	IsBackupPe     bool
	Mtu            uint16
}

type EvpnControlFlag uint8

const (
	BACKUP_PE    EvpnControlFlag = 1 << 0
	PRIMARY_PE   EvpnControlFlag = 1 << 1
	CONTROL_WORD EvpnControlFlag = 1 << 2
	FLOW_LABEL   EvpnControlFlag = 1 << 3
	CI_LABEL     EvpnControlFlag = 1 << 4
)

func (e *Layer2AttributesExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_L2_ATTRIBUTES)

	if e.IsBackupPe {
		buf[3] |= uint8(BACKUP_PE)
	} else if e.IsPrimaryPe {
		buf[3] |= uint8(PRIMARY_PE)
	}
	if e.HasControlWord {
		buf[3] |= uint8(CONTROL_WORD)
	}
	if e.HasFlowLabel {
		buf[3] |= uint8(FLOW_LABEL)
	}
	if e.HasCILabel {
		buf[3] |= uint8(CI_LABEL)
	}
	binary.BigEndian.PutUint16(buf[4:6], e.Mtu)
	return buf, nil
}

func (e *Layer2AttributesExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString("evpn-l2-info: ")
	if e.IsPrimaryPe {
		buf.WriteString("is-primary-pe, ")
	}
	if e.IsBackupPe {
		buf.WriteString("is-backup-pe, ")
	}
	if e.HasControlWord {
		buf.WriteString("control-word, ")
	}
	if e.HasFlowLabel {
		buf.WriteString("flow-label, ")
	}
	if e.HasCILabel {
		buf.WriteString("ci-label, ")
	}

	buf.WriteString("mtu " + strconv.FormatUint(uint64(e.Mtu), 10))
	return buf.String()
}

func (e *Layer2AttributesExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type        ExtendedCommunityAttrType    `json:"type"`
		Subtype     ExtendedCommunityAttrSubType `json:"subtype"`
		CILabel     bool                         `json:"ci_label,omitempty"`
		FlowLabel   bool                         `json:"flow_label,omitempty"`
		ControlWord bool                         `json:"control_word,omitempty"`
		PrimaryPe   bool                         `json:"is_primary_pe,omitempty"`
		BackupPe    bool                         `json:"is_backup_pe,omitempty"`
		Mtu         uint16                       `json:"mtu"`
	}{
		Type:        t,
		Subtype:     s,
		CILabel:     e.HasCILabel,
		FlowLabel:   e.HasFlowLabel,
		ControlWord: e.HasControlWord,
		PrimaryPe:   e.IsPrimaryPe,
		BackupPe:    e.IsBackupPe,
		Mtu:         e.Mtu,
	})
}

func (e *Layer2AttributesExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_L2_ATTRIBUTES
}

type ETreeExtended struct {
	Label  uint32
	IsLeaf bool
}

func (e *ETreeExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_ETREE)
	if e.IsLeaf {
		buf[2] = byte(1)
	}
	buf[3] = 0
	buf[4] = 0
	buf[5] = byte(e.Label >> 16 & 0xff)
	buf[6] = byte(e.Label >> 8 & 0xff)
	buf[7] = byte(e.Label & 0xff)
	return buf, nil
}

func (e *ETreeExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString("etree:")
	if e.IsLeaf {
		buf.WriteString("leaf:")
	} else {
		buf.WriteString("root:")
	}
	buf.WriteString(strconv.FormatUint(uint64(e.Label), 10))
	return buf.String()
}

func (e *ETreeExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Label   uint32                       `json:"label"`
		IsLeaf  bool                         `json:"is_leaf"`
	}{
		Type:    t,
		Subtype: s,
		Label:   e.Label,
		IsLeaf:  e.IsLeaf,
	})
}

func (e *ETreeExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_ETREE
}

func NewETreeExtended(label uint32, isLeaf bool) *ETreeExtended {
	return &ETreeExtended{
		Label:  label,
		IsLeaf: isLeaf,
	}
}

type MulticastFlagsExtended struct {
	IsIGMPProxy bool
	IsMLDProxy  bool
}

type MulticastFlags uint8

const (
	IGMP_PROXY MulticastFlags = 1 << 0
	MLD_PROXY  MulticastFlags = 1 << 1
)

func (e *MulticastFlagsExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_EVPN)
	buf[1] = byte(EC_SUBTYPE_MULTICAST_FLAGS)

	if e.IsIGMPProxy {
		buf[3] |= uint8(IGMP_PROXY)
	} else if e.IsMLDProxy {
		buf[3] |= uint8(MLD_PROXY)
	}
	return buf, nil
}

func (e *MulticastFlagsExtended) String() string {
	ss := make([]string, 0, 2)
	if e.IsIGMPProxy {
		ss = append(ss, "igmp-proxy")
	}
	if e.IsMLDProxy {
		ss = append(ss, "mld-proxy")
	}
	return "multicast-flags: " + strings.Join(ss, ",")
}

func (e *MulticastFlagsExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type      ExtendedCommunityAttrType    `json:"type"`
		Subtype   ExtendedCommunityAttrSubType `json:"subtype"`
		IGMPProxy bool                         `json:"is_igmp_proxy,omitempty"`
		MLDProxy  bool                         `json:"is_mld_proxy,omitempty"`
	}{
		Type:      t,
		Subtype:   s,
		IGMPProxy: e.IsIGMPProxy,
		MLDProxy:  e.IsMLDProxy,
	})
}

func (e *MulticastFlagsExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_EVPN, EC_SUBTYPE_MULTICAST_FLAGS
}

func NewMulticastFlagsExtended(isIGMPProxy bool, isMLDProxy bool) *MulticastFlagsExtended {
	return &MulticastFlagsExtended{
		IsIGMPProxy: isIGMPProxy,
		IsMLDProxy:  isMLDProxy,
	}
}

func parseEvpnExtended(data []byte) (ExtendedCommunityInterface, error) {
	if ExtendedCommunityAttrType(data[0]) != EC_TYPE_EVPN {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("ext comm type is not EC_TYPE_EVPN: %d", data[0]))
	}
	subType := ExtendedCommunityAttrSubType(data[1])
	switch subType {
	case EC_SUBTYPE_ESI_LABEL:
		var isSingleActive bool
		if data[2] > 0 {
			isSingleActive = true
		}
		label := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
		return &ESILabelExtended{
			IsSingleActive: isSingleActive,
			Label:          label,
		}, nil
	case EC_SUBTYPE_ES_IMPORT:
		return &ESImportRouteTarget{
			ESImport: net.HardwareAddr(data[2:8]),
		}, nil
	case EC_SUBTYPE_MAC_MOBILITY:
		var isSticky bool
		if data[2] > 0 {
			isSticky = true
		}
		seq := binary.BigEndian.Uint32(data[4:8])
		return &MacMobilityExtended{
			Sequence: seq,
			IsSticky: isSticky,
		}, nil
	case EC_SUBTYPE_ROUTER_MAC:
		return &RouterMacExtended{
			Mac: net.HardwareAddr(data[2:8]),
		}, nil
	case EC_SUBTYPE_L2_ATTRIBUTES:
		if flags := data[3]; flags == 0 {
			return &Layer2AttributesExtended{
				Mtu: binary.BigEndian.Uint16(data[4:6]),
			}, nil
		} else {
			return &Layer2AttributesExtended{
				HasCILabel:     flags&uint8(CI_LABEL) > 0,
				HasFlowLabel:   flags&uint8(FLOW_LABEL) > 0,
				HasControlWord: flags&uint8(CONTROL_WORD) > 0,
				IsPrimaryPe:    flags&uint8(PRIMARY_PE) > 0,
				IsBackupPe:     flags&uint8(BACKUP_PE) > 0,
				Mtu:            binary.BigEndian.Uint16(data[4:6]),
			}, nil
		}
	case EC_SUBTYPE_ETREE:
		var isLeaf bool
		if data[2] == 1 {
			isLeaf = true
		}
		label := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
		return &ETreeExtended{
			IsLeaf: isLeaf,
			Label:  label,
		}, nil
	case EC_SUBTYPE_MULTICAST_FLAGS:
		if flags := data[3]; flags != 0 {
			return &MulticastFlagsExtended{
				IsIGMPProxy: flags&uint8(IGMP_PROXY) > 0,
				IsMLDProxy:  flags&uint8(MLD_PROXY) > 0,
			}, nil
		}
	}
	return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("unknown evpn subtype: %d", subType))
}

type TrafficRateExtended struct {
	AS   uint16
	Rate float32
}

func (e *TrafficRateExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE)
	binary.BigEndian.PutUint16(buf[2:4], e.AS)
	binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(e.Rate))
	return buf, nil
}

func (e *TrafficRateExtended) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	if e.Rate == 0 {
		buf.WriteString("discard")
	} else {
		buf.WriteString("rate: " + strconv.FormatFloat(float64(e.Rate), 'f', 6, 32))
	}
	if e.AS != 0 {
		buf.WriteString("(as: " + strconv.FormatUint(uint64(e.AS), 10) + ")")
	}
	return buf.String()
}

func (e *TrafficRateExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		As      uint16                       `json:"as"`
		Rate    float32                      `json:"rate"`
	}{t, s, e.AS, e.Rate})
}

func (e *TrafficRateExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE
}

func NewTrafficRateExtended(as uint16, rate float32) *TrafficRateExtended {
	return &TrafficRateExtended{
		AS:   as,
		Rate: rate,
	}
}

type TrafficActionExtended struct {
	Terminal bool
	Sample   bool
}

func (e *TrafficActionExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION)
	if e.Terminal {
		buf[7] = 0x01
	}
	if e.Sample {
		buf[7] = buf[7] | 0x2
	}
	return buf, nil
}

func (e *TrafficActionExtended) String() string {
	ss := make([]string, 0, 2)
	if e.Terminal {
		ss = append(ss, "terminal")
	}
	if e.Sample {
		ss = append(ss, "sample")
	}
	return "action: " + strings.Join(ss, "-")
}

func (e *TrafficActionExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type     ExtendedCommunityAttrType    `json:"type"`
		Subtype  ExtendedCommunityAttrSubType `json:"subtype"`
		Terminal bool                         `json:"terminal"`
		Sample   bool                         `json:"sample"`
	}{t, s, e.Terminal, e.Sample})
}

func (e *TrafficActionExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION
}

func NewTrafficActionExtended(terminal bool, sample bool) *TrafficActionExtended {
	return &TrafficActionExtended{
		Terminal: terminal,
		Sample:   sample,
	}
}

type RedirectTwoOctetAsSpecificExtended struct {
	TwoOctetAsSpecificExtended
}

func (e *RedirectTwoOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf, err := e.TwoOctetAsSpecificExtended.Serialize()
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT)
	return buf, err
}

func (e *RedirectTwoOctetAsSpecificExtended) String() string {
	return "redirect: " + e.TwoOctetAsSpecificExtended.String()
}

func (e *RedirectTwoOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{t, s, e.TwoOctetAsSpecificExtended.String()})
}

func (e *RedirectTwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_REDIRECT
}

func NewRedirectTwoOctetAsSpecificExtended(as uint16, localAdmin uint32) *RedirectTwoOctetAsSpecificExtended {
	return &RedirectTwoOctetAsSpecificExtended{*NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)}
}

type RedirectIPv4AddressSpecificExtended struct {
	IPv4AddressSpecificExtended
}

func (e *RedirectIPv4AddressSpecificExtended) Serialize() ([]byte, error) {
	buf, err := e.IPv4AddressSpecificExtended.Serialize()
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT)
	return buf, err
}

func (e *RedirectIPv4AddressSpecificExtended) String() string {
	return "redirect: " + e.IPv4AddressSpecificExtended.String()
}

func (e *RedirectIPv4AddressSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{t, s, e.IPv4AddressSpecificExtended.String()})
}

func (e *RedirectIPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_SUBTYPE_FLOWSPEC_REDIRECT
}

func NewRedirectIPv4AddressSpecificExtended(ipv4 netip.Addr, localAdmin uint16) (*RedirectIPv4AddressSpecificExtended, error) {
	e, err := NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, ipv4, localAdmin, false)
	if err != nil {
		return nil, err
	}
	return &RedirectIPv4AddressSpecificExtended{*e}, nil
}

type RedirectIPv6AddressSpecificExtended struct {
	IPv6AddressSpecificExtended
}

func (e *RedirectIPv6AddressSpecificExtended) Serialize() ([]byte, error) {
	buf, err := e.IPv6AddressSpecificExtended.Serialize()
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6)
	return buf, err
}

func (e *RedirectIPv6AddressSpecificExtended) String() string {
	return "redirect: " + e.IPv6AddressSpecificExtended.String()
}

func (e *RedirectIPv6AddressSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{t, s, e.IPv6AddressSpecificExtended.String()})
}

func (e *RedirectIPv6AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6
}

func NewRedirectIPv6AddressSpecificExtended(ipv6 netip.Addr, localAdmin uint16) (*RedirectIPv6AddressSpecificExtended, error) {
	e, err := NewIPv6AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, ipv6, localAdmin, false)
	if err != nil {
		return nil, err
	}
	return &RedirectIPv6AddressSpecificExtended{*e}, nil
}

type RedirectFourOctetAsSpecificExtended struct {
	FourOctetAsSpecificExtended
}

func (e *RedirectFourOctetAsSpecificExtended) Serialize() ([]byte, error) {
	buf, err := e.FourOctetAsSpecificExtended.Serialize()
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT)
	return buf, err
}

func (e *RedirectFourOctetAsSpecificExtended) String() string {
	return "redirect: " + e.FourOctetAsSpecificExtended.String()
}

func (e *RedirectFourOctetAsSpecificExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   string                       `json:"value"`
	}{t, s, e.FourOctetAsSpecificExtended.String()})
}

func (e *RedirectFourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3, EC_SUBTYPE_FLOWSPEC_REDIRECT
}

func NewRedirectFourOctetAsSpecificExtended(as uint32, localAdmin uint16) *RedirectFourOctetAsSpecificExtended {
	return &RedirectFourOctetAsSpecificExtended{*NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)}
}

type TrafficRemarkExtended struct {
	DSCP uint8
}

func (e *TrafficRemarkExtended) Serialize() ([]byte, error) {
	buf := make([]byte, 8)
	buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
	buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK)
	buf[7] = e.DSCP
	return buf, nil
}

func (e *TrafficRemarkExtended) String() string {
	return "remark: " + strconv.FormatUint(uint64(e.DSCP), 10)
}

func (e *TrafficRemarkExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   uint8                        `json:"value"`
	}{t, s, e.DSCP})
}

func (e *TrafficRemarkExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK
}

func NewTrafficRemarkExtended(dscp uint8) *TrafficRemarkExtended {
	return &TrafficRemarkExtended{
		DSCP: dscp,
	}
}

func parseGenericTransitiveExperimentalExtended(data []byte) (ExtendedCommunityInterface, error) {
	typ := ExtendedCommunityAttrType(data[0])
	if typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("ext comm type is not EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL: %d", data[0]))
	}
	subType := ExtendedCommunityAttrSubType(data[1])
	switch subType {
	case EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE:
		asn := binary.BigEndian.Uint16(data[2:4])
		bits := binary.BigEndian.Uint32(data[4:8])
		rate := math.Float32frombits(bits)
		return NewTrafficRateExtended(asn, rate), nil
	case EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION:
		terminal := data[7]&0x1 == 1
		sample := data[7]>>1&0x1 == 1
		return NewTrafficActionExtended(terminal, sample), nil
	case EC_SUBTYPE_FLOWSPEC_REDIRECT:
		// RFC7674
		switch typ {
		case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL:
			as := binary.BigEndian.Uint16(data[2:4])
			localAdmin := binary.BigEndian.Uint32(data[4:8])
			return NewRedirectTwoOctetAsSpecificExtended(as, localAdmin), nil
		case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2:
			ipv4, _ := netip.AddrFromSlice(data[2:6])
			localAdmin := binary.BigEndian.Uint16(data[6:8])
			return NewRedirectIPv4AddressSpecificExtended(ipv4, localAdmin)
		case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3:
			as := binary.BigEndian.Uint32(data[2:6])
			localAdmin := binary.BigEndian.Uint16(data[6:8])
			return NewRedirectFourOctetAsSpecificExtended(as, localAdmin), nil
		}
	case EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK:
		dscp := data[7]
		return NewTrafficRemarkExtended(dscp), nil
	case EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6:
		if len(data) < 20 {
			return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all extended community bytes for IPv6 FlowSpec are available")
		}

		ipv6, _ := netip.AddrFromSlice(data[2:18])
		localAdmin := binary.BigEndian.Uint16(data[18:20])
		return NewRedirectIPv6AddressSpecificExtended(ipv6, localAdmin)
	case EC_SUBTYPE_L2_INFO:
		switch data[2] {
		case byte(LAYER2ENCAPSULATION_TYPE_VPLS):
			controlFlags := data[3]
			mtu := binary.BigEndian.Uint16(data[4:6])
			return NewVPLSExtended(controlFlags, mtu), nil
		}
	}
	return &UnknownExtended{
		Type:  ExtendedCommunityAttrType(data[0]),
		Value: data[1:8],
	}, nil
}

func parseIP6FlowSpecExtended(data []byte) (ExtendedCommunityInterface, error) {
	typ := ExtendedCommunityAttrType(data[0])
	if typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("ext comm type is not EC_TYPE_FLOWSPEC: %d", data[0]))
	}
	subType := ExtendedCommunityAttrSubType(data[1])
	switch subType {
	case EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6:
		// RFC7674
		switch typ {
		case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL:
			ipv6, _ := netip.AddrFromSlice(data[2:18])
			localAdmin := binary.BigEndian.Uint16(data[18:20])
			return NewRedirectIPv6AddressSpecificExtended(ipv6, localAdmin)
		}
	}
	v := make([]byte, 19)
	copy(v, data[1:20])
	return &UnknownIP6Extended{
		Type:  ExtendedCommunityAttrType(data[0]),
		Value: v,
	}, nil
}

type UnknownExtended struct {
	Type  ExtendedCommunityAttrType
	Value []byte
}

func (e *UnknownExtended) Serialize() ([]byte, error) {
	if len(e.Value) != 7 {
		return nil, fmt.Errorf("invalid value length for unknown extended community: %d", len(e.Value))
	}
	buf := make([]byte, 8)
	buf[0] = uint8(e.Type)
	copy(buf[1:], e.Value)
	return buf, nil
}

func (e *UnknownExtended) String() string {
	var buf [8]byte
	copy(buf[1:], e.Value)
	return strconv.FormatUint(binary.BigEndian.Uint64(buf[:]), 10)
}

func (e *UnknownExtended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   []byte                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.Value,
	})
}

func (e *UnknownExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	var subType ExtendedCommunityAttrSubType
	if len(e.Value) > 0 {
		// Use the first byte of value as the sub type
		subType = ExtendedCommunityAttrSubType(e.Value[0])
	}
	return e.Type, subType
}

func NewUnknownExtended(typ ExtendedCommunityAttrType, value []byte) *UnknownExtended {
	v := make([]byte, 7)
	copy(v, value)
	return &UnknownExtended{
		Type:  typ,
		Value: v,
	}
}

// UnknownIP6Extended represents an unknown IPv6 Extended Community (type 25).
// IPv6 Extended Communities are 20 bytes long, unlike the 8-byte regular Extended Communities.
type UnknownIP6Extended struct {
	Type  ExtendedCommunityAttrType
	Value []byte // 19 bytes (type byte is separate)
}

func (e *UnknownIP6Extended) Serialize() ([]byte, error) {
	if len(e.Value) != 19 {
		return nil, fmt.Errorf("invalid value length for unknown IPv6 extended community: %d", len(e.Value))
	}
	buf := make([]byte, 20)
	buf[0] = uint8(e.Type)
	copy(buf[1:], e.Value)
	return buf, nil
}

func (e *UnknownIP6Extended) String() string {
	return fmt.Sprintf("%d %x", e.Type, e.Value)
}

func (e *UnknownIP6Extended) MarshalJSON() ([]byte, error) {
	t, s := e.GetTypes()
	return json.Marshal(struct {
		Type    ExtendedCommunityAttrType    `json:"type"`
		Subtype ExtendedCommunityAttrSubType `json:"subtype"`
		Value   []byte                       `json:"value"`
	}{
		Type:    t,
		Subtype: s,
		Value:   e.Value,
	})
}

func (e *UnknownIP6Extended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
	var subType ExtendedCommunityAttrSubType
	if len(e.Value) > 0 {
		subType = ExtendedCommunityAttrSubType(e.Value[0])
	}
	return e.Type, subType
}

func (e *UnknownIP6Extended) Flat() map[string]string {
	return map[string]string{}
}

type PathAttributeExtendedCommunities struct {
	PathAttribute
	Value []ExtendedCommunityInterface
}

func ParseExtended(data []byte) (ExtendedCommunityInterface, error) {
	if len(data) < 8 {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all extended community bytes are available")
	}
	attrType := ExtendedCommunityAttrType(data[0])
	subtype := ExtendedCommunityAttrSubType(data[1])
	transitive := false
	switch attrType {
	case EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC:
		as := binary.BigEndian.Uint16(data[2:4])
		localAdmin := binary.BigEndian.Uint32(data[4:8])

		if subtype == EC_SUBTYPE_LINK_BANDWIDTH {
			return NewLinkBandwidthExtended(as, math.Float32frombits(localAdmin)), nil
		} else {
			return NewTwoOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil
		}
	case EC_TYPE_TRANSITIVE_IP4_SPECIFIC:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC:
		ipv4, _ := netip.AddrFromSlice(data[2:6])
		localAdmin := binary.BigEndian.Uint16(data[6:8])
		return NewIPv4AddressSpecificExtended(subtype, ipv4, localAdmin, transitive)
	case EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC:
		as := binary.BigEndian.Uint32(data[2:6])
		localAdmin := binary.BigEndian.Uint16(data[6:8])
		return NewFourOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil
	case EC_TYPE_TRANSITIVE_OPAQUE:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_OPAQUE:
		return parseOpaqueExtended(transitive, data)
	case EC_TYPE_EVPN:
		return parseEvpnExtended(data)
	case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3:
		return parseGenericTransitiveExperimentalExtended(data)
	case EC_TYPE_MUP:
		return parseMUPExtended(data)
	default:
		return &UnknownExtended{
			Type:  ExtendedCommunityAttrType(data[0]),
			Value: data[1:8],
		}, nil
	}
}

func (p *PathAttributeExtendedCommunities) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length%8 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "extendedcommunities length isn't correct")
	}
	for len(value) >= 8 {
		e, err := ParseExtended(value)
		if err != nil {
			return err
		}
		p.Value = append(p.Value, e)
		value = value[8:]
	}
	return nil
}

func (p *PathAttributeExtendedCommunities) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0)
	for _, p := range p.Value {
		ebuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, ebuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeExtendedCommunities) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	for idx, v := range p.Value {
		buf.WriteString("[")
		buf.WriteString(v.String())
		buf.WriteString("]")
		if idx < len(p.Value)-1 {
			buf.WriteString(", ")
		}
	}
	return "{Extcomms: " + buf.String() + "}"
}

func (p *PathAttributeExtendedCommunities) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType                  `json:"type"`
		Value []ExtendedCommunityInterface `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeExtendedCommunities(value []ExtendedCommunityInterface) *PathAttributeExtendedCommunities {
	l := len(value) * 8
	t := BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
	return &PathAttributeExtendedCommunities{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Value: value,
	}
}

type PathAttributeAs4Path struct {
	PathAttribute
	Value []*As4PathParam
}

func (p *PathAttributeAs4Path) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length == 0 {
		// ibgp or something
		return nil
	}
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
	isAs4, err := validateAsPathValueBytes(value)
	if err != nil {
		return err
	}

	if !isAs4 {
		return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed")
	}

	for len(value) > 0 {
		tuple := &As4PathParam{}
		if err := tuple.DecodeFromBytes(value); err != nil {
			return err
		}
		p.Value = append(p.Value, tuple)
		if len(value) < tuple.Len() {
			return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed")
		}
		value = value[tuple.Len():]
	}
	return nil
}

func (p *PathAttributeAs4Path) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0)
	for _, v := range p.Value {
		vbuf, err := v.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, vbuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeAs4Path) String() string {
	params := make([]string, 0, len(p.Value))
	for _, param := range p.Value {
		params = append(params, param.String())
	}
	return strings.Join(params, " ")
}

func (p *PathAttributeAs4Path) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType     `json:"type"`
		Value []*As4PathParam `json:"as_paths"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeAs4Path(value []*As4PathParam) *PathAttributeAs4Path {
	var l int
	for _, v := range value {
		l += v.Len()
	}
	t := BGP_ATTR_TYPE_AS4_PATH
	return &PathAttributeAs4Path{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Value: value,
	}
}

type PathAttributeAs4Aggregator struct {
	PathAttribute
	Value PathAttributeAggregatorParam
}

func (p *PathAttributeAs4Aggregator) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length != 8 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "AS4 Aggregator length is incorrect")
	}
	p.Value.AS = binary.BigEndian.Uint32(value[:4])
	// The length was validated above.
	p.Value.Address, _ = netip.AddrFromSlice(value[4:])
	return nil
}

func (p *PathAttributeAs4Aggregator) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint32(buf[0:], p.Value.AS)
	copy(buf[4:], p.Value.Address.AsSlice())
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeAs4Aggregator) String() string {
	return "{As4Aggregator: {AS: " +
		strconv.FormatUint(uint64(p.Value.AS), 10) + ", Address: " + p.Value.Address.String() + "}}"
}

func (p *PathAttributeAs4Aggregator) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type    BGPAttrType `json:"type"`
		AS      uint32      `json:"as"`
		Address string      `json:"address"`
	}{
		Type:    p.GetType(),
		AS:      p.Value.AS,
		Address: p.Value.Address.String(),
	})
}

func NewPathAttributeAs4Aggregator(as uint32, address netip.Addr) (*PathAttributeAs4Aggregator, error) {
	if !address.Is4() {
		return nil, fmt.Errorf("invalid address: %v", address)
	}
	t := BGP_ATTR_TYPE_AS4_AGGREGATOR
	return &PathAttributeAs4Aggregator{
		PathAttribute: PathAttribute{
			Flags:  PathAttrFlags[t],
			Type:   t,
			Length: 8,
		},
		Value: PathAttributeAggregatorParam{
			AS:      as,
			Address: address,
		},
	}, nil
}

type TunnelEncapSubTLVInterface interface {
	Len() int
	DecodeFromBytes([]byte) error
	Serialize() ([]byte, error)
	String() string
	MarshalJSON() ([]byte, error)
}

type TunnelEncapSubTLV struct {
	Type   EncapSubTLVType
	Length uint16
}

func (t *TunnelEncapSubTLV) Len() int {
	if t.Type >= 0x80 {
		return 3 + int(t.Length)
	}
	return 2 + int(t.Length)
}

func (t *TunnelEncapSubTLV) DecodeFromBytes(data []byte) (value []byte, err error) {
	if len(data) < 1 {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLV bytes available")
	}
	t.Type = EncapSubTLVType(data[0])
	if t.Type >= 0x80 {
		if len(data) < 3 {
			return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLV bytes available")
		}
		t.Length = binary.BigEndian.Uint16(data[1:3])
		data = data[3:]
	} else {
		if len(data) < 2 {
			return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLV bytes available")
		}
		t.Length = uint16(data[1])
		data = data[2:]
	}
	if len(data) < int(t.Length) {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLV bytes available")
	}
	return data[:t.Length], nil
}

func (t *TunnelEncapSubTLV) Serialize(value []byte) (buf []byte, err error) {
	t.Length = uint16(len(value))
	if t.Type >= 0x80 {
		buf = append(make([]byte, 3), value...)
		binary.BigEndian.PutUint16(buf[1:3], t.Length)
	} else {
		buf = append(make([]byte, 2), value...)
		buf[1] = uint8(t.Length)
	}
	buf[0] = uint8(t.Type)
	return buf, nil
}

type TunnelEncapSubTLVUnknown struct {
	TunnelEncapSubTLV
	Value []byte
}

func (t *TunnelEncapSubTLVUnknown) DecodeFromBytes(data []byte) error {
	value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	t.Value = value
	return nil
}

func (t *TunnelEncapSubTLVUnknown) Serialize() ([]byte, error) {
	return t.TunnelEncapSubTLV.Serialize(t.Value)
}

func (t *TunnelEncapSubTLVUnknown) String() string {
	return fmt.Sprintf("{Type: %d, Value: %x}", t.Type, t.Value)
}

func (t *TunnelEncapSubTLVUnknown) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  EncapSubTLVType `json:"type"`
		Value []byte          `json:"value"`
	}{
		Type:  t.Type,
		Value: t.Value,
	})
}

func NewTunnelEncapSubTLVUnknown(typ EncapSubTLVType, value []byte) *TunnelEncapSubTLVUnknown {
	return &TunnelEncapSubTLVUnknown{
		TunnelEncapSubTLV: TunnelEncapSubTLV{
			Type: typ,
		},
		Value: value,
	}
}

type TunnelEncapSubTLVEncapsulation struct {
	TunnelEncapSubTLV
	Key    uint32 // this represent both SessionID for L2TPv3 case and GRE-key for GRE case (RFC5512 4.)
	Cookie []byte
}

func (t *TunnelEncapSubTLVEncapsulation) DecodeFromBytes(data []byte) error {
	value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if t.Length < 4 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLVEncapsulation bytes available")
	}
	t.Key = binary.BigEndian.Uint32(value[:4])
	t.Cookie = value[4:]
	return nil
}

func (t *TunnelEncapSubTLVEncapsulation) Serialize() ([]byte, error) {
	buf := make([]byte, 4, 4+len(t.Cookie))
	binary.BigEndian.PutUint32(buf, t.Key)
	buf = append(buf, t.Cookie...)
	return t.TunnelEncapSubTLV.Serialize(buf)
}

func (t *TunnelEncapSubTLVEncapsulation) String() string {
	return fmt.Sprintf("{Key: %d, Cookie: %x}", t.Key, t.Cookie)
}

func (t *TunnelEncapSubTLVEncapsulation) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type   EncapSubTLVType `json:"type"`
		Key    uint32          `json:"key"`
		Cookie []byte          `json:"cookie"`
	}{
		Type:   t.Type,
		Key:    t.Key,
		Cookie: t.Cookie,
	})
}

func NewTunnelEncapSubTLVEncapsulation(key uint32, cookie []byte) *TunnelEncapSubTLVEncapsulation {
	return &TunnelEncapSubTLVEncapsulation{
		TunnelEncapSubTLV: TunnelEncapSubTLV{
			Type: ENCAP_SUBTLV_TYPE_ENCAPSULATION,
		},
		Key:    key,
		Cookie: cookie,
	}
}

type TunnelEncapSubTLVProtocol struct {
	TunnelEncapSubTLV
	Protocol uint16
}

func (t *TunnelEncapSubTLVProtocol) DecodeFromBytes(data []byte) error {
	value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if t.Length < 2 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLVProtocol bytes available")
	}
	t.Protocol = binary.BigEndian.Uint16(value[:2])
	return nil
}

func (t *TunnelEncapSubTLVProtocol) Serialize() ([]byte, error) {
	var buf [2]byte
	binary.BigEndian.PutUint16(buf[:2], t.Protocol)
	return t.TunnelEncapSubTLV.Serialize(buf[:])
}

func (t *TunnelEncapSubTLVProtocol) String() string {
	return fmt.Sprintf("{Protocol: %d}", t.Protocol)
}

func (t *TunnelEncapSubTLVProtocol) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type     EncapSubTLVType `json:"type"`
		Protocol uint16          `json:"protocol"`
	}{
		Type:     t.Type,
		Protocol: t.Protocol,
	})
}

func NewTunnelEncapSubTLVProtocol(protocol uint16) *TunnelEncapSubTLVProtocol {
	return &TunnelEncapSubTLVProtocol{
		TunnelEncapSubTLV: TunnelEncapSubTLV{
			Type: ENCAP_SUBTLV_TYPE_PROTOCOL,
		},
		Protocol: protocol,
	}
}

type TunnelEncapSubTLVColor struct {
	TunnelEncapSubTLV
	Color uint32
}

func (t *TunnelEncapSubTLVColor) DecodeFromBytes(data []byte) error {
	value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if t.Length != 8 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Invalid TunnelEncapSubTLVColor length")
	}
	t.Color = binary.BigEndian.Uint32(value[4:8])
	return nil
}

func (t *TunnelEncapSubTLVColor) Serialize() ([]byte, error) {
	var buf [8]byte
	buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
	buf[1] = byte(EC_SUBTYPE_COLOR)
	binary.BigEndian.PutUint32(buf[4:8], t.Color)
	return t.TunnelEncapSubTLV.Serialize(buf[:])
}

func (t *TunnelEncapSubTLVColor) String() string {
	return fmt.Sprintf("{Color: %d}", t.Color)
}

func (t *TunnelEncapSubTLVColor) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  EncapSubTLVType `json:"type"`
		Color uint32          `json:"color"`
	}{
		Type:  t.Type,
		Color: t.Color,
	})
}

func NewTunnelEncapSubTLVColor(color uint32) *TunnelEncapSubTLVColor {
	return &TunnelEncapSubTLVColor{
		TunnelEncapSubTLV: TunnelEncapSubTLV{
			Type: ENCAP_SUBTLV_TYPE_COLOR,
		},
		Color: color,
	}
}

type TunnelEncapSubTLVEgressEndpoint struct {
	TunnelEncapSubTLV
	Address netip.Addr
}

// Tunnel Egress Endpoint Sub-TLV subfield positions
const (
	EGRESS_ENDPOINT_RESERVED_POS = 0
	EGRESS_ENDPOINT_FAMILY_POS   = 4
	EGRESS_ENDPOINT_ADDRESS_POS  = 6
)

func (t *TunnelEncapSubTLVEgressEndpoint) DecodeFromBytes(data []byte) error {
	value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if t.Length < EGRESS_ENDPOINT_ADDRESS_POS {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLVEgressEndpoint bytes available")
	}
	addressFamily := binary.BigEndian.Uint16(value[EGRESS_ENDPOINT_FAMILY_POS : EGRESS_ENDPOINT_FAMILY_POS+2])

	var addressLen uint16
	switch addressFamily {
	case 0:
		addressLen = 0
	case AFI_IP:
		addressLen = net.IPv4len
	case AFI_IP6:
		addressLen = net.IPv6len
	default:
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Unsupported address family in TunnelEncapSubTLVEgressEndpoint")
	}
	if t.Length != EGRESS_ENDPOINT_ADDRESS_POS+addressLen {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLVEgressEndpoint address bytes available")
	}
	if addressFamily != 0 {
		// the length was validated above.
		t.Address, _ = netip.AddrFromSlice(value[EGRESS_ENDPOINT_ADDRESS_POS : EGRESS_ENDPOINT_ADDRESS_POS+addressLen])
	}

	return nil
}

func (t *TunnelEncapSubTLVEgressEndpoint) Serialize() ([]byte, error) {
	var length uint32 = EGRESS_ENDPOINT_ADDRESS_POS
	var family uint16
	var ip net.IP
	if !t.Address.IsValid() {
		family = 0
	} else if t.Address.Is4() {
		length += net.IPv4len
		family = AFI_IP
		ip = t.Address.AsSlice()
	} else {
		length += net.IPv6len
		family = AFI_IP6
		ip = t.Address.AsSlice()
	}
	buf := make([]byte, length)
	binary.BigEndian.PutUint32(buf, 0)
	binary.BigEndian.PutUint16(buf[EGRESS_ENDPOINT_FAMILY_POS:], family)
	if family != 0 {
		copy(buf[EGRESS_ENDPOINT_ADDRESS_POS:], ip)
	}
	return t.TunnelEncapSubTLV.Serialize(buf)
}

func (t *TunnelEncapSubTLVEgressEndpoint) String() string {
	address := ""
	if t.Address.IsValid() {
		address = t.Address.String()
	}
	return fmt.Sprintf("{EgressEndpoint: %s}", address)
}

func (t *TunnelEncapSubTLVEgressEndpoint) MarshalJSON() ([]byte, error) {
	address := ""
	if t.Address.IsValid() {
		address = t.Address.String()
	}

	return json.Marshal(struct {
		Type    EncapSubTLVType `json:"type"`
		Address string          `json:"address"`
	}{
		Type:    t.Type,
		Address: address,
	})
}

func NewTunnelEncapSubTLVEgressEndpoint(address netip.Addr) (*TunnelEncapSubTLVEgressEndpoint, error) {
	if !address.IsValid() {
		return nil, fmt.Errorf("invalid address: %v", address)
	}
	return &TunnelEncapSubTLVEgressEndpoint{
		TunnelEncapSubTLV: TunnelEncapSubTLV{
			Type: ENCAP_SUBTLV_TYPE_EGRESS_ENDPOINT,
		},
		Address: address,
	}, nil
}

type TunnelEncapSubTLVUDPDestPort struct {
	TunnelEncapSubTLV
	UDPDestPort uint16
}

func (t *TunnelEncapSubTLVUDPDestPort) DecodeFromBytes(data []byte) error {
	value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if t.Length < 2 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapSubTLVUDPDestPort bytes available")
	}
	t.UDPDestPort = binary.BigEndian.Uint16(value[:2])
	return nil
}

func (t *TunnelEncapSubTLVUDPDestPort) Serialize() ([]byte, error) {
	buf := make([]byte, 2)
	binary.BigEndian.PutUint16(buf, t.UDPDestPort)
	return t.TunnelEncapSubTLV.Serialize(buf)
}

func (t *TunnelEncapSubTLVUDPDestPort) String() string {
	return fmt.Sprintf("{UDPDestPort: %d}", t.UDPDestPort)
}

func (t *TunnelEncapSubTLVUDPDestPort) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type        EncapSubTLVType `json:"type"`
		UDPDestPort uint16          `json:"port"`
	}{
		Type:        t.Type,
		UDPDestPort: t.UDPDestPort,
	})
}

func NewTunnelEncapSubTLVUDPDestPort(port uint16) *TunnelEncapSubTLVUDPDestPort {
	return &TunnelEncapSubTLVUDPDestPort{
		TunnelEncapSubTLV: TunnelEncapSubTLV{
			Type: ENCAP_SUBTLV_TYPE_UDP_DEST_PORT,
		},
		UDPDestPort: port,
	}
}

type TunnelEncapTLV struct {
	Type   TunnelType
	Length uint16
	Value  []TunnelEncapSubTLVInterface
}

func (t *TunnelEncapTLV) Len() int {
	var l int
	for _, v := range t.Value {
		l += v.Len()
	}
	return 4 + l // Type(2) + Length(2) + Value(variable)
}

func (t *TunnelEncapTLV) DecodeFromBytes(data []byte) error {
	if len(data) < 4 {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not enough TunnelEncapTLV bytes available")
	}
	t.Type = TunnelType(binary.BigEndian.Uint16(data[:2]))
	t.Length = binary.BigEndian.Uint16(data[2:4])
	data = data[4:]
	if len(data) < int(t.Length) {
		return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all TunnelEncapTLV bytes available")
	}
	value := data[:t.Length]
	for len(value) > 2 {
		subType := EncapSubTLVType(value[0])
		var subTlv TunnelEncapSubTLVInterface
		switch subType {
		case ENCAP_SUBTLV_TYPE_ENCAPSULATION:
			subTlv = &TunnelEncapSubTLVEncapsulation{}
		case ENCAP_SUBTLV_TYPE_PROTOCOL:
			subTlv = &TunnelEncapSubTLVProtocol{}
		case ENCAP_SUBTLV_TYPE_COLOR:
			subTlv = &TunnelEncapSubTLVColor{}
		case ENCAP_SUBTLV_TYPE_UDP_DEST_PORT:
			subTlv = &TunnelEncapSubTLVUDPDestPort{}
		case ENCAP_SUBTLV_TYPE_EGRESS_ENDPOINT:
			subTlv = &TunnelEncapSubTLVEgressEndpoint{}
		case ENCAP_SUBTLV_TYPE_SRPREFERENCE:
			subTlv = &TunnelEncapSubTLVSRPreference{}
		case ENCAP_SUBTLV_TYPE_SRBINDING_SID:
			subTlv = &TunnelEncapSubTLVSRBSID{}
		case ENCAP_SUBTLV_TYPE_SRSEGMENT_LIST:
			subTlv = &TunnelEncapSubTLVSRSegmentList{}
		case ENCAP_SUBTLV_TYPE_SRENLP:
			subTlv = &TunnelEncapSubTLVSRENLP{}
		case ENCAP_SUBTLV_TYPE_SRPRIORITY:
			subTlv = &TunnelEncapSubTLVSRPriority{}
		case ENCAP_SUBTLV_TYPE_SRCANDIDATE_PATH_NAME:
			subTlv = &TunnelEncapSubTLVSRCandidatePathName{}
		default:
			subTlv = &TunnelEncapSubTLVUnknown{
				TunnelEncapSubTLV: TunnelEncapSubTLV{
					Type: subType,
				},
			}
		}
		err := subTlv.DecodeFromBytes(value)
		if err != nil {
			return err
		}
		t.Value = append(t.Value, subTlv)
		value = value[subTlv.Len():]
	}
	return nil
}

func (p *TunnelEncapTLV) Serialize() ([]byte, error) {
	buf := make([]byte, 4)
	for _, t := range p.Value {
		tBuf, err := t.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, tBuf...)
	}
	binary.BigEndian.PutUint16(buf, uint16(p.Type))
	binary.BigEndian.PutUint16(buf[2:], uint16(len(buf)-4))
	return buf, nil
}

func (p *TunnelEncapTLV) String() string {
	tlvList := make([]string, len(p.Value))
	for i, v := range p.Value {
		tlvList[i] = v.String()
	}
	return fmt.Sprintf("{%s: %s}", p.Type, strings.Join(tlvList, ", "))
}

func (p *TunnelEncapTLV) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  TunnelType                   `json:"type"`
		Value []TunnelEncapSubTLVInterface `json:"value"`
	}{
		Type:  p.Type,
		Value: p.Value,
	})
}

func NewTunnelEncapTLV(typ TunnelType, value []TunnelEncapSubTLVInterface) *TunnelEncapTLV {
	return &TunnelEncapTLV{
		Type:  typ,
		Value: value,
	}
}

type PathAttributeTunnelEncap struct {
	PathAttribute
	Value []*TunnelEncapTLV
}

func (p *PathAttributeTunnelEncap) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	for len(value) > 4 {
		tlv := &TunnelEncapTLV{}
		err = tlv.DecodeFromBytes(value)
		if err != nil {
			return err
		}
		p.Value = append(p.Value, tlv)
		value = value[4+tlv.Length:]
	}
	return nil
}

func (p *PathAttributeTunnelEncap) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0)
	for _, t := range p.Value {
		bbuf, err := t.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeTunnelEncap) String() string {
	tlvList := make([]string, len(p.Value))
	for i, v := range p.Value {
		tlvList[i] = v.String()
	}
	return "{TunnelEncap: " + strings.Join(tlvList, ", ") + "}"
}

func (p *PathAttributeTunnelEncap) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType       `json:"type"`
		Value []*TunnelEncapTLV `json:"value"`
	}{
		Type:  p.Type,
		Value: p.Value,
	})
}

func NewPathAttributeTunnelEncap(value []*TunnelEncapTLV) *PathAttributeTunnelEncap {
	var l int
	for _, v := range value {
		l += v.Len()
	}
	t := BGP_ATTR_TYPE_TUNNEL_ENCAP
	return &PathAttributeTunnelEncap{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Value: value,
	}
}

type PmsiTunnelIDInterface interface {
	Len() int
	Serialize() ([]byte, error)
	String() string
}

type DefaultPmsiTunnelID struct {
	Value []byte
}

func (i *DefaultPmsiTunnelID) Len() int {
	return len(i.Value)
}

func (i *DefaultPmsiTunnelID) Serialize() ([]byte, error) {
	return i.Value, nil
}

func (i *DefaultPmsiTunnelID) String() string {
	return string(i.Value)
}

func NewDefaultPmsiTunnelID(value []byte) *DefaultPmsiTunnelID {
	return &DefaultPmsiTunnelID{
		Value: value,
	}
}

type IngressReplTunnelID struct {
	Value netip.Addr
}

func (i *IngressReplTunnelID) Len() int {
	return i.Value.BitLen() / 8
}

func (i *IngressReplTunnelID) Serialize() ([]byte, error) {
	return i.Value.AsSlice(), nil
}

func (i *IngressReplTunnelID) String() string {
	return i.Value.String()
}

func NewIngressReplTunnelID(value netip.Addr) (*IngressReplTunnelID, error) {
	if !value.IsValid() {
		return nil, fmt.Errorf("invalid address: %v", value)
	}

	return &IngressReplTunnelID{
		Value: value,
	}, nil
}

type PathAttributePmsiTunnel struct {
	PathAttribute
	IsLeafInfoRequired bool
	TunnelType         PmsiTunnelType
	Label              uint32
	TunnelID           PmsiTunnelIDInterface
}

func (p *PathAttributePmsiTunnel) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	if p.Length < 5 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "PMSI Tunnel length is incorrect")
	}

	if value[0]&0x01 > 0 {
		p.IsLeafInfoRequired = true
	}
	p.TunnelType = PmsiTunnelType(value[1])
	if p.Label, err = labelDecode(value[2:5]); err != nil {
		return err
	}

	switch p.TunnelType {
	case PMSI_TUNNEL_TYPE_INGRESS_REPL:
		id, _ := netip.AddrFromSlice(value[5:])
		p.TunnelID = &IngressReplTunnelID{id}
	default:
		p.TunnelID = &DefaultPmsiTunnelID{value[5:]}
	}
	return nil
}

func (p *PathAttributePmsiTunnel) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 2)
	if p.IsLeafInfoRequired {
		buf[0] = 0x01
	}
	buf[1] = byte(p.TunnelType)
	tbuf, err := labelSerialize(p.Label)
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)
	tbuf, err = p.TunnelID.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, tbuf...)
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributePmsiTunnel) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	fmt.Fprintf(buf, "{Pmsi: type: %s,", p.TunnelType)
	if p.IsLeafInfoRequired {
		buf.WriteString(" leaf-info-required,")
	}
	fmt.Fprintf(buf, " label: %d, tunnel-id: %s}", p.Label, p.TunnelID)
	return buf.String()
}

func (p *PathAttributePmsiTunnel) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type               BGPAttrType `json:"type"`
		IsLeafInfoRequired bool        `json:"is-leaf-info-required"`
		TunnelType         uint8       `json:"tunnel-type"`
		Label              uint32      `json:"label"`
		TunnelID           string      `json:"tunnel-id"`
	}{
		Type:               p.Type,
		IsLeafInfoRequired: p.IsLeafInfoRequired,
		TunnelType:         uint8(p.TunnelType),
		Label:              p.Label,
		TunnelID:           p.TunnelID.String(),
	})
}

func NewPathAttributePmsiTunnel(typ PmsiTunnelType, isLeafInfoRequired bool, label uint32, id PmsiTunnelIDInterface) *PathAttributePmsiTunnel {
	if id == nil {
		return nil
	}
	// Flags(1) + TunnelType(1) + Label(3) + TunnelID(variable)
	l := 5 + id.Len()
	t := BGP_ATTR_TYPE_PMSI_TUNNEL
	return &PathAttributePmsiTunnel{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		IsLeafInfoRequired: isLeafInfoRequired,
		TunnelType:         typ,
		Label:              label,
		TunnelID:           id,
	}
}

func ParsePmsiTunnel(args []string) (*PathAttributePmsiTunnel, error) {
	// Format:
	// "<type>" ["leaf-info-required"] "<label>" "<tunnel-id>"
	if len(args) < 3 {
		return nil, fmt.Errorf("invalid pmsi tunnel arguments: %s", args)
	}

	var tunnelType PmsiTunnelType
	var isLeafInfoRequired bool
	switch args[0] {
	case "ingress-repl":
		tunnelType = PMSI_TUNNEL_TYPE_INGRESS_REPL
	default:
		typ, err := strconv.ParseUint(args[0], 10, 8)
		if err != nil {
			return nil, fmt.Errorf("invalid pmsi tunnel type: %s", args[0])
		}
		tunnelType = PmsiTunnelType(typ)
	}

	indx := 1
	if args[indx] == "leaf-info-required" {
		isLeafInfoRequired = true
		indx++
	}

	label, err := strconv.ParseUint(args[indx], 10, 32)
	if err != nil {
		return nil, fmt.Errorf("invalid pmsi tunnel label: %s", args[indx])
	}
	indx++

	var id PmsiTunnelIDInterface
	switch tunnelType {
	case PMSI_TUNNEL_TYPE_INGRESS_REPL:
		ip, ok := netip.ParseAddr(args[indx])
		if ok != nil {
			return nil, fmt.Errorf("invalid pmsi tunnel identifier: %s", args[indx])
		}
		id = &IngressReplTunnelID{Value: ip}
	default:
		id = &DefaultPmsiTunnelID{Value: []byte(args[indx])}
	}

	return NewPathAttributePmsiTunnel(tunnelType, isLeafInfoRequired, uint32(label), id), nil
}

type PathAttributeIP6ExtendedCommunities struct {
	PathAttribute
	Value []ExtendedCommunityInterface
}

func ParseIP6Extended(data []byte) (ExtendedCommunityInterface, error) {
	if len(data) < 20 {
		return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all extended community bytes are available")
	}
	attrType := ExtendedCommunityAttrType(data[0])
	subtype := ExtendedCommunityAttrSubType(data[1])
	transitive := false
	switch attrType {
	case EC_TYPE_TRANSITIVE_IP6_SPECIFIC:
		transitive = true
		fallthrough
	case EC_TYPE_NON_TRANSITIVE_IP6_SPECIFIC:
		ipv6, _ := netip.AddrFromSlice(data[2:18])
		localAdmin := binary.BigEndian.Uint16(data[18:20])
		return NewIPv6AddressSpecificExtended(subtype, ipv6, localAdmin, transitive)
	case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL:
		return parseIP6FlowSpecExtended(data)
	default:
		v := make([]byte, 19)
		copy(v, data[1:20])
		return &UnknownIP6Extended{
			Type:  ExtendedCommunityAttrType(data[0]),
			Value: v,
		}, nil
	}
}

func (p *PathAttributeIP6ExtendedCommunities) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if p.Length%20 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "extendedcommunities length isn't correct")
	}
	for len(value) >= 20 {
		e, err := ParseIP6Extended(value)
		if err != nil {
			return err
		}
		p.Value = append(p.Value, e)
		value = value[20:]
	}
	return nil
}

func (p *PathAttributeIP6ExtendedCommunities) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0)
	for _, p := range p.Value {
		ebuf, err := p.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, ebuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeIP6ExtendedCommunities) String() string {
	buf := make([]string, len(p.Value))
	for i, v := range p.Value {
		buf[i] = "[" + v.String() + "]"
	}
	return "{Extcomms: " + strings.Join(buf, ",") + "}"
}

func (p *PathAttributeIP6ExtendedCommunities) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType                  `json:"type"`
		Value []ExtendedCommunityInterface `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeIP6ExtendedCommunities(value []ExtendedCommunityInterface) *PathAttributeIP6ExtendedCommunities {
	l := len(value) * 20
	t := BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES
	return &PathAttributeIP6ExtendedCommunities{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Value: value,
	}
}

type AigpTLVType uint8

const (
	AIGP_TLV_UNKNOWN AigpTLVType = iota
	AIGP_TLV_IGP_METRIC
)

type AigpTLVInterface interface {
	Serialize() ([]byte, error)
	String() string
	MarshalJSON() ([]byte, error)
	Type() AigpTLVType
	Len() int
}

type AigpTLVDefault struct {
	typ   AigpTLVType
	Value []byte
}

func (t *AigpTLVDefault) Serialize() ([]byte, error) {
	buf := make([]byte, 3+len(t.Value))
	buf[0] = uint8(t.Type())
	binary.BigEndian.PutUint16(buf[1:], uint16(3+len(t.Value)))
	copy(buf[3:], t.Value)
	return buf, nil
}

func (t *AigpTLVDefault) String() string {
	return fmt.Sprintf("{Type: %d, Value: %v}", t.Type(), t.Value)
}

func (t *AigpTLVDefault) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  AigpTLVType `json:"type"`
		Value []byte      `json:"value"`
	}{
		Type:  t.Type(),
		Value: t.Value,
	})
}

func (t *AigpTLVDefault) Type() AigpTLVType {
	return t.typ
}

func (t *AigpTLVDefault) Len() int {
	return 3 + len(t.Value) // Type(1) + Length(2) + Value(variable)
}

func NewAigpTLVDefault(typ AigpTLVType, value []byte) *AigpTLVDefault {
	return &AigpTLVDefault{
		typ:   typ,
		Value: value,
	}
}

type AigpTLVIgpMetric struct {
	Metric uint64
}

func (t *AigpTLVIgpMetric) Serialize() ([]byte, error) {
	buf := make([]byte, 11)
	buf[0] = uint8(AIGP_TLV_IGP_METRIC)
	binary.BigEndian.PutUint16(buf[1:], uint16(11))
	binary.BigEndian.PutUint64(buf[3:], t.Metric)
	return buf, nil
}

func (t *AigpTLVIgpMetric) String() string {
	return fmt.Sprintf("{Metric: %d}", t.Metric)
}

func (t *AigpTLVIgpMetric) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type   AigpTLVType `json:"type"`
		Metric uint64      `json:"metric"`
	}{
		Type:   AIGP_TLV_IGP_METRIC,
		Metric: t.Metric,
	})
}

func NewAigpTLVIgpMetric(metric uint64) *AigpTLVIgpMetric {
	return &AigpTLVIgpMetric{
		Metric: metric,
	}
}

func (t *AigpTLVIgpMetric) Type() AigpTLVType {
	return AIGP_TLV_IGP_METRIC
}

func (t *AigpTLVIgpMetric) Len() int {
	return 11
}

type PathAttributeAigp struct {
	PathAttribute
	Values []AigpTLVInterface
}

func (p *PathAttributeAigp) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data, options...)
	if err != nil {
		return err
	}
	var values []AigpTLVInterface
	for len(value) > 3 {
		typ := value[0]
		length := binary.BigEndian.Uint16(value[1:3])
		if length <= 3 {
			return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Malformed BGP message")
		}
		if len(value) < int(length) {
			return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Aigp TLV length exceeds remaining data")
		}
		v := value[3:length]
		switch AigpTLVType(typ) {
		case AIGP_TLV_IGP_METRIC:
			if len(v) < 8 {
				return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Aigp IGP Metric TLV is too short")
			}
			metric := binary.BigEndian.Uint64(v)
			values = append(values, NewAigpTLVIgpMetric(metric))
		default:
			values = append(values, NewAigpTLVDefault(AigpTLVType(typ), v))
		}
		value = value[length:]
	}
	if len(value) != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
		return NewMessageError(eCode, eSubCode, nil, "Aigp length is incorrect")
	}
	p.Values = values
	return nil
}

func (p *PathAttributeAigp) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0)
	for _, t := range p.Values {
		bbuf, err := t.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeAigp) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString("{Aigp: [")
	for _, v := range p.Values {
		buf.WriteString(v.String())
	}
	buf.WriteString("]}")
	return buf.String()
}

func (p *PathAttributeAigp) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType        `json:"type"`
		Value []AigpTLVInterface `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Values,
	})
}

func NewPathAttributeAigp(values []AigpTLVInterface) *PathAttributeAigp {
	var l int
	for _, v := range values {
		l += v.Len()
	}
	t := BGP_ATTR_TYPE_AIGP
	return &PathAttributeAigp{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Values: values,
	}
}

type LargeCommunity struct {
	ASN        uint32
	LocalData1 uint32
	LocalData2 uint32
}

func (c *LargeCommunity) Serialize() ([]byte, error) {
	buf := make([]byte, 12)
	binary.BigEndian.PutUint32(buf, c.ASN)
	binary.BigEndian.PutUint32(buf[4:], c.LocalData1)
	binary.BigEndian.PutUint32(buf[8:], c.LocalData2)
	return buf, nil
}

func (c *LargeCommunity) String() string {
	return fmt.Sprintf("%d:%d:%d", c.ASN, c.LocalData1, c.LocalData2)
}

func (c *LargeCommunity) Eq(rhs *LargeCommunity) bool {
	return c.ASN == rhs.ASN && c.LocalData1 == rhs.LocalData1 && c.LocalData2 == rhs.LocalData2
}

func NewLargeCommunity(asn, data1, data2 uint32) *LargeCommunity {
	return &LargeCommunity{
		ASN:        asn,
		LocalData1: data1,
		LocalData2: data2,
	}
}

func ParseLargeCommunity(value string) (*LargeCommunity, error) {
	elems := strings.Split(value, ":")
	if len(elems) != 3 {
		return nil, errors.New("invalid large community format")
	}
	v := make([]uint32, 0, 3)
	for _, elem := range elems {
		e, err := strconv.ParseUint(elem, 10, 32)
		if err != nil {
			return nil, errors.New("invalid large community format")
		}
		v = append(v, uint32(e))
	}
	return NewLargeCommunity(v[0], v[1], v[2]), nil
}

type PathAttributeLargeCommunities struct {
	PathAttribute
	Values []*LargeCommunity
}

func (p *PathAttributeLargeCommunities) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	if p.Length%12 != 0 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return NewMessageError(eCode, eSubCode, nil, "large communities length isn't correct")
	}
	p.Values = make([]*LargeCommunity, 0, p.Length/12)
	for len(value) >= 12 {
		asn := binary.BigEndian.Uint32(value[:4])
		data1 := binary.BigEndian.Uint32(value[4:8])
		data2 := binary.BigEndian.Uint32(value[8:12])
		p.Values = append(p.Values, NewLargeCommunity(asn, data1, data2))
		value = value[12:]
	}
	return nil
}

func (p *PathAttributeLargeCommunities) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 0, len(p.Values)*12)
	for _, t := range p.Values {
		bbuf, err := t.Serialize()
		if err != nil {
			return nil, err
		}
		buf = append(buf, bbuf...)
	}
	return p.PathAttribute.Serialize(buf, options...)
}

func (p *PathAttributeLargeCommunities) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, 32))
	buf.WriteString("{LargeCommunity: [ ")
	ss := []string{}
	for _, v := range p.Values {
		ss = append(ss, v.String())
	}
	buf.WriteString(strings.Join(ss, ", "))
	buf.WriteString("]}")
	return buf.String()
}

func (p *PathAttributeLargeCommunities) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type  BGPAttrType       `json:"type"`
		Value []*LargeCommunity `json:"value"`
	}{
		Type:  p.GetType(),
		Value: p.Values,
	})
}

func NewPathAttributeLargeCommunities(values []*LargeCommunity) *PathAttributeLargeCommunities {
	l := len(values) * 12
	t := BGP_ATTR_TYPE_LARGE_COMMUNITY
	return &PathAttributeLargeCommunities{
		PathAttribute: PathAttribute{
			Flags:  getPathAttrFlags(t, l),
			Type:   t,
			Length: uint16(l),
		},
		Values: values,
	}
}

type PathAttributeUnknown struct {
	PathAttribute
	Value []byte
}

func (p *PathAttributeUnknown) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	value, err := p.PathAttribute.DecodeFromBytes(data)
	if err != nil {
		return err
	}
	p.Value = value
	return nil
}

func (p *PathAttributeUnknown) Serialize(options ...*MarshallingOption) ([]byte, error) {
	return p.PathAttribute.Serialize(p.Value, options...)
}

func (p *PathAttributeUnknown) String() string {
	return fmt.Sprintf("{Flags: %s, Type: %s, Value: %v}", p.Flags, p.Type, p.Value)
}

func (p *PathAttributeUnknown) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Flags BGPAttrFlag `json:"flags"`
		Type  BGPAttrType `json:"type"`
		Value []byte      `json:"value"`
	}{
		Flags: p.GetFlags(),
		Type:  p.GetType(),
		Value: p.Value,
	})
}

func NewPathAttributeUnknown(flags BGPAttrFlag, typ BGPAttrType, value []byte) *PathAttributeUnknown {
	l := len(value)
	if l > 255 {
		flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
	}
	return &PathAttributeUnknown{
		PathAttribute: PathAttribute{
			Flags:  flags,
			Type:   typ,
			Length: uint16(l),
		},
		Value: value,
	}
}

// BGPUpdateAttributes defines a map with a key as bgp attribute type
// and value as bool. Value set to true indicates that the attribute specified by the key
// exists in the bgp update.
type BGPUpdateAttributes struct {
	Attribute map[BGPAttrType]bool
}

func getBGPUpdateAttributes(data []byte) map[BGPAttrType]bool {
	m := make(map[BGPAttrType]bool)
	for p := 0; p < len(data); {
		flag := data[p]
		p++
		if p < len(data) {
			t := data[p]
			m[BGPAttrType(t)] = true
		} else {
			break
		}
		p++
		var l uint16
		// Checking for Extened
		if flag&0x10 == 0x10 {
			if p+2 <= len(data) {
				l = binary.BigEndian.Uint16(data[p : p+2])
			} else {
				break
			}
			p += 2
		} else {
			if p < len(data) {
				l = uint16(data[p])
				p++
			} else {
				break
			}
		}
		p += int(l)
	}
	return m
}

func getBGPUpdateAttributesFromMsg(msg *BGPUpdate) map[BGPAttrType]bool {
	m := make(map[BGPAttrType]bool)
	for _, p := range msg.PathAttributes {
		m[p.GetType()] = true
	}

	return m
}

func GetPathAttribute(data []byte) (PathAttributeInterface, error) {
	if len(data) < 2 {
		eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
		eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
		return nil, NewMessageError(eCode, eSubCode, data, "attribute type length is short")
	}
	switch BGPAttrType(data[1]) {
	case BGP_ATTR_TYPE_ORIGIN:
		return &PathAttributeOrigin{}, nil
	case BGP_ATTR_TYPE_AS_PATH:
		return &PathAttributeAsPath{}, nil
	case BGP_ATTR_TYPE_NEXT_HOP:
		return &PathAttributeNextHop{}, nil
	case BGP_ATTR_TYPE_MULTI_EXIT_DISC:
		return &PathAttributeMultiExitDisc{}, nil
	case BGP_ATTR_TYPE_LOCAL_PREF:
		return &PathAttributeLocalPref{}, nil
	case BGP_ATTR_TYPE_ATOMIC_AGGREGATE:
		return &PathAttributeAtomicAggregate{}, nil
	case BGP_ATTR_TYPE_AGGREGATOR:
		return &PathAttributeAggregator{}, nil
	case BGP_ATTR_TYPE_COMMUNITIES:
		return &PathAttributeCommunities{}, nil
	case BGP_ATTR_TYPE_ORIGINATOR_ID:
		return &PathAttributeOriginatorId{}, nil
	case BGP_ATTR_TYPE_CLUSTER_LIST:
		return &PathAttributeClusterList{}, nil
	case BGP_ATTR_TYPE_MP_REACH_NLRI:
		return &PathAttributeMpReachNLRI{}, nil
	case BGP_ATTR_TYPE_MP_UNREACH_NLRI:
		return &PathAttributeMpUnreachNLRI{}, nil
	case BGP_ATTR_TYPE_EXTENDED_COMMUNITIES:
		return &PathAttributeExtendedCommunities{}, nil
	case BGP_ATTR_TYPE_AS4_PATH:
		return &PathAttributeAs4Path{}, nil
	case BGP_ATTR_TYPE_AS4_AGGREGATOR:
		return &PathAttributeAs4Aggregator{}, nil
	case BGP_ATTR_TYPE_TUNNEL_ENCAP:
		return &PathAttributeTunnelEncap{}, nil
	case BGP_ATTR_TYPE_PMSI_TUNNEL:
		return &PathAttributePmsiTunnel{}, nil
	case BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES:
		return &PathAttributeIP6ExtendedCommunities{}, nil
	case BGP_ATTR_TYPE_AIGP:
		return &PathAttributeAigp{}, nil
	case BGP_ATTR_TYPE_LARGE_COMMUNITY:
		return &PathAttributeLargeCommunities{}, nil
	case BGP_ATTR_TYPE_LS:
		return &PathAttributeLs{}, nil
	case BGP_ATTR_TYPE_PREFIX_SID:
		return &PathAttributePrefixSID{}, nil
	}
	return &PathAttributeUnknown{}, nil
}

type PathNLRI struct {
	NLRI NLRI
	ID   uint32
}

func (p *PathNLRI) toSlice(isAddPath bool) ([]byte, error) {
	var buf []byte
	if isAddPath {
		var tmp [4]byte
		binary.BigEndian.PutUint32(tmp[:], p.ID)
		buf = append(buf, tmp[:]...)
	}
	nbuf, err := p.NLRI.Serialize()
	if err != nil {
		return nil, err
	}
	buf = append(buf, nbuf...)

	return buf, nil
}

func (p PathNLRI) String() string {
	return p.NLRI.String() + ":" + strconv.FormatUint(uint64(p.ID), 10)
}

type BGPUpdate struct {
	WithdrawnRoutesLen    uint16
	WithdrawnRoutes       []PathNLRI
	TotalPathAttributeLen uint16
	PathAttributes        []PathAttributeInterface
	NLRI                  []PathNLRI
}

func (msg *BGPUpdate) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	var strongestError error

	// cache error codes
	eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
	eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)

	// check withdrawn route length
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "message length isn't enough for withdrawn route length")
	}

	msg.WithdrawnRoutesLen = binary.BigEndian.Uint16(data[:2])
	data = data[2:]

	// check withdrawn route
	if len(data) < int(msg.WithdrawnRoutesLen) {
		return NewMessageError(eCode, eSubCode, nil, "withdrawn route length exceeds message length")
	}

	addpathLen := 0
	if IsAddPathEnabled(true, RF_IPv4_UC, options) {
		addpathLen = 4
	}

	msg.WithdrawnRoutes = make([]PathNLRI, 0, msg.WithdrawnRoutesLen)
	for routelen := msg.WithdrawnRoutesLen; routelen > 0; {
		id := uint32(0)
		if addpathLen != 0 {
			if len(data) < 4 {
				return NewMessageError(eCode, eSubCode, nil, "withdrawn route length is short")
			}
			id = binary.BigEndian.Uint32(data[:4])
			data = data[4:]
		}
		w := &IPAddrPrefix{}
		err := w.decodeFromBytes(data, net.IPv4len, options...)
		if err != nil {
			return err
		}
		wLen := uint16(w.Len(options...) + addpathLen)
		if wLen > routelen {
			return NewMessageError(eCode, eSubCode, nil, "Withdrawn route length exceeds withdrawn routes boundary")
		}
		routelen -= wLen
		if len(data) < w.Len(options...) {
			return NewMessageError(eCode, eSubCode, nil, "Withdrawn route length is short")
		}
		data = data[w.Len(options...):]
		msg.WithdrawnRoutes = append(msg.WithdrawnRoutes, PathNLRI{NLRI: w, ID: id})
	}

	// check path total attribute length
	if len(data) < 2 {
		return NewMessageError(eCode, eSubCode, nil, "message length isn't enough for path total attribute length")
	}

	msg.TotalPathAttributeLen = binary.BigEndian.Uint16(data[:2])
	data = data[2:]

	// check path attribute
	if len(data) < int(msg.TotalPathAttributeLen) {
		return NewMessageError(eCode, eSubCode, nil, "path total attribute length exceeds message length")
	}
	attributes := getBGPUpdateAttributes(data)
	o := MarshallingOption{
		attributes: attributes,
	}
	options = append(options, &o)

	msg.PathAttributes = []PathAttributeInterface{}
	for pathlen := msg.TotalPathAttributeLen; pathlen > 0; {
		var e error
		if pathlen < 3 {
			e = NewMessageErrorWithErrorHandling(
				eCode, BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR, data, ERROR_HANDLING_TREAT_AS_WITHDRAW, nil, "insufficient data to decode")
			if e.(*MessageError).Stronger(strongestError) {
				strongestError = e
			}
			data = data[pathlen:]
			break
		}
		p, err := GetPathAttribute(data)
		if err != nil {
			return err
		}
		err = p.DecodeFromBytes(data, options...)
		if err != nil {
			e = err.(*MessageError)
			if e.(*MessageError).SubTypeCode == BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR {
				e.(*MessageError).ErrorHandling = ERROR_HANDLING_TREAT_AS_WITHDRAW
			} else {
				e.(*MessageError).ErrorHandling = getErrorHandlingFromPathAttribute(p.GetType())
				e.(*MessageError).ErrorAttribute = &p
			}
			if e.(*MessageError).Stronger(strongestError) {
				strongestError = e
			}
		}
		pLen := uint16(p.Len(options...))
		if pLen > pathlen {
			e = NewMessageErrorWithErrorHandling(
				eCode, BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR, data, ERROR_HANDLING_TREAT_AS_WITHDRAW, nil, "path attribute length exceeds path attributes boundary")
			if e.(*MessageError).Stronger(strongestError) {
				strongestError = e
			}
			return strongestError
		}
		pathlen -= pLen
		if len(data) < p.Len(options...) {
			e = NewMessageErrorWithErrorHandling(
				eCode, BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR, data, ERROR_HANDLING_TREAT_AS_WITHDRAW, nil, "attribute length is short")
			if e.(*MessageError).Stronger(strongestError) {
				strongestError = e
			}
			return strongestError
		}
		data = data[p.Len(options...):]
		if e == nil || e.(*MessageError).ErrorHandling != ERROR_HANDLING_ATTRIBUTE_DISCARD {
			msg.PathAttributes = append(msg.PathAttributes, p)
		}
	}

	msg.NLRI = make([]PathNLRI, 0)
	for restlen := len(data); restlen > 0; {
		id := uint32(0)
		if addpathLen != 0 {
			if len(data) < 4 {
				return NewMessageError(eCode, eSubCode, nil, "NLRI length is short")
			}
			id = binary.BigEndian.Uint32(data[:4])
			data = data[4:]
		}
		n := &IPAddrPrefix{}
		err := n.decodeFromBytes(data, net.IPv4len, options...)
		if err != nil {
			return err
		}
		restlen -= n.Len(options...) + addpathLen
		if len(data) < n.Len(options...) {
			return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "NLRI length is short")
		}
		if n.Len(options...) > 32 {
			return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "NLRI length is too long")
		}
		data = data[n.Len(options...):]
		msg.NLRI = append(msg.NLRI, PathNLRI{NLRI: n, ID: id})
	}

	return strongestError
}

func (msg *BGPUpdate) Serialize(options ...*MarshallingOption) ([]byte, error) {
	wbuf := make([]byte, 2)
	v4AddPath := IsAddPathEnabled(false, RF_IPv4_UC, options)
	for _, w := range msg.WithdrawnRoutes {
		onewbuf, err := w.toSlice(v4AddPath)
		if err != nil {
			return nil, err
		}
		wbuf = append(wbuf, onewbuf...)
	}
	msg.WithdrawnRoutesLen = uint16(len(wbuf) - 2)
	binary.BigEndian.PutUint16(wbuf, msg.WithdrawnRoutesLen)

	attributes := getBGPUpdateAttributesFromMsg(msg)
	o := MarshallingOption{
		attributes: attributes,
	}
	options = append(options, &o)
	pbuf := make([]byte, 2)
	for _, p := range msg.PathAttributes {
		onepbuf, err := p.Serialize(options...)
		if err != nil {
			return nil, err
		}
		pbuf = append(pbuf, onepbuf...)
	}
	msg.TotalPathAttributeLen = uint16(len(pbuf) - 2)
	binary.BigEndian.PutUint16(pbuf, msg.TotalPathAttributeLen)

	buf := append(wbuf, pbuf...)
	for _, n := range msg.NLRI {
		nbuf, err := n.toSlice(v4AddPath)
		if err != nil {
			return nil, err
		}
		buf = append(buf, nbuf...)
	}

	return buf, nil
}

func (msg *BGPUpdate) IsEndOfRib() (bool, Family) {
	if len(msg.WithdrawnRoutes) == 0 && len(msg.NLRI) == 0 {
		if len(msg.PathAttributes) == 0 {
			return true, RF_IPv4_UC
		} else if len(msg.PathAttributes) == 1 && msg.PathAttributes[0].GetType() == BGP_ATTR_TYPE_MP_UNREACH_NLRI {
			unreach := msg.PathAttributes[0].(*PathAttributeMpUnreachNLRI)
			if len(unreach.Value) == 0 {
				return true, NewFamily(unreach.AFI, unreach.SAFI)
			}
		}
	}
	return false, Family(0)
}

func NewBGPUpdateMessage(withdrawnRoutes []PathNLRI, pathattrs []PathAttributeInterface, nlri []PathNLRI) *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_UPDATE},
		Body:   &BGPUpdate{0, withdrawnRoutes, 0, pathattrs, nlri},
	}
}

func NewEndOfRib(family Family) *BGPMessage {
	if family == RF_IPv4_UC {
		return NewBGPUpdateMessage(nil, nil, nil)
	} else {
		unreach, _ := NewPathAttributeMpUnreachNLRI(family, []PathNLRI{})
		return NewBGPUpdateMessage(nil, []PathAttributeInterface{unreach}, nil)
	}
}

type BGPNotification struct {
	ErrorCode    uint8
	ErrorSubcode uint8
	Data         []byte
}

func (msg *BGPNotification) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 2 {
		return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Not all Notification bytes available")
	}
	msg.ErrorCode = data[0]
	msg.ErrorSubcode = data[1]
	if len(data) > 2 {
		msg.Data = data[2:]
	}
	return nil
}

func (msg *BGPNotification) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 2, 2+len(msg.Data))
	buf[0] = msg.ErrorCode
	buf[1] = msg.ErrorSubcode
	buf = append(buf, msg.Data...)
	return buf, nil
}

func NewBGPNotificationMessage(errcode uint8, errsubcode uint8, data []byte) *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_NOTIFICATION},
		Body:   &BGPNotification{errcode, errsubcode, data},
	}
}

// RFC8538 makes a suggestion that which Cease notification subcodes should be
// mapped to the Hard Reset. This function takes a subcode and returns true if
// the subcode should be treated as a Hard Reset. Otherwise, it returns false.
//
// The second argument is a boolean value that indicates whether the Hard Reset
// should be performed on the Admin Reset. This reflects the RFC8538's
// suggestion that the implementation should provide a control to treat the
// Admin Reset as a Hard Reset. When the second argument is true, the function
// returns true if the subcode is BGP_ERROR_SUB_ADMINISTRATIVE_RESET.
// Otherwise, it returns false.
//
// As RFC8538 states, it is not mandatory to follow this suggestion. You can
// use this function when you want to follow the suggestion.
func ShouldHardReset(subcode uint8, hardResetOnAdminReset bool) bool {
	switch subcode {
	case BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED,
		BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN,
		BGP_ERROR_SUB_PEER_DECONFIGURED,
		BGP_ERROR_SUB_HARD_RESET:
		return true
	default:
		if hardResetOnAdminReset && subcode == BGP_ERROR_SUB_ADMINISTRATIVE_RESET {
			return true
		}
		return false
	}
}

type BGPKeepAlive struct{}

func (msg *BGPKeepAlive) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	return nil
}

func (msg *BGPKeepAlive) Serialize(options ...*MarshallingOption) ([]byte, error) {
	return nil, nil
}

func NewBGPKeepAliveMessage() *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Len: BGP_HEADER_LENGTH, Type: BGP_MSG_KEEPALIVE},
		Body:   &BGPKeepAlive{},
	}
}

type BGPRouteRefresh struct {
	AFI         uint16
	Demarcation uint8
	SAFI        uint8
}

func (msg *BGPRouteRefresh) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	if len(data) < 4 {
		return NewMessageError(BGP_ERROR_ROUTE_REFRESH_MESSAGE_ERROR, BGP_ERROR_SUB_INVALID_MESSAGE_LENGTH, nil, "Not all RouteRefresh bytes available")
	}
	msg.AFI = binary.BigEndian.Uint16(data[:2])
	msg.Demarcation = data[2]
	msg.SAFI = data[3]
	return nil
}

func (msg *BGPRouteRefresh) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint16(buf[:2], msg.AFI)
	buf[2] = msg.Demarcation
	buf[3] = msg.SAFI
	return buf, nil
}

func NewBGPRouteRefreshMessage(afi uint16, demarcation uint8, safi uint8) *BGPMessage {
	return &BGPMessage{
		Header: BGPHeader{Type: BGP_MSG_ROUTE_REFRESH},
		Body:   &BGPRouteRefresh{afi, demarcation, safi},
	}
}

type BGPBody interface {
	DecodeFromBytes([]byte, ...*MarshallingOption) error
	Serialize(...*MarshallingOption) ([]byte, error)
}

const (
	BGP_HEADER_LENGTH      = 19
	BGP_MAX_MESSAGE_LENGTH = 4096
	// BGP_MAX_EXTENDED_MESSAGE_LENGTH is the largest BGP message a
	// peer may exchange once both sides have advertised the RFC 8654
	// Extended Message capability. RFC 8654 Section 2 fixes the cap
	// at 65535 octets; Section 6 limits the relaxation to UPDATE,
	// NOTIFICATION and ROUTE-REFRESH (OPEN and KEEPALIVE keep the
	// 4096-octet ceiling).
	BGP_MAX_EXTENDED_MESSAGE_LENGTH = 65535
)

type BGPHeader struct {
	Marker []byte
	Len    uint16
	Type   uint8
}

func (msg *BGPHeader) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
	// minimum BGP message length
	if uint16(len(data)) < BGP_HEADER_LENGTH {
		return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "not all BGP message header")
	}

	// RFC 4271 Section 6.1: marker must be all ones.
	if binary.BigEndian.Uint64(data[:8]) != 0xffffffffffffffff ||
		binary.BigEndian.Uint64(data[8:16]) != 0xffffffffffffffff {
		return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_CONNECTION_NOT_SYNCHRONIZED, nil, "marker is not all ones")
	}

	msg.Len = binary.BigEndian.Uint16(data[16:18])
	if int(msg.Len) < BGP_HEADER_LENGTH {
		return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "unknown message type")
	}

	msg.Type = data[18]
	return nil
}

func (msg *BGPHeader) Serialize(options ...*MarshallingOption) ([]byte, error) {
	buf := make([]byte, BGP_HEADER_LENGTH)
	for i := range buf[:16] {
		buf[i] = 0xff
	}
	binary.BigEndian.PutUint16(buf[16:18], msg.Len)
	buf[18] = msg.Type
	return buf, nil
}

type BGPMessage struct {
	Header BGPHeader
	Body   BGPBody
}

func parseBody(h *BGPHeader, data []byte, options ...*MarshallingOption) (*BGPMessage, error) {
	if len(data) < int(h.Len)-BGP_HEADER_LENGTH {
		return nil, NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Not all BGP message bytes available")
	}
	msg := &BGPMessage{Header: *h}

	switch msg.Header.Type {
	case BGP_MSG_OPEN:
		msg.Body = &BGPOpen{}
	case BGP_MSG_UPDATE:
		msg.Body = &BGPUpdate{}
	case BGP_MSG_NOTIFICATION:
		msg.Body = &BGPNotification{}
	case BGP_MSG_KEEPALIVE:
		msg.Body = &BGPKeepAlive{}
	case BGP_MSG_ROUTE_REFRESH:
		msg.Body = &BGPRouteRefresh{}
	default:
		return nil, NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_TYPE, nil, "unknown message type")
	}
	err := msg.Body.DecodeFromBytes(data, options...)
	return msg, err
}

func ParseBGPMessage(data []byte, options ...*MarshallingOption) (*BGPMessage, error) {
	h := &BGPHeader{}
	if err := h.DecodeFromBytes(data, options...); err != nil {
		return nil, err
	}

	if int(h.Len) > len(data) {
		return nil, NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "unknown message type")
	}

	return parseBody(h, data[BGP_HEADER_LENGTH:h.Len], options...)
}

func ParseBGPBody(h *BGPHeader, data []byte, options ...*MarshallingOption) (*BGPMessage, error) {
	return parseBody(h, data, options...)
}

func (msg *BGPMessage) Serialize(options ...*MarshallingOption) ([]byte, error) {
	b, err := msg.Body.Serialize(options...)
	if err != nil {
		return nil, err
	}
	if msg.Header.Len == 0 {
		// RFC 8654 Section 4 + Section 6: with the BGP Extended
		// Message Capability negotiated the cap rises to 65535 for
		// UPDATE, NOTIFICATION and ROUTE-REFRESH; OPEN and KEEPALIVE
		// stay at 4096. The caller sets MarshallingOption.ExtendedMessage
		// only when the session negotiated the capability, so an
		// uncapped serialise on a peer that did not advertise the
		// capability still hits the 4096-octet ceiling.
		maxLen := BGP_MAX_MESSAGE_LENGTH
		if IsExtendedMessageSerialization(options) {
			switch msg.Header.Type {
			case BGP_MSG_UPDATE, BGP_MSG_NOTIFICATION, BGP_MSG_ROUTE_REFRESH:
				maxLen = BGP_MAX_EXTENDED_MESSAGE_LENGTH
			}
		}
		if BGP_HEADER_LENGTH+len(b) > maxLen {
			return nil, NewMessageError(0, 0, nil, fmt.Sprintf("too long message length %d", BGP_HEADER_LENGTH+len(b)))
		}
		msg.Header.Len = BGP_HEADER_LENGTH + uint16(len(b))
	}
	h, err := msg.Header.Serialize(options...)
	if err != nil {
		return nil, err
	}
	return append(h, b...), nil
}

type ErrorHandling int

const (
	ERROR_HANDLING_NONE ErrorHandling = iota
	ERROR_HANDLING_ATTRIBUTE_DISCARD
	ERROR_HANDLING_TREAT_AS_WITHDRAW
	ERROR_HANDLING_AFISAFI_DISABLE
	ERROR_HANDLING_SESSION_RESET
)

func getErrorHandlingFromPathAttribute(t BGPAttrType) ErrorHandling {
	switch t {
	case BGP_ATTR_TYPE_ORIGIN:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_AS_PATH:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_AS4_PATH:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_NEXT_HOP:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_MULTI_EXIT_DISC:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_LOCAL_PREF:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_ATOMIC_AGGREGATE:
		return ERROR_HANDLING_ATTRIBUTE_DISCARD
	case BGP_ATTR_TYPE_AGGREGATOR:
		return ERROR_HANDLING_ATTRIBUTE_DISCARD
	case BGP_ATTR_TYPE_AS4_AGGREGATOR:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_COMMUNITIES:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_ORIGINATOR_ID:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_CLUSTER_LIST:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_MP_REACH_NLRI:
		return ERROR_HANDLING_AFISAFI_DISABLE
	case BGP_ATTR_TYPE_MP_UNREACH_NLRI:
		return ERROR_HANDLING_AFISAFI_DISABLE
	case BGP_ATTR_TYPE_EXTENDED_COMMUNITIES:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_PMSI_TUNNEL:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_LARGE_COMMUNITY:
		return ERROR_HANDLING_TREAT_AS_WITHDRAW
	case BGP_ATTR_TYPE_TUNNEL_ENCAP:
		return ERROR_HANDLING_ATTRIBUTE_DISCARD
	case BGP_ATTR_TYPE_AIGP:
		return ERROR_HANDLING_ATTRIBUTE_DISCARD
	default:
		return ERROR_HANDLING_ATTRIBUTE_DISCARD
	}
}

type MessageError struct {
	TypeCode       uint8
	SubTypeCode    uint8
	Data           []byte
	Message        string
	ErrorHandling  ErrorHandling
	ErrorAttribute *PathAttributeInterface
}

func NewMessageError(typeCode, subTypeCode uint8, data []byte, msg string) error {
	return &MessageError{
		TypeCode:       typeCode,
		SubTypeCode:    subTypeCode,
		Data:           data,
		ErrorHandling:  ERROR_HANDLING_SESSION_RESET,
		ErrorAttribute: nil,
		Message:        msg,
	}
}

func NewMessageErrorWithErrorHandling(typeCode, subTypeCode uint8, data []byte, errorHandling ErrorHandling, errorAttribute *PathAttributeInterface, msg string) error {
	return &MessageError{
		TypeCode:       typeCode,
		SubTypeCode:    subTypeCode,
		Data:           data,
		ErrorHandling:  errorHandling,
		ErrorAttribute: errorAttribute,
		Message:        msg,
	}
}

func (e *MessageError) Error() string {
	return e.Message
}

func (e *MessageError) Stronger(err error) bool {
	if err == nil {
		return true
	}
	if msgErr, ok := err.(*MessageError); ok {
		return e.ErrorHandling > msgErr.ErrorHandling
	}
	return false
}

func (e *TwoOctetAsSpecificExtended) Flat() map[string]string {
	if e.SubType == EC_SUBTYPE_ROUTE_TARGET {
		return map[string]string{"routeTarget": e.String()}
	}
	return map[string]string{}
}

func (e *ColorExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *EncapExtended) Flat() map[string]string {
	return map[string]string{"encaspulation": e.TunnelType.String()}
}

func (e *DefaultGatewayExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *ValidationExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *LinkBandwidthExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *OpaqueExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *IPv4AddressSpecificExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *IPv6AddressSpecificExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *FourOctetAsSpecificExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *ESILabelExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *ETreeExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *MulticastFlagsExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *ESImportRouteTarget) Flat() map[string]string {
	return map[string]string{}
}

func (e *MacMobilityExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *RouterMacExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *Layer2AttributesExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *TrafficRateExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *TrafficRemarkExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *RedirectIPv4AddressSpecificExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *RedirectIPv6AddressSpecificExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *RedirectFourOctetAsSpecificExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *UnknownExtended) Flat() map[string]string {
	return map[string]string{}
}

func (e *TrafficActionExtended) Flat() map[string]string {
	return map[string]string{}
}

func (p *PathAttributeExtendedCommunities) Flat() map[string]string {
	flat := map[string]string{}
	for _, ec := range p.Value {
		// TODO: what do we do in case of conflict?
		_ = FlatUpdate(flat, ec.Flat())
	}
	return flat
}

func (p *PathAttribute) Flat() map[string]string {
	return map[string]string{}
}

func (l *LabeledVPNIPAddrPrefix) Flat() map[string]string {
	return map[string]string{
		"Prefix":    l.Prefix.Addr().String(),
		"PrefixLen": fmt.Sprintf("%d", l.Prefix.Bits()),
		"NLRI":      l.String(),
		"Label":     l.Labels.String(),
	}
}

func (l *EVPNNLRI) Flat() map[string]string {
	return map[string]string{}
}

func (l *RouteTargetMembershipNLRI) Flat() map[string]string {
	return map[string]string{}
}

func (l *OpaqueNLRI) Flat() map[string]string {
	return map[string]string{}
}

// Update a Flat representation by adding elements of the second
// one. If two elements use same keys, values are separated with
// ';'. In this case, it returns an error but the update has been
// realized.
func FlatUpdate(f1, f2 map[string]string) error {
	conflict := false
	for k2, v2 := range f2 {
		if v1, ok := f1[k2]; ok {
			f1[k2] = v1 + ";" + v2
			conflict = true
		} else {
			f1[k2] = v2
		}
	}
	if conflict {
		return errors.New("keys conflict")
	} else {
		return nil
	}
}
