// 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
attributes map[BGPAttrType]bool
}
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_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_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
}
data = data[2:]
if len(data) < 4 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityMultiProtocol bytes available")
}
c.CapValue = NewFamily(binary.BigEndian.Uint16(data[:2]), data[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,
},
}
}
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
}
data = data[2:]
if len(data) < 2 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityGracefulRestart bytes available")
}
restart := binary.BigEndian.Uint16(data[:2])
c.Flags = uint8(restart >> 12)
c.Time = restart & 0xfff
data = data[2:]
valueLen := int(c.CapLen) - 2
if valueLen >= 4 && len(data) >= valueLen {
c.Tuples = make([]*CapGracefulRestartTuple, 0, valueLen/4)
for i := valueLen; i >= 4; i -= 4 {
t := &CapGracefulRestartTuple{
binary.BigEndian.Uint16(data[:2]),
data[2], data[3],
}
c.Tuples = append(c.Tuples, t)
data = data[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
}
data = data[2:]
if len(data) < 4 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFourOctetASNumber bytes available")
}
c.CapValue = binary.BigEndian.Uint32(data[:4])
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
}
if len(data) < 2 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
}
data = data[2:]
rest := len(data)
if rest < 1 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
}
rest -= 1
c.HostNameLen = data[0]
hostNameLen := int(c.HostNameLen)
if rest < hostNameLen {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
}
if len(data) < hostNameLen+2 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
}
c.HostName = string(data[1 : hostNameLen+1])
rest -= hostNameLen
if rest < 1 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilityFQDN bytes allowed")
}
rest -= 1
domainNameLen := data[hostNameLen+1]
if rest < 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(data[hostNameLen+2:])
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
}
data = data[2:]
if len(data) < 2 {
return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilitySoftwareVersion bytes allowed")
}
softwareVersionLen := data[0]
if len(data[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(data[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_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 {
l.Labels = []uint32{}
return nil
}
l.Labels = labels
return nil
}
func (l *MPLSLabelStack) Serialize(options ...*MarshallingOption) ([]byte, error) {
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 {
l.Labels.Labels = []uint32{}
}
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 {
l.Labels.Labels = []uint32{}
}
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:]
if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
if len(data) < int(er.IPAddressLength/8) {
return malformedAttrListErr("bad length of MAC/IP Advertisement Route")
}
// The length was validated above
er.IPAddress, _ = netip.AddrFromSlice(data[:er.IPAddressLength/8])
} else if er.IPAddressLength != 0 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid IP address length: %d", er.IPAddressLength))
}
if len(data) < int(er.IPAddressLength/8) {
return malformedAttrListErr("bad length of MAC/IP Advertisement Route")
}
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".
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) {
return json.Marshal(struct {
RD RouteDistinguisherInterface `json:"rd"`
ESI string `json:"esi"`
Etag uint32 `json:"etag"`
MacAddress string `json:"mac"`
IPAddress string `json:"ip"`
Labels []uint32 `json:"labels"`
}{
RD: er.RD,
ESI: er.ESI.String(),
Etag: er.ETag,
MacAddress: er.MacAddress.String(),
IPAddress: er.IPAddress.String(),
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)
if !ipAddress.IsValid() {
return nil, fmt.Errorf("invalid IP address")
}
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)
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)
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)
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 nil
}
tlv := data[lsNLRIHdrLen:]
if len(tlv) < tlvHdrLen {
return malformedAttrListErr("Malformed Node NLRI")
}
tlvType := LsTLVType(binary.BigEndian.Uint16(tlv[:2]))
if tlvType != LS_TLV_LOCAL_NODE_DESC {
return malformedAttrListErr("Mandatory TLV missing")
}
l.LocalNodeDesc = &LsTLVNodeDescriptor{}
if err := l.LocalNodeDesc.DecodeFromBytes(tlv); err != nil {
return malformedAttrListErr(fmt.Sprintf("Malformed Node NLRI: %v", err))
}
return nil
}
func (l *LsNodeNLRI) String() string {
if l.LocalNodeDesc == nil {
return "NODE { EMPTY }"
}
local := l.LocalNodeDesc.(*LsTLVNodeDescriptor).Extract()
return fmt.Sprintf("NODE { AS:%v BGP-LS ID:%v %v %v:%v }", local.Asn, local.BGPLsID, local.IGPRouterID, l.ProtocolID.String(), l.Identifier)
}
func (l *LsNodeNLRI) Serialize() ([]byte, error) {
if l.LocalNodeDesc == nil {
return nil, 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
}
func (l *LsLinkDescriptor) ParseTLVs(tlvs []LsTLVInterface) {
for _, tlv := range tlvs {
switch v := tlv.(type) {
case *LsTLVLinkID:
l.LinkLocalID = &v.Local
l.LinkRemoteID = &v.Remote
case *LsTLVIPv4InterfaceAddr:
l.InterfaceAddrIPv4 = &v.IP
case *LsTLVIPv4NeighborAddr:
l.NeighborAddrIPv4 = &v.IP
case *LsTLVIPv6InterfaceAddr:
l.InterfaceAddrIPv6 = &v.IP
case *LsTLVIPv6NeighborAddr:
l.NeighborAddrIPv6 = &v.IP
}
}
}
func (l *LsLinkDescriptor) String() string {
switch {
case l.InterfaceAddrIPv4 != nil && l.NeighborAddrIPv4 != nil:
return fmt.Sprintf("%v->%v", l.InterfaceAddrIPv4, l.NeighborAddrIPv4)
case l.InterfaceAddrIPv6 != nil && l.NeighborAddrIPv6 != nil:
return fmt.Sprintf("%v->%v", l.InterfaceAddrIPv6, l.NeighborAddrIPv6)
case l.LinkLocalID != nil && l.LinkRemoteID != nil:
return fmt.Sprintf("%v->%v", *l.LinkLocalID, *l.LinkRemoteID)
case l.InterfaceAddrIPv4 != nil:
return fmt.Sprintf("%v->UNKNOWN", l.InterfaceAddrIPv4)
case l.NeighborAddrIPv4 != nil:
return fmt.Sprintf("UNKNOWN->%v", l.NeighborAddrIPv4)
case l.InterfaceAddrIPv6 != nil:
return fmt.Sprintf("%v->UNKNOWN", l.InterfaceAddrIPv6)
case l.NeighborAddrIPv6 != nil:
return fmt.Sprintf("UNKNOWN->%v", l.NeighborAddrIPv6)
case l.LinkLocalID != nil:
return fmt.Sprintf("%v->UNKNOWN", *l.LinkLocalID)
case l.LinkRemoteID != nil:
return fmt.Sprintf("UNKNOWN->%v", *l.LinkRemoteID)
default:
return "UNKNOWN"
}
}
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 nil
}
tlv := data[lsNLRIHdrLen:]
m := make(map[LsTLVType]bool)
for len(tlv) >= tlvHdrLen {
sub := &LsTLV{}
_, err := sub.DecodeFromBytes(tlv)
if err != nil {
return err
}
m[sub.Type] = true
var subTLV LsTLVInterface
switch sub.Type {
case LS_TLV_LOCAL_NODE_DESC, LS_TLV_REMOTE_NODE_DESC:
subTLV = &LsTLVNodeDescriptor{}
case LS_TLV_LINK_ID:
subTLV = &LsTLVLinkID{}
case LS_TLV_IPV4_INTERFACE_ADDR:
subTLV = &LsTLVIPv4InterfaceAddr{}
case LS_TLV_IPV4_NEIGHBOR_ADDR:
subTLV = &LsTLVIPv4NeighborAddr{}
case LS_TLV_IPV6_INTERFACE_ADDR:
subTLV = &LsTLVIPv6InterfaceAddr{}
case LS_TLV_IPV6_NEIGHBOR_ADDR:
subTLV = &LsTLVIPv6NeighborAddr{}
default:
tlv = tlv[sub.Len():]
l.Length -= uint16(sub.Len())
continue
}
if err := subTLV.DecodeFromBytes(tlv); err != nil {
return err
}
tlv = tlv[subTLV.Len():]
switch sub.Type {
case LS_TLV_LOCAL_NODE_DESC:
l.LocalNodeDesc = subTLV
case LS_TLV_REMOTE_NODE_DESC:
l.RemoteNodeDesc = subTLV
default:
l.LinkDesc = append(l.LinkDesc, subTLV)
}
}
required := []LsTLVType{LS_TLV_LOCAL_NODE_DESC, LS_TLV_REMOTE_NODE_DESC}
for _, tlv := range required {
if _, ok := m[tlv]; !ok {
return malformedAttrListErr("Required TLV missing")
}
}
return nil
}
func (l *LsLinkNLRI) Serialize() ([]byte, error) {
if l.LocalNodeDesc == nil || l.RemoteNodeDesc == nil {
return nil, 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
}
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
}
}
}
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}", local.IGPRouterID, ips, ospf)
}
func (l *LsPrefixV4NLRI) DecodeFromBytes(data []byte) error {
if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
return nil
}
tlv := data[lsNLRIHdrLen:]
m := make(map[LsTLVType]bool)
for len(tlv) >= tlvHdrLen {
sub := &LsTLV{}
_, err := sub.DecodeFromBytes(tlv)
if err != nil {
return err
}
m[sub.Type] = true
var subTLV LsTLVInterface
switch sub.Type {
case LS_TLV_LOCAL_NODE_DESC:
subTLV = &LsTLVNodeDescriptor{}
case LS_TLV_OSPF_ROUTE_TYPE:
subTLV = &LsTLVOspfRouteType{}
case LS_TLV_IP_REACH_INFO:
subTLV = &LsTLVIPReachability{}
default:
tlv = tlv[sub.Len():]
l.Length -= uint16(sub.Len())
continue
}
if err := subTLV.DecodeFromBytes(tlv); err != nil {
return err
}
tlv = tlv[subTLV.Len():]
switch sub.Type {
case LS_TLV_LOCAL_NODE_DESC:
l.LocalNodeDesc = subTLV
default:
l.PrefixDesc = append(l.PrefixDesc, subTLV)
}
}
required := []LsTLVType{LS_TLV_IP_REACH_INFO, LS_TLV_LOCAL_NODE_DESC}
for _, tlv := range required {
if _, ok := m[tlv]; !ok {
return malformedAttrListErr("Required TLV missing")
}
}
for _, tlv := range l.PrefixDesc {
switch v := tlv.(type) {
case *LsTLVIPReachability:
if v.PrefixLength > 8*net.IPv4len {
return malformedAttrListErr("Unexpected IP Reachability info")
}
}
}
return nil
}
func (l *LsPrefixV4NLRI) Serialize() ([]byte, error) {
if l.LocalNodeDesc == nil {
return nil, 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}", local.IGPRouterID, ips, ospf)
}
func (l *LsPrefixV6NLRI) DecodeFromBytes(data []byte) error {
if err := l.LsNLRI.DecodeFromBytes(data); err != nil {
return nil
}
tlv := data[lsNLRIHdrLen:]
m := make(map[LsTLVType]bool)
for len(tlv) >= tlvHdrLen {
sub := &LsTLV{}
_, err := sub.DecodeFromBytes(tlv)
if err != nil {
return err
}
m[sub.Type] = true
var subTLV LsTLVInterface
switch sub.Type {
case LS_TLV_LOCAL_NODE_DESC:
subTLV = &LsTLVNodeDescriptor{}
case LS_TLV_OSPF_ROUTE_TYPE:
subTLV = &LsTLVOspfRouteType{}
case LS_TLV_IP_REACH_INFO:
subTLV = &LsTLVIPReachability{}
default:
tlv = tlv[sub.Len():]
l.Length -= uint16(sub.Len())
continue
}
if err := subTLV.DecodeFromBytes(tlv); err != nil {
return err
}
tlv = tlv[subTLV.Len():]
switch sub.Type {
case LS_TLV_LOCAL_NODE_DESC:
l.LocalNodeDesc = subTLV
default:
l.PrefixDesc = append(l.PrefixDesc, subTLV)
}
}
required := []LsTLVType{LS_TLV_IP_REACH_INFO, LS_TLV_LOCAL_NODE_DESC}
for _, tlv := range required {
if _, ok := m[tlv]; !ok {
return malformedAttrListErr("Required TLV missing")
}
}
return nil
}
func (l *LsPrefixV6NLRI) Serialize() ([]byte, error) {
if l.LocalNodeDesc == nil {
return nil, 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 nil
}
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
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_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.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: BGP_ASPATH_ATTR_TYPE_SET,
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 := (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) < 2 {
return malformedAttrListErr("Incorrect IP reachability Info length")
}
// https://tools.ietf.org/html/rfc7752#section-3.2.3.2
if value[0] > 128 || value[0] == 0 {
return malformedAttrListErr("Incorrect IP prefix length")
}
ll := (int(value[0])-1)/8 + 1
if len(value[1:]) != ll {
return malformedAttrListErr("Malformed IP reachability TLV")
}
l.PrefixLength = value[0]
l.Prefix = value[1 : 1+ll]
return nil
}
func (l *LsTLVIPReachability) Serialize() ([]byte, error) {
b := []byte{l.PrefixLength}
return l.LsTLV.Serialize(append(b, l.Prefix...))
}
func (l *LsTLVIPReachability) String() string {
return fmt.Sprintf("{IP Reachability: %v/%v}", l.Prefix, l.PrefixLength)
}
func (l *LsTLVIPReachability) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type LsTLVType `json:"type"`
PrefixLength uint8 `json:"prefix_length"`
Prefix string `json:"prefix"`
}{
Type: l.Type,
PrefixLength: l.PrefixLength,
Prefix: fmt.Sprintf("%v", l.Prefix),
})
}
func (l *LsTLVIPReachability) GetLsTLV() LsTLV {
return l.LsTLV
}
type LsTLVAdminGroup struct {
LsTLV
AdminGroup uint32
}
func NewLsTLVAdminGroup(l *uint32) *LsTLVAdminGroup {
return &LsTLVAdminGroup{
LsTLV: LsTLV{
Type: BGP_ASPATH_ATTR_TYPE_SET,
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 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 >> 0)
}
if l.IPv6Supported {
flags = flags & (1 >> 1)
}
ranges := []LsSrLabelRange{}
var length uint16
for _, r := range l.Ranges {
ranges = append(ranges, LsSrLabelRange{
Range: r.End - r.Begin,
FirstLabel: LsTLVSIDLabel{
LsTLV: LsTLV{
Type: BGP_ASPATH_ATTR_TYPE_SET,
Length: 4,
},
SID: r.Begin,
},
})
length += 4
}
return &LsTLVSrCapabilities{
LsTLV: LsTLV{
Type: BGP_ASPATH_ATTR_TYPE_SET,
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<<0) > 0,
IPv6Supported: l.Flags&(1<<1) > 0,
}
for _, r := range l.Ranges {
lsc.Ranges = append(lsc.Ranges, LsSrRange{
Begin: r.FirstLabel.SID,
End: r.FirstLabel.SID + r.Range,
})
}
return lsc
}
func (l *LsTLVSrCapabilities) DecodeFromBytes(data []byte) error {
value, err := l.LsTLV.DecodeFromBytes(data)
if err != nil {
return err
}
if l.Type != LS_TLV_SR_CAPABILITIES {
return malformedAttrListErr("Unexpected TLV type")
}
if len(value) < 2 {
return malformedAttrListErr("Incorrect SR Capabilities length")
}
l.Flags = value[0]
// Skip two bytes: flags and reserved.
value = value[2:]
// The value field should be at least eight bytes long. Three bytes
// for the range size and five or six bytes for the SID/Label TLV.
for len(value) > 8 {
// First, parse range size (3 bytes)
buf := []byte{0, 0, 0, 0}
for i := 1; i < len(buf); i++ {
buf[i] = value[i-1]
}
r := binary.BigEndian.Uint32(buf)
value = value[3:]
// Second, parse SID/Label sub-TLV.
label := LsTLVSIDLabel{}
if err := label.DecodeFromBytes(value); err != nil {
return err
}
l.Ranges = append(l.Ranges, LsSrLabelRange{
Range: r,
FirstLabel: label,
})
value = value[label.Len():]
}
if len(value) > 0 {
return malformedAttrListErr("Malformed SR Capabilities TLV")
}
return nil
}
func (l *LsTLVSrCapabilities) Serialize() ([]byte, error) {
buf := make([]byte, 0)
buf = append(buf, l.Flags)
buf = append(buf, 0)
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 {
buf.WriteString(fmt.Sprintf("%v:%v ", r.FirstLabel.SID, r.FirstLabel.SID+r.Range))
}
return fmt.Sprintf("{SR Capabilities: Flags:%v SRGB Ranges: %v}", l.Flags, buf.String())
}
func (l *LsTLVSrCapabilities) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type LsTLVType `json:"type"`
Flags uint8 `json:"flags"`
Ranges []LsSrLabelRange `json:"ranges"`
}{
Type: l.Type,
Flags: l.Flags,
Ranges: l.Ranges,
})
}
type LsTLVSrLocalBlock struct {
LsTLV
Flags uint8
Ranges []LsSrLabelRange
}
type LsSrLocalBlock struct {
Ranges []LsSrRange `json:"ranges"`
}
func 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: BGP_ASPATH_ATTR_TYPE_SET,
Length: 4,
},
SID: r.Begin,
},
})
length += 4
}
return &LsTLVSrLocalBlock{
LsTLV: LsTLV{
Type: BGP_ASPATH_ATTR_TYPE_SET,
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 {
buf.WriteString(fmt.Sprintf("%v:%v ", r.FirstLabel.SID, r.FirstLabel.SID+r.Range))
}
return fmt.Sprintf("{SR LocalBlock: Flags:%v SRGB Ranges: %v}", l.Flags, buf.String())
}
func (l *LsTLVSrLocalBlock) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type LsTLVType `json:"type"`
Flags uint8 `json:"flags"`
Ranges []LsSrLabelRange `json:"ranges"`
}{
Type: l.Type,
Flags: l.Flags,
Ranges: l.Ranges,
})
}
func (l *LsTLVSrLocalBlock) 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])
if len(data) < int(4+tlvLen) {
return nil
}
switch tlvType {
case LS_TLV_SRV6_SID_STRUCTURE:
if tlvLen != 4 {
data = data[4+tlvLen:]
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[4+tlvLen:]
}
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 {
buf.WriteString(fmt.Sprintf("%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: BGP_ASPATH_ATTR_TYPE_SET,
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: BGP_ASPATH_ATTR_TYPE_SET,
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: BGP_ASPATH_ATTR_TYPE_SET,
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 >> 0)
}
if l.NoUnicast {
flags = flags & (1 >> 1)
}
if l.LocalAddress {
flags = flags & (1 >> 2)
}
if l.PropagateNSSA {
flags = flags & (1 >> 3)
}
return &LsTLVIGPFlags{
LsTLV: LsTLV{
Type: BGP_ASPATH_ATTR_TYPE_SET,
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<<0) > 0,
NoUnicast: l.Flags&(1<<1) > 0,
LocalAddress: l.Flags&(1<<2) > 0,
PropagateNSSA: l.Flags&(1<<3) > 0,
}
}
func (l *LsTLVIGPFlags) DecodeFromBytes(data []byte) error {
value, err := l.LsTLV.DecodeFromBytes(data)
if err != nil {
return err
}
if l.Type != LS_TLV_IGP_FLAGS {
return malformedAttrListErr("Unexpected TLV type")
}
if l.Length != 1 {
return malformedAttrListErr("Node Flag Bits TLV malformed")
}
l.Flags = value[0]
return nil
}
func (l *LsTLVIGPFlags) Serialize() ([]byte, error) {
return l.LsTLV.Serialize([]byte{l.Flags})
}
func (l *LsTLVIGPFlags) String() string {
flags := "XXXXPLND"
var buf bytes.Buffer
for i := 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: BGP_ASPATH_ATTR_TYPE_SET,
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:
tlv = tlv[sub.Len():]
l.Length -= uint16(sub.Len())
continue
}
if err := subTLV.DecodeFromBytes(tlv); err != nil {
return err
}
l.SubTLVs = append(l.SubTLVs, subTLV)
tlv = tlv[subTLV.Len():]
}
_, 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 {
return l.NLRI.DecodeFromBytes(data[4:])
}
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"`
}
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"`
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 *uint32 `json:"sr_prefix_sid,omitempty"`
}
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 *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:
l.Prefix.SrPrefixSID = &v.SID
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{}
// 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_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)
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")
}
p.Value, _ = netip.AddrFromSlice(value)
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
nexthopbin = slices.Delete(nexthopbin, BGP_ATTR_NHLEN_VPN_RD+BGP_ATTR_NHLEN_IPV6_GLOBAL, 2*BGP_ATTR_NHLEN_VPN_RD+BGP_ATTR_NHLEN_IPV6_GLOBAL) // skip second RD
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 := afi == AFI_IP6 || p.Nexthop.IsValid() && 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 := afi == AFI_IP6 && nextHops[0].IsValid() && 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)
}
}
return &UnknownExtended{
Type: ExtendedCommunityAttrType(data[0]),
Value: data[1:20],
}, 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,
}
}
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) < 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_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:
return &UnknownExtended{
Type: ExtendedCommunityAttrType(data[0]),
Value: data[1:8],
}, 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
}
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) {
break
}
v := value[3:length]
switch AigpTLVType(typ) {
case AIGP_TLV_IGP_METRIC:
if len(v) < 8 {
break
}
metric := binary.BigEndian.Uint64(v)
p.Values = append(p.Values, NewAigpTLVIgpMetric(metric))
default:
p.Values = append(p.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")
}
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
}
routelen -= uint16(w.Len(options...) + addpathLen)
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
}
}
pathlen -= uint16(p.Len(options...))
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
)
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")
}
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 {
if BGP_HEADER_LENGTH+len(b) > BGP_MAX_MESSAGE_LENGTH {
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
}
}
// Code generated by "stringer -type=BGPAttrType"; DO NOT EDIT.
package bgp
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[BGP_ATTR_TYPE_ORIGIN-1]
_ = x[BGP_ATTR_TYPE_AS_PATH-2]
_ = x[BGP_ATTR_TYPE_NEXT_HOP-3]
_ = x[BGP_ATTR_TYPE_MULTI_EXIT_DISC-4]
_ = x[BGP_ATTR_TYPE_LOCAL_PREF-5]
_ = x[BGP_ATTR_TYPE_ATOMIC_AGGREGATE-6]
_ = x[BGP_ATTR_TYPE_AGGREGATOR-7]
_ = x[BGP_ATTR_TYPE_COMMUNITIES-8]
_ = x[BGP_ATTR_TYPE_ORIGINATOR_ID-9]
_ = x[BGP_ATTR_TYPE_CLUSTER_LIST-10]
_ = x[BGP_ATTR_TYPE_MP_REACH_NLRI-14]
_ = x[BGP_ATTR_TYPE_MP_UNREACH_NLRI-15]
_ = x[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES-16]
_ = x[BGP_ATTR_TYPE_AS4_PATH-17]
_ = x[BGP_ATTR_TYPE_AS4_AGGREGATOR-18]
_ = x[BGP_ATTR_TYPE_PMSI_TUNNEL-22]
_ = x[BGP_ATTR_TYPE_TUNNEL_ENCAP-23]
_ = x[BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIES-25]
_ = x[BGP_ATTR_TYPE_AIGP-26]
_ = x[BGP_ATTR_TYPE_LS-29]
_ = x[BGP_ATTR_TYPE_LARGE_COMMUNITY-32]
_ = x[BGP_ATTR_TYPE_PREFIX_SID-40]
}
const (
_BGPAttrType_name_0 = "BGP_ATTR_TYPE_ORIGINBGP_ATTR_TYPE_AS_PATHBGP_ATTR_TYPE_NEXT_HOPBGP_ATTR_TYPE_MULTI_EXIT_DISCBGP_ATTR_TYPE_LOCAL_PREFBGP_ATTR_TYPE_ATOMIC_AGGREGATEBGP_ATTR_TYPE_AGGREGATORBGP_ATTR_TYPE_COMMUNITIESBGP_ATTR_TYPE_ORIGINATOR_IDBGP_ATTR_TYPE_CLUSTER_LIST"
_BGPAttrType_name_1 = "BGP_ATTR_TYPE_MP_REACH_NLRIBGP_ATTR_TYPE_MP_UNREACH_NLRIBGP_ATTR_TYPE_EXTENDED_COMMUNITIESBGP_ATTR_TYPE_AS4_PATHBGP_ATTR_TYPE_AS4_AGGREGATOR"
_BGPAttrType_name_2 = "BGP_ATTR_TYPE_PMSI_TUNNELBGP_ATTR_TYPE_TUNNEL_ENCAP"
_BGPAttrType_name_3 = "BGP_ATTR_TYPE_IP6_EXTENDED_COMMUNITIESBGP_ATTR_TYPE_AIGP"
_BGPAttrType_name_4 = "BGP_ATTR_TYPE_LS"
_BGPAttrType_name_5 = "BGP_ATTR_TYPE_LARGE_COMMUNITY"
_BGPAttrType_name_6 = "BGP_ATTR_TYPE_PREFIX_SID"
)
var (
_BGPAttrType_index_0 = [...]uint8{0, 20, 41, 63, 92, 116, 146, 170, 195, 222, 248}
_BGPAttrType_index_1 = [...]uint8{0, 27, 56, 90, 112, 140}
_BGPAttrType_index_2 = [...]uint8{0, 25, 51}
_BGPAttrType_index_3 = [...]uint8{0, 38, 56}
)
func (i BGPAttrType) String() string {
switch {
case 1 <= i && i <= 10:
i -= 1
return _BGPAttrType_name_0[_BGPAttrType_index_0[i]:_BGPAttrType_index_0[i+1]]
case 14 <= i && i <= 18:
i -= 14
return _BGPAttrType_name_1[_BGPAttrType_index_1[i]:_BGPAttrType_index_1[i+1]]
case 22 <= i && i <= 23:
i -= 22
return _BGPAttrType_name_2[_BGPAttrType_index_2[i]:_BGPAttrType_index_2[i+1]]
case 25 <= i && i <= 26:
i -= 25
return _BGPAttrType_name_3[_BGPAttrType_index_3[i]:_BGPAttrType_index_3[i+1]]
case i == 29:
return _BGPAttrType_name_4
case i == 32:
return _BGPAttrType_name_5
case i == 40:
return _BGPAttrType_name_6
default:
return "BGPAttrType(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
// 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 (
"strconv"
"strings"
)
const AS_TRANS = 23456
const BGP_PORT = 179
//go:generate stringer -type=FSMState
type FSMState int
const (
BGP_FSM_IDLE FSMState = iota
BGP_FSM_CONNECT
BGP_FSM_ACTIVE
BGP_FSM_OPENSENT
BGP_FSM_OPENCONFIRM
BGP_FSM_ESTABLISHED
)
// partially taken from http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
type Protocol int
const (
Unknown Protocol = iota
ICMP = 0x01
IGMP = 0x02
TCP = 0x06
EGP = 0x08
IGP = 0x09
UDP = 0x11
RSVP = 0x2e
GRE = 0x2f
OSPF = 0x59
IPIP = 0x5e
PIM = 0x67
SCTP = 0x84
)
var ProtocolNameMap = map[Protocol]string{
Unknown: "unknown",
ICMP: "icmp",
IGMP: "igmp",
TCP: "tcp",
EGP: "egp",
IGP: "igp",
UDP: "udp",
RSVP: "rsvp",
GRE: "gre",
OSPF: "ospf",
IPIP: "ipip",
PIM: "pim",
SCTP: "sctp",
}
func (p Protocol) String() string {
name, ok := ProtocolNameMap[p]
if !ok {
return strconv.Itoa(int(p))
}
return name
}
type TCPFlag int
const (
_ TCPFlag = iota
TCP_FLAG_FIN = 0x01
TCP_FLAG_SYN = 0x02
TCP_FLAG_RST = 0x04
TCP_FLAG_PUSH = 0x08
TCP_FLAG_ACK = 0x10
TCP_FLAG_URGENT = 0x20
TCP_FLAG_ECE = 0x40
TCP_FLAG_CWR = 0x80
)
var TCPFlagNameMap = map[TCPFlag]string{
TCP_FLAG_FIN: "F",
TCP_FLAG_SYN: "S",
TCP_FLAG_RST: "R",
TCP_FLAG_PUSH: "P",
TCP_FLAG_ACK: "A",
TCP_FLAG_URGENT: "U",
TCP_FLAG_CWR: "C",
TCP_FLAG_ECE: "E",
}
// Prepares a sorted list of flags because map iterations does not happen
// in a consistent order in Golang.
var TCPSortedFlags = []TCPFlag{
TCP_FLAG_FIN,
TCP_FLAG_SYN,
TCP_FLAG_RST,
TCP_FLAG_PUSH,
TCP_FLAG_ACK,
TCP_FLAG_URGENT,
TCP_FLAG_ECE,
TCP_FLAG_CWR,
}
func (f TCPFlag) String() string {
flags := make([]string, 0, len(TCPSortedFlags))
for _, v := range TCPSortedFlags {
if f&v > 0 {
flags = append(flags, TCPFlagNameMap[v])
}
}
return strings.Join(flags, "")
}
type BitmaskFlagOp uint8
const (
BITMASK_FLAG_OP_OR BitmaskFlagOp = iota
BITMASK_FLAG_OP_MATCH = 0x01
BITMASK_FLAG_OP_NOT = 0x02
BITMASK_FLAG_OP_NOT_MATCH = 0x03
BITMASK_FLAG_OP_AND = 0x40
BITMASK_FLAG_OP_END = 0x80
)
var BitmaskFlagOpNameMap = map[BitmaskFlagOp]string{
BITMASK_FLAG_OP_OR: " ",
BITMASK_FLAG_OP_AND: "&",
BITMASK_FLAG_OP_END: "E",
BITMASK_FLAG_OP_NOT: "!",
BITMASK_FLAG_OP_MATCH: "=",
}
// Note: Meaning of "" is different from that of the numeric operator because
// RFC5575 says if the Match bit in the bitmask operand is set, it should be
// "strictly" matching against the given value.
var BitmaskFlagOpValueMap = map[string]BitmaskFlagOp{
" ": BITMASK_FLAG_OP_OR,
"": BITMASK_FLAG_OP_OR,
"==": BITMASK_FLAG_OP_MATCH,
"=": BITMASK_FLAG_OP_MATCH,
"!": BITMASK_FLAG_OP_NOT,
"!=": BITMASK_FLAG_OP_NOT_MATCH,
"=!": BITMASK_FLAG_OP_NOT_MATCH, // For the backward compatibility
"&": BITMASK_FLAG_OP_AND,
"E": BITMASK_FLAG_OP_END,
}
func (f BitmaskFlagOp) String() string {
ops := make([]string, 0, 3)
if f&BITMASK_FLAG_OP_AND > 0 {
ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_AND])
} else {
ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_OR])
}
if f&BITMASK_FLAG_OP_NOT > 0 {
ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_NOT])
}
if f&BITMASK_FLAG_OP_MATCH > 0 {
ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_MATCH])
}
return strings.Join(ops, "")
}
type FragmentFlag int
const (
FRAG_FLAG_NOT FragmentFlag = iota
FRAG_FLAG_DONT = 0x01
FRAG_FLAG_IS = 0x02
FRAG_FLAG_FIRST = 0x04
FRAG_FLAG_LAST = 0x08
)
var FragmentFlagNameMap = map[FragmentFlag]string{
FRAG_FLAG_NOT: "not-a-fragment",
FRAG_FLAG_DONT: "dont-fragment",
FRAG_FLAG_IS: "is-fragment",
FRAG_FLAG_FIRST: "first-fragment",
FRAG_FLAG_LAST: "last-fragment",
}
// Prepares a sorted list of flags because map iterations does not happen
// in a consistent order in Golang.
var FragmentSortedFlags = []FragmentFlag{
FRAG_FLAG_NOT,
FRAG_FLAG_DONT,
FRAG_FLAG_IS,
FRAG_FLAG_FIRST,
FRAG_FLAG_LAST,
}
func (f FragmentFlag) String() string {
flags := make([]string, 0, len(FragmentSortedFlags))
for _, v := range FragmentSortedFlags {
if f&v > 0 {
flags = append(flags, FragmentFlagNameMap[v])
}
}
// Note: If multiple bits are set, joins them with "+".
return strings.Join(flags, "+")
}
type DECNumOp uint8
const (
DEC_NUM_OP_TRUE DECNumOp = iota // true always with END bit set
DEC_NUM_OP_EQ = 0x01
DEC_NUM_OP_GT = 0x02
DEC_NUM_OP_GT_EQ = 0x03
DEC_NUM_OP_LT = 0x04
DEC_NUM_OP_LT_EQ = 0x05
DEC_NUM_OP_NOT_EQ = 0x06
DEC_NUM_OP_FALSE = 0x07 // false always with END bit set
DEC_NUM_OP_OR = 0x00
DEC_NUM_OP_AND = 0x40
DEC_NUM_OP_END = 0x80
)
var DECNumOpNameMap = map[DECNumOp]string{
DEC_NUM_OP_TRUE: "true",
DEC_NUM_OP_EQ: "==",
DEC_NUM_OP_GT: ">",
DEC_NUM_OP_GT_EQ: ">=",
DEC_NUM_OP_LT: "<",
DEC_NUM_OP_LT_EQ: "<=",
DEC_NUM_OP_NOT_EQ: "!=",
DEC_NUM_OP_FALSE: "false",
// DEC_NUM_OP_OR: " ", // duplicate with DEC_NUM_OP_TRUE
DEC_NUM_OP_AND: "&",
DEC_NUM_OP_END: "E",
}
var DECNumOpValueMap = map[string]DECNumOp{
"true": DEC_NUM_OP_TRUE,
"": DEC_NUM_OP_EQ,
"==": DEC_NUM_OP_EQ,
"=": DEC_NUM_OP_EQ,
">": DEC_NUM_OP_GT,
">=": DEC_NUM_OP_GT_EQ,
"<": DEC_NUM_OP_LT,
"<=": DEC_NUM_OP_LT_EQ,
"!=": DEC_NUM_OP_NOT_EQ,
"=!": DEC_NUM_OP_NOT_EQ,
"!": DEC_NUM_OP_NOT_EQ,
"false": DEC_NUM_OP_FALSE,
" ": DEC_NUM_OP_OR,
"&": DEC_NUM_OP_AND,
"E": DEC_NUM_OP_END,
}
func (f DECNumOp) String() string {
ops := make([]string, 0)
logicFlag := f & 0xc0 // higher 2 bits
if logicFlag&DEC_NUM_OP_AND > 0 {
ops = append(ops, DECNumOpNameMap[DEC_NUM_OP_AND])
} else {
ops = append(ops, " ") // DEC_NUM_OP_OR
}
// Omits DEC_NUM_OP_END
cmpFlag := f & 0x7 // lower 3 bits
for v, s := range DECNumOpNameMap {
if cmpFlag == v {
ops = append(ops, s)
break
}
}
return strings.Join(ops, "")
}
// Potentially taken from https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
type EthernetType int
const (
IPv4 EthernetType = 0x0800
ARP EthernetType = 0x0806
RARP EthernetType = 0x8035
VMTP EthernetType = 0x805B
APPLE_TALK EthernetType = 0x809B
AARP EthernetType = 0x80F3
IPX EthernetType = 0x8137
SNMP EthernetType = 0x814C
NET_BIOS EthernetType = 0x8191
XTP EthernetType = 0x817D
IPv6 EthernetType = 0x86DD
PPPoE_DISCOVERY EthernetType = 0x8863
PPPoE_SESSION EthernetType = 0x8864
LOOPBACK EthernetType = 0x9000
)
var EthernetTypeNameMap = map[EthernetType]string{
IPv4: "ipv4",
ARP: "arp",
RARP: "rarp",
VMTP: "vmtp",
APPLE_TALK: "apple-talk",
AARP: "aarp",
IPX: "ipx",
SNMP: "snmp",
NET_BIOS: "net-bios",
XTP: "xtp",
IPv6: "ipv6",
PPPoE_DISCOVERY: "pppoe-discovery",
PPPoE_SESSION: "pppoe-session",
LOOPBACK: "loopback",
}
func (t EthernetType) String() string {
if name, ok := EthernetTypeNameMap[t]; ok {
return name
}
return strconv.Itoa(int(t))
}
// Code generated by "stringer -type=ESIType"; DO NOT EDIT.
package bgp
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ESI_ARBITRARY-0]
_ = x[ESI_LACP-1]
_ = x[ESI_MSTP-2]
_ = x[ESI_MAC-3]
_ = x[ESI_ROUTERID-4]
_ = x[ESI_AS-5]
}
const _ESIType_name = "ESI_ARBITRARYESI_LACPESI_MSTPESI_MACESI_ROUTERIDESI_AS"
var _ESIType_index = [...]uint8{0, 13, 21, 29, 36, 48, 54}
func (i ESIType) String() string {
if i >= ESIType(len(_ESIType_index)-1) {
return "ESIType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ESIType_name[_ESIType_index[i]:_ESIType_index[i+1]]
}
// Code generated by "stringer -type=FSMState"; DO NOT EDIT.
package bgp
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[BGP_FSM_IDLE-0]
_ = x[BGP_FSM_CONNECT-1]
_ = x[BGP_FSM_ACTIVE-2]
_ = x[BGP_FSM_OPENSENT-3]
_ = x[BGP_FSM_OPENCONFIRM-4]
_ = x[BGP_FSM_ESTABLISHED-5]
}
const _FSMState_name = "BGP_FSM_IDLEBGP_FSM_CONNECTBGP_FSM_ACTIVEBGP_FSM_OPENSENTBGP_FSM_OPENCONFIRMBGP_FSM_ESTABLISHED"
var _FSMState_index = [...]uint8{0, 12, 27, 41, 57, 76, 95}
func (i FSMState) String() string {
if i < 0 || i >= FSMState(len(_FSMState_index)-1) {
return "FSMState(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _FSMState_name[_FSMState_index[i]:_FSMState_index[i+1]]
}
// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bgp
import (
"net/netip"
)
func NewTestBGPOpenMessage() *BGPMessage {
p1 := NewOptionParameterCapability(
[]ParameterCapabilityInterface{NewCapRouteRefresh()})
p2 := NewOptionParameterCapability(
[]ParameterCapabilityInterface{NewCapMultiProtocol(RF_IPv4_UC)})
g := &CapGracefulRestartTuple{4, 2, 3}
p3 := NewOptionParameterCapability(
[]ParameterCapabilityInterface{NewCapGracefulRestart(false, true, 100,
[]*CapGracefulRestartTuple{g})})
p4 := NewOptionParameterCapability(
[]ParameterCapabilityInterface{NewCapFourOctetASNumber(100000)})
p5 := NewOptionParameterCapability(
[]ParameterCapabilityInterface{NewCapAddPath([]*CapAddPathTuple{NewCapAddPathTuple(RF_IPv4_UC, BGP_ADD_PATH_BOTH)})})
msg, _ := NewBGPOpenMessage(11033, 303, netip.MustParseAddr("100.4.10.3"),
[]OptionParameterInterface{p1, p2, p3, p4, p5})
return msg
}
func NewTestBGPUpdateMessage() *BGPMessage {
w1, _ := NewIPAddrPrefix(netip.MustParsePrefix("121.1.3.2/23"))
w2, _ := NewIPAddrPrefix(netip.MustParsePrefix("100.33.3.0/17"))
w := []NLRI{w1, w2}
aspath1 := []AsPathParamInterface{
NewAsPathParam(2, []uint16{1000}),
NewAsPathParam(1, []uint16{1001, 1002}),
NewAsPathParam(2, []uint16{1003, 1004}),
}
aspath2 := []AsPathParamInterface{
NewAs4PathParam(2, []uint32{1000000}),
NewAs4PathParam(1, []uint32{1000001, 1002}),
NewAs4PathParam(2, []uint32{1003, 100004}),
}
aspath3 := []*As4PathParam{
NewAs4PathParam(2, []uint32{1000000}),
NewAs4PathParam(1, []uint32{1000001, 1002}),
NewAs4PathParam(2, []uint32{1003, 100004}),
}
isTransitive := true
ex3, _ := NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, netip.MustParseAddr("192.2.1.2"), 3000, isTransitive)
ecommunities := []ExtendedCommunityInterface{
NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, 10003, 3<<20, isTransitive),
NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, 1<<20, 300, isTransitive),
ex3,
NewOpaqueExtended(false, []byte{1, 2, 3, 4, 5, 6, 7}),
NewValidationExtended(VALIDATION_STATE_INVALID),
NewUnknownExtended(99, []byte{0, 1, 2, 3, 4, 5, 6, 7}),
NewESILabelExtended(1000, true),
NewESImportRouteTarget("11:22:33:44:55:66"),
NewMacMobilityExtended(123, false),
}
vpn1, _ := NewLabeledVPNIPAddrPrefix(netip.MustParsePrefix("192.0.9.0/24"), *NewMPLSLabelStack(1, 2, 3),
NewRouteDistinguisherTwoOctetAS(256, 10000))
rd, _ := NewRouteDistinguisherIPAddressAS(netip.MustParseAddr("10.0.1.1"), 10001)
vpn2, _ := NewLabeledVPNIPAddrPrefix(netip.MustParsePrefix("192.10.8.0/24"), *NewMPLSLabelStack(5, 6, 7, 8), rd)
prefixes1 := []NLRI{vpn1, vpn2}
nlri, _ := NewIPAddrPrefix(netip.MustParsePrefix("fe80:1234:1234:5667:8967:af12:8912:1023/128"))
prefixes2 := []NLRI{nlri}
vpn3, _ := NewLabeledVPNIPAddrPrefix(netip.MustParsePrefix("fe80:1234:1234:5667:8967:af12:1203:33a1/128"), *NewMPLSLabelStack(5, 6), NewRouteDistinguisherFourOctetAS(5, 6))
prefixes3 := []NLRI{vpn3}
mpls, _ := NewLabeledIPAddrPrefix(netip.MustParsePrefix("192.168.0.0/25"), *NewMPLSLabelStack(5, 6, 7))
prefixes4 := []NLRI{mpls}
r2, _ := NewEVPNMacIPAdvertisementRoute(NewRouteDistinguisherFourOctetAS(5, 6), EthernetSegmentIdentifier{ESI_ARBITRARY, make([]byte, 9)}, 3, "01:23:45:67:89:ab", netip.MustParseAddr("192.2.1.2"), []uint32{3, 4})
r3, _ := NewEVPNMulticastEthernetTagRoute(NewRouteDistinguisherFourOctetAS(5, 6), 3, netip.MustParseAddr("192.2.1.2"))
r4, _ := NewEVPNEthernetSegmentRoute(NewRouteDistinguisherFourOctetAS(5, 6), EthernetSegmentIdentifier{ESI_ARBITRARY, make([]byte, 9)}, netip.MustParseAddr("192.2.1.1"))
r5, _ := NewEVPNIPPrefixRoute(NewRouteDistinguisherFourOctetAS(5, 6), EthernetSegmentIdentifier{ESI_ARBITRARY, make([]byte, 9)}, 5, 24, netip.MustParseAddr("192.2.1.0"), netip.MustParseAddr("192.3.1.1"), 5)
prefixes5 := []NLRI{
NewEVPNEthernetAutoDiscoveryRoute(NewRouteDistinguisherFourOctetAS(5, 6), EthernetSegmentIdentifier{ESI_ARBITRARY, make([]byte, 9)}, 2, 2),
r2,
r3,
r4,
r5,
}
prefixes6 := []NLRI{NewVPLSNLRI(NewRouteDistinguisherFourOctetAS(5, 6), 101, 100, 10, 1000)}
panh, _ := NewPathAttributeNextHop(netip.MustParseAddr("129.1.1.2"))
paag1, _ := NewPathAttributeAggregator(uint16(30002), netip.MustParseAddr("129.0.2.99"))
paag2, _ := NewPathAttributeAggregator(uint32(30002), netip.MustParseAddr("129.0.2.99"))
paag3, _ := NewPathAttributeAggregator(uint32(300020), netip.MustParseAddr("129.0.2.99"))
paag4, _ := NewPathAttributeAs4Aggregator(10000, netip.MustParseAddr("112.22.2.1"))
paorig, _ := NewPathAttributeOriginatorId(netip.MustParseAddr("10.10.0.1"))
pacluster, _ := NewPathAttributeClusterList([]netip.Addr{netip.MustParseAddr("10.10.0.2"), netip.MustParseAddr("10.10.0.3")})
p := []PathAttributeInterface{
NewPathAttributeOrigin(3),
NewPathAttributeAsPath(aspath1),
NewPathAttributeAsPath(aspath2),
panh,
NewPathAttributeMultiExitDisc(1 << 20),
NewPathAttributeLocalPref(1 << 22),
NewPathAttributeAtomicAggregate(),
paag1,
paag2,
paag3,
NewPathAttributeCommunities([]uint32{1, 3}),
paorig,
pacluster,
NewPathAttributeExtendedCommunities(ecommunities),
NewPathAttributeAs4Path(aspath3),
paag4,
}
toList := func(l []NLRI) []PathNLRI {
r := make([]PathNLRI, 0, len(l))
for _, p := range l {
r = append(r, PathNLRI{NLRI: p})
}
return r
}
// Create MP_REACH_NLRI attributes with error handling
mp1, _ := NewPathAttributeMpReachNLRI(RF_IPv4_VPN, toList(prefixes1), netip.MustParseAddr("112.22.2.0"))
mp2, _ := NewPathAttributeMpReachNLRI(RF_IPv6_UC, toList(prefixes2), netip.MustParseAddr("1023::"))
mp3, _ := NewPathAttributeMpReachNLRI(RF_IPv6_VPN, toList(prefixes3), netip.MustParseAddr("fe80::"))
mp4, _ := NewPathAttributeMpReachNLRI(RF_IPv4_MPLS, toList(prefixes4), netip.MustParseAddr("129.1.1.1"))
mp5, _ := NewPathAttributeMpReachNLRI(RF_EVPN, toList(prefixes5), netip.MustParseAddr("129.1.1.1"))
mp6, _ := NewPathAttributeMpReachNLRI(RF_VPLS, toList(prefixes6), netip.MustParseAddr("135.1.1.1"))
mpUnreach1, _ := NewPathAttributeMpUnreachNLRI(RF_IPv4_VPN, toList(prefixes1))
p = append(p, mp1, mp2, mp3, mp4, mp5, mp6, mpUnreach1,
// NewPathAttributeMpReachNLRI("112.22.2.0", []NLRI{}),
// NewPathAttributeMpUnreachNLRI([]NLRI{}),
NewPathAttributeUnknown(BGP_ATTR_FLAG_TRANSITIVE, 100, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
)
prefix, _ := NewIPAddrPrefix(netip.MustParsePrefix("13.2.3.1/24"))
n := []NLRI{prefix}
return NewBGPUpdateMessage(toList(w), p, toList(n))
}
package bgp
import (
"encoding/binary"
"encoding/json"
"fmt"
"net/netip"
)
// MUPExtended represents BGP MUP Extended Community as described in
// https://datatracker.ietf.org/doc/html/draft-mpmz-bess-mup-safi-00#section-3.2
type MUPExtended struct {
SubType ExtendedCommunityAttrSubType
SegmentID2 uint16
SegmentID4 uint32
}
func (e *MUPExtended) Serialize() ([]byte, error) {
buf := make([]byte, 8)
buf[0] = byte(EC_TYPE_MUP)
buf[1] = byte(EC_SUBTYPE_MUP_DIRECT_SEG)
binary.BigEndian.PutUint16(buf[2:4], e.SegmentID2)
binary.BigEndian.PutUint32(buf[4:8], e.SegmentID4)
return buf, nil
}
func (e *MUPExtended) String() string {
return fmt.Sprintf("%d:%d", e.SegmentID2, e.SegmentID4)
}
func (e *MUPExtended) MarshalJSON() ([]byte, error) {
t, s := e.GetTypes()
return json.Marshal(struct {
Type ExtendedCommunityAttrType `json:"type"`
Subtype ExtendedCommunityAttrSubType `json:"subtype"`
SegmentID string `json:"segmend_id"`
}{
Type: t,
Subtype: s,
SegmentID: fmt.Sprintf("%d:%d", e.SegmentID2, e.SegmentID4),
})
}
func (e *MUPExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
return EC_TYPE_MUP, EC_SUBTYPE_MUP_DIRECT_SEG
}
func (e *MUPExtended) Flat() map[string]string {
return map[string]string{}
}
func NewMUPExtended(sid2 uint16, sid4 uint32) *MUPExtended {
return &MUPExtended{
SubType: EC_SUBTYPE_MUP_DIRECT_SEG,
SegmentID2: sid2,
SegmentID4: sid4,
}
}
func parseMUPExtended(data []byte) (ExtendedCommunityInterface, error) {
typ := ExtendedCommunityAttrType(data[0])
if typ != EC_TYPE_MUP {
return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("ext comm type is not EC_TYPE_MUP: %d", data[0]))
}
subType := ExtendedCommunityAttrSubType(data[1])
if subType == EC_SUBTYPE_MUP_DIRECT_SEG {
sid2 := binary.BigEndian.Uint16(data[2:4])
sid4 := binary.BigEndian.Uint32(data[4:8])
return NewMUPExtended(sid2, sid4), nil
}
return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("unknown mup subtype: %d", subType))
}
// BGP MUP SAFI Architecture Type as described in
// https://datatracker.ietf.org/doc/html/draft-mpmz-bess-mup-safi-00#section-3.1
const (
MUP_ARCH_TYPE_UNDEFINED = iota
MUP_ARCH_TYPE_3GPP_5G
)
// BGP MUP SAFI Route Type as described in
// https://datatracker.ietf.org/doc/html/draft-mpmz-bess-mup-safi-00#section-3.1
const (
_ = iota
MUP_ROUTE_TYPE_INTERWORK_SEGMENT_DISCOVERY
MUP_ROUTE_TYPE_DIRECT_SEGMENT_DISCOVERY
MUP_ROUTE_TYPE_TYPE_1_SESSION_TRANSFORMED
MUP_ROUTE_TYPE_TYPE_2_SESSION_TRANSFORMED
)
type MUPRouteTypeInterface interface {
DecodeFromBytes([]byte, uint16) error
Serialize() ([]byte, error)
AFI() uint16
Len() int
String() string
MarshalJSON() ([]byte, error)
rd() RouteDistinguisherInterface
}
func getMUPRouteType(at uint8, rt uint16) (MUPRouteTypeInterface, error) {
switch rt {
case MUP_ROUTE_TYPE_INTERWORK_SEGMENT_DISCOVERY:
if at == MUP_ARCH_TYPE_3GPP_5G {
return &MUPInterworkSegmentDiscoveryRoute{}, nil
}
case MUP_ROUTE_TYPE_DIRECT_SEGMENT_DISCOVERY:
if at == MUP_ARCH_TYPE_3GPP_5G {
return &MUPDirectSegmentDiscoveryRoute{}, nil
}
case MUP_ROUTE_TYPE_TYPE_1_SESSION_TRANSFORMED:
if at == MUP_ARCH_TYPE_3GPP_5G {
return &MUPType1SessionTransformedRoute{}, nil
}
case MUP_ROUTE_TYPE_TYPE_2_SESSION_TRANSFORMED:
if at == MUP_ARCH_TYPE_3GPP_5G {
return &MUPType2SessionTransformedRoute{}, nil
}
}
return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Unknown MUP Architecture and Route type: %d, %d", at, rt))
}
type MUPNLRI struct {
Afi uint16
ArchitectureType uint8
RouteType uint16
Length uint8
RouteTypeData MUPRouteTypeInterface
}
func (n *MUPNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
if len(data) < 4 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all MUPNLRI bytes available")
}
n.ArchitectureType = data[0]
n.RouteType = binary.BigEndian.Uint16(data[1:3])
n.Length = data[3]
data = data[4:]
if len(data) < int(n.Length) {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all MUPNLRI Route type bytes available")
}
r, err := getMUPRouteType(n.ArchitectureType, n.RouteType)
if err != nil {
return err
}
n.RouteTypeData = r
return n.RouteTypeData.DecodeFromBytes(data[:n.Length], n.Afi)
}
func (n *MUPNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
buf := make([]byte, 4)
buf[0] = n.ArchitectureType
binary.BigEndian.PutUint16(buf[1:3], n.RouteType)
buf[3] = n.Length
tbuf, err := n.RouteTypeData.Serialize()
if err != nil {
return nil, err
}
return append(buf, tbuf...), nil
}
func (n *MUPNLRI) Len(options ...*MarshallingOption) int {
return int(n.Length) + 4
}
func (n *MUPNLRI) String() string {
if n.RouteTypeData != nil {
return n.RouteTypeData.String()
}
return fmt.Sprintf("%d:%d:%d", n.ArchitectureType, n.RouteType, n.Length)
}
func (n *MUPNLRI) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
ArchitectureType uint8 `json:"arch_type"`
RouteType uint16 `json:"route_type"`
Value MUPRouteTypeInterface `json:"value"`
}{
ArchitectureType: n.ArchitectureType,
RouteType: n.RouteType,
Value: n.RouteTypeData,
})
}
func (n *MUPNLRI) RD() RouteDistinguisherInterface {
return n.RouteTypeData.rd()
}
func (l *MUPNLRI) Flat() map[string]string {
return map[string]string{}
}
func NewMUPNLRI(afi uint16, at uint8, rt uint16, data MUPRouteTypeInterface) *MUPNLRI {
var l uint8
if data != nil {
l = uint8(data.Len())
}
return &MUPNLRI{
Afi: afi,
ArchitectureType: at,
RouteType: rt,
Length: l,
RouteTypeData: data,
}
}
func TEIDString(nlri NLRI) string {
s := ""
switch n := nlri.(type) {
case *MUPNLRI:
switch route := n.RouteTypeData.(type) {
case *MUPType1SessionTransformedRoute:
s = route.TEID.String()
default:
s = ""
}
}
return s
}
func QFIString(nlri NLRI) string {
s := ""
switch n := nlri.(type) {
case *MUPNLRI:
switch route := n.RouteTypeData.(type) {
case *MUPType1SessionTransformedRoute:
s = fmt.Sprintf("%d", route.QFI)
default:
s = ""
}
}
return s
}
func EndpointString(nlri NLRI) string {
s := ""
switch n := nlri.(type) {
case *MUPNLRI:
switch route := n.RouteTypeData.(type) {
case *MUPType1SessionTransformedRoute:
s = route.EndpointAddress.String()
default:
s = ""
}
}
return s
}
// MUPInterworkSegmentDiscoveryRoute represents BGP Interwork Segment Discovery route as described in
// https://datatracker.ietf.org/doc/html/draft-mpmz-bess-mup-safi-00#section-3.1.1
type MUPInterworkSegmentDiscoveryRoute struct {
RD RouteDistinguisherInterface
Prefix netip.Prefix
}
func NewMUPInterworkSegmentDiscoveryRoute(rd RouteDistinguisherInterface, prefix netip.Prefix) *MUPNLRI {
afi := uint16(AFI_IP)
if prefix.Addr().Is6() {
afi = AFI_IP6
}
return NewMUPNLRI(afi, MUP_ARCH_TYPE_3GPP_5G, MUP_ROUTE_TYPE_INTERWORK_SEGMENT_DISCOVERY, &MUPInterworkSegmentDiscoveryRoute{
RD: rd,
Prefix: prefix,
})
}
func (r *MUPInterworkSegmentDiscoveryRoute) DecodeFromBytes(data []byte, afi uint16) error {
if len(data) < 8 {
return malformedAttrListErr("invalid Interwork Segment Discovery Route length")
}
r.RD = GetRouteDistinguisher(data)
p := r.RD.Len()
if len(data) < p+1 {
return malformedAttrListErr("invalid Interwork Segment Discovery Route length")
}
bits := int(data[p])
p += 1
byteLen := (bits + 7) / 8
if len(data[p:]) < byteLen {
return malformedAttrListErr("prefix bytes is short")
}
addrLen := 4
if afi == AFI_IP6 {
addrLen = 16
}
if bits > addrLen*8 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "prefix length is too long")
}
b := make([]byte, addrLen)
if len(data) < p+byteLen {
return malformedAttrListErr("invalid Interwork Segment Discovery Route length")
}
copy(b[:byteLen], data[p:p+byteLen])
addr, ok := netip.AddrFromSlice(b)
if !ok {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid Prefix: %x", data[p:]))
}
r.Prefix = netip.PrefixFrom(addr, bits)
if r.Prefix.Bits() == -1 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid Prefix: %s", r.Prefix))
}
return nil
}
func (r *MUPInterworkSegmentDiscoveryRoute) Serialize() ([]byte, error) {
var buf []byte
var err error
if r.RD != nil {
buf, err = r.RD.Serialize()
if err != nil {
return nil, err
}
} else {
buf = make([]byte, 8)
}
buf = append(buf, uint8(r.Prefix.Bits()))
byteLen := (r.Prefix.Bits() + 7) / 8
buf = append(buf, r.Prefix.Addr().AsSlice()[:byteLen]...)
return buf, nil
}
func (r *MUPInterworkSegmentDiscoveryRoute) AFI() uint16 {
if r.Prefix.Addr().Is6() {
return AFI_IP6
}
return AFI_IP
}
func (r *MUPInterworkSegmentDiscoveryRoute) Len() int {
// RD(8) + PrefixLength(1) + Prefix(variable)
return 9 + (r.Prefix.Bits()+7)/8
}
func (r *MUPInterworkSegmentDiscoveryRoute) String() string {
// I-D.draft-mpmz-bess-mup-safi-01
// 3.1.1. BGP Interwork Segment Discovery route
// For the purpose of BGP route key processing, only the RD, Prefix Length and Prefix are considered to be part of the prefix in the NLRI.
return fmt.Sprintf("[type:isd][rd:%s][prefix:%s]", r.RD, r.Prefix)
}
func (r *MUPInterworkSegmentDiscoveryRoute) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
RD RouteDistinguisherInterface `json:"rd"`
Prefix string `json:"prefix"`
}{
RD: r.RD,
Prefix: r.Prefix.String(),
})
}
func (r *MUPInterworkSegmentDiscoveryRoute) rd() RouteDistinguisherInterface {
return r.RD
}
// MUPDirectSegmentDiscoveryRoute represents BGP Direct Segment Discovery route as described in
// https://datatracker.ietf.org/doc/html/draft-mpmz-bess-mup-safi-00#section-3.1.2
type MUPDirectSegmentDiscoveryRoute struct {
RD RouteDistinguisherInterface
Address netip.Addr
}
func NewMUPDirectSegmentDiscoveryRoute(rd RouteDistinguisherInterface, address netip.Addr) *MUPNLRI {
afi := uint16(AFI_IP)
if address.Is6() {
afi = AFI_IP6
}
return NewMUPNLRI(afi, MUP_ARCH_TYPE_3GPP_5G, MUP_ROUTE_TYPE_DIRECT_SEGMENT_DISCOVERY, &MUPDirectSegmentDiscoveryRoute{
RD: rd,
Address: address,
})
}
func (r *MUPDirectSegmentDiscoveryRoute) DecodeFromBytes(data []byte, afi uint16) error {
if len(data) < 8 {
return malformedAttrListErr("invalid Direct Segment Discovery Route length")
}
r.RD = GetRouteDistinguisher(data)
rdLen := r.RD.Len()
if len(data) != 12 && len(data) != 24 {
return malformedAttrListErr("invalid Direct Segment Discovery Route length")
}
if len(data) == 12 {
address, ok := netip.AddrFromSlice(data[rdLen : rdLen+4])
if !ok {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid Address: %s", data[rdLen:rdLen+4]))
}
r.Address = address
} else if len(data) == 24 {
address, ok := netip.AddrFromSlice(data[rdLen : rdLen+16])
if !ok {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid Address: %d", data[rdLen:rdLen+16]))
}
r.Address = address
}
return nil
}
func (r *MUPDirectSegmentDiscoveryRoute) Serialize() ([]byte, error) {
var buf []byte
var err error
if r.RD != nil {
buf, err = r.RD.Serialize()
if err != nil {
return nil, err
}
} else {
buf = make([]byte, 8)
}
buf = append(buf, r.Address.AsSlice()...)
return buf, nil
}
func (r *MUPDirectSegmentDiscoveryRoute) AFI() uint16 {
if r.Address.Is6() {
return AFI_IP6
}
return AFI_IP
}
func (r *MUPDirectSegmentDiscoveryRoute) Len() int {
// RD(8) + Address(4 or 16)
return 8 + r.Address.BitLen()/8
}
func (r *MUPDirectSegmentDiscoveryRoute) String() string {
// I-D.draft-mpmz-bess-mup-safi-01
// 3.1.2. BGP Direct Segment Discovery route
// For the purpose of BGP route key processing, only the RD and Address are considered to be part of the prefix in the NLRI.
return fmt.Sprintf("[type:dsd][rd:%s][prefix:%s]", r.RD, r.Address)
}
func (r *MUPDirectSegmentDiscoveryRoute) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
RD RouteDistinguisherInterface `json:"rd"`
Address string `json:"address"`
}{
RD: r.RD,
Address: r.Address.String(),
})
}
func (r *MUPDirectSegmentDiscoveryRoute) rd() RouteDistinguisherInterface {
return r.RD
}
// MUPType1SessionTransformedRoute3GPP5G represents 3GPP 5G specific Type 1 Session Transformed (ST) Route as described in
// https://datatracker.ietf.org/doc/html/draft-mpmz-bess-mup-safi-03#section-3.1.3
type MUPType1SessionTransformedRoute struct {
RD RouteDistinguisherInterface
Prefix netip.Prefix
TEID netip.Addr
QFI uint8
EndpointAddressLength uint8
EndpointAddress netip.Addr
SourceAddressLength uint8
SourceAddress *netip.Addr
}
func NewMUPType1SessionTransformedRoute(rd RouteDistinguisherInterface, prefix netip.Prefix, teid netip.Addr, qfi uint8, ea netip.Addr, sa *netip.Addr) *MUPNLRI {
afi := uint16(AFI_IP)
if prefix.Addr().Is6() {
afi = uint16(AFI_IP6)
}
r := &MUPType1SessionTransformedRoute{
RD: rd,
Prefix: prefix,
TEID: teid,
QFI: qfi,
EndpointAddressLength: uint8(ea.BitLen()),
EndpointAddress: ea,
}
if sa != nil {
r.SourceAddressLength = uint8(sa.BitLen())
r.SourceAddress = sa
}
return NewMUPNLRI(afi, MUP_ARCH_TYPE_3GPP_5G, MUP_ROUTE_TYPE_TYPE_1_SESSION_TRANSFORMED, r)
}
func (r *MUPType1SessionTransformedRoute) DecodeFromBytes(data []byte, afi uint16) error {
if len(data) < 8 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 1 Session Transformed Route length")
}
r.RD = GetRouteDistinguisher(data)
p := r.RD.Len()
if len(data) < p+1 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 1 Session Transformed Route length")
}
prefixLength := int(data[p])
p += 1
addrLen := 0
switch afi {
case AFI_IP:
if prefixLength > 32 {
return malformedAttrListErr(fmt.Sprintf("Invalid Prefix length: %d", prefixLength))
}
addrLen = 4
case AFI_IP6:
if prefixLength > 128 {
return malformedAttrListErr(fmt.Sprintf("Invalid Prefix length: %d", prefixLength))
}
addrLen = 16
default:
return malformedAttrListErr(fmt.Sprintf("Invalid AFI: %d", afi))
}
byteLen := (prefixLength + 7) / 8
b := make([]byte, addrLen)
if len(data) < p+byteLen {
return malformedAttrListErr("invalid 3GPP 5G specific Type 1 Session Transformed Route length")
}
copy(b[:byteLen], data[p:p+byteLen])
addr, ok := netip.AddrFromSlice(b)
if !ok {
return malformedAttrListErr(fmt.Sprintf("Invalid Prefix: %x", b))
}
r.Prefix = netip.PrefixFrom(addr, prefixLength)
p += byteLen
if len(data) < p+4+1+1 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 1 Session Transformed Route length")
}
r.TEID, ok = netip.AddrFromSlice(data[p : p+4])
if !ok {
return malformedAttrListErr(fmt.Sprintf("Invalid TEID: %x", r.TEID))
}
p += 4
r.QFI = data[p]
p += 1
r.EndpointAddressLength = data[p]
p += 1
if r.EndpointAddressLength == 32 || r.EndpointAddressLength == 128 {
if len(data) < p+int(r.EndpointAddressLength/8) {
return malformedAttrListErr("invalid 3GPP 5G specific Type 1 Session Transformed Route length")
}
ea, ok := netip.AddrFromSlice(data[p : p+int(r.EndpointAddressLength/8)])
if !ok {
return malformedAttrListErr(fmt.Sprintf("Invalid Endpoint Address: %x", data[p:p+int(r.EndpointAddressLength/8)]))
}
r.EndpointAddress = ea
} else {
return malformedAttrListErr(fmt.Sprintf("Invalid Endpoint Address length: %d", r.EndpointAddressLength))
}
p += int(r.EndpointAddressLength / 8)
if len(data) < p+1 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 1 Session Transformed Route length")
}
r.SourceAddressLength = data[p]
p += 1
if r.SourceAddressLength == 32 || r.SourceAddressLength == 128 {
if len(data) < p+int(r.SourceAddressLength/8) {
return malformedAttrListErr("invalid 3GPP 5G specific Type 1 Session Transformed Route length")
}
sa, ok := netip.AddrFromSlice(data[p : p+int(r.SourceAddressLength/8)])
if !ok {
return malformedAttrListErr(fmt.Sprintf("Invalid Source Address: %x", data[p:p+int(r.SourceAddressLength/8)]))
}
r.SourceAddress = &sa
}
return nil
}
func (r *MUPType1SessionTransformedRoute) Serialize() ([]byte, error) {
var buf []byte
var err error
if r.RD != nil {
buf, err = r.RD.Serialize()
if err != nil {
return nil, err
}
} else {
buf = make([]byte, 8)
}
buf = append(buf, byte(r.Prefix.Bits()))
byteLen := (r.Prefix.Bits() + 7) / 8
buf = append(buf, r.Prefix.Addr().AsSlice()[:byteLen]...)
buf = append(buf, r.TEID.AsSlice()...)
buf = append(buf, r.QFI)
buf = append(buf, r.EndpointAddressLength)
buf = append(buf, r.EndpointAddress.AsSlice()...)
buf = append(buf, r.SourceAddressLength)
if r.SourceAddressLength > 0 {
buf = append(buf, r.SourceAddress.AsSlice()...)
}
return buf, nil
}
func (r *MUPType1SessionTransformedRoute) AFI() uint16 {
if r.Prefix.Addr().Is6() {
return AFI_IP6
}
return AFI_IP
}
func (r *MUPType1SessionTransformedRoute) Len() int {
// RD(8) + PrefixLength(1) + Prefix(variable)
// + TEID(4) + QFI(1) + EndpointAddressLength(1) + EndpointAddress(4 or 16) + SourceAddressLength(1) + SourceAddress(4 or 16)
l := 16 + (r.Prefix.Bits()+7)/8 + int(r.EndpointAddressLength/8)
if r.SourceAddressLength > 0 {
l += int(r.SourceAddressLength / 8)
}
return l
}
func (r *MUPType1SessionTransformedRoute) String() string {
// I-D.draft-mpmz-bess-mup-safi-01
// 3.1.3. BGP Type 1 Session Transformed (ST) Route
// For the purpose of BGP route key processing, only the RD, Prefix Length and Prefix are considered to be part of the prefix in the NLRI.
return fmt.Sprintf("[type:t1st][rd:%s][prefix:%s]", r.RD, r.Prefix)
}
func (r *MUPType1SessionTransformedRoute) MarshalJSON() ([]byte, error) {
d := struct {
RD RouteDistinguisherInterface `json:"rd"`
Prefix string `json:"prefix"`
TEID string `json:"teid"`
QFI uint8 `json:"qfi"`
EndpointAddress string `json:"endpoint_address"`
SourceAddress string `json:"source_address"`
}{
RD: r.RD,
Prefix: r.Prefix.String(),
TEID: r.TEID.String(),
QFI: r.QFI,
EndpointAddress: r.EndpointAddress.String(),
}
if r.SourceAddress != nil {
d.SourceAddress = r.SourceAddress.String()
}
return json.Marshal(d)
}
func (r *MUPType1SessionTransformedRoute) rd() RouteDistinguisherInterface {
return r.RD
}
// MUPType2SessionTransformedRoute represents 3GPP 5G specific Type 2 Session Transformed (ST) Route as described in
// https://datatracker.ietf.org/doc/html/draft-mpmz-bess-mup-safi-00#section-3.1.4
type MUPType2SessionTransformedRoute struct {
RD RouteDistinguisherInterface
EndpointAddressLength uint8
EndpointAddress netip.Addr
TEID netip.Addr
}
func NewMUPType2SessionTransformedRoute(rd RouteDistinguisherInterface, eaLen uint8, ea netip.Addr, teid netip.Addr) *MUPNLRI {
afi := uint16(AFI_IP)
if ea.Is6() {
afi = AFI_IP6
}
return NewMUPNLRI(afi, MUP_ARCH_TYPE_3GPP_5G, MUP_ROUTE_TYPE_TYPE_2_SESSION_TRANSFORMED, &MUPType2SessionTransformedRoute{
RD: rd,
EndpointAddressLength: eaLen,
EndpointAddress: ea,
TEID: teid,
})
}
func (r *MUPType2SessionTransformedRoute) DecodeFromBytes(data []byte, afi uint16) error {
if len(data) < 8 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 2 Session Transformed Route length")
}
r.RD = GetRouteDistinguisher(data)
p := r.RD.Len()
if len(data) < p+1 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 2 Session Transformed Route length")
}
r.EndpointAddressLength = data[p]
if afi == AFI_IP && r.EndpointAddressLength > 64 || afi == AFI_IP6 && r.EndpointAddressLength > 160 {
return malformedAttrListErr(fmt.Sprintf("Invalid Endpoint Address Length: %d", r.EndpointAddressLength))
}
p += 1
var ea netip.Addr
var ok bool
teidLen := 0
switch afi {
case AFI_IP:
if len(data) < p+4 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 2 Session Transformed Route length")
}
ea, ok = netip.AddrFromSlice(data[p : p+4])
if !ok {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid Endpoint Address: %x", data[p:p+int(r.EndpointAddressLength/8)]))
}
p += 4
teidLen = int(r.EndpointAddressLength) - 32
case AFI_IP6:
if len(data) < p+16 {
return malformedAttrListErr("invalid 3GPP 5G specific Type 2 Session Transformed Route length")
}
ea, ok = netip.AddrFromSlice(data[p : p+16])
if !ok {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid Endpoint Address: %x", data[p:p+int(r.EndpointAddressLength/8)]))
}
p += 16
teidLen = int(r.EndpointAddressLength) - 128
default:
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, fmt.Sprintf("Invalid AFI: %d", afi))
}
r.EndpointAddress = ea
if teidLen > 0 {
l := (teidLen + 7) / 8
b := make([]byte, 4)
if len(data) < p+l {
return malformedAttrListErr("invalid 3GPP 5G specific Type 2 Session Transformed Route length")
}
copy(b[:l], data[p:p+l])
a, ok := netip.AddrFromSlice(b)
if !ok {
return malformedAttrListErr(fmt.Sprintf("Invalid TEID: %x", data[p:p+l]))
}
r.TEID = a
} else {
r.TEID = netip.AddrFrom4([4]byte{0, 0, 0, 0})
}
return nil
}
func (r *MUPType2SessionTransformedRoute) Serialize() ([]byte, error) {
var buf []byte
var err error
if r.RD != nil {
buf, err = r.RD.Serialize()
if err != nil {
return nil, err
}
} else {
buf = make([]byte, 8)
}
buf = append(buf, r.EndpointAddressLength)
buf = append(buf, r.EndpointAddress.AsSlice()...)
teidLen := int(r.EndpointAddressLength) - r.EndpointAddress.BitLen()
if teidLen > 0 {
byteLen := (teidLen + 7) / 8
buf = append(buf, r.TEID.AsSlice()[:byteLen]...)
}
return buf, nil
}
func (r *MUPType2SessionTransformedRoute) AFI() uint16 {
if r.EndpointAddress.Is6() {
return AFI_IP6
}
return AFI_IP
}
func (r *MUPType2SessionTransformedRoute) Len() int {
// RD(8) + EndpointAddressLength(1) + EndpointAddress(4 or 16)
// + TEID(4)
// Endpoint Address Length includes TEID Length
return 9 + (int(r.EndpointAddressLength)+7)/8
}
func (r *MUPType2SessionTransformedRoute) String() string {
// I-D.draft-mpmz-bess-mup-safi-01
// 3.1.4. BGP Type 2 Session Transformed (ST) Route
// For the purpose of BGP route key processing, only the RD, Endpoint Address and Architecture specific Endpoint Identifier are considered to be part of the prefix in the NLRI.
return fmt.Sprintf("[type:t2st][rd:%s][endpoint-address-length:%d][endpoint:%s][teid:%s]", r.RD, r.EndpointAddressLength, r.EndpointAddress, r.TEID)
}
func (r *MUPType2SessionTransformedRoute) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
RD RouteDistinguisherInterface `json:"rd"`
EndpointAddressLength uint8 `json:"endpoint_address_length"`
EndpointAddress string `json:"endpoint_address"`
TEID string `json:"teid"`
}{
RD: r.RD,
EndpointAddressLength: r.EndpointAddressLength,
EndpointAddress: r.EndpointAddress.String(),
TEID: r.TEID.String(),
})
}
func (r *MUPType2SessionTransformedRoute) rd() RouteDistinguisherInterface {
return r.RD
}
package bgp
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"net"
"net/netip"
)
const (
prefixSIDtlvHdrLen = 4
)
// BGP Prefix-SID TLV Types
// https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-prefix-sid-tlv-types
type TLVType uint8
const (
_ TLVType = iota // Reserved
TLVTypeLavelIndex
_ // Deprecated
TLVTypeOriginatorSRGB
_ // Deprecated
TLVTypeSRv6L3Service
TLVTypeSRv6L2Service
)
type TLV struct {
Type TLVType
Length uint16
}
func (t *TLV) Len() int {
return int(t.Length) + 3 // Type(1) + Length(2)
}
func (t *TLV) Serialize(value []byte) ([]byte, error) {
if len(value) != t.Len() {
return nil, malformedAttrListErr("serialization failed: Prefix SID TLV malformed")
}
p := 0
value[p] = byte(t.Type)
p++
binary.BigEndian.PutUint16(value[p:p+2], t.Length)
return value, nil
}
func (t *TLV) DecodeFromBytes(data []byte) ([]byte, error) {
if len(data) < 3 {
return nil, malformedAttrListErr("decoding failed: Prefix SID TLV malformed")
}
p := 0
t.Type = TLVType(data[p])
p++
t.Length = binary.BigEndian.Uint16(data[p : p+2])
p += 2
if len(data[p:]) < int(t.Length) {
return nil, malformedAttrListErr("decoding failed: Prefix SID TLV malformed")
}
return data[p : p+int(t.Length)], nil
}
// PrefixSIDTLVInterface defines standard set of methods to handle Prefix SID attribute's TLVs
type PrefixSIDTLVInterface interface {
Len() int
DecodeFromBytes([]byte) error
Serialize() ([]byte, error)
String() string
MarshalJSON() ([]byte, error)
}
type PrefixSIDAttribute struct {
TLVs []PrefixSIDTLVInterface
}
type PathAttributePrefixSID struct {
PathAttribute
TLVs []PrefixSIDTLVInterface
}
func NewPathAttributePrefixSID(values ...PrefixSIDTLVInterface) *PathAttributePrefixSID {
var l int
for _, v := range values {
l += v.Len()
}
return &PathAttributePrefixSID{
PathAttribute: PathAttribute{
Flags: getPathAttrFlags(BGP_ATTR_TYPE_PREFIX_SID, l),
Type: BGP_ATTR_TYPE_PREFIX_SID,
Length: uint16(l),
},
TLVs: values,
}
}
func (p *PathAttributePrefixSID) DecodeFromBytes(data []byte, options ...*MarshallingOption) error {
tlvs, err := p.PathAttribute.DecodeFromBytes(data)
if err != nil {
return err
}
for len(tlvs) >= prefixSIDtlvHdrLen {
t := &TLV{}
_, err := t.DecodeFromBytes(tlvs)
if err != nil {
return err
}
var tlv PrefixSIDTLVInterface
switch t.Type {
case TLVTypeSRv6L3Service, TLVTypeSRv6L2Service:
tlv = &SRv6ServiceTLV{
SubTLVs: make([]PrefixSIDTLVInterface, 0),
}
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 *PathAttributePrefixSID) Serialize(options ...*MarshallingOption) ([]byte, error) {
buf := make([]byte, 0)
for _, tlv := range p.TLVs {
s, err := tlv.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, s...)
}
return p.PathAttribute.Serialize(buf)
}
func (p *PathAttributePrefixSID) String() string {
var buf bytes.Buffer
for _, tlv := range p.TLVs {
buf.WriteString(fmt.Sprintf("%s ", tlv.String()))
}
return fmt.Sprintf("{Prefix SID attributes: %s}", buf.String())
}
func (p *PathAttributePrefixSID) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type BGPAttrType `json:"type"`
Flags BGPAttrFlag `json:"flags"`
PrefixSIDAttribute
}{
p.GetType(),
p.GetFlags(),
*p.Extract(),
})
}
func (p *PathAttributePrefixSID) Extract() *PrefixSIDAttribute {
psid := &PrefixSIDAttribute{
TLVs: make([]PrefixSIDTLVInterface, 0),
}
psid.TLVs = append(psid.TLVs, p.TLVs...)
return psid
}
// SRv6L3Service defines the structure of SRv6 L3 Service object
type SRv6L3Service struct {
SubTLVs []PrefixSIDTLVInterface
}
// SRv6L3ServiceAttribute defines the structure of SRv6 L3 Service attribute
// Deprecated: Use SRv6ServiceTLV instead.
type SRv6L3ServiceAttribute struct {
TLV
SubTLVs []PrefixSIDTLVInterface
}
func (s *SRv6L3ServiceAttribute) Len() int {
return int(s.Length) + prefixSIDtlvHdrLen
}
func (s *SRv6L3ServiceAttribute) Serialize() ([]byte, error) {
buf := make([]byte, s.Length+3)
p := 4
for _, tlv := range s.SubTLVs {
s, err := tlv.Serialize()
if err != nil {
return nil, err
}
copy(buf[p:p+len(s)], s)
p += len(s)
}
return s.TLV.Serialize(buf)
}
func (s *SRv6L3ServiceAttribute) DecodeFromBytes(data []byte) error {
stlvs, err := s.TLV.DecodeFromBytes(data)
if err != nil {
return err
}
if s.Length < 1 {
return malformedAttrListErr("SRv6L3ServiceAttribute malformed")
}
stlvs = stlvs[1:] // RESERVED(1)
for len(stlvs) >= subTLVHdrLen {
t := &SubTLV{}
_, err := t.DecodeFromBytes(stlvs)
if err != nil {
return err
}
var stlv PrefixSIDTLVInterface
switch t.Type {
case 1:
stlv = &SRv6InformationSubTLV{
SubSubTLVs: make([]PrefixSIDTLVInterface, 0),
}
default:
if len(data) < t.Len() {
return malformedAttrListErr("SRv6L3ServiceAttribute/SubTLV malformed")
}
data = data[t.Len():]
continue
}
if err := stlv.DecodeFromBytes(stlvs); err != nil {
return err
}
if len(stlvs) < t.Len() {
return malformedAttrListErr("SRv6L3ServiceAttribute/SubTLV malformed")
}
stlvs = stlvs[t.Len():]
s.SubTLVs = append(s.SubTLVs, stlv)
}
return nil
}
func (s *SRv6L3ServiceAttribute) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type TLVType `json:"type"`
SRv6L3Service
}{
s.Type,
*s.Extract(),
})
}
func (s *SRv6L3ServiceAttribute) String() string {
var buf bytes.Buffer
for _, tlv := range s.SubTLVs {
buf.WriteString(fmt.Sprintf("%s ", tlv.String()))
}
return fmt.Sprintf("{SRv6 L3 Service Attribute: %s}", buf.String())
}
func (s *SRv6L3ServiceAttribute) Extract() *SRv6L3Service {
l3 := &SRv6L3Service{
SubTLVs: make([]PrefixSIDTLVInterface, 0),
}
l3.SubTLVs = append(l3.SubTLVs, s.SubTLVs...)
return l3
}
const (
subTLVHdrLen = 3
)
type SubTLVType uint8
type SubTLV struct {
Type SubTLVType
Length uint16
}
func (s *SubTLV) Len() int {
return int(s.Length) + subTLVHdrLen
}
func (s *SubTLV) Serialize(value []byte) ([]byte, error) {
if len(value) != int(s.Length) {
return nil, malformedAttrListErr("serialization failed: Prefix SID TLV malformed")
}
// Extra byte is reserved
buf := make([]byte, subTLVHdrLen+len(value))
buf[0] = byte(s.Type)
binary.BigEndian.PutUint16(buf[1:4], s.Length)
// 4th reserved byte
copy(buf[4:], value)
return buf, nil
}
func (s *SubTLV) DecodeFromBytes(data []byte) ([]byte, error) {
if len(data) < subTLVHdrLen {
return nil, malformedAttrListErr("decoding failed: Prefix SID Sub TLV malformed")
}
s.Type = SubTLVType(data[0])
s.Length = binary.BigEndian.Uint16(data[1:3])
if len(data) < s.Len() {
return nil, malformedAttrListErr("decoding failed: Prefix SID Sub TLV malformed")
}
return data[subTLVHdrLen:s.Len()], nil
}
type SRv6InformationSTLV struct {
SID []byte `json:"sid"`
Flags uint8 `json:"flags"`
EndpointBehavior uint16 `json:"endpoint_behavior"`
SubSubTLVs []PrefixSIDTLVInterface `json:"sub_sub_tlvs,omitempty"`
}
// SRv6InformationSubTLV defines a structure of SRv6 Information Sub TLV (type 1) object
// https://tools.ietf.org/html/draft-dawra-bess-srv6-services-02#section-2.1.1
type SRv6InformationSubTLV struct {
SubTLV
SID []byte
Flags uint8
EndpointBehavior uint16
SubSubTLVs []PrefixSIDTLVInterface
}
func NewSRv6InformationSubTLV(sid netip.Addr, behavior SRBehavior, values ...PrefixSIDTLVInterface) *SRv6InformationSubTLV {
l := 21 // RESERVED1(1) + SID(16) + Flags(1) + Endpoint Behavior(2) + RESERVED2(1)
for _, v := range values {
l += v.Len()
}
return &SRv6InformationSubTLV{
SubTLV: SubTLV{
Type: 1,
Length: uint16(l),
},
SID: sid.AsSlice(),
Flags: 0,
EndpointBehavior: uint16(behavior),
SubSubTLVs: values,
}
}
func (s *SRv6InformationSubTLV) Len() int {
return int(s.Length) + subTLVHdrLen
}
func (s *SRv6InformationSubTLV) Serialize() ([]byte, error) {
buf := make([]byte, s.Length)
p := 0
copy(buf[p:], s.SID)
p += len(s.SID)
buf[p] = s.Flags
p++
binary.BigEndian.PutUint16(buf[p:p+2], s.EndpointBehavior)
p += 2
// Reserved byte
buf[p] = 0x0
p++
for _, sstlv := range s.SubSubTLVs {
sbuf, err := sstlv.Serialize()
if err != nil {
return nil, err
}
copy(buf[p:], sbuf)
p += len(sbuf)
}
return s.SubTLV.Serialize(buf)
}
func (s *SRv6InformationSubTLV) DecodeFromBytes(data []byte) error {
if len(data) < subTLVHdrLen {
return malformedAttrListErr("decoding failed: Prefix SID TLV malformed")
}
s.Type = SubTLVType(data[0])
s.Length = binary.BigEndian.Uint16(data[1:3])
// 4th reserved byte
p := 4
s.SID = make([]byte, 16)
if len(data) < p+16+1+2+1 {
return malformedAttrListErr("decoding failed: Prefix SID TLV malformed")
}
copy(s.SID, data[p:p+16])
p += 16
s.Flags = data[p]
p++
s.EndpointBehavior = binary.BigEndian.Uint16(data[p : p+2])
p += 2
// reserved byte
p++
if p+3 > len(data) {
// There is no Sub Sub TLVs detected, returning
return nil
}
stlvs := data[p:]
for len(stlvs) >= prefixSIDtlvHdrLen {
t := &SubSubTLV{}
_, err := t.DecodeFromBytes(stlvs)
if err != nil {
return err
}
var sstlv PrefixSIDTLVInterface
switch t.Type {
case 1:
sstlv = &SRv6SIDStructureSubSubTLV{}
default:
stlvs = stlvs[t.Len():]
continue
}
if err := sstlv.DecodeFromBytes(stlvs); err != nil {
return err
}
stlvs = stlvs[t.Len():]
s.SubSubTLVs = append(s.SubSubTLVs, sstlv)
}
return nil
}
func (s *SRv6InformationSubTLV) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type SubTLVType `json:"type"`
SRv6InformationSTLV
}{
s.Type,
*s.Extract(),
})
}
func (s *SRv6InformationSubTLV) String() string {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("SID: %s ", net.IP(s.SID).To16().String()))
buf.WriteString(fmt.Sprintf("Flag: %d ", s.Flags))
buf.WriteString(fmt.Sprintf("Endpoint Behavior: %d ", s.EndpointBehavior))
for _, tlv := range s.SubSubTLVs {
buf.WriteString(fmt.Sprintf("%s ", tlv.String()))
}
return fmt.Sprintf("{SRv6 Information Sub TLV: %s}", buf.String())
}
func (s *SRv6InformationSubTLV) Extract() *SRv6InformationSTLV {
info := &SRv6InformationSTLV{
SID: s.SID,
Flags: s.Flags,
EndpointBehavior: s.EndpointBehavior,
SubSubTLVs: make([]PrefixSIDTLVInterface, 0),
}
info.SubSubTLVs = append(info.SubSubTLVs, s.SubSubTLVs...)
return info
}
const (
subSubTLVHdrLen = 3
)
type SubSubTLVType uint8
type SubSubTLV struct {
Type SubSubTLVType
Length uint16
}
func (s *SubSubTLV) Len() int {
return int(s.Length) + subSubTLVHdrLen
}
func (s *SubSubTLV) Serialize(value []byte) ([]byte, error) {
if len(value) != int(s.Length) {
return nil, malformedAttrListErr("serialization failed: Prefix SID TLV malformed")
}
// Extra byte is reserved
buf := make([]byte, subSubTLVHdrLen+len(value))
p := 0
buf[p] = byte(s.Type)
p++
binary.BigEndian.PutUint16(buf[p:p+2], s.Length)
p += 2
copy(buf[p:], value)
return buf, nil
}
func (s *SubSubTLV) DecodeFromBytes(data []byte) ([]byte, error) {
if len(data) < prefixSIDtlvHdrLen {
return nil, malformedAttrListErr("decoding failed: Prefix SID Sub Sub TLV malformed")
}
s.Type = SubSubTLVType(data[0])
s.Length = binary.BigEndian.Uint16(data[1:3])
if len(data) < s.Len() || s.Len() < prefixSIDtlvHdrLen {
return nil, malformedAttrListErr("decoding failed: Prefix SID Sub Sub TLV malformed")
}
return data[prefixSIDtlvHdrLen:s.Len()], nil
}
// SRv6SIDStructureSubSubTLV defines a structure of SRv6 SID Structure Sub Sub TLV (type 1) object
// https://www.rfc-editor.org/rfc/rfc9252.html#section-3.2.1
type SRv6SIDStructureSubSubTLV struct {
SubSubTLV
LocatorBlockLength uint8
LocatorNodeLength uint8
FunctionLength uint8
ArgumentLength uint8
TranspositionLength uint8
TranspositionOffset uint8
}
func NewSRv6SIDStructureSubSubTLV(lbl, lnl, fl, al, tl, to uint8) *SRv6SIDStructureSubSubTLV {
return &SRv6SIDStructureSubSubTLV{
SubSubTLV: SubSubTLV{
Type: 1,
Length: 6,
},
LocatorBlockLength: lbl,
LocatorNodeLength: lnl,
FunctionLength: fl,
ArgumentLength: al,
TranspositionLength: tl,
TranspositionOffset: to,
}
}
func (s *SRv6SIDStructureSubSubTLV) Len() int {
return int(s.Length) + subSubTLVHdrLen
}
func (s *SRv6SIDStructureSubSubTLV) Serialize() ([]byte, error) {
buf := make([]byte, s.Length)
p := 0
buf[p] = s.LocatorBlockLength
p++
buf[p] = s.LocatorNodeLength
p++
buf[p] = s.FunctionLength
p++
buf[p] = s.ArgumentLength
p++
buf[p] = s.TranspositionLength
p++
buf[p] = s.TranspositionOffset
return s.SubSubTLV.Serialize(buf)
}
func (s *SRv6SIDStructureSubSubTLV) DecodeFromBytes(data []byte) error {
if len(data) < 9 {
return malformedAttrListErr("decoding failed: Prefix SID Sub Sub TLV malformed")
}
s.Type = SubSubTLVType(data[0])
s.Length = binary.BigEndian.Uint16(data[1:3])
s.LocatorBlockLength = data[3]
s.LocatorNodeLength = data[4]
s.FunctionLength = data[5]
s.ArgumentLength = data[6]
s.TranspositionLength = data[7]
s.TranspositionOffset = data[8]
return nil
}
func (s *SRv6SIDStructureSubSubTLV) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type SubSubTLVType `json:"type"`
LocatorBlockLength uint8 `json:"locator_block_length"`
LocatorNodeLength uint8 `json:"locator_node_length"`
FunctionLength uint8 `json:"function_length"`
ArgumentLength uint8 `json:"argument_length"`
TranspositionLength uint8 `json:"transposition_length"`
TranspositionOffset uint8 `json:"transposition_offset"`
}{
Type: s.Type,
LocatorBlockLength: s.LocatorBlockLength,
LocatorNodeLength: s.LocatorNodeLength,
FunctionLength: s.FunctionLength,
ArgumentLength: s.ArgumentLength,
TranspositionLength: s.TranspositionLength,
TranspositionOffset: s.TranspositionOffset,
})
}
func (s *SRv6SIDStructureSubSubTLV) String() string {
return fmt.Sprintf("{SRv6 Structure Sub Sub TLV: [ Locator Block Length: %d, Locator Node Length: %d, Function Length: %d, Argument Length: %d, Transposition Length: %d, Transposition Offset: %d] }",
s.LocatorBlockLength,
s.LocatorNodeLength,
s.FunctionLength,
s.ArgumentLength,
s.TranspositionLength,
s.TranspositionOffset,
)
}
// SRv6ServiceTLV represents SRv6 Service TLV.
// https://www.rfc-editor.org/rfc/rfc9252.html#section-2
type SRv6ServiceTLV struct {
TLV
SubTLVs []PrefixSIDTLVInterface
}
func NewSRv6ServiceTLV(t TLVType, values ...PrefixSIDTLVInterface) *SRv6ServiceTLV {
l := 1 // RESERVED(1)
for _, v := range values {
l += v.Len()
}
return &SRv6ServiceTLV{
TLV: TLV{
Type: t,
Length: uint16(l),
},
SubTLVs: values,
}
}
func (s *SRv6ServiceTLV) Len() int {
return int(s.Length) + 3 // Type(1) + Length(2)
}
func (t *SRv6ServiceTLV) Serialize() ([]byte, error) {
buf := make([]byte, t.Len())
p := 4
for _, tlv := range t.SubTLVs {
b, err := tlv.Serialize()
if err != nil {
return nil, err
}
copy(buf[p:p+len(b)], b)
p += len(b)
}
return t.TLV.Serialize(buf)
}
func (s *SRv6ServiceTLV) DecodeFromBytes(data []byte) error {
stlvs, err := s.TLV.DecodeFromBytes(data)
if err != nil {
return err
}
if s.Length < 1 {
return malformedAttrListErr("SRv6ServiceTLV malformed")
}
stlvs = stlvs[1:] // RESERVED(1)
for len(stlvs) >= subTLVHdrLen {
t := &SubTLV{}
_, err := t.DecodeFromBytes(stlvs)
if err != nil {
return err
}
var stlv PrefixSIDTLVInterface
switch t.Type {
case 1:
stlv = &SRv6InformationSubTLV{
SubSubTLVs: make([]PrefixSIDTLVInterface, 0),
}
default:
if len(data) < t.Len() {
return malformedAttrListErr("SRv6ServiceTLV malformed")
}
data = data[t.Len():]
continue
}
if err := stlv.DecodeFromBytes(stlvs); err != nil {
return err
}
if len(stlvs) < t.Len() {
return malformedAttrListErr("SRv6ServiceTLV malformed")
}
stlvs = stlvs[t.Len():]
s.SubTLVs = append(s.SubTLVs, stlv)
}
return nil
}
func (t *SRv6ServiceTLV) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type TLVType `json:"type"`
SubTLVs []PrefixSIDTLVInterface
}{
t.Type,
t.SubTLVs,
})
}
func (t *SRv6ServiceTLV) String() string {
var buf bytes.Buffer
for _, tlv := range t.SubTLVs {
buf.WriteString(fmt.Sprintf("%s ", tlv.String()))
}
return fmt.Sprintf("{SRv6 Service TLV: %s}", buf.String())
}
package bgp
import (
"encoding/binary"
"encoding/json"
"fmt"
"net"
"strconv"
)
type SRPolicyNLRI struct {
rf Family
Length uint8
Distinguisher uint32
Color uint32
Endpoint []byte
}
const (
// SRPolicyIPv4NLRILen defines IPv4 SR Policy NLRI portion length in bits
SRPolicyIPv4NLRILen = 96
// SRPolicyIPv6NLRILen defines IPv6 SR Policy NLRI portion length in bits
SRPolicyIPv6NLRILen = 192
)
func (s *SRPolicyNLRI) Flat() map[string]string {
return map[string]string{}
}
func (s *SRPolicyNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
if len(data) < 1 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Malformed SR Policy NLRI")
}
switch data[0] {
case SRPolicyIPv4NLRILen:
s.rf = RF_SR_POLICY_IPv4
case SRPolicyIPv6NLRILen:
s.rf = RF_SR_POLICY_IPv6
default:
msg := fmt.Sprintf("Invalid length %d for SR Policy NLRI", len(data))
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
}
if len(data) < 1+4+4 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Malformed SR Policy NLRI")
}
p := 0
s.Length = data[p] / 8
p++
s.Distinguisher = binary.BigEndian.Uint32(data[p : p+4])
p += 4
s.Color = binary.BigEndian.Uint32(data[p : p+4])
p += 4
s.Endpoint = data[p:]
return nil
}
func (s *SRPolicyNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
buf := make([]byte, 1+s.Length)
p := 0
buf[0] = s.Length * 8
p++
binary.BigEndian.PutUint32(buf[p:p+4], s.Distinguisher)
p += 4
binary.BigEndian.PutUint32(buf[p:p+4], s.Color)
p += 4
copy(buf[p:], s.Endpoint)
return buf, nil
}
func (s *SRPolicyNLRI) Len(options ...*MarshallingOption) int {
buf, _ := s.Serialize(options...)
return len(buf)
}
func (s *SRPolicyNLRI) String() string {
var endp string
switch s.rf.Afi() {
case AFI_IP:
endp = net.IP(s.Endpoint).To4().String()
case AFI_IP6:
endp = net.IP(s.Endpoint).To16().String()
default:
endp = "[" + string(s.Endpoint) + "]"
}
return fmt.Sprintf("{ Length: %d (bytes), Distinguisher: %d, Color %d, Endpoint: %s }", s.Length, s.Distinguisher, s.Color, endp)
}
func (s *SRPolicyNLRI) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Length uint8 `json:"length"`
Distinguisher uint32 `json:"distinguisher"`
Color uint32 `json:"color"`
Endpoint string `json:"endpoint"`
}{
Length: s.Length,
Distinguisher: s.Distinguisher,
Color: s.Color,
Endpoint: string(s.Endpoint),
})
}
func NewSRPolicy(family Family, l uint32, d uint32, c uint32, ep []byte) (*SRPolicyNLRI, error) {
switch family {
case RF_SR_POLICY_IPv4, RF_SR_POLICY_IPv6:
default:
return nil, fmt.Errorf("invalid family: %d", family)
}
return &SRPolicyNLRI{
rf: family,
Length: uint8(l / 8),
Distinguisher: d,
Color: c,
Endpoint: ep,
}, nil
}
type TunnelEncapSubTLVSRPreference struct {
TunnelEncapSubTLV
Flags uint8
Preference uint32
}
func (t *TunnelEncapSubTLVSRPreference) DecodeFromBytes(data []byte) error {
value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return err
}
// Second byte carries the length of SR Preference SubTLV
if t.Length != 6 {
msg := fmt.Sprintf("Invalid TunnelEncapSubTLVSRPreference length: %d", t.Length)
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
}
t.Flags = value[0]
t.Preference = binary.BigEndian.Uint32(value[2:6])
return nil
}
func (t *TunnelEncapSubTLVSRPreference) Serialize() ([]byte, error) {
buf := make([]byte, 6)
buf[0] = t.Flags
binary.BigEndian.PutUint32(buf[2:6], t.Preference)
return t.TunnelEncapSubTLV.Serialize(buf[:])
}
func (t *TunnelEncapSubTLVSRPreference) String() string {
return fmt.Sprintf("{Flags: 0x%02x, Preference: %d}", t.Flags, t.Preference)
}
func (t *TunnelEncapSubTLVSRPreference) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
Flags uint8 `json:"flags"`
Preference uint32 `json:"preference"`
}{
Type: t.Type,
Flags: t.Flags,
Preference: t.Preference,
})
}
func NewTunnelEncapSubTLVSRPreference(flags uint32, preference uint32) *TunnelEncapSubTLVSRPreference {
return &TunnelEncapSubTLVSRPreference{
TunnelEncapSubTLV: TunnelEncapSubTLV{
Type: ENCAP_SUBTLV_TYPE_SRPREFERENCE,
Length: 6,
},
Flags: uint8(flags),
Preference: preference,
}
}
type TunnelEncapSubTLVSRPriority struct {
TunnelEncapSubTLV
Priority uint8
}
func (t *TunnelEncapSubTLVSRPriority) DecodeFromBytes(data []byte) error {
value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return err
}
// Second byte carries the length of SR Preference SubTLV
if t.Length != 2 {
msg := fmt.Sprintf("Invalid TunnelEncapSubTLVSRPriority length: %d", t.Length)
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
}
t.Priority = value[0]
return nil
}
func (t *TunnelEncapSubTLVSRPriority) Serialize() ([]byte, error) {
buf := make([]byte, 1+1)
buf[0] = t.Priority
return t.TunnelEncapSubTLV.Serialize(buf[:])
}
func (t *TunnelEncapSubTLVSRPriority) String() string {
return fmt.Sprintf("{Priority: %d}", t.Priority)
}
func (t *TunnelEncapSubTLVSRPriority) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
Priority uint8 `json:"priority"`
}{
Type: t.Type,
Priority: t.Priority,
})
}
func NewTunnelEncapSubTLVSRPriority(priority uint8) *TunnelEncapSubTLVSRPriority {
return &TunnelEncapSubTLVSRPriority{
TunnelEncapSubTLV: TunnelEncapSubTLV{
Type: ENCAP_SUBTLV_TYPE_SRPRIORITY,
Length: 2,
},
Priority: priority,
}
}
type TunnelEncapSubTLVSRCandidatePathName struct {
TunnelEncapSubTLV
CandidatePathName string
}
func (t *TunnelEncapSubTLVSRCandidatePathName) DecodeFromBytes(data []byte) error {
value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return err
}
// Skip Reserved byte
if int(t.Length) < t.Len() {
return malformedAttrListErr("TunnelEncapSubTLVSRCandidatePathName length is too short")
}
t.CandidatePathName = string(value[1:t.Len()])
return nil
}
func (t *TunnelEncapSubTLVSRCandidatePathName) Serialize() ([]byte, error) {
buf := make([]byte, 1+len(t.CandidatePathName))
copy(buf[1:], t.CandidatePathName)
return t.TunnelEncapSubTLV.Serialize(buf[:])
}
func (t *TunnelEncapSubTLVSRCandidatePathName) String() string {
return fmt.Sprintf("{Candidate Path Name: %s}", t.CandidatePathName)
}
func (t *TunnelEncapSubTLVSRCandidatePathName) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
CandidatePathName string `json:"candidate_path_name"`
}{
Type: t.Type,
CandidatePathName: t.CandidatePathName,
})
}
func NewTunnelEncapSubTLVSRCandidatePathName(cpn string) *TunnelEncapSubTLVSRCandidatePathName {
return &TunnelEncapSubTLVSRCandidatePathName{
TunnelEncapSubTLV: TunnelEncapSubTLV{
Type: ENCAP_SUBTLV_TYPE_SRCANDIDATE_PATH_NAME,
Length: uint16(len(cpn) + 1), // length of Candidate Path name string + 1 Reserved byte
},
CandidatePathName: cpn,
}
}
type SRENLPValue uint8
const (
// ENLPType1 Indicates to push an IPv4 Explicit NULL label on an unlabeled IPv4
// packet, but do not push an IPv6 Explicit NULL label on an
// unlabeled IPv6 packet.
ENLPType1 SRENLPValue = 1
// ENLPType2 Indicates to push an IPv6 Explicit NULL label on an unlabeled IPv6
// packet, but do not push an IPv4 Explicit NULL label on an
// unlabeled IPv4 packet.
ENLPType2 SRENLPValue = 2
// ENLPType3 Indicates to push an IPv4 Explicit NULL label on an unlabeled IPv4
// packet, and push an IPv6 Explicit NULL label on an unlabeled
// IPv6 packet.
ENLPType3 SRENLPValue = 3
// ENLPType4 Indicates to not push an Explicit NULL label.
ENLPType4 SRENLPValue = 4
)
type TunnelEncapSubTLVSRENLP struct {
TunnelEncapSubTLV
Flags uint8
ENLP SRENLPValue
}
func (t *TunnelEncapSubTLVSRENLP) DecodeFromBytes(data []byte) error {
value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return err
}
// Second byte carries the length of SR Preference SubTLV
if t.Length != 3 {
msg := fmt.Sprintf("Invalid TunnelEncapSubTLVSRENLP length: %d", t.Length)
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
}
t.Flags = value[0]
switch SRENLPValue(value[2]) {
case ENLPType1:
case ENLPType2:
case ENLPType3:
case ENLPType4:
default:
msg := fmt.Sprintf("Invalid ENLP Type: %d", value[2])
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
}
t.ENLP = SRENLPValue(value[2])
return nil
}
func (t *TunnelEncapSubTLVSRENLP) Serialize() ([]byte, error) {
buf := make([]byte, t.Length)
buf[0] = t.Flags
buf[2] = byte(t.ENLP)
return t.TunnelEncapSubTLV.Serialize(buf[:])
}
func (t *TunnelEncapSubTLVSRENLP) String() string {
return fmt.Sprintf("{Flags: 0x%02x, ENLP Type: %d}", t.Flags, t.ENLP)
}
func (t *TunnelEncapSubTLVSRENLP) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
Flags uint8 `json:"flags"`
ENLP uint8 `json:"enlp"`
}{
Type: t.Type,
Flags: t.Flags,
ENLP: uint8(t.ENLP),
})
}
func NewTunnelEncapSubTLVSRENLP(flags uint32, enlp SRENLPValue) *TunnelEncapSubTLVSRENLP {
return &TunnelEncapSubTLVSRENLP{
TunnelEncapSubTLV: TunnelEncapSubTLV{
Type: ENCAP_SUBTLV_TYPE_SRENLP,
Length: 3,
},
Flags: uint8(flags),
ENLP: enlp,
}
}
type BSID struct {
Value []byte
}
func (b *BSID) String() string {
switch len(b.Value) {
case 0:
return "n/a"
case 4:
bsid := binary.BigEndian.Uint32(b.Value)
bsid >>= 12
return strconv.Itoa(int(bsid))
case 16:
return net.IP(b.Value).To16().String()
default:
return "invalid"
}
}
func (b *BSID) Serialize() []byte {
return b.Value
}
func (b *BSID) Len() int {
return len(b.Value)
}
func NewBSID(v []byte) (*BSID, error) {
var bsid *BSID
switch len(v) {
case 0:
case 4:
t := binary.BigEndian.Uint32(v)
t <<= 12
bsid = &BSID{
Value: make([]byte, len(v)),
}
binary.BigEndian.PutUint32(bsid.Value, t)
case 16:
bsid = &BSID{
Value: make([]byte, len(v)),
}
copy(bsid.Value, v)
default:
return nil, fmt.Errorf("invalid length %d", len(v))
}
return bsid, nil
}
type TunnelEncapSubTLVSRBSID struct {
TunnelEncapSubTLV
Flags uint8
BSID *BSID
}
func (t *TunnelEncapSubTLVSRBSID) DecodeFromBytes(data []byte) error {
value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
// Check Sub TLV length, only 3 possible length are allowed
switch t.Length {
case 2: // No BSID, do not initializing BSID struct
case 6:
fallthrough
case 18:
t.BSID = &BSID{
Value: make([]byte, t.Length-2),
}
copy(t.BSID.Value, value[2:t.Length])
default:
msg := fmt.Sprintf("Invalid TunnelEncapSubTLVSRBSID length: %d", t.Length)
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
}
t.Flags = value[0]
return nil
}
func (t *TunnelEncapSubTLVSRBSID) Serialize() ([]byte, error) {
l := 2
if t.BSID != nil {
l += t.BSID.Len()
}
buf := make([]byte, l) // 1st byte Flags, 2nd byte Reserved, 3rd+ BSID
buf[0] = t.Flags
if t.BSID != nil {
bsid := t.BSID.Serialize()
copy(buf[2:], bsid)
}
return t.TunnelEncapSubTLV.Serialize(buf[:])
}
func (t *TunnelEncapSubTLVSRBSID) String() string {
return fmt.Sprintf("{S-Flag: %t, I-Flag: %t, BSID: %s}", t.Flags&0x80 == 0x80, t.Flags&0x40 == 0x40, t.BSID.String())
}
func (t *TunnelEncapSubTLVSRBSID) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
Flags uint8 `json:"flags"`
BSID string `json:"binding_sid,omitempty"`
}{
Type: t.Type,
Flags: t.Flags,
BSID: t.BSID.String(),
})
}
type TunnelEncapSubTLVSRv6BSID struct {
TunnelEncapSubTLV
Flags uint8
BSID *BSID
EPBAS *SRv6EndpointBehaviorStructure
}
func (t *TunnelEncapSubTLVSRv6BSID) DecodeFromBytes(data []byte) error {
value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
if t.Length < 3 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "TunnelEncapSubTLVSRv6BSID length is too short")
}
t.Flags = value[0]
t.BSID, err = NewBSID(value[2:t.Length])
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
return nil
}
func (t *TunnelEncapSubTLVSRv6BSID) Serialize() ([]byte, error) {
buf := make([]byte, t.Length)
buf[0] = t.Flags
copy(buf[2:t.BSID.Len()], t.BSID.Serialize())
return t.TunnelEncapSubTLV.Serialize(buf[:])
}
func (t *TunnelEncapSubTLVSRv6BSID) String() string {
return fmt.Sprintf("{S-Flag: %t, I-Flag: %t, B-Flag: %t, BSID: %s}", t.Flags&0x80 == 0x80, t.Flags&0x40 == 0x40, t.Flags&0x20 == 0x20, t.BSID.String())
}
func (t *TunnelEncapSubTLVSRv6BSID) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
Flags uint8 `json:"flags"`
BSID string `json:"binding_sid,omitempty"`
}{
Type: t.Type,
Flags: t.Flags,
BSID: t.BSID.String(),
})
}
// SegmentType defines a type of Segment in Segment List
type SegmentType int
const (
// TypeA Segment Sub-TLV encodes a single SR-MPLS SID
TypeA SegmentType = 1
// TypeB Segment Sub-TLV encodes a single SRv6 SID.
TypeB SegmentType = 13
// TypeC Segment Sub-TLV encodes an IPv4 node address, SR Algorithm
// and an optional SR-MPLS SID
TypeC SegmentType = 3
// TypeD Segment Sub-TLV encodes an IPv6 node address, SR Algorithm
// and an optional SR-MPLS SID.
TypeD SegmentType = 4
// TypeE Segment Sub-TLV encodes an IPv4 node address, a local
// interface Identifier (Local Interface ID) and an optional SR-MPLS
// SID.
TypeE SegmentType = 5
// TypeF Segment Sub-TLV encodes an adjacency local address, an
// adjacency remote address and an optional SR-MPLS SID.
TypeF SegmentType = 6
// TypeG Segment Sub-TLV encodes an IPv6 Link Local adjacency with
// IPv6 local node address, a local interface identifier (Local
// Interface ID), IPv6 remote node address , a remote interface
// identifier (Remote Interface ID) and an optional SR-MPLS SID.
TypeG SegmentType = 7
// TypeH Segment Sub-TLV encodes an adjacency local address, an
// adjacency remote address and an optional SR-MPLS SID.
TypeH SegmentType = 8
// TypeI Segment Sub-TLV encodes an IPv6 node address, SR Algorithm
// and an optional SRv6 SID.
TypeI SegmentType = 14
// TypeJ Segment Sub-TLV encodes an IPv6 Link Local adjacency with
// local node address, a local interface identifier (Local Interface
// ID), remote IPv6 node address, a remote interface identifier (Remote
// Interface ID) and an optional SRv6 SID.
TypeJ SegmentType = 15
// TypeK Segment Sub-TLV encodes an adjacency local address, an
// adjacency remote address and an optional SRv6 SID.
TypeK SegmentType = 16
)
// Weight sub-TLV specifies the weight associated to a given segment list.
type SegmentListWeight struct {
TunnelEncapSubTLV
Flags uint8
Weight uint32
}
func (s *SegmentListWeight) DecodeFromBytes(data []byte) error {
value, err := s.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
if s.Length < 6 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "SegmentListWeight length is too short")
}
s.Flags = value[0]
s.Weight = binary.BigEndian.Uint32(value[2:6])
return nil
}
func (s *SegmentListWeight) Serialize() ([]byte, error) {
buf := make([]byte, 6)
buf[0] = s.Flags
binary.BigEndian.PutUint32(buf[2:6], s.Weight)
return s.TunnelEncapSubTLV.Serialize(buf)
}
func (s *SegmentListWeight) String() string {
return fmt.Sprintf("{Flags: 0x%02x, Weight: %d}", s.Flags, s.Weight)
}
func (s *SegmentListWeight) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
Flags uint8 `json:"flags"`
Weight uint32 `json:"weight,omitempty"`
}{
Type: s.Type,
Flags: s.Flags,
Weight: s.Weight,
})
}
type SegmentTypeA struct {
TunnelEncapSubTLV
Flags uint8
Label uint32
}
func (s *SegmentTypeA) DecodeFromBytes(data []byte) error {
value, err := s.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
if s.Length < 6 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "SegmentTypeA length is too short")
}
s.Flags = value[0]
s.Label = binary.BigEndian.Uint32(value[2:6])
return nil
}
func (s *SegmentTypeA) Serialize() ([]byte, error) {
buf := make([]byte, 6)
buf[0] = s.Flags
binary.BigEndian.PutUint32(buf[2:6], s.Label)
return s.TunnelEncapSubTLV.Serialize(buf)
}
func (s *SegmentTypeA) String() string {
return fmt.Sprintf("{V-flag: %t, A-flag:, %t S-flag: %t, B-flag: %t, Label: %d TC: %d S: %t TTL: %d}",
s.Flags&0x80 == 0x80, s.Flags&0x40 == 0x40, s.Flags&0x20 == 0x20, s.Flags&0x10 == 0x10,
s.Label>>12, s.Label&0x00000e00>>9, s.Label&0x00000100 == 0x00000100, s.Label&0x000000ff)
}
func (s *SegmentTypeA) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
VFlag bool `json:"v_flag"`
AFlag bool `json:"a_flag"`
SFlag bool `json:"s_flag"`
BFlag bool `json:"b_flag"`
Label uint32 `json:"label"`
TC uint8 `json:"tc"`
S bool `json:"s"`
TTL uint8 `json:"ttl"`
}{
Type: s.Type,
VFlag: s.Flags&0x80 == 0x80,
AFlag: s.Flags&0x40 == 0x40,
SFlag: s.Flags&0x20 == 0x20,
BFlag: s.Flags&0x10 == 0x10,
Label: s.Label >> 12,
TC: uint8(s.Label & 0x00000e00 >> 9),
S: s.Label&0x00000100 == 0x00000100,
TTL: uint8(s.Label & 0x000000ff),
})
}
//go:generate go run internal/generate.go SRBehavior
//go:generate stringer -type=SRBehavior
type SRBehavior int32
type SRv6EndpointBehaviorStructure struct {
Behavior SRBehavior
BlockLen uint8
NodeLen uint8
FuncLen uint8
ArgLen uint8
}
func (s *SRv6EndpointBehaviorStructure) DecodeFromBytes(data []byte) error {
if len(data) < 8 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Malformed BGP message")
}
behavior := binary.BigEndian.Uint16(data[:2])
s.Behavior = SRBehavior(behavior)
s.BlockLen = data[4]
s.NodeLen = data[5]
s.FuncLen = data[6]
s.ArgLen = data[7]
return nil
}
func (s *SRv6EndpointBehaviorStructure) Serialize() ([]byte, error) {
buf := make([]byte, 8)
binary.BigEndian.PutUint16(buf[:2], uint16(s.Behavior))
buf[4] = s.BlockLen
buf[5] = s.NodeLen
buf[6] = s.FuncLen
buf[7] = s.ArgLen
return buf, nil
}
func (s *SRv6EndpointBehaviorStructure) String() string {
return fmt.Sprintf("{Behavior: %s, BlockLen: %d, NodeLen: %d, FuncLen: %d, ArgLen: %d}",
s.Behavior.String(), s.BlockLen, s.NodeLen, s.FuncLen, s.ArgLen)
}
func (s *SRv6EndpointBehaviorStructure) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Behavior SRBehavior `json:"behavior"`
BlockLen uint8 `json:"block_Len"`
NodeLen uint8 `json:"node_len"`
FuncLen uint8 `json:"func_len"`
ArgLen uint8 `json:"arg_len"`
}{
Behavior: s.Behavior,
BlockLen: s.BlockLen,
NodeLen: s.NodeLen,
FuncLen: s.FuncLen,
ArgLen: s.ArgLen,
})
}
type SegmentTypeB struct {
TunnelEncapSubTLV
Flags uint8
SID []byte
SRv6EBS *SRv6EndpointBehaviorStructure
}
func (s *SegmentTypeB) DecodeFromBytes(data []byte) error {
value, err := s.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
if len(value) < 18 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Malformed BGP message")
}
s.Flags = value[0]
s.SID = value[2:18]
if len(value) == 26 {
s.SRv6EBS = &SRv6EndpointBehaviorStructure{}
err = s.SRv6EBS.DecodeFromBytes(value[18:])
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
}
return nil
}
func (s *SegmentTypeB) Serialize() ([]byte, error) {
buf := make([]byte, 18)
buf[0] = s.Flags
copy(buf[2:], s.SID)
if s.SRv6EBS != nil {
if ebs, _ := s.SRv6EBS.Serialize(); ebs != nil {
buf = append(buf, ebs...)
}
}
return s.TunnelEncapSubTLV.Serialize(buf)
}
func (s *SegmentTypeB) String() string {
if s.SRv6EBS == nil {
return fmt.Sprintf("{V-flag: %t, A-flag:, %t S-flag: %t, B-flag: %t, Sid: %s}",
s.Flags&0x80 == 0x80, s.Flags&0x40 == 0x40, s.Flags&0x20 == 0x20, s.Flags&0x10 == 0x10, net.IP(s.SID).To16().String())
} else {
return fmt.Sprintf("{V-flag: %t, A-flag:, %t S-flag: %t, B-flag: %t, Sid: %s, Ebs: %s}",
s.Flags&0x80 == 0x80, s.Flags&0x40 == 0x40, s.Flags&0x20 == 0x20, s.Flags&0x10 == 0x10, net.IP(s.SID).To16().String(),
s.SRv6EBS.String())
}
}
func (s *SegmentTypeB) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
VFlag bool `json:"v_flag"`
AFlag bool `json:"a_flag"`
SFlag bool `json:"s_flag"`
BFlag bool `json:"b_flag"`
Sid string `json:"sid"`
SRv6EBS *SRv6EndpointBehaviorStructure `json:"endpointBehaviorStructure"`
}{
Type: s.Type,
VFlag: s.Flags&0x80 == 0x80,
AFlag: s.Flags&0x40 == 0x40,
SFlag: s.Flags&0x20 == 0x20,
BFlag: s.Flags&0x10 == 0x10,
Sid: net.IP(s.SID).To16().String(),
SRv6EBS: s.SRv6EBS,
})
}
const (
// SegmentListSubTLVWeight defines code for Segment List's Weight sub-TLV
SegmentListSubTLVWeight = 9
)
type TunnelEncapSubTLVSRSegmentList struct {
TunnelEncapSubTLV
Weight *SegmentListWeight
Segments []TunnelEncapSubTLVInterface
}
func (t *TunnelEncapSubTLVSRSegmentList) DecodeFromBytes(data []byte) error {
value, err := t.TunnelEncapSubTLV.DecodeFromBytes(data)
if err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
if len(value) < 1 {
return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, "Malformed BGP message")
}
// Skip reserved byte to access inner SubTLV type
value = value[1:]
var segments []TunnelEncapSubTLVInterface
p := 0
for p < t.Len()-4 {
var segment TunnelEncapSubTLVInterface
switch SegmentType(value[0]) {
case SegmentListSubTLVWeight:
t.Weight = &SegmentListWeight{}
if err := t.Weight.DecodeFromBytes(value); err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
p += t.Weight.Len()
value = value[t.Weight.Len():]
continue
case TypeA:
segment = &SegmentTypeA{}
if err := segment.DecodeFromBytes(value); err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
case TypeB:
segment = &SegmentTypeB{}
if err := segment.DecodeFromBytes(value); err != nil {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, err.Error())
}
case TypeC:
fallthrough
case TypeD:
fallthrough
case TypeE:
fallthrough
case TypeF:
fallthrough
case TypeG:
fallthrough
case TypeH:
fallthrough
case TypeI:
fallthrough
case TypeJ:
fallthrough
case TypeK:
msg := fmt.Sprintf("Invalid SR Policy Segment SubTLV %d is not yet supported", value[0])
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
default:
msg := fmt.Sprintf("Invalid SR Policy Segment List SubTLV %d", value[0])
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, msg)
}
segments = append(segments, segment)
p += segment.Len()
value = value[segment.Len():]
}
if len(segments) == 0 {
t.Segments = nil
} else {
t.Segments = segments
}
return nil
}
func (t *TunnelEncapSubTLVSRSegmentList) Serialize() ([]byte, error) {
buf := make([]byte, 0)
// Add reserved byte
buf = append(buf, 0x0)
if t.Weight != nil {
wbuf, err := t.Weight.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, wbuf...)
}
for _, s := range t.Segments {
sbuf, err := s.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, sbuf...)
}
return t.TunnelEncapSubTLV.Serialize(buf[:])
}
func (t *TunnelEncapSubTLVSRSegmentList) String() string {
msg := "{"
if t.Weight != nil {
msg += "Weight: " + t.Weight.String() + ","
}
msg += "Segment List: [ "
for _, s := range t.Segments {
msg += s.String() + ","
}
msg += " ] }"
return msg
}
func (t *TunnelEncapSubTLVSRSegmentList) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type EncapSubTLVType `json:"type"`
Weight *SegmentListWeight
Segments []TunnelEncapSubTLVInterface
}{
Type: t.Type,
Weight: t.Weight,
Segments: t.Segments,
})
}
// Code generated by "stringer -type=SRBehavior"; DO NOT EDIT.
package bgp
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[RESERVED-0]
_ = x[END-1]
_ = x[END_WITH_PSP-2]
_ = x[END_WITH_USP-3]
_ = x[END_WITH_PSP_USP-4]
_ = x[ENDX-5]
_ = x[ENDX_WITH_PSP-6]
_ = x[ENDX_WITH_USP-7]
_ = x[ENDX_WITH_PSP_USP-8]
_ = x[ENDT-9]
_ = x[ENDT_WITH_PSP-10]
_ = x[ENDT_WITH_USP-11]
_ = x[ENDT_WITH_PSP_USP-12]
_ = x[END_B6_ENCAPS-14]
_ = x[END_BM-15]
_ = x[END_DX6-16]
_ = x[END_DX4-17]
_ = x[END_DT6-18]
_ = x[END_DT4-19]
_ = x[END_DT46-20]
_ = x[END_DX2-21]
_ = x[END_DX2V-22]
_ = x[END_DT2U-23]
_ = x[END_DT2M-24]
_ = x[END_B6_ENCAPS_Red-27]
_ = x[END_WITH_USD-28]
_ = x[END_WITH_PSP_USD-29]
_ = x[END_WITH_USP_USD-30]
_ = x[END_WITH_PSP_USP_USD-31]
_ = x[ENDX_WITH_USD-32]
_ = x[ENDX_WITH_PSP_USD-33]
_ = x[ENDX_WITH_USP_USD-34]
_ = x[ENDX_WITH_PSP_USP_USD-35]
_ = x[ENDT_WITH_USD-36]
_ = x[ENDT_WITH_PSP_USD-37]
_ = x[ENDT_WITH_USP_USD-38]
_ = x[ENDT_WITH_PSP_USP_USD-39]
_ = x[ENDM_GTP6D-69]
_ = x[ENDM_GTP6DI-70]
_ = x[ENDM_GTP6E-71]
_ = x[ENDM_GTP4E-72]
}
const (
_SRBehavior_name_0 = "RESERVEDENDEND_WITH_PSPEND_WITH_USPEND_WITH_PSP_USPENDXENDX_WITH_PSPENDX_WITH_USPENDX_WITH_PSP_USPENDTENDT_WITH_PSPENDT_WITH_USPENDT_WITH_PSP_USP"
_SRBehavior_name_1 = "END_B6_ENCAPSEND_BMEND_DX6END_DX4END_DT6END_DT4END_DT46END_DX2END_DX2VEND_DT2UEND_DT2M"
_SRBehavior_name_2 = "END_B6_ENCAPS_RedEND_WITH_USDEND_WITH_PSP_USDEND_WITH_USP_USDEND_WITH_PSP_USP_USDENDX_WITH_USDENDX_WITH_PSP_USDENDX_WITH_USP_USDENDX_WITH_PSP_USP_USDENDT_WITH_USDENDT_WITH_PSP_USDENDT_WITH_USP_USDENDT_WITH_PSP_USP_USD"
_SRBehavior_name_3 = "ENDM_GTP6DENDM_GTP6DIENDM_GTP6EENDM_GTP4E"
)
var (
_SRBehavior_index_0 = [...]uint8{0, 8, 11, 23, 35, 51, 55, 68, 81, 98, 102, 115, 128, 145}
_SRBehavior_index_1 = [...]uint8{0, 13, 19, 26, 33, 40, 47, 55, 62, 70, 78, 86}
_SRBehavior_index_2 = [...]uint8{0, 17, 29, 45, 61, 81, 94, 111, 128, 149, 162, 179, 196, 217}
_SRBehavior_index_3 = [...]uint8{0, 10, 21, 31, 41}
)
func (i SRBehavior) String() string {
switch {
case 0 <= i && i <= 12:
return _SRBehavior_name_0[_SRBehavior_index_0[i]:_SRBehavior_index_0[i+1]]
case 14 <= i && i <= 24:
i -= 14
return _SRBehavior_name_1[_SRBehavior_index_1[i]:_SRBehavior_index_1[i+1]]
case 27 <= i && i <= 39:
i -= 27
return _SRBehavior_name_2[_SRBehavior_index_2[i]:_SRBehavior_index_2[i+1]]
case 69 <= i && i <= 72:
i -= 69
return _SRBehavior_name_3[_SRBehavior_index_3[i]:_SRBehavior_index_3[i+1]]
default:
return "SRBehavior(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
package bgp
import (
"fmt"
"math"
"net"
"net/netip"
"slices"
"strconv"
)
// Validator for BGPUpdate
func ValidateUpdateMsg(m *BGPUpdate, rfs map[Family]BGPAddPathMode, isEBGP bool, isConfed bool, loopbackNextHopAllowed bool) (bool, error) {
var strongestError error
eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
eSubCodeAttrList := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
eSubCodeMissing := uint8(BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE)
if len(m.NLRI) > 0 || len(m.WithdrawnRoutes) > 0 {
if _, ok := rfs[RF_IPv4_UC]; !ok {
return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not available for session", RF_IPv4_UC))
}
}
seen := make(map[BGPAttrType]PathAttributeInterface)
newAttrs := make([]PathAttributeInterface, 0, len(seen))
// check path attribute
for _, a := range m.PathAttributes {
// check duplication
if _, ok := seen[a.GetType()]; !ok {
seen[a.GetType()] = a
newAttrs = append(newAttrs, a)
// check specific path attribute
ok, err := ValidateAttribute(a, rfs, isEBGP, isConfed, loopbackNextHopAllowed)
if !ok {
msgErr := err.(*MessageError)
if msgErr.ErrorHandling == ERROR_HANDLING_SESSION_RESET {
return false, err
} else if msgErr.Stronger(strongestError) {
strongestError = err
}
}
} else if a.GetType() == BGP_ATTR_TYPE_MP_REACH_NLRI || a.GetType() == BGP_ATTR_TYPE_MP_UNREACH_NLRI {
eMsg := "the path attribute appears twice. Type : " + strconv.Itoa(int(a.GetType()))
return false, NewMessageError(eCode, eSubCodeAttrList, nil, eMsg)
} else {
eMsg := "the path attribute appears twice. Type : " + strconv.Itoa(int(a.GetType()))
e := NewMessageErrorWithErrorHandling(eCode, eSubCodeAttrList, nil, ERROR_HANDLING_ATTRIBUTE_DISCARD, nil, eMsg)
if e.(*MessageError).Stronger(strongestError) {
strongestError = e
}
}
}
m.PathAttributes = newAttrs
if _, ok := seen[BGP_ATTR_TYPE_MP_REACH_NLRI]; ok || len(m.NLRI) > 0 {
// check the existence of well-known mandatory attributes
exist := func(attrs []BGPAttrType) (bool, BGPAttrType) {
for _, attr := range attrs {
_, ok := seen[attr]
if !ok {
return false, attr
}
}
return true, 0
}
mandatory := []BGPAttrType{BGP_ATTR_TYPE_ORIGIN, BGP_ATTR_TYPE_AS_PATH}
if len(m.NLRI) > 0 {
mandatory = append(mandatory, BGP_ATTR_TYPE_NEXT_HOP)
}
if ok, t := exist(mandatory); !ok {
eMsg := "well-known mandatory attributes are not present. type : " + strconv.Itoa(int(t))
data := []byte{byte(t)}
e := NewMessageErrorWithErrorHandling(eCode, eSubCodeMissing, data, ERROR_HANDLING_TREAT_AS_WITHDRAW, nil, eMsg)
if e.(*MessageError).Stronger(strongestError) {
strongestError = e
}
}
}
return strongestError == nil, strongestError
}
func ValidateAttribute(a PathAttributeInterface, rfs map[Family]BGPAddPathMode, isEBGP bool, isConfed bool, loopbackNextHopAllowed bool) (bool, error) {
var strongestError error
eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
eSubCodeBadOrigin := uint8(BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE)
eSubCodeBadNextHop := uint8(BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE)
eSubCodeUnknown := uint8(BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE)
eSubCodeMalformedAspath := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH)
checkPrefix := func(family Family, l []PathNLRI) error {
if _, ok := rfs[family]; !ok {
return NewMessageError(0, 0, nil, fmt.Sprintf("Address-family %s not available for this session", family))
}
for _, prefix := range l {
switch family {
case RF_FS_IPv4_UC, RF_FS_IPv6_UC, RF_FS_IPv4_VPN, RF_FS_IPv6_VPN, RF_FS_L2_VPN:
t := BGPFlowSpecType(0)
for _, v := range prefix.NLRI.(*FlowSpecNLRI).Value {
if v.Type() <= t {
return NewMessageError(0, 0, nil, fmt.Sprintf("%s nlri violate strict type ordering", family))
}
t = v.Type()
}
}
}
return nil
}
switch p := a.(type) {
case *PathAttributeMpUnreachNLRI:
if err := checkPrefix(NewFamily(p.AFI, p.SAFI), p.Value); err != nil {
return false, err
}
case *PathAttributeMpReachNLRI:
if err := checkPrefix(NewFamily(p.AFI, p.SAFI), p.Value); err != nil {
return false, err
}
case *PathAttributeOrigin:
v := p.Value
if v != BGP_ORIGIN_ATTR_TYPE_IGP &&
v != BGP_ORIGIN_ATTR_TYPE_EGP &&
v != BGP_ORIGIN_ATTR_TYPE_INCOMPLETE {
data, _ := a.Serialize()
eMsg := "invalid origin attribute. value : " + strconv.Itoa(int(v))
e := NewMessageErrorWithErrorHandling(eCode, eSubCodeBadOrigin, data, getErrorHandlingFromPathAttribute(p.GetType()), nil, eMsg)
if e.(*MessageError).Stronger(strongestError) {
strongestError = e
}
}
case *PathAttributeNextHop:
isZero := func(ip net.IP) bool {
res := ip[0] & 0xff
return res == 0x00
}
isClassDorE := func(ip net.IP) bool {
if ip.To4() == nil {
// needs to verify ipv6 too?
return false
}
res := ip[0] & 0xe0
return res == 0xe0
}
addr := net.IP(p.Value.AsSlice())
// check IP address represents host address
if !loopbackNextHopAllowed && p.Value.IsLoopback() || isZero(addr) || isClassDorE(addr) {
eMsg := "invalid nexthop address"
data, _ := a.Serialize()
e := NewMessageErrorWithErrorHandling(eCode, eSubCodeBadNextHop, data, getErrorHandlingFromPathAttribute(p.GetType()), nil, eMsg)
if e.(*MessageError).Stronger(strongestError) {
strongestError = e
}
}
case *PathAttributeAsPath:
if isEBGP {
if isConfed {
if segType := p.Value[0].GetType(); segType != BGP_ASPATH_ATTR_TYPE_CONFED_SEQ {
return false, NewMessageError(eCode, eSubCodeMalformedAspath, nil, fmt.Sprintf("segment type is not confederation seq (%d)", segType))
}
} else {
for _, param := range p.Value {
segType := param.GetType()
switch segType {
case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
err := NewMessageErrorWithErrorHandling(
eCode, eSubCodeMalformedAspath, nil, getErrorHandlingFromPathAttribute(p.GetType()), nil, fmt.Sprintf("segment type confederation(%d) found", segType))
if err.(*MessageError).Stronger(strongestError) {
strongestError = err
}
}
}
}
}
case *PathAttributeLargeCommunities:
uniq := make([]*LargeCommunity, 0, len(p.Values))
for _, x := range p.Values {
found := slices.ContainsFunc(uniq, x.Eq)
if !found {
uniq = append(uniq, x)
}
}
p.Values = uniq
case *PathAttributeUnknown:
if p.GetFlags()&BGP_ATTR_FLAG_OPTIONAL == 0 {
eMsg := fmt.Sprintf("unrecognized well-known attribute %s", p.GetType())
data, _ := a.Serialize()
return false, NewMessageError(eCode, eSubCodeUnknown, data, eMsg)
}
}
return strongestError == nil, strongestError
}
// validator for PathAttribute
func validatePathAttributeFlags(t BGPAttrType, flags BGPAttrFlag) string {
/*
* RFC 4271 P.17 For well-known attributes, the Transitive bit MUST be set to 1.
*/
if flags&BGP_ATTR_FLAG_OPTIONAL == 0 && flags&BGP_ATTR_FLAG_TRANSITIVE == 0 {
eMsg := fmt.Sprintf("well-known attribute %s must have transitive flag 1", t)
return eMsg
}
/*
* RFC 4271 P.17 For well-known attributes and for optional non-transitive attributes,
* the Partial bit MUST be set to 0.
*/
if flags&BGP_ATTR_FLAG_OPTIONAL == 0 && flags&BGP_ATTR_FLAG_PARTIAL != 0 {
eMsg := fmt.Sprintf("well-known attribute %s must have partial bit 0", t)
return eMsg
}
if flags&BGP_ATTR_FLAG_OPTIONAL != 0 && flags&BGP_ATTR_FLAG_TRANSITIVE == 0 && flags&BGP_ATTR_FLAG_PARTIAL != 0 {
eMsg := fmt.Sprintf("optional non-transitive attribute %s must have partial bit 0", t)
return eMsg
}
// check flags are correct
if f, ok := PathAttrFlags[t]; ok {
if f != flags & ^BGP_ATTR_FLAG_EXTENDED_LENGTH & ^BGP_ATTR_FLAG_PARTIAL {
eMsg := fmt.Sprintf("flags are invalid. attribute type: %s, expect: %s, actual: %s", t, f, flags)
return eMsg
}
}
return ""
}
func validateAsPathValueBytes(data []byte) (bool, error) {
eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH)
if len(data)%2 != 0 {
return false, NewMessageError(eCode, eSubCode, nil, "AS PATH length is not odd")
}
tryParse := func(data []byte, use4byte bool) (bool, error) {
for len(data) > 0 {
if len(data) < 2 {
return false, NewMessageError(eCode, eSubCode, nil, "AS PATH header is short")
}
segType := data[0]
if segType == 0 || segType > 4 {
return false, NewMessageError(eCode, eSubCode, nil, "unknown AS_PATH seg type")
}
asNum := data[1]
data = data[2:]
if asNum == 0 || int(asNum) > math.MaxUint8 {
return false, NewMessageError(eCode, eSubCode, nil, "AS PATH the number of AS is incorrect")
}
segLength := int(asNum)
if use4byte {
segLength *= 4
} else {
segLength *= 2
}
if segLength > len(data) {
return false, NewMessageError(eCode, eSubCode, nil, "seg length is short")
}
data = data[segLength:]
}
return true, nil
}
_, err := tryParse(data, true)
if err == nil {
return true, nil
}
_, err = tryParse(data, false)
if err == nil {
return false, nil
}
return false, NewMessageError(eCode, eSubCode, nil, "can't parse AS_PATH")
}
func ValidateOpenMsg(m *BGPOpen, expectedAS uint32, myAS uint32, myId netip.Addr) (uint32, error) {
if m.Version != 4 {
return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER, nil, fmt.Sprintf("unsupported version %d", m.Version))
}
as := uint32(m.MyAS)
for _, p := range m.OptParams {
paramCap, y := p.(*OptionParameterCapability)
if !y {
continue
}
for _, c := range paramCap.Capability {
if c.Code() == BGP_CAP_FOUR_OCTET_AS_NUMBER {
cap := c.(*CapFourOctetASNumber)
as = cap.CapValue
}
}
}
// rfc6286 (Autonomous-System-Wide Unique BGP Identifier for BGP-4)
// If the BGP Identifier field of the OPEN message is zero, or if it
// is the same as the BGP Identifier of the local BGP speaker and the
// message is from an internal peer, then the Error Subcode is set to
// "Bad BGP Identifier".
routerId := m.ID
if routerId.IsUnspecified() {
return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_BAD_BGP_IDENTIFIER, nil, fmt.Sprintf("bad BGP identifier %s (0.0.0.0)", routerId.String()))
}
if as == myAS && routerId == myId {
return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_BAD_BGP_IDENTIFIER, nil, fmt.Sprintf("bad BGP identifier %s", routerId.String()))
}
if expectedAS != 0 && as != expectedAS {
return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_BAD_PEER_AS, nil, fmt.Sprintf("as number mismatch expected %d, received %d", expectedAS, as))
}
if m.HoldTime < 3 && m.HoldTime != 0 {
return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME, nil, fmt.Sprintf("unacceptable hold time %d", m.HoldTime))
}
return as, nil
}
package bgp
import (
"encoding/binary"
"encoding/json"
"fmt"
)
// VPLSNLRI represents an NLRI for VPLS, as defined in [RFC 4761, section 3.2.2].
//
// Path Attribute - MP_REACH_NLRI
// Flags: 0x90, Optional, Extended-Length, Non-transitive, Complete
// Type Code: MP_REACH_NLRI (14)
// Length: 28
// Address family identifier (AFI): Layer-2 VPN (25)
// Subsequent address family identifier (SAFI): VPLS (65)
// Next hop: 192.0.2.7
// IPv4 Address: 192.0.2.7
// Number of Subnetwork points of attachment (SNPA): 0
// Network Layer Reachability Information (NLRI)
// Length: 17
// RD: 65017:104
// CE-ID: 1
// Label Block Offset: 1
// Label Block Size: 8
// Label Block Base: 800000 (bottom)
//
// [RFC 4761, section 3.2.2]: https://www.rfc-editor.org/rfc/rfc4761.html#section-3.2.2.
type VPLSNLRI struct {
VEID uint16
VEBlockOffset uint16
VEBlockSize uint16
LabelBlockBase uint32
rd RouteDistinguisherInterface
}
func (n *VPLSNLRI) decodeFromBytes(data []byte, options ...*MarshallingOption) error {
/*
RFC6074 Section 7 BGP-AD and VPLS-BGP Interoperability
Both BGP-AD and VPLS-BGP [RFC4761] use the same AFI/SAFI. In order
for both BGP-AD and VPLS-BGP to co-exist, the NLRI length must be
used as a demultiplexer.
The BGP-AD NLRI has an NLRI length of 12 bytes, containing only an
8-byte RD and a 4-byte VSI-ID. VPLS-BGP [RFC4761] uses a 17-byte
NLRI length. Therefore, implementations of BGP-AD must ignore NLRI
that are greater than 12 bytes.
*/
if len(data) < 2 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all VPLS NLRI bytes available")
}
length := int(binary.BigEndian.Uint16(data[:2]))
if len(data) < length+2 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all VPLS NLRI bytes available")
}
if length == 12 { // BGP-AD
// BGP-AD is not supported yet
return nil
}
if len(data) < 19 {
return NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "Not all VPLS NLRI bytes available")
}
// VPLS-BGP
n.rd = GetRouteDistinguisher(data[2:10])
n.VEID = binary.BigEndian.Uint16(data[10:12])
n.VEBlockOffset = binary.BigEndian.Uint16(data[12:14])
n.VEBlockSize = binary.BigEndian.Uint16(data[14:16])
labelBlockBase := uint32(data[16])<<16 | uint32(data[17])<<8 | uint32(data[18])
n.LabelBlockBase = labelBlockBase >> 4
return nil
}
func (n *VPLSNLRI) Serialize(options ...*MarshallingOption) ([]byte, error) {
buf := make([]byte, 16)
labelBaseBuf := make([]byte, 3)
binary.BigEndian.PutUint16(buf[:2], 17)
rdbuf, err := n.rd.Serialize()
if err != nil {
return nil, err
}
copy(buf[2:10], rdbuf[:8])
binary.BigEndian.PutUint16(buf[10:12], n.VEID)
binary.BigEndian.PutUint16(buf[12:14], n.VEBlockOffset)
binary.BigEndian.PutUint16(buf[14:16], n.VEBlockSize)
labelBlockBase := n.LabelBlockBase << 4
labelBaseBuf[0] = byte(labelBlockBase >> 16 & 0xff)
labelBaseBuf[1] = byte(labelBlockBase >> 8 & 0xff)
labelBaseBuf[2] = byte(labelBlockBase & 0xff)
return append(buf, labelBaseBuf...), nil
}
func (n *VPLSNLRI) Len(options ...*MarshallingOption) int {
// Length (2) + Route Distinguisher (8) + VE ID (2) + VE Block Offset (2)
// + VE Block Size (2) + Label Block Base (3)
return 19
}
func (n *VPLSNLRI) String() string {
return fmt.Sprintf("%s:%d:%d (Block Size: %d, Label Block Base: %d)", n.rd, n.VEID, n.VEBlockOffset, n.VEBlockSize, n.LabelBlockBase)
}
func (n *VPLSNLRI) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
RD RouteDistinguisherInterface `json:"rd"`
VEID uint16 `json:"id"`
VEBlockOffset uint16 `json:"blockoffset"`
VEBlockSize uint16 `json:"blocksize"`
LabelBlockBase uint32 `json:"labelblockbase"`
}{
RD: n.rd,
VEID: n.VEID,
VEBlockOffset: n.VEBlockOffset,
VEBlockSize: n.VEBlockSize,
LabelBlockBase: n.LabelBlockBase,
})
}
func (n *VPLSNLRI) RD() RouteDistinguisherInterface {
return n.rd
}
func (l *VPLSNLRI) Flat() map[string]string {
return map[string]string{}
}
func NewVPLSNLRI(rd RouteDistinguisherInterface, id uint16, blockOffset uint16, blockSize uint16, labelBlockBase uint32) *VPLSNLRI {
return &VPLSNLRI{
rd: rd,
VEID: id,
VEBlockOffset: blockOffset,
VEBlockSize: blockSize,
LabelBlockBase: labelBlockBase,
}
}
// VPLSExtended repsents BGP VPLS Extended Community as described in [RFC 4761, section 3.2.4].
//
// Path Attribute - EXTENDED_COMMUNITIES
// Flags: 0xc0, Optional, Transitive, Complete
// Type Code: EXTENDED_COMMUNITIES (16)
// Length: 16
// Carried extended communities: (2 communities)
// Route Target: 65017:104 [Transitive 2-Octet AS-Specific]
// Layer2 Info: [Generic Transitive Experimental Use]
// Type: Generic Transitive Experimental Use (0x80)
// Subtype (Experimental): Layer2 Info (0x0a)
// Encaps Type: VPLS (19)
// Control Flags: 0x00
// Layer-2 MTU: 0
//
// [RFC 4761, section 3.2.4]: https://www.rfc-editor.org/rfc/rfc4761.html#section-3.2.4
type VPLSExtended struct {
SubType ExtendedCommunityAttrSubType
ControlFlags uint8
MTU uint16
}
func (e *VPLSExtended) Serialize() ([]byte, error) {
buf := make([]byte, 8)
buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL)
buf[1] = byte(EC_SUBTYPE_L2_INFO)
buf[2] = byte(LAYER2ENCAPSULATION_TYPE_VPLS)
buf[3] = e.ControlFlags
binary.BigEndian.PutUint16(buf[4:6], e.MTU)
// 6-8: reserved, but Juniper says this is "site preference"
return buf, nil
}
func (e *VPLSExtended) String() string {
return fmt.Sprintf("encaps: VPLS, control flags:0x%x, mtu: %d", e.ControlFlags, e.MTU)
}
func (e *VPLSExtended) 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 *VPLSExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_L2_INFO
}
func (e *VPLSExtended) Flat() map[string]string {
return map[string]string{}
}
func NewVPLSExtended(flags uint8, mtu uint16) *VPLSExtended {
return &VPLSExtended{
SubType: EC_SUBTYPE_L2_INFO,
ControlFlags: flags,
MTU: mtu,
}
}
// Copyright (C) 2014,2015 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 bmp
import (
"encoding/binary"
"errors"
"fmt"
"math"
"net/netip"
"github.com/osrg/gobgp/v4/pkg/packet/bgp"
)
type BMPHeader struct {
Version uint8
Length uint32
Type uint8
}
const (
BMP_VERSION = 3
BMP_HEADER_SIZE = 6
BMP_PEER_HEADER_SIZE = 42
)
const (
BMP_DEFAULT_PORT = 11019
)
const (
BMP_PEER_TYPE_GLOBAL uint8 = iota
BMP_PEER_TYPE_L3VPN
BMP_PEER_TYPE_LOCAL
BMP_PEER_TYPE_LOCAL_RIB
)
const (
BMP_PEER_FLAG_IPV6 = 1 << 7
BMP_PEER_FLAG_POST_POLICY = 1 << 6
BMP_PEER_FLAG_TWO_AS = 1 << 5
BMP_PEER_FLAG_ADJ_RIB_TYP = 1 << 4
)
func (h *BMPHeader) DecodeFromBytes(data []byte) error {
if len(data) < BMP_HEADER_SIZE {
return errors.New("invalid BMP header length")
}
h.Version = data[0]
if data[0] != BMP_VERSION {
return fmt.Errorf("error version")
}
h.Length = binary.BigEndian.Uint32(data[1:5])
h.Type = data[5]
return nil
}
func (h *BMPHeader) Serialize() ([]byte, error) {
buf := make([]byte, BMP_HEADER_SIZE)
buf[0] = h.Version
binary.BigEndian.PutUint32(buf[1:], h.Length)
buf[5] = h.Type
return buf, nil
}
type BMPPeerHeader struct {
PeerType uint8
Flags uint8
PeerDistinguisher uint64
PeerAddress netip.Addr
PeerAS uint32
PeerBGPID netip.Addr
Timestamp float64
}
func NewBMPPeerHeader(t uint8, flags uint8, dist uint64, address netip.Addr, as uint32, id netip.Addr, stamp float64) *BMPPeerHeader {
// TODO: check id is v4
if address.Is6() {
flags |= BMP_PEER_FLAG_IPV6
}
h := &BMPPeerHeader{
PeerType: t,
Flags: flags,
PeerDistinguisher: dist,
PeerAddress: address,
PeerAS: as,
PeerBGPID: id,
Timestamp: stamp,
}
return h
}
func (h *BMPPeerHeader) IsPostPolicy() bool {
if h.Flags&BMP_PEER_FLAG_POST_POLICY != 0 {
return true
} else {
return false
}
}
func (h *BMPPeerHeader) IsAdjRIBOut() bool {
return h.Flags&BMP_PEER_FLAG_ADJ_RIB_TYP != 0
}
func (h *BMPPeerHeader) DecodeFromBytes(data []byte) error {
if len(data) < BMP_PEER_HEADER_SIZE {
return errors.New("invalid BMP Peer header length")
}
h.PeerType = data[0]
h.Flags = data[1]
h.PeerDistinguisher = binary.BigEndian.Uint64(data[2:10])
if h.Flags&BMP_PEER_FLAG_IPV6 != 0 {
h.PeerAddress, _ = netip.AddrFromSlice(data[10:26])
} else {
h.PeerAddress, _ = netip.AddrFromSlice(data[22:26])
}
h.PeerAS = binary.BigEndian.Uint32(data[26:30])
h.PeerBGPID, _ = netip.AddrFromSlice(data[30:34])
timestamp1 := binary.BigEndian.Uint32(data[34:38])
timestamp2 := binary.BigEndian.Uint32(data[38:42])
h.Timestamp = float64(timestamp1) + float64(timestamp2)*math.Pow10(-6)
return nil
}
func (h *BMPPeerHeader) Serialize() ([]byte, error) {
buf := make([]byte, BMP_PEER_HEADER_SIZE)
buf[0] = h.PeerType
buf[1] = h.Flags
binary.BigEndian.PutUint64(buf[2:10], h.PeerDistinguisher)
if h.Flags&BMP_PEER_FLAG_IPV6 != 0 {
copy(buf[10:26], h.PeerAddress.AsSlice())
} else {
copy(buf[22:26], h.PeerAddress.AsSlice())
}
binary.BigEndian.PutUint32(buf[26:30], h.PeerAS)
copy(buf[30:34], h.PeerBGPID.AsSlice())
t1, t2 := math.Modf(h.Timestamp)
t2 = math.Ceil(t2 * math.Pow10(6))
binary.BigEndian.PutUint32(buf[34:38], uint32(t1))
binary.BigEndian.PutUint32(buf[38:42], uint32(t2))
return buf, nil
}
type BMPRouteMonitoring struct {
BGPUpdate *bgp.BGPMessage
BGPUpdatePayload []byte
}
func NewBMPRouteMonitoring(p BMPPeerHeader, update *bgp.BGPMessage) *BMPMessage {
return &BMPMessage{
Header: BMPHeader{
Version: BMP_VERSION,
Type: BMP_MSG_ROUTE_MONITORING,
},
PeerHeader: p,
Body: &BMPRouteMonitoring{
BGPUpdate: update,
},
}
}
func (body *BMPRouteMonitoring) ParseBody(msg *BMPMessage, data []byte, options ...*bgp.MarshallingOption) error {
var err error
body.BGPUpdate, err = bgp.ParseBGPMessage(data, options...)
return err
}
func (body *BMPRouteMonitoring) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
if body.BGPUpdatePayload != nil {
return body.BGPUpdatePayload, nil
}
return body.BGPUpdate.Serialize(options...)
}
const (
BMP_STAT_TYPE_REJECTED = iota
BMP_STAT_TYPE_DUPLICATE_PREFIX
BMP_STAT_TYPE_DUPLICATE_WITHDRAW
BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP
BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP
BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID
BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP
BMP_STAT_TYPE_ADJ_RIB_IN
BMP_STAT_TYPE_LOC_RIB
BMP_STAT_TYPE_PER_AFI_SAFI_ADJ_RIB_IN
BMP_STAT_TYPE_PER_AFI_SAFI_LOC_RIB
BMP_STAT_TYPE_WITHDRAW_UPDATE
BMP_STAT_TYPE_WITHDRAW_PREFIX
BMP_STAT_TYPE_DUPLICATE_UPDATE
BMP_STAT_TYPE_ADJ_RIB_OUT_PRE_POLICY
BMP_STAT_TYPE_ADJ_RIB_OUT_POST_POLICY
BMP_STAT_TYPE_PER_AFI_SAFI_ADJ_RIB_OUT_PRE_POLICY
BMP_STAT_TYPE_PER_AFI_SAFI_ADJ_RIB_OUT_POST_POLICY
)
type BMPStatsTLVInterface interface {
ParseValue([]byte) error
Serialize() ([]byte, error)
}
type BMPStatsTLV struct {
Type uint16
Length uint16
}
type BMPStatsTLV32 struct {
BMPStatsTLV
Value uint32
}
func NewBMPStatsTLV32(t uint16, v uint32) *BMPStatsTLV32 {
return &BMPStatsTLV32{
BMPStatsTLV: BMPStatsTLV{
Type: t,
Length: 4,
},
Value: v,
}
}
func (s *BMPStatsTLV32) ParseValue(data []byte) error {
if s.Length != 4 {
return fmt.Errorf("invalid length: %d bytes (%d bytes expected)", s.Length, 4)
}
s.Value = binary.BigEndian.Uint32(data[:4])
return nil
}
func (s *BMPStatsTLV32) Serialize() ([]byte, error) {
buf := make([]byte, 8)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], 4)
binary.BigEndian.PutUint32(buf[4:8], s.Value)
return buf, nil
}
type BMPStatsTLV64 struct {
BMPStatsTLV
Value uint64
}
func NewBMPStatsTLV64(t uint16, v uint64) *BMPStatsTLV64 {
return &BMPStatsTLV64{
BMPStatsTLV: BMPStatsTLV{
Type: t,
Length: 8,
},
Value: v,
}
}
func (s *BMPStatsTLV64) ParseValue(data []byte) error {
if s.Length != 8 {
return fmt.Errorf("invalid length: %d bytes (%d bytes expected)", s.Length, 8)
}
s.Value = binary.BigEndian.Uint64(data[:8])
return nil
}
func (s *BMPStatsTLV64) Serialize() ([]byte, error) {
buf := make([]byte, 12)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], 8)
binary.BigEndian.PutUint64(buf[4:12], s.Value)
return buf, nil
}
type BMPStatsTLVPerAfiSafi64 struct {
BMPStatsTLV
AFI uint16
SAFI uint8
Value uint64
}
func NewBMPStatsTLVPerAfiSafi64(t uint16, afi uint16, safi uint8, v uint64) *BMPStatsTLVPerAfiSafi64 {
return &BMPStatsTLVPerAfiSafi64{
BMPStatsTLV: BMPStatsTLV{
Type: t,
Length: 11,
},
AFI: afi,
SAFI: safi,
Value: v,
}
}
func (s *BMPStatsTLVPerAfiSafi64) ParseValue(data []byte) error {
if s.Length != 11 {
return fmt.Errorf("invalid length: %d bytes (%d bytes expected)", s.Length, 11)
}
s.AFI = binary.BigEndian.Uint16(data[:2])
s.SAFI = data[2]
s.Value = binary.BigEndian.Uint64(data[3:11])
return nil
}
func (s *BMPStatsTLVPerAfiSafi64) Serialize() ([]byte, error) {
buf := make([]byte, 15)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], 11)
binary.BigEndian.PutUint16(buf[4:6], s.AFI)
buf[6] = s.SAFI
binary.BigEndian.PutUint64(buf[7:15], s.Value)
return buf, nil
}
type BMPStatisticsReport struct {
Count uint32
Stats []BMPStatsTLVInterface
}
func NewBMPStatisticsReport(p BMPPeerHeader, stats []BMPStatsTLVInterface) *BMPMessage {
return &BMPMessage{
Header: BMPHeader{
Version: BMP_VERSION,
Type: BMP_MSG_STATISTICS_REPORT,
},
PeerHeader: p,
Body: &BMPStatisticsReport{
Count: uint32(len(stats)),
Stats: stats,
},
}
}
func (body *BMPStatisticsReport) ParseBody(msg *BMPMessage, data []byte, options ...*bgp.MarshallingOption) error {
body.Count = binary.BigEndian.Uint32(data[:4])
data = data[4:]
for len(data) >= 4 {
tl := BMPStatsTLV{
Type: binary.BigEndian.Uint16(data[:2]),
Length: binary.BigEndian.Uint16(data[2:4]),
}
data = data[4:]
if len(data) < int(tl.Length) {
return fmt.Errorf("value length is not enough: %d bytes (%d bytes expected)", len(data), tl.Length)
}
var s BMPStatsTLVInterface
switch tl.Type {
case BMP_STAT_TYPE_ADJ_RIB_IN, BMP_STAT_TYPE_LOC_RIB, BMP_STAT_TYPE_ADJ_RIB_OUT_PRE_POLICY,
BMP_STAT_TYPE_ADJ_RIB_OUT_POST_POLICY:
s = &BMPStatsTLV64{BMPStatsTLV: tl}
case BMP_STAT_TYPE_PER_AFI_SAFI_ADJ_RIB_IN, BMP_STAT_TYPE_PER_AFI_SAFI_LOC_RIB,
BMP_STAT_TYPE_PER_AFI_SAFI_ADJ_RIB_OUT_PRE_POLICY, BMP_STAT_TYPE_PER_AFI_SAFI_ADJ_RIB_OUT_POST_POLICY:
s = &BMPStatsTLVPerAfiSafi64{BMPStatsTLV: tl}
case BMP_STAT_TYPE_REJECTED, BMP_STAT_TYPE_DUPLICATE_PREFIX,
BMP_STAT_TYPE_DUPLICATE_WITHDRAW, BMP_STAT_TYPE_INV_UPDATE_DUE_TO_CLUSTER_LIST_LOOP,
BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_PATH_LOOP, BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID,
BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP, BMP_STAT_TYPE_WITHDRAW_UPDATE,
BMP_STAT_TYPE_WITHDRAW_PREFIX, BMP_STAT_TYPE_DUPLICATE_UPDATE:
s = &BMPStatsTLV32{BMPStatsTLV: tl}
default:
switch tl.Length {
case 4:
s = &BMPStatsTLV32{BMPStatsTLV: tl}
case 8:
s = &BMPStatsTLV64{BMPStatsTLV: tl}
default:
return fmt.Errorf("value length %d is not known for unknown stat type %d", tl.Length, tl.Type)
}
}
if err := s.ParseValue(data); err != nil {
return err
}
body.Stats = append(body.Stats, s)
data = data[tl.Length:]
}
return nil
}
func (body *BMPStatisticsReport) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
buf := make([]byte, 4)
body.Count = uint32(len(body.Stats))
binary.BigEndian.PutUint32(buf[:4], body.Count)
for _, tlv := range body.Stats {
tlvBuf, err := tlv.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, tlvBuf...)
}
return buf, nil
}
const (
BMP_peerDownByUnknownReason = iota
BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION
BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION
BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION
BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION
BMP_PEER_DOWN_REASON_PEER_DE_CONFIGURED
)
type BMPPeerDownNotification struct {
Reason uint8
BGPNotification *bgp.BGPMessage
Data []byte
}
func NewBMPPeerDownNotification(p BMPPeerHeader, reason uint8, notification *bgp.BGPMessage, data []byte) *BMPMessage {
b := &BMPPeerDownNotification{
Reason: reason,
}
switch reason {
case BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION, BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION:
b.BGPNotification = notification
case BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION:
b.Data = data
default:
}
return &BMPMessage{
Header: BMPHeader{
Version: BMP_VERSION,
Type: BMP_MSG_PEER_DOWN_NOTIFICATION,
},
PeerHeader: p,
Body: b,
}
}
func (body *BMPPeerDownNotification) ParseBody(msg *BMPMessage, data []byte, options ...*bgp.MarshallingOption) error {
body.Reason = data[0]
data = data[1:]
if body.Reason == BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION || body.Reason == BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION {
notification, err := bgp.ParseBGPMessage(data, options...)
if err != nil {
return err
}
body.BGPNotification = notification
} else {
body.Data = data
}
return nil
}
func (body *BMPPeerDownNotification) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
buf := make([]byte, 1)
buf[0] = body.Reason
switch body.Reason {
case BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION, BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION:
if body.BGPNotification != nil {
b, err := body.BGPNotification.Serialize(options...)
if err != nil {
return nil, err
} else {
buf = append(buf, b...)
}
}
default:
if body.Data != nil {
buf = append(buf, body.Data...)
}
}
return buf, nil
}
type BMPPeerUpNotification struct {
LocalAddress netip.Addr
LocalPort uint16
RemotePort uint16
SentOpenMsg *bgp.BGPMessage
ReceivedOpenMsg *bgp.BGPMessage
}
func NewBMPPeerUpNotification(p BMPPeerHeader, lAddr netip.Addr, lPort, rPort uint16, sent, recv *bgp.BGPMessage) *BMPMessage {
b := &BMPPeerUpNotification{
LocalAddress: lAddr,
LocalPort: lPort,
RemotePort: rPort,
SentOpenMsg: sent,
ReceivedOpenMsg: recv,
}
return &BMPMessage{
Header: BMPHeader{
Version: BMP_VERSION,
Type: BMP_MSG_PEER_UP_NOTIFICATION,
},
PeerHeader: p,
Body: b,
}
}
func (body *BMPPeerUpNotification) ParseBody(msg *BMPMessage, data []byte, options ...*bgp.MarshallingOption) error {
if msg.PeerHeader.Flags&BMP_PEER_FLAG_IPV6 != 0 {
body.LocalAddress, _ = netip.AddrFromSlice(data[:16])
} else {
body.LocalAddress, _ = netip.AddrFromSlice(data[12:16])
}
body.LocalPort = binary.BigEndian.Uint16(data[16:18])
body.RemotePort = binary.BigEndian.Uint16(data[18:20])
data = data[20:]
sentopen, err := bgp.ParseBGPMessage(data, options...)
if err != nil {
return err
}
body.SentOpenMsg = sentopen
data = data[body.SentOpenMsg.Header.Len:]
body.ReceivedOpenMsg, err = bgp.ParseBGPMessage(data, options...)
if err != nil {
return err
}
return nil
}
func (body *BMPPeerUpNotification) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
buf := make([]byte, 20)
if body.LocalAddress.Is4() {
copy(buf[12:16], body.LocalAddress.AsSlice())
} else {
copy(buf[:16], body.LocalAddress.AsSlice())
}
binary.BigEndian.PutUint16(buf[16:18], body.LocalPort)
binary.BigEndian.PutUint16(buf[18:20], body.RemotePort)
m, _ := body.SentOpenMsg.Serialize(options...)
buf = append(buf, m...)
m, _ = body.ReceivedOpenMsg.Serialize(options...)
buf = append(buf, m...)
return buf, nil
}
const (
BMP_INIT_TLV_TYPE_STRING = iota
BMP_INIT_TLV_TYPE_SYS_DESCR
BMP_INIT_TLV_TYPE_SYS_NAME
)
type BMPInfoTLVInterface interface {
ParseValue([]byte) error
Serialize() ([]byte, error)
}
type BMPInfoTLV struct {
Type uint16
Length uint16
}
type BMPInfoTLVString struct {
BMPInfoTLV
Value string
}
func NewBMPInfoTLVString(t uint16, v string) *BMPInfoTLVString {
return &BMPInfoTLVString{
BMPInfoTLV: BMPInfoTLV{Type: t},
Value: v,
}
}
func (s *BMPInfoTLVString) ParseValue(data []byte) error {
s.Value = string(data[:s.Length])
return nil
}
func (s *BMPInfoTLVString) Serialize() ([]byte, error) {
s.Length = uint16(len([]byte(s.Value)))
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
buf = append(buf, []byte(s.Value)...)
return buf, nil
}
type BMPInfoTLVUnknown struct {
BMPInfoTLV
Value []byte
}
func NewBMPInfoTLVUnknown(t uint16, v []byte) *BMPInfoTLVUnknown {
return &BMPInfoTLVUnknown{
BMPInfoTLV: BMPInfoTLV{Type: t},
Value: v,
}
}
func (s *BMPInfoTLVUnknown) ParseValue(data []byte) error {
s.Value = data[:s.Length]
return nil
}
func (s *BMPInfoTLVUnknown) Serialize() ([]byte, error) {
s.Length = uint16(len(s.Value))
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
buf = append(buf, s.Value...)
return buf, nil
}
type BMPInitiation struct {
Info []BMPInfoTLVInterface
}
func NewBMPInitiation(info []BMPInfoTLVInterface) *BMPMessage {
return &BMPMessage{
Header: BMPHeader{
Version: BMP_VERSION,
Type: BMP_MSG_INITIATION,
},
Body: &BMPInitiation{
Info: info,
},
}
}
func (body *BMPInitiation) ParseBody(msg *BMPMessage, data []byte, options ...*bgp.MarshallingOption) error {
for len(data) >= 4 {
tl := BMPInfoTLV{
Type: binary.BigEndian.Uint16(data[:2]),
Length: binary.BigEndian.Uint16(data[2:4]),
}
data = data[4:]
if len(data) < int(tl.Length) {
return fmt.Errorf("value length is not enough: %d bytes (%d bytes expected)", len(data), tl.Length)
}
var tlv BMPInfoTLVInterface
switch tl.Type {
case BMP_INIT_TLV_TYPE_STRING, BMP_INIT_TLV_TYPE_SYS_DESCR, BMP_INIT_TLV_TYPE_SYS_NAME:
tlv = &BMPInfoTLVString{BMPInfoTLV: tl}
default:
tlv = &BMPInfoTLVUnknown{BMPInfoTLV: tl}
}
if err := tlv.ParseValue(data); err != nil {
return err
}
body.Info = append(body.Info, tlv)
data = data[tl.Length:]
}
return nil
}
func (body *BMPInitiation) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
buf := make([]byte, 0)
for _, tlv := range body.Info {
b, err := tlv.Serialize()
if err != nil {
return buf, err
}
buf = append(buf, b...)
}
return buf, nil
}
const (
BMP_TERM_TLV_TYPE_STRING = iota
BMP_TERM_TLV_TYPE_REASON
)
const (
BMP_TERM_REASON_ADMIN = iota
BMP_TERM_REASON_UNSPEC
BMP_TERM_REASON_OUT_OF_RESOURCES
BMP_TERM_REASON_REDUNDANT_CONNECTION
BMP_TERM_REASON_PERMANENTLY_ADMIN
)
type BMPTermTLVInterface interface {
ParseValue([]byte) error
Serialize() ([]byte, error)
}
type BMPTermTLV struct {
Type uint16
Length uint16
}
type BMPTermTLVString struct {
BMPTermTLV
Value string
}
func NewBMPTermTLVString(t uint16, v string) *BMPTermTLVString {
return &BMPTermTLVString{
BMPTermTLV: BMPTermTLV{Type: t},
Value: v,
}
}
func (s *BMPTermTLVString) ParseValue(data []byte) error {
s.Value = string(data[:s.Length])
return nil
}
func (s *BMPTermTLVString) Serialize() ([]byte, error) {
s.Length = uint16(len([]byte(s.Value)))
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
buf = append(buf, []byte(s.Value)...)
return buf, nil
}
type BMPTermTLV16 struct {
BMPTermTLV
Value uint16
}
func NewBMPTermTLV16(t uint16, v uint16) *BMPTermTLV16 {
return &BMPTermTLV16{
BMPTermTLV: BMPTermTLV{Type: t},
Value: v,
}
}
func (s *BMPTermTLV16) ParseValue(data []byte) error {
s.Value = binary.BigEndian.Uint16(data[:2])
return nil
}
func (s *BMPTermTLV16) Serialize() ([]byte, error) {
s.Length = 2
buf := make([]byte, 6)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
binary.BigEndian.PutUint16(buf[4:6], s.Value)
return buf, nil
}
type BMPTermTLVUnknown struct {
BMPTermTLV
Value []byte
}
func NewBMPTermTLVUnknown(t uint16, v []byte) *BMPTermTLVUnknown {
return &BMPTermTLVUnknown{
BMPTermTLV: BMPTermTLV{Type: t},
Value: v,
}
}
func (s *BMPTermTLVUnknown) ParseValue(data []byte) error {
s.Value = data[:s.Length]
return nil
}
func (s *BMPTermTLVUnknown) Serialize() ([]byte, error) {
s.Length = uint16(len(s.Value))
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
buf = append(buf, s.Value...)
return buf, nil
}
type BMPTermination struct {
Info []BMPTermTLVInterface
}
func NewBMPTermination(info []BMPTermTLVInterface) *BMPMessage {
return &BMPMessage{
Header: BMPHeader{
Version: BMP_VERSION,
Type: BMP_MSG_TERMINATION,
},
Body: &BMPTermination{
Info: info,
},
}
}
func (body *BMPTermination) ParseBody(msg *BMPMessage, data []byte, options ...*bgp.MarshallingOption) error {
for len(data) >= 4 {
tl := BMPTermTLV{
Type: binary.BigEndian.Uint16(data[:2]),
Length: binary.BigEndian.Uint16(data[2:4]),
}
data = data[4:]
if len(data) < int(tl.Length) {
return fmt.Errorf("value length is not enough: %d bytes (%d bytes expected)", len(data), tl.Length)
}
var tlv BMPTermTLVInterface
switch tl.Type {
case BMP_TERM_TLV_TYPE_STRING:
tlv = &BMPTermTLVString{BMPTermTLV: tl}
case BMP_TERM_TLV_TYPE_REASON:
tlv = &BMPTermTLV16{BMPTermTLV: tl}
default:
tlv = &BMPTermTLVUnknown{BMPTermTLV: tl}
}
if err := tlv.ParseValue(data); err != nil {
return err
}
body.Info = append(body.Info, tlv)
data = data[tl.Length:]
}
return nil
}
func (body *BMPTermination) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
buf := make([]byte, 0)
for _, tlv := range body.Info {
b, err := tlv.Serialize()
if err != nil {
return buf, err
}
buf = append(buf, b...)
}
return buf, nil
}
const (
BMP_ROUTE_MIRRORING_TLV_TYPE_BGP_MSG = iota
BMP_ROUTE_MIRRORING_TLV_TYPE_INFO
)
const (
BMP_ROUTE_MIRRORING_INFO_ERR_PDU = iota
BMP_ROUTE_MIRRORING_INFO_MSG_LOST
)
type BMPRouteMirrTLVInterface interface {
ParseValue([]byte) error
Serialize() ([]byte, error)
}
type BMPRouteMirrTLV struct {
Type uint16
Length uint16
}
type BMPRouteMirrTLVBGPMsg struct {
BMPRouteMirrTLV
Value *bgp.BGPMessage
}
func NewBMPRouteMirrTLVBGPMsg(t uint16, v *bgp.BGPMessage) *BMPRouteMirrTLVBGPMsg {
return &BMPRouteMirrTLVBGPMsg{
BMPRouteMirrTLV: BMPRouteMirrTLV{Type: t},
Value: v,
}
}
func (s *BMPRouteMirrTLVBGPMsg) ParseValue(data []byte) error {
v, err := bgp.ParseBGPMessage(data)
if err != nil {
return err
}
s.Value = v
return nil
}
func (s *BMPRouteMirrTLVBGPMsg) Serialize() ([]byte, error) {
m, err := s.Value.Serialize()
if err != nil {
return nil, err
}
s.Length = uint16(len(m))
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
buf = append(buf, m...)
return buf, nil
}
type BMPRouteMirrTLV16 struct {
BMPRouteMirrTLV
Value uint16
}
func NewBMPRouteMirrTLV16(t uint16, v uint16) *BMPRouteMirrTLV16 {
return &BMPRouteMirrTLV16{
BMPRouteMirrTLV: BMPRouteMirrTLV{Type: t},
Value: v,
}
}
func (s *BMPRouteMirrTLV16) ParseValue(data []byte) error {
s.Value = binary.BigEndian.Uint16(data[:2])
return nil
}
func (s *BMPRouteMirrTLV16) Serialize() ([]byte, error) {
s.Length = 2
buf := make([]byte, 6)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
binary.BigEndian.PutUint16(buf[4:6], s.Value)
return buf, nil
}
type BMPRouteMirrTLVUnknown struct {
BMPRouteMirrTLV
Value []byte
}
func NewBMPRouteMirrTLVUnknown(t uint16, v []byte) *BMPRouteMirrTLVUnknown {
return &BMPRouteMirrTLVUnknown{
BMPRouteMirrTLV: BMPRouteMirrTLV{Type: t},
Value: v,
}
}
func (s *BMPRouteMirrTLVUnknown) ParseValue(data []byte) error {
s.Value = data[:s.Length]
return nil
}
func (s *BMPRouteMirrTLVUnknown) Serialize() ([]byte, error) {
s.Length = uint16(len(s.Value))
buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[:2], s.Type)
binary.BigEndian.PutUint16(buf[2:4], s.Length)
buf = append(buf, s.Value...)
return buf, nil
}
type BMPRouteMirroring struct {
Info []BMPRouteMirrTLVInterface
}
func NewBMPRouteMirroring(p BMPPeerHeader, info []BMPRouteMirrTLVInterface) *BMPMessage {
return &BMPMessage{
Header: BMPHeader{
Version: BMP_VERSION,
Type: BMP_MSG_ROUTE_MIRRORING,
},
PeerHeader: p,
Body: &BMPRouteMirroring{
Info: info,
},
}
}
func (body *BMPRouteMirroring) ParseBody(msg *BMPMessage, data []byte, options ...*bgp.MarshallingOption) error {
for len(data) >= 4 {
tl := BMPRouteMirrTLV{
Type: binary.BigEndian.Uint16(data[:2]),
Length: binary.BigEndian.Uint16(data[2:4]),
}
data = data[4:]
if len(data) < int(tl.Length) {
return fmt.Errorf("value length is not enough: %d bytes (%d bytes expected)", len(data), tl.Length)
}
var tlv BMPRouteMirrTLVInterface
switch tl.Type {
case BMP_ROUTE_MIRRORING_TLV_TYPE_BGP_MSG:
tlv = &BMPRouteMirrTLVBGPMsg{BMPRouteMirrTLV: tl}
case BMP_ROUTE_MIRRORING_TLV_TYPE_INFO:
tlv = &BMPRouteMirrTLV16{BMPRouteMirrTLV: tl}
default:
tlv = &BMPRouteMirrTLVUnknown{BMPRouteMirrTLV: tl}
}
if err := tlv.ParseValue(data); err != nil {
return err
}
body.Info = append(body.Info, tlv)
data = data[tl.Length:]
}
return nil
}
func (body *BMPRouteMirroring) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
buf := make([]byte, 0)
for _, tlv := range body.Info {
b, err := tlv.Serialize()
if err != nil {
return buf, err
}
buf = append(buf, b...)
}
return buf, nil
}
type BMPBody interface {
// Sigh, some body messages need a BMPHeader to parse the body
// data so we need to pass BMPHeader (avoid DecodeFromBytes
// function name).
ParseBody(*BMPMessage, []byte, ...*bgp.MarshallingOption) error
Serialize(...*bgp.MarshallingOption) ([]byte, error)
}
type BMPMessage struct {
Header BMPHeader
PeerHeader BMPPeerHeader
Body BMPBody
}
func (msg *BMPMessage) Serialize(options ...*bgp.MarshallingOption) ([]byte, error) {
buf := make([]byte, 0)
if msg.Header.Type != BMP_MSG_INITIATION && msg.Header.Type != BMP_MSG_TERMINATION {
p, err := msg.PeerHeader.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, p...)
}
b, err := msg.Body.Serialize(options...)
if err != nil {
return nil, err
}
buf = append(buf, b...)
if msg.Header.Length == 0 {
msg.Header.Length = uint32(BMP_HEADER_SIZE + len(buf))
}
h, err := msg.Header.Serialize()
if err != nil {
return nil, err
}
return append(h, buf...), nil
}
func (msg *BMPMessage) Len() int {
return int(msg.Header.Length)
}
const (
BMP_MSG_ROUTE_MONITORING = iota
BMP_MSG_STATISTICS_REPORT
BMP_MSG_PEER_DOWN_NOTIFICATION
BMP_MSG_PEER_UP_NOTIFICATION
BMP_MSG_INITIATION
BMP_MSG_TERMINATION
BMP_MSG_ROUTE_MIRRORING
)
func ParseBMPMessage(data []byte) (msg *BMPMessage, err error) {
return parseBMPMessage(data, nil)
}
func ParseBMPMessageWithOptions(data []byte, options func(BMPPeerHeader) []*bgp.MarshallingOption) (msg *BMPMessage, err error) {
return parseBMPMessage(data, options)
}
func parseBMPMessage(data []byte, optionsFunc func(BMPPeerHeader) []*bgp.MarshallingOption) (msg *BMPMessage, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("not all data bytes are available")
}
}()
msg = &BMPMessage{}
err = msg.Header.DecodeFromBytes(data)
if err != nil {
return nil, err
}
data = data[BMP_HEADER_SIZE:msg.Header.Length]
switch msg.Header.Type {
case BMP_MSG_ROUTE_MONITORING:
msg.Body = &BMPRouteMonitoring{}
case BMP_MSG_STATISTICS_REPORT:
msg.Body = &BMPStatisticsReport{}
case BMP_MSG_PEER_DOWN_NOTIFICATION:
msg.Body = &BMPPeerDownNotification{}
case BMP_MSG_PEER_UP_NOTIFICATION:
msg.Body = &BMPPeerUpNotification{}
case BMP_MSG_INITIATION:
msg.Body = &BMPInitiation{}
case BMP_MSG_TERMINATION:
msg.Body = &BMPTermination{}
case BMP_MSG_ROUTE_MIRRORING:
msg.Body = &BMPRouteMirroring{}
default:
return nil, fmt.Errorf("unsupported BMP message type: %d", msg.Header.Type)
}
var options []*bgp.MarshallingOption
if msg.Header.Type != BMP_MSG_INITIATION && msg.Header.Type != BMP_MSG_TERMINATION {
if err := msg.PeerHeader.DecodeFromBytes(data); err != nil {
return nil, fmt.Errorf("failed to decode BMP peer header: %w", err)
}
data = data[BMP_PEER_HEADER_SIZE:]
if optionsFunc != nil {
options = optionsFunc(msg.PeerHeader)
}
}
err = msg.Body.ParseBody(msg, data, options...)
if err != nil {
if msg.Header.Type == BMP_MSG_ROUTE_MONITORING {
return msg, err
}
return nil, err
}
return msg, nil
}
func SplitBMP(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 || len(data) < BMP_HEADER_SIZE {
return 0, nil, nil
}
tmpHdr := &BMPHeader{}
if err = tmpHdr.DecodeFromBytes(data[:BMP_HEADER_SIZE]); err != nil {
return 0, nil, nil
}
if len(data) < int(tmpHdr.Length) {
return 0, nil, nil
}
return int(tmpHdr.Length), data[:tmpHdr.Length], nil
}
// Copyright (C) 2015 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 mrt
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math"
"net/netip"
"time"
"github.com/osrg/gobgp/v4/pkg/packet/bgp"
)
const (
MRT_COMMON_HEADER_LEN = 12
)
type MRTType uint16
const (
NULL MRTType = 0 // deprecated
START MRTType = 1 // deprecated
DIE MRTType = 2 // deprecated
I_AM_DEAD MRTType = 3 // deprecated
PEER_DOWN MRTType = 4 // deprecated
BGP MRTType = 5 // deprecated
RIP MRTType = 6 // deprecated
IDRP MRTType = 7 // deprecated
RIPNG MRTType = 8 // deprecated
BGP4PLUS MRTType = 9 // deprecated
BGP4PLUS01 MRTType = 10 // deprecated
OSPFv2 MRTType = 11
TABLE_DUMP MRTType = 12
TABLE_DUMPv2 MRTType = 13
BGP4MP MRTType = 16
BGP4MP_ET MRTType = 17
ISIS MRTType = 32
ISIS_ET MRTType = 33
OSPFv3 MRTType = 48
OSPFv3_ET MRTType = 49
)
func (t MRTType) HasExtendedTimestamp() bool {
switch t {
case BGP4MP_ET, ISIS_ET, OSPFv3_ET:
return true
default:
return false
}
}
type MRTSubTyper interface {
ToUint16() uint16
}
type MRTSubTypeTableDumpv2 uint16
const (
PEER_INDEX_TABLE MRTSubTypeTableDumpv2 = 1
RIB_IPV4_UNICAST MRTSubTypeTableDumpv2 = 2
RIB_IPV4_MULTICAST MRTSubTypeTableDumpv2 = 3
RIB_IPV6_UNICAST MRTSubTypeTableDumpv2 = 4
RIB_IPV6_MULTICAST MRTSubTypeTableDumpv2 = 5
RIB_GENERIC MRTSubTypeTableDumpv2 = 6
GEO_PEER_TABLE MRTSubTypeTableDumpv2 = 7 // RFC6397
RIB_IPV4_UNICAST_ADDPATH MRTSubTypeTableDumpv2 = 8 // RFC8050
RIB_IPV4_MULTICAST_ADDPATH MRTSubTypeTableDumpv2 = 9 // RFC8050
RIB_IPV6_UNICAST_ADDPATH MRTSubTypeTableDumpv2 = 10 // RFC8050
RIB_IPV6_MULTICAST_ADDPATH MRTSubTypeTableDumpv2 = 11 // RFC8050
RIB_GENERIC_ADDPATH MRTSubTypeTableDumpv2 = 12 // RFC8050
)
func (t MRTSubTypeTableDumpv2) ToUint16() uint16 {
return uint16(t)
}
type MRTSubTypeBGP4MP uint16
const (
STATE_CHANGE MRTSubTypeBGP4MP = 0
MESSAGE MRTSubTypeBGP4MP = 1
MESSAGE_AS4 MRTSubTypeBGP4MP = 4
STATE_CHANGE_AS4 MRTSubTypeBGP4MP = 5
MESSAGE_LOCAL MRTSubTypeBGP4MP = 6
MESSAGE_AS4_LOCAL MRTSubTypeBGP4MP = 7
MESSAGE_ADDPATH MRTSubTypeBGP4MP = 8 // RFC8050
MESSAGE_AS4_ADDPATH MRTSubTypeBGP4MP = 9 // RFC8050
MESSAGE_LOCAL_ADDPATH MRTSubTypeBGP4MP = 10 // RFC8050
MESSAGE_AS4_LOCAL_ADDPATH MRTSubTypeBGP4MP = 11 // RFC8050
)
func (t MRTSubTypeBGP4MP) ToUint16() uint16 {
return uint16(t)
}
type BGPState uint16
const (
IDLE BGPState = 1
CONNECT BGPState = 2
ACTIVE BGPState = 3
OPENSENT BGPState = 4
OPENCONFIRM BGPState = 5
ESTABLISHED BGPState = 6
)
func packValues(values ...any) ([]byte, error) {
b := new(bytes.Buffer)
for _, v := range values {
err := binary.Write(b, binary.BigEndian, v)
if err != nil {
return nil, err
}
}
return b.Bytes(), nil
}
type MRTHeader struct {
Timestamp uint32
Type MRTType
SubType uint16
Len uint32
ExtendedTimestampMicroseconds uint32
}
func ParseHeader(data []byte) (*MRTHeader, error) {
if len(data) < MRT_COMMON_HEADER_LEN {
return nil, fmt.Errorf("not all MRTHeader bytes are available. expected: %d, actual: %d", MRT_COMMON_HEADER_LEN, len(data))
}
h := &MRTHeader{}
h.Timestamp = binary.BigEndian.Uint32(data[:4])
h.Type = MRTType(binary.BigEndian.Uint16(data[4:6]))
h.SubType = binary.BigEndian.Uint16(data[6:8])
h.Len = binary.BigEndian.Uint32(data[8:12])
if h.Type.HasExtendedTimestamp() {
if len(data) < 16 {
return nil, fmt.Errorf("not all MRTHeader bytes are available. expected: %d, actual: %d", 16, len(data))
}
h.ExtendedTimestampMicroseconds = binary.BigEndian.Uint32(data[12:16])
}
return h, nil
}
func (h *MRTHeader) Serialize() ([]byte, error) {
fields := []any{h.Timestamp, h.Type, h.SubType, h.Len}
if h.Type.HasExtendedTimestamp() {
fields = append(fields, h.ExtendedTimestampMicroseconds)
}
return packValues(fields...)
}
func NewMRTHeader(timestamp time.Time, t MRTType, subtype MRTSubTyper, l uint32) (*MRTHeader, error) {
ms := uint32(0)
if t.HasExtendedTimestamp() {
ms = uint32(timestamp.UnixMicro() - timestamp.Unix()*1000000)
}
return &MRTHeader{
Timestamp: uint32(timestamp.Unix()),
Type: t,
SubType: subtype.ToUint16(),
Len: l,
ExtendedTimestampMicroseconds: ms,
}, nil
}
func (h *MRTHeader) GetTime() time.Time {
t := int64(h.Timestamp)
ms := int64(h.ExtendedTimestampMicroseconds)
return time.Unix(t, ms*1000)
}
type MRTMessage struct {
Header MRTHeader
Body Body
}
func (m *MRTMessage) Serialize() ([]byte, error) {
buf, err := m.Body.Serialize()
if err != nil {
return nil, err
}
m.Header.Len = uint32(len(buf))
bbuf, err := m.Header.Serialize()
if err != nil {
return nil, err
}
return append(bbuf, buf...), nil
}
func NewMRTMessage(timestamp time.Time, t MRTType, subtype MRTSubTyper, body Body) (*MRTMessage, error) {
header, err := NewMRTHeader(timestamp, t, subtype, 0)
if err != nil {
return nil, err
}
return &MRTMessage{
Header: *header,
Body: body,
}, nil
}
type Body interface {
Serialize() ([]byte, error)
}
type Peer struct {
Type uint8
BgpId netip.Addr
IpAddress netip.Addr
AS uint32
}
var errNotAllPeerBytesAvailable = errors.New("not all Peer bytes are available")
func (p *Peer) decodeFromBytes(data []byte) ([]byte, error) {
if len(data) < 5 {
return nil, errNotAllPeerBytesAvailable
}
p.Type = data[0]
p.BgpId, _ = netip.AddrFromSlice(data[1:5])
data = data[5:]
if p.Type&1 > 0 {
if len(data) < 16 {
return nil, errNotAllPeerBytesAvailable
}
p.IpAddress, _ = netip.AddrFromSlice(data[:16])
data = data[16:]
} else {
if len(data) < 4 {
return nil, errNotAllPeerBytesAvailable
}
p.IpAddress, _ = netip.AddrFromSlice(data[:4])
data = data[4:]
}
if p.Type&(1<<1) > 0 {
if len(data) < 4 {
return nil, errNotAllPeerBytesAvailable
}
p.AS = binary.BigEndian.Uint32(data[:4])
data = data[4:]
} else {
if len(data) < 2 {
return nil, errNotAllPeerBytesAvailable
}
p.AS = uint32(binary.BigEndian.Uint16(data[:2]))
data = data[2:]
}
return data, nil
}
func (p *Peer) Serialize() ([]byte, error) {
var err error
var bbuf []byte
buf := make([]byte, 5)
buf[0] = p.Type
copy(buf[1:], p.BgpId.AsSlice())
if p.Type&1 > 0 {
buf = append(buf, p.IpAddress.AsSlice()...)
} else {
buf = append(buf, p.IpAddress.AsSlice()...)
}
if p.Type&(1<<1) > 0 {
bbuf, err = packValues(p.AS)
} else {
if p.AS > uint32(math.MaxUint16) {
return nil, fmt.Errorf("AS number is beyond 2 octet. %d > %d", p.AS, math.MaxUint16)
}
bbuf, err = packValues(uint16(p.AS))
}
if err != nil {
return nil, err
}
return append(buf, bbuf...), nil
}
func NewPeer(bgpid netip.Addr, ipaddr netip.Addr, asn uint32, isAS4 bool) *Peer {
// TODO: return error if bgpid is IPv6
t := 0
if ipaddr.Is6() {
t |= 1
}
if isAS4 {
t |= 1 << 1
}
return &Peer{
Type: uint8(t),
BgpId: bgpid,
IpAddress: ipaddr,
AS: asn,
}
}
func (p *Peer) String() string {
return fmt.Sprintf("PEER ENTRY: ID [%s] Addr [%s] AS [%d]", p.BgpId, p.IpAddress, p.AS)
}
type PeerIndexTable struct {
CollectorBgpId netip.Addr
ViewName string
Peers []*Peer
}
var errNnotAllPeerIndexBytesAvailable = errors.New("not all PeerIndexTable bytes are available")
func parsePeerIndexTable(data []byte) (*PeerIndexTable, error) {
t := &PeerIndexTable{}
if len(data) < 6 {
return nil, errNnotAllPeerIndexBytesAvailable
}
t.CollectorBgpId, _ = netip.AddrFromSlice(data[:4])
viewLen := binary.BigEndian.Uint16(data[4:6])
if len(data) < 6+int(viewLen) {
return nil, errNnotAllPeerIndexBytesAvailable
}
t.ViewName = string(data[6 : 6+viewLen])
data = data[6+viewLen:]
if len(data) < 2 {
return nil, errNnotAllPeerIndexBytesAvailable
}
peerNum := binary.BigEndian.Uint16(data[:2])
data = data[2:]
t.Peers = make([]*Peer, 0, peerNum)
var err error
for range peerNum {
p := &Peer{}
data, err = p.decodeFromBytes(data)
if err != nil {
return nil, err
}
t.Peers = append(t.Peers, p)
}
return t, nil
}
func (t *PeerIndexTable) Serialize() ([]byte, error) {
buf := make([]byte, 8+len(t.ViewName))
copy(buf, t.CollectorBgpId.AsSlice())
binary.BigEndian.PutUint16(buf[4:], uint16(len(t.ViewName)))
copy(buf[6:], t.ViewName)
binary.BigEndian.PutUint16(buf[6+len(t.ViewName):], uint16(len(t.Peers)))
for _, peer := range t.Peers {
bbuf, err := peer.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, bbuf...)
}
return buf, nil
}
func NewPeerIndexTable(bgpid netip.Addr, viewname string, peers []*Peer) *PeerIndexTable {
// TODO: return error if bgpid is IPv6
return &PeerIndexTable{
CollectorBgpId: bgpid,
ViewName: viewname,
Peers: peers,
}
}
func (t *PeerIndexTable) String() string {
return fmt.Sprintf("PEER_INDEX_TABLE: CollectorBgpId [%s] ViewName [%s] Peers [%s]", t.CollectorBgpId, t.ViewName, t.Peers)
}
type RibEntry struct {
PeerIndex uint16
OriginatedTime uint32
PathIdentifier uint32
PathAttributes []bgp.PathAttributeInterface
isAddPath bool
}
var errNotAllRibEntryBytesAvailable = errors.New("not all RibEntry bytes are available")
func parseRibEntry(data []byte, family bgp.Family, isAddPath bool, prefix ...bgp.NLRI) (*RibEntry, []byte, error) {
if len(data) < 8 {
return nil, data, errNotAllRibEntryBytesAvailable
}
e := &RibEntry{
isAddPath: isAddPath,
}
e.PeerIndex = binary.BigEndian.Uint16(data[:2])
e.OriginatedTime = binary.BigEndian.Uint32(data[2:6])
if e.isAddPath {
if len(data) < 10+2 {
return nil, nil, errNotAllRibEntryBytesAvailable
}
e.PathIdentifier = binary.BigEndian.Uint32(data[6:10])
data = data[10:]
} else {
data = data[6:]
}
totalLen := binary.BigEndian.Uint16(data[:2])
data = data[2:]
options := &bgp.MarshallingOption{
MRT: true,
}
for attrLen := totalLen; attrLen > 0; {
p, err := bgp.GetPathAttribute(data)
if err != nil {
return nil, nil, err
}
// HACK: keeps compatibility
if len(prefix) > 1 {
return nil, nil, fmt.Errorf("only one prefix should be used")
}
err = p.DecodeFromBytes(data, options)
if err != nil {
return nil, nil, err
}
// RFC 6396 4.3.4
mp, ok := p.(*bgp.PathAttributeMpReachNLRI)
if ok && len(prefix) == 0 {
return nil, nil, fmt.Errorf("prefix is not provided for MP_REACH_NLRI")
} else if ok {
mp.AFI = family.Afi()
mp.SAFI = family.Safi()
mp.Value = []bgp.PathNLRI{{NLRI: prefix[0], ID: e.PathIdentifier}}
}
attrLen -= uint16(p.Len())
if len(data) < p.Len() {
return nil, nil, errNotAllRibEntryBytesAvailable
}
data = data[p.Len():]
e.PathAttributes = append(e.PathAttributes, p)
}
return e, data, nil
}
func (e *RibEntry) Serialize() ([]byte, error) {
pbuf := make([]byte, 0)
totalLen := 0
options := &bgp.MarshallingOption{
MRT: true,
}
for _, pattr := range e.PathAttributes {
pb, err := pattr.Serialize(options)
if err != nil {
return nil, err
}
pbuf = append(pbuf, pb...)
totalLen += len(pb)
}
var buf []byte
if e.isAddPath {
buf = make([]byte, 12, 12+len(pbuf))
binary.BigEndian.PutUint16(buf, e.PeerIndex)
binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime)
binary.BigEndian.PutUint32(buf[6:], e.PathIdentifier)
binary.BigEndian.PutUint16(buf[10:], uint16(totalLen))
} else {
buf = make([]byte, 8, 8+len(pbuf))
binary.BigEndian.PutUint16(buf, e.PeerIndex)
binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime)
binary.BigEndian.PutUint16(buf[6:], uint16(totalLen))
}
buf = append(buf, pbuf...)
return buf, nil
}
func NewRibEntry(index uint16, time uint32, pathId uint32, pathAttrs []bgp.PathAttributeInterface, isAddPath bool) *RibEntry {
return &RibEntry{
PeerIndex: index,
OriginatedTime: time,
PathIdentifier: pathId,
PathAttributes: pathAttrs,
isAddPath: isAddPath,
}
}
func (e *RibEntry) String() string {
if e.isAddPath {
return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathIdentifier[%d] PathAttributes [%v]", e.PeerIndex, e.OriginatedTime, e.PathIdentifier, e.PathAttributes)
} else {
return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathAttributes [%v]", e.PeerIndex, e.OriginatedTime, e.PathAttributes)
}
}
type Rib struct {
SequenceNumber uint32
Prefix bgp.NLRI
Entries []*RibEntry
Family bgp.Family
isAddPath bool
}
func parseRib(data []byte, family bgp.Family, isAddPath bool) (*Rib, error) {
u := &Rib{
Family: family,
isAddPath: isAddPath,
}
if len(data) < 4 {
return nil, errors.New("not all RibIpv4Unicast message bytes available")
}
u.SequenceNumber = binary.BigEndian.Uint32(data[:4])
data = data[4:]
afi, safi := u.Family.Afi(), u.Family.Safi()
if afi == 0 && safi == 0 {
if len(data) < 3 {
return nil, errors.New("not all RibIpv4Unicast message bytes available")
}
afi = binary.BigEndian.Uint16(data[:2])
safi = data[2]
data = data[3:]
family = bgp.NewFamily(afi, safi)
}
prefix, err := bgp.NLRIFromSlice(family, data)
if err != nil {
return nil, err
}
u.Prefix = prefix
if len(data) < prefix.Len()+2 {
return nil, errors.New("not all RibIpv4Unicast message bytes available")
}
data = data[prefix.Len():]
entryNum := binary.BigEndian.Uint16(data[:2])
data = data[2:]
u.Entries = make([]*RibEntry, 0, entryNum)
for range entryNum {
var e *RibEntry
e, data, err = parseRibEntry(data, family, u.isAddPath, prefix)
if err != nil {
return nil, err
}
u.Entries = append(u.Entries, e)
}
return u, nil
}
func (u *Rib) Serialize() ([]byte, error) {
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, u.SequenceNumber)
switch u.Family {
case bgp.RF_FS_IPv4_UC, bgp.RF_IPv4_MC, bgp.RF_IPv6_UC, bgp.RF_IPv6_MC:
var bbuf [2]byte
binary.BigEndian.PutUint16(bbuf[:], u.Family.Afi())
buf = append(buf, bbuf[:]...)
buf = append(buf, u.Family.Safi())
default:
}
bbuf, err := u.Prefix.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, bbuf...)
bbuf, err = packValues(uint16(len(u.Entries)))
if err != nil {
return nil, err
}
buf = append(buf, bbuf...)
for _, entry := range u.Entries {
bbuf, err = entry.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, bbuf...)
}
return buf, nil
}
func NewRib(seq uint32, family bgp.Family, prefix bgp.NLRI, entries []*RibEntry) *Rib {
return &Rib{
SequenceNumber: seq,
Family: family,
Prefix: prefix,
Entries: entries,
isAddPath: entries[0].isAddPath,
}
}
func (u *Rib) String() string {
return fmt.Sprintf("RIB: Seq [%d] Prefix [%s] Entries [%s]", u.SequenceNumber, u.Prefix, u.Entries)
}
type GeoPeer struct {
Type uint8
BgpId netip.Addr
Latitude float32
Longitude float32
}
func (p *GeoPeer) decodeFromBytes(data []byte) ([]byte, error) {
if len(data) < 13 {
return nil, fmt.Errorf("not all GeoPeer bytes are available")
}
// Peer IP Address and Peer AS should not be included
p.Type = data[0]
if p.Type != uint8(0) {
return nil, fmt.Errorf("unsupported peer type for GeoPeer: %d", p.Type)
}
p.BgpId, _ = netip.AddrFromSlice(data[1:5])
p.Latitude = math.Float32frombits(binary.BigEndian.Uint32(data[5:9]))
p.Longitude = math.Float32frombits(binary.BigEndian.Uint32(data[9:13]))
return data[13:], nil
}
func (p *GeoPeer) Serialize() ([]byte, error) {
buf := make([]byte, 13)
buf[0] = uint8(0) // Peer IP Address and Peer AS should not be included
if !p.BgpId.Is4() {
return nil, fmt.Errorf("invalid BgpId: %s", p.BgpId)
}
copy(buf[1:5], p.BgpId.AsSlice())
binary.BigEndian.PutUint32(buf[5:9], math.Float32bits(p.Latitude))
binary.BigEndian.PutUint32(buf[9:13], math.Float32bits(p.Longitude))
return buf, nil
}
func NewGeoPeer(bgpid netip.Addr, latitude float32, longitude float32) (*GeoPeer, error) {
if !bgpid.Is4() {
return nil, fmt.Errorf("invalid BgpId: %s", bgpid)
}
return &GeoPeer{
Type: 0, // Peer IP Address and Peer AS should not be included
BgpId: bgpid,
Latitude: latitude,
Longitude: longitude,
}, nil
}
func (p *GeoPeer) String() string {
return fmt.Sprintf("PEER ENTRY: ID [%s] Latitude [%f] Longitude [%f]", p.BgpId, p.Latitude, p.Longitude)
}
type GeoPeerTable struct {
CollectorBgpId netip.Addr
CollectorLatitude float32
CollectorLongitude float32
Peers []*GeoPeer
}
func parseGeoPeerTable(data []byte) (*GeoPeerTable, error) {
if len(data) < 14 {
return nil, fmt.Errorf("not all GeoPeerTable bytes are available")
}
t := &GeoPeerTable{}
t.CollectorBgpId, _ = netip.AddrFromSlice(data[:4])
t.CollectorLatitude = math.Float32frombits(binary.BigEndian.Uint32(data[4:8]))
t.CollectorLongitude = math.Float32frombits(binary.BigEndian.Uint32(data[8:12]))
peerCount := binary.BigEndian.Uint16(data[12:14])
data = data[14:]
t.Peers = make([]*GeoPeer, 0, peerCount)
var err error
for range peerCount {
p := &GeoPeer{}
if data, err = p.decodeFromBytes(data); err != nil {
return nil, err
}
t.Peers = append(t.Peers, p)
}
return t, nil
}
func (t *GeoPeerTable) Serialize() ([]byte, error) {
buf := make([]byte, 14)
if !t.CollectorBgpId.Is4() {
return nil, fmt.Errorf("invalid CollectorBgpId: %s", t.CollectorBgpId)
}
copy(buf[:4], t.CollectorBgpId.AsSlice())
binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(t.CollectorLatitude))
binary.BigEndian.PutUint32(buf[8:12], math.Float32bits(t.CollectorLongitude))
binary.BigEndian.PutUint16(buf[12:14], uint16(len(t.Peers)))
for _, peer := range t.Peers {
pbuf, err := peer.Serialize()
if err != nil {
return nil, err
}
buf = append(buf, pbuf...)
}
return buf, nil
}
func NewGeoPeerTable(bgpid netip.Addr, latitude float32, longitude float32, peers []*GeoPeer) (*GeoPeerTable, error) {
if !bgpid.Is4() {
return nil, fmt.Errorf("invalid BgpId: %s", bgpid)
}
return &GeoPeerTable{
CollectorBgpId: bgpid,
CollectorLatitude: latitude,
CollectorLongitude: longitude,
Peers: peers,
}, nil
}
func (t *GeoPeerTable) String() string {
return fmt.Sprintf("GEO_PEER_TABLE: CollectorBgpId [%s] CollectorLatitude [%f] CollectorLongitude [%f] Peers [%s]", t.CollectorBgpId, t.CollectorLatitude, t.CollectorLongitude, t.Peers)
}
type BGP4MPHeader struct {
PeerAS uint32
LocalAS uint32
InterfaceIndex uint16
AddressFamily uint16
PeerIpAddress netip.Addr
LocalIpAddress netip.Addr
isAS4 bool
}
func (m *BGP4MPHeader) decodeFromBytes(data []byte) ([]byte, error) {
if m.isAS4 && len(data) < 12 {
return nil, errors.New("not all BGP4MPMessageAS4 bytes available")
} else if !m.isAS4 && len(data) < 8 {
return nil, errors.New("not all BGP4MPMessageAS bytes available")
}
if m.isAS4 {
m.PeerAS = binary.BigEndian.Uint32(data[:4])
m.LocalAS = binary.BigEndian.Uint32(data[4:8])
data = data[8:]
} else {
m.PeerAS = uint32(binary.BigEndian.Uint16(data[:2]))
m.LocalAS = uint32(binary.BigEndian.Uint16(data[2:4]))
data = data[4:]
}
m.InterfaceIndex = binary.BigEndian.Uint16(data[:2])
m.AddressFamily = binary.BigEndian.Uint16(data[2:4])
switch m.AddressFamily {
case bgp.AFI_IP:
if len(data) < 12 {
return nil, errors.New("not all IPv4 peer bytes available")
}
m.PeerIpAddress, _ = netip.AddrFromSlice(data[4:8])
m.LocalIpAddress, _ = netip.AddrFromSlice(data[8:12])
data = data[12:]
case bgp.AFI_IP6:
if len(data) < 36 {
return nil, errors.New("not all IPv6 peer bytes available")
}
m.PeerIpAddress, _ = netip.AddrFromSlice(data[4:20])
m.LocalIpAddress, _ = netip.AddrFromSlice(data[20:36])
data = data[36:]
default:
return nil, fmt.Errorf("unsupported address family: %d", m.AddressFamily)
}
return data, nil
}
func (m *BGP4MPHeader) serialize() ([]byte, error) {
var values []any
if m.isAS4 {
values = []any{m.PeerAS, m.LocalAS, m.InterfaceIndex, m.AddressFamily}
} else {
values = []any{uint16(m.PeerAS), uint16(m.LocalAS), m.InterfaceIndex, m.AddressFamily}
}
buf, err := packValues(values...)
if err != nil {
return nil, err
}
var bbuf []byte
switch m.AddressFamily {
case bgp.AFI_IP:
bbuf = make([]byte, 8)
copy(bbuf, m.PeerIpAddress.AsSlice())
copy(bbuf[4:], m.LocalIpAddress.AsSlice())
case bgp.AFI_IP6:
bbuf = make([]byte, 32)
copy(bbuf, m.PeerIpAddress.AsSlice())
copy(bbuf[16:], m.LocalIpAddress.AsSlice())
default:
return nil, fmt.Errorf("unsupported address family: %d", m.AddressFamily)
}
return append(buf, bbuf...), nil
}
func newBGP4MPHeader(peeras, localas uint32, intfindex uint16, peerip, localip netip.Addr, isAS4 bool) (*BGP4MPHeader, error) {
var af uint16
if !peerip.IsValid() || !localip.IsValid() {
return nil, fmt.Errorf("Peer IP Address and Local IP Address must be valid")
}
if peerip.Is4() && localip.Is4() {
af = bgp.AFI_IP
} else if peerip.Is6() && localip.Is6() {
af = bgp.AFI_IP6
} else {
return nil, fmt.Errorf("peer IP Address and Local IP Address must have the same address family")
}
return &BGP4MPHeader{
PeerAS: peeras,
LocalAS: localas,
InterfaceIndex: intfindex,
AddressFamily: af,
PeerIpAddress: peerip,
LocalIpAddress: localip,
isAS4: isAS4,
}, nil
}
type BGP4MPStateChange struct {
*BGP4MPHeader
OldState BGPState
NewState BGPState
}
func parseBGP4MPStateChange(hdr *BGP4MPHeader, data []byte) (*BGP4MPStateChange, error) {
m := &BGP4MPStateChange{
BGP4MPHeader: hdr,
}
rest, err := m.decodeFromBytes(data)
if err != nil {
return nil, err
}
if len(rest) < 4 {
return nil, fmt.Errorf("not all BGP4MPStateChange bytes available")
}
m.OldState = BGPState(binary.BigEndian.Uint16(rest[:2]))
m.NewState = BGPState(binary.BigEndian.Uint16(rest[2:4]))
return m, nil
}
func (m *BGP4MPStateChange) Serialize() ([]byte, error) {
buf, err := m.serialize()
if err != nil {
return nil, err
}
bbuf, err := packValues(m.OldState, m.NewState)
if err != nil {
return nil, err
}
return append(buf, bbuf...), nil
}
func NewBGP4MPStateChange(peeras, localas uint32, intfindex uint16, peerip, localip netip.Addr, isAS4 bool, oldstate, newstate BGPState) (*BGP4MPStateChange, error) {
header, err := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
if err != nil {
return nil, err
}
return &BGP4MPStateChange{
BGP4MPHeader: header,
OldState: oldstate,
NewState: newstate,
}, nil
}
type BGP4MPMessage struct {
*BGP4MPHeader
BGPMessage *bgp.BGPMessage
BGPMessagePayload []byte
isLocal bool
isAddPath bool
}
func parseBGP4MPMessage(hdr *BGP4MPHeader, isLocal bool, isAddPath bool, data []byte) (*BGP4MPMessage, error) {
m := &BGP4MPMessage{
BGP4MPHeader: hdr,
isLocal: isLocal,
isAddPath: isAddPath,
}
rest, err := m.decodeFromBytes(data)
if err != nil {
return nil, err
}
if len(rest) < bgp.BGP_HEADER_LENGTH {
return nil, fmt.Errorf("not all BGP4MPMessageAS4 bytes available")
}
msg, err := bgp.ParseBGPMessage(rest)
if err != nil {
return nil, err
}
m.BGPMessage = msg
return m, nil
}
func (m *BGP4MPMessage) Serialize() ([]byte, error) {
buf, err := m.serialize()
if err != nil {
return nil, err
}
if m.BGPMessagePayload != nil {
return append(buf, m.BGPMessagePayload...), nil
}
bbuf, err := m.BGPMessage.Serialize()
if err != nil {
return nil, err
}
return append(buf, bbuf...), nil
}
func NewBGP4MPMessage(peeras, localas uint32, intfindex uint16, peerip, localip netip.Addr, isAS4 bool, msg *bgp.BGPMessage) (*BGP4MPMessage, error) {
header, err := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
if err != nil {
return nil, err
}
return &BGP4MPMessage{
BGP4MPHeader: header,
BGPMessage: msg,
}, nil
}
func NewBGP4MPMessageLocal(peeras, localas uint32, intfindex uint16, peerip, localip netip.Addr, isAS4 bool, msg *bgp.BGPMessage) (*BGP4MPMessage, error) {
header, err := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
if err != nil {
return nil, err
}
return &BGP4MPMessage{
BGP4MPHeader: header,
BGPMessage: msg,
isLocal: true,
}, nil
}
func NewBGP4MPMessageAddPath(peeras, localas uint32, intfindex uint16, peerip, localip netip.Addr, isAS4 bool, msg *bgp.BGPMessage) (*BGP4MPMessage, error) {
header, err := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
if err != nil {
return nil, err
}
return &BGP4MPMessage{
BGP4MPHeader: header,
BGPMessage: msg,
isAddPath: true,
}, nil
}
func NewBGP4MPMessageLocalAddPath(peeras, localas uint32, intfindex uint16, peerip, localip netip.Addr, isAS4 bool, msg *bgp.BGPMessage) (*BGP4MPMessage, error) {
header, err := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
if err != nil {
return nil, err
}
return &BGP4MPMessage{
BGP4MPHeader: header,
BGPMessage: msg,
isLocal: true,
isAddPath: true,
}, nil
}
func (m *BGP4MPMessage) String() string {
title := "BGP4MP_MSG"
if m.isAS4 {
title += "_AS4"
}
if m.isLocal {
title += "_LOCAL"
}
if m.isAddPath {
title += "_ADDPATH"
}
return fmt.Sprintf("%s: PeerAS [%d] LocalAS [%d] InterfaceIndex [%d] PeerIP [%s] LocalIP [%s] BGPMessage [%v]", title, m.PeerAS, m.LocalAS, m.InterfaceIndex, m.PeerIpAddress, m.LocalIpAddress, m.BGPMessage)
}
// This function can be passed into a bufio.Scanner.Split() to read buffered mrt msgs
func SplitMrt(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if cap(data) < MRT_COMMON_HEADER_LEN { // read more
return 0, nil, nil
}
hdr, errh := ParseHeader(data[:MRT_COMMON_HEADER_LEN])
if errh != nil {
return 0, nil, errh
}
totlen := int(hdr.Len + MRT_COMMON_HEADER_LEN)
if len(data) < totlen { // need to read more
return 0, nil, nil
}
return totlen, data[:totlen], nil
}
func ParseBody(data []byte, h *MRTHeader) (*MRTMessage, error) {
if len(data) < int(h.Len) {
return nil, fmt.Errorf("not all MRT message bytes available. expected: %d, actual: %d", int(h.Len), len(data))
}
var err error
var body Body
msg := &MRTMessage{Header: *h}
switch h.Type {
case TABLE_DUMPv2:
subType := MRTSubTypeTableDumpv2(h.SubType)
rf := bgp.Family(0)
isAddPath := false
switch subType {
case PEER_INDEX_TABLE:
body, err = parsePeerIndexTable(data)
case RIB_IPV4_UNICAST:
rf = bgp.RF_IPv4_UC
case RIB_IPV4_MULTICAST:
rf = bgp.RF_IPv4_MC
case RIB_IPV6_UNICAST:
rf = bgp.RF_IPv6_UC
case RIB_IPV6_MULTICAST:
rf = bgp.RF_IPv6_MC
case RIB_GENERIC:
case GEO_PEER_TABLE:
body, err = parseGeoPeerTable(data)
case RIB_IPV4_UNICAST_ADDPATH:
rf = bgp.RF_IPv4_UC
isAddPath = true
case RIB_IPV4_MULTICAST_ADDPATH:
rf = bgp.RF_IPv4_MC
isAddPath = true
case RIB_IPV6_UNICAST_ADDPATH:
rf = bgp.RF_IPv6_UC
isAddPath = true
case RIB_IPV6_MULTICAST_ADDPATH:
rf = bgp.RF_IPv6_MC
isAddPath = true
case RIB_GENERIC_ADDPATH:
isAddPath = true
default:
return nil, fmt.Errorf("unsupported table dumpv2 subtype: %v", subType)
}
if body == nil {
body, err = parseRib(data, rf, isAddPath)
}
case BGP4MP:
subType := MRTSubTypeBGP4MP(h.SubType)
isAS4 := true
switch subType {
case STATE_CHANGE:
isAS4 = false
fallthrough
case STATE_CHANGE_AS4:
body, err = parseBGP4MPStateChange(&BGP4MPHeader{isAS4: isAS4}, data)
case MESSAGE:
isAS4 = false
fallthrough
case MESSAGE_AS4:
body, err = parseBGP4MPMessage(&BGP4MPHeader{isAS4: isAS4}, false, false, data)
case MESSAGE_LOCAL:
isAS4 = false
fallthrough
case MESSAGE_AS4_LOCAL:
body, err = parseBGP4MPMessage(&BGP4MPHeader{isAS4: isAS4}, true, false, data)
case MESSAGE_ADDPATH:
isAS4 = false
fallthrough
case MESSAGE_AS4_ADDPATH:
body, err = parseBGP4MPMessage(&BGP4MPHeader{isAS4: isAS4}, false, true, data)
case MESSAGE_LOCAL_ADDPATH:
isAS4 = false
fallthrough
case MESSAGE_AS4_LOCAL_ADDPATH:
body, err = parseBGP4MPMessage(&BGP4MPHeader{isAS4: isAS4}, true, true, data)
default:
return nil, fmt.Errorf("unsupported bgp4mp subtype: %v", subType)
}
default:
return nil, fmt.Errorf("unsupported type: %v", h.Type)
}
if err != nil {
return nil, err
}
msg.Body = body
return msg, nil
}
// Copyright (C) 2015 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 rtr
import (
"encoding/binary"
"errors"
"fmt"
"net/netip"
)
const (
RPKI_DEFAULT_PORT = 323
)
const (
RTR_SERIAL_NOTIFY = iota
RTR_SERIAL_QUERY
RTR_RESET_QUERY
RTR_CACHE_RESPONSE
RTR_IPV4_PREFIX
_
RTR_IPV6_PREFIX
RTR_END_OF_DATA
RTR_CACHE_RESET
_
RTR_ERROR_REPORT
)
const (
RTR_SERIAL_NOTIFY_LEN = 12
RTR_SERIAL_QUERY_LEN = 12
RTR_RESET_QUERY_LEN = 8
RTR_CACHE_RESPONSE_LEN = 8
RTR_IPV4_PREFIX_LEN = 20
RTR_IPV6_PREFIX_LEN = 32
RTR_END_OF_DATA_LEN = 12
RTR_CACHE_RESET_LEN = 8
RTR_MIN_LEN = 8
RTR_ERROR_REPORT_ERR_PDU_LEN = 4
RTR_ERROR_REPORT_ERR_TEXT_LEN = 4
)
const (
WITHDRAWAL uint8 = iota
ANNOUNCEMENT
)
const (
CORRUPT_DATA uint16 = iota
INTERNAL_ERROR
NO_DATA_AVAILABLE
INVALID_REQUEST
UNSUPPORTED_PROTOCOL_VERSION
UNSUPPORTED_PDU_TYPE
WITHDRAWAL_OF_UNKNOWN_RECORD
DUPLICATE_ANNOUNCEMENT_RECORD
)
type RTRMessage interface {
DecodeFromBytes([]byte) error
Serialize() ([]byte, error)
}
type RTRCommon struct {
Version uint8
Type uint8
SessionID uint16
Len uint32
SerialNumber uint32
}
func (m *RTRCommon) DecodeFromBytes(data []byte) error {
if len(data) < RTR_SERIAL_NOTIFY_LEN {
return errors.New("data too short for RTRCommon")
}
m.Version = data[0]
m.Type = data[1]
m.SessionID = binary.BigEndian.Uint16(data[2:4])
m.Len = binary.BigEndian.Uint32(data[4:8])
m.SerialNumber = binary.BigEndian.Uint32(data[8:12])
return nil
}
func (m *RTRCommon) Serialize() ([]byte, error) {
data := make([]byte, m.Len)
data[0] = m.Version
data[1] = m.Type
binary.BigEndian.PutUint16(data[2:4], m.SessionID)
binary.BigEndian.PutUint32(data[4:8], m.Len)
binary.BigEndian.PutUint32(data[8:12], m.SerialNumber)
return data, nil
}
type RTRSerialNotify struct {
RTRCommon
}
func NewRTRSerialNotify(id uint16, sn uint32) *RTRSerialNotify {
return &RTRSerialNotify{
RTRCommon{
Type: RTR_SERIAL_NOTIFY,
SessionID: id,
Len: RTR_SERIAL_NOTIFY_LEN,
SerialNumber: sn,
},
}
}
type RTRSerialQuery struct {
RTRCommon
}
func NewRTRSerialQuery(id uint16, sn uint32) *RTRSerialQuery {
return &RTRSerialQuery{
RTRCommon{
Type: RTR_SERIAL_QUERY,
SessionID: id,
Len: RTR_SERIAL_QUERY_LEN,
SerialNumber: sn,
},
}
}
type RTRReset struct {
Version uint8
Type uint8
Len uint32
}
func (m *RTRReset) DecodeFromBytes(data []byte) error {
if len(data) < RTR_RESET_QUERY_LEN {
return errors.New("data too short for RTRReset")
}
m.Version = data[0]
m.Type = data[1]
m.Len = binary.BigEndian.Uint32(data[4:8])
return nil
}
func (m *RTRReset) Serialize() ([]byte, error) {
data := make([]byte, m.Len)
data[0] = m.Version
data[1] = m.Type
binary.BigEndian.PutUint32(data[4:8], m.Len)
return data, nil
}
type RTRResetQuery struct {
RTRReset
}
func NewRTRResetQuery() *RTRResetQuery {
return &RTRResetQuery{
RTRReset{
Type: RTR_RESET_QUERY,
Len: RTR_RESET_QUERY_LEN,
},
}
}
type RTRCacheResponse struct {
Version uint8
Type uint8
SessionID uint16
Len uint32
}
func (m *RTRCacheResponse) DecodeFromBytes(data []byte) error {
if len(data) < RTR_CACHE_RESPONSE_LEN {
return errors.New("data too short for RTRCacheResponse")
}
m.Version = data[0]
m.Type = data[1]
m.SessionID = binary.BigEndian.Uint16(data[2:4])
m.Len = binary.BigEndian.Uint32(data[4:8])
return nil
}
func (m *RTRCacheResponse) Serialize() ([]byte, error) {
data := make([]byte, m.Len)
data[0] = m.Version
data[1] = m.Type
binary.BigEndian.PutUint16(data[2:4], m.SessionID)
binary.BigEndian.PutUint32(data[4:8], m.Len)
return data, nil
}
func NewRTRCacheResponse(id uint16) *RTRCacheResponse {
return &RTRCacheResponse{
Type: RTR_CACHE_RESPONSE,
SessionID: id,
Len: RTR_CACHE_RESPONSE_LEN,
}
}
type RTRIPPrefix struct {
Version uint8
Type uint8
Len uint32
Flags uint8
PrefixLen uint8
MaxLen uint8
Prefix netip.Addr
AS uint32
}
func (m *RTRIPPrefix) DecodeFromBytes(data []byte) error {
if len(data) < RTR_IPV4_PREFIX_LEN {
return errors.New("data too short for RTRIPPrefix")
}
m.Version = data[0]
m.Type = data[1]
m.Len = binary.BigEndian.Uint32(data[4:8])
m.Flags = data[8]
m.PrefixLen = data[9]
m.MaxLen = data[10]
if m.Type == RTR_IPV4_PREFIX {
m.Prefix, _ = netip.AddrFromSlice(data[12:16])
m.AS = binary.BigEndian.Uint32(data[16:20])
} else {
if len(data) < RTR_IPV6_PREFIX_LEN {
return errors.New("data too short for RTRIPPrefix")
}
m.Prefix, _ = netip.AddrFromSlice(data[12:28])
m.AS = binary.BigEndian.Uint32(data[28:32])
}
return nil
}
func (m *RTRIPPrefix) Serialize() ([]byte, error) {
data := make([]byte, m.Len)
data[0] = m.Version
data[1] = m.Type
binary.BigEndian.PutUint32(data[4:8], m.Len)
data[8] = m.Flags
data[9] = m.PrefixLen
data[10] = m.MaxLen
if m.Type == RTR_IPV4_PREFIX {
copy(data[12:16], m.Prefix.AsSlice())
binary.BigEndian.PutUint32(data[16:20], m.AS)
} else {
copy(data[12:28], m.Prefix.AsSlice())
binary.BigEndian.PutUint32(data[28:32], m.AS)
}
return data, nil
}
func NewRTRIPPrefix(prefix netip.Addr, prefixLen, maxLen uint8, as uint32, flags uint8) *RTRIPPrefix {
var pduType uint8
var pduLen uint32
if prefix.Is4() && prefixLen <= 32 {
pduType = RTR_IPV4_PREFIX
pduLen = RTR_IPV4_PREFIX_LEN
} else if prefix.Is6() && prefixLen <= 128 {
pduType = RTR_IPV6_PREFIX
pduLen = RTR_IPV6_PREFIX_LEN
} else {
// TODO: return error; !prefix.IsValid() or invalid prefix length
return nil
}
return &RTRIPPrefix{
Type: pduType,
Len: pduLen,
Flags: flags,
PrefixLen: prefixLen,
MaxLen: maxLen,
Prefix: prefix,
AS: as,
}
}
type RTREndOfData struct {
RTRCommon
}
func NewRTREndOfData(id uint16, sn uint32) *RTREndOfData {
return &RTREndOfData{
RTRCommon{
Type: RTR_END_OF_DATA,
SessionID: id,
Len: RTR_END_OF_DATA_LEN,
SerialNumber: sn,
},
}
}
type RTRCacheReset struct {
RTRReset
}
func NewRTRCacheReset() *RTRCacheReset {
return &RTRCacheReset{
RTRReset{
Type: RTR_CACHE_RESET,
Len: RTR_CACHE_RESET_LEN,
},
}
}
type RTRErrorReport struct {
Version uint8
Type uint8
ErrorCode uint16
Len uint32
PDULen uint32
PDU []byte
TextLen uint32
Text []byte
}
func (m *RTRErrorReport) DecodeFromBytes(data []byte) error {
if len(data) < 12 {
return errors.New("data too short for RTRErrorReport")
}
m.Version = data[0]
m.Type = data[1]
m.ErrorCode = binary.BigEndian.Uint16(data[2:4])
m.Len = binary.BigEndian.Uint32(data[4:8])
m.PDULen = binary.BigEndian.Uint32(data[8:12])
if len(data) < 12+int(m.PDULen)+4 {
return errors.New("data too short for RTRErrorReport")
}
m.PDU = make([]byte, m.PDULen)
copy(m.PDU, data[12:12+m.PDULen])
m.TextLen = binary.BigEndian.Uint32(data[12+m.PDULen : 16+m.PDULen])
m.Text = make([]byte, m.TextLen)
copy(m.Text, data[16+m.PDULen:])
return nil
}
func (m *RTRErrorReport) Serialize() ([]byte, error) {
data := make([]byte, m.Len)
data[0] = m.Version
data[1] = m.Type
binary.BigEndian.PutUint16(data[2:4], m.ErrorCode)
binary.BigEndian.PutUint32(data[4:8], m.Len)
binary.BigEndian.PutUint32(data[8:12], m.PDULen)
copy(data[12:], m.PDU)
binary.BigEndian.PutUint32(data[12+m.PDULen:16+m.PDULen], m.TextLen)
copy(data[16+m.PDULen:], m.Text)
return data, nil
}
func NewRTRErrorReport(errCode uint16, errPDU []byte, errMsg []byte) *RTRErrorReport {
pdu := &RTRErrorReport{Type: RTR_ERROR_REPORT, ErrorCode: errCode}
if errPDU != nil {
if errPDU[1] == RTR_ERROR_REPORT {
return nil
}
pdu.PDULen = uint32(len(errPDU))
pdu.PDU = errPDU
}
if errMsg != nil {
pdu.Text = errMsg
pdu.TextLen = uint32(len(errMsg))
}
pdu.Len = uint32(RTR_MIN_LEN) + uint32(RTR_ERROR_REPORT_ERR_PDU_LEN) + pdu.PDULen + uint32(RTR_ERROR_REPORT_ERR_TEXT_LEN) + pdu.TextLen
return pdu
}
func ParseRTR(data []byte) (RTRMessage, error) {
if len(data) < RTR_MIN_LEN {
return nil, fmt.Errorf("not all bytes are available for RTR message")
}
var msg RTRMessage
switch data[1] {
case RTR_SERIAL_NOTIFY:
msg = &RTRSerialNotify{}
case RTR_SERIAL_QUERY:
msg = &RTRSerialQuery{}
case RTR_RESET_QUERY:
msg = &RTRResetQuery{}
case RTR_CACHE_RESPONSE:
msg = &RTRCacheResponse{}
case RTR_IPV4_PREFIX:
msg = &RTRIPPrefix{}
case RTR_IPV6_PREFIX:
msg = &RTRIPPrefix{}
case RTR_END_OF_DATA:
msg = &RTREndOfData{}
case RTR_CACHE_RESET:
msg = &RTRCacheReset{}
case RTR_ERROR_REPORT:
msg = &RTRErrorReport{}
default:
return nil, fmt.Errorf("unknown RTR message type %d", data[1])
}
err := msg.DecodeFromBytes(data)
return msg, err
}
// Code generated by "stringer -type=afi"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[afiIP-1]
_ = x[afiIP6-2]
_ = x[afiEther-3]
_ = x[afiMax-4]
}
const _afi_name = "afiIPafiIP6afiEtherafiMax"
var _afi_index = [...]uint8{0, 5, 11, 19, 25}
func (i afi) String() string {
i -= 1
if i >= afi(len(_afi_index)-1) {
return "afi(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _afi_name[_afi_index[i]:_afi_index[i+1]]
}
// Code generated by "stringer -type=APIType"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[interfaceAdd-0]
_ = x[interfaceDelete-1]
_ = x[interfaceAddressAdd-2]
_ = x[interfaceAddressDelete-3]
_ = x[interfaceUp-4]
_ = x[interfaceDown-5]
_ = x[_interfaceSetMaster-6]
_ = x[_interfaceSetProtoDown-7]
_ = x[RouteAdd-8]
_ = x[RouteDelete-9]
_ = x[_routeNotifyOwner-10]
_ = x[redistributeAdd-11]
_ = x[_redistributeDelete-12]
_ = x[_redistributeDefaultAdd-13]
_ = x[_redistributeDefaultDelete-14]
_ = x[routerIDAdd-15]
_ = x[_routerIDDelete-16]
_ = x[routerIDUpdate-17]
_ = x[Hello-18]
_ = x[_capabilities-19]
_ = x[nexthopRegister-20]
_ = x[nexthopUnregister-21]
_ = x[nexthopUpdate-22]
_ = x[_interfaceNBRAddressAdd-23]
_ = x[_interfaceNBRAddressDelete-24]
_ = x[_interfaceBFDDestUpdate-25]
_ = x[_importRouteRegister-26]
_ = x[_importRouteUnregister-27]
_ = x[_importCheckUpdate-28]
_ = x[_bfdDestRegister-29]
_ = x[_bfdDestDeregister-30]
_ = x[_bfdDestUpdate-31]
_ = x[_bfdDestReplay-32]
_ = x[RedistributeRouteAdd-33]
_ = x[RedistributeRouteDel-34]
_ = x[_vrfUnregister-35]
_ = x[_vrfAdd-36]
_ = x[_vrfDelete-37]
_ = x[vrfLabel-38]
_ = x[_interfaceVRFUpdate-39]
_ = x[_bfdClientRegister-40]
_ = x[_bfdClientDeregister-41]
_ = x[_interfaceEnableRADV-42]
_ = x[_interfaceDisableRADV-43]
_ = x[ipv4NexthopLookupMRIB-44]
_ = x[_interfaceLinkParams-45]
_ = x[_mplsLabelsAdd-46]
_ = x[_mplsLabelsDelete-47]
_ = x[_mplsLabelsReplace-48]
_ = x[_srPolicySet-49]
_ = x[_srPolicyDelete-50]
_ = x[_srPolicyNotifyStatus-51]
_ = x[_ipmrRouteStats-52]
_ = x[labelManagerConnect-53]
_ = x[labelManagerConnectAsync-54]
_ = x[getLabelChunk-55]
_ = x[releaseLabelChunk-56]
_ = x[_fecRegister-57]
_ = x[_fecUnregister-58]
_ = x[_fecUpdate-59]
_ = x[_advertiseDefaultGW-60]
_ = x[_advertiseSviMACIP-61]
_ = x[_advertiseSubnet-62]
_ = x[_advertiseAllVNI-63]
_ = x[_localESAdd-64]
_ = x[_localESDel-65]
_ = x[_remoteESVTEPAdd-66]
_ = x[_remoteESVTEPDel-67]
_ = x[_localESEVIAdd-68]
_ = x[_localESEVIDel-69]
_ = x[_vniAdd-70]
_ = x[_vniDel-71]
_ = x[_l3VNIAdd-72]
_ = x[_l3VNIDel-73]
_ = x[_remoteVTEPAdd-74]
_ = x[_remoteVTEPDel-75]
_ = x[_macIPAdd-76]
_ = x[_macIPDel-77]
_ = x[_ipPrefixRouteAdd-78]
_ = x[_ipPrefixRouteDel-79]
_ = x[_remoteMACIPAdd-80]
_ = x[_remoteMACIPDel-81]
_ = x[_duplicateAddrDetection-82]
_ = x[_pwAdd-83]
_ = x[_pwDelete-84]
_ = x[_pwSet-85]
_ = x[_pwUnset-86]
_ = x[_pwStatusUpdate-87]
_ = x[_ruleAdd-88]
_ = x[_ruleDelete-89]
_ = x[_ruleNotifyOwner-90]
_ = x[_tableManagerConnect-91]
_ = x[_getTableChunk-92]
_ = x[_releaseTableChunk-93]
_ = x[_ipSetCreate-94]
_ = x[_ipSetDestroy-95]
_ = x[_ipSetEntryAdd-96]
_ = x[_ipSetEntryDelete-97]
_ = x[_ipSetNotifyOwner-98]
_ = x[_ipSetEntryNotifyOwner-99]
_ = x[_ipTableAdd-100]
_ = x[_ipTableDelete-101]
_ = x[_ipTableNotifyOwner-102]
_ = x[_vxlanFloodControl-103]
_ = x[_vxlanSgAdd-104]
_ = x[_vxlanSgDel-105]
_ = x[_vxlanSgReplay-106]
_ = x[_mlagProcessUp-107]
_ = x[_mlagProcessDown-108]
_ = x[_mlagClientRegister-109]
_ = x[_mlagClientUnregister-110]
_ = x[_mlagClientForwardMsg-111]
_ = x[_nhgAdd-112]
_ = x[_nhgDel-113]
_ = x[_nhgNotifyOwner-114]
_ = x[_nhgEvpnRemoteNhAdd-115]
_ = x[_nhgEvpnRemoteNhDel-116]
_ = x[_srv6LocatorAdd-117]
_ = x[_srv6LocatorDelete-118]
_ = x[_srv6ManagerGetLocatorChunk-119]
_ = x[_srv6ManagerReleaseLocatorChunk-120]
_ = x[zebraError-121]
_ = x[_clientCapabilities-122]
_ = x[_opaqueMessage-123]
_ = x[_opaqueRegister-124]
_ = x[_opaqueUnregister-125]
_ = x[_neighDiscover-126]
_ = x[_RouteNotifyRequest-127]
_ = x[_ClientCloseNotify-128]
_ = x[_NhrpNeighAdded-129]
_ = x[_NhrpNeighRemoved-130]
_ = x[_NhrpNeighGet-131]
_ = x[_NhrpNeighRegister-132]
_ = x[_NhrpNeighUnregister-133]
_ = x[_NeighIPAdd-134]
_ = x[_NeighIPDel-135]
_ = x[_ConfigureArp-136]
_ = x[_GreGet-137]
_ = x[_GreUpdate-138]
_ = x[_GreSourceSet-139]
_ = x[BackwardIPv6RouteAdd-140]
_ = x[BackwardIPv6RouteDelete-141]
_ = x[zapi6Frr8dot2MinDifferentAPIType-26]
_ = x[zapi6Frr7dot3MinDifferentAPIType-49]
_ = x[zapi6Frr7dot2MinDifferentAPIType-48]
_ = x[zapi6Frr6MinDifferentAPIType-7]
_ = x[zapi5ClMinDifferentAPIType-19]
_ = x[zapi5MinDifferentAPIType-7]
_ = x[zapi4MinDifferentAPIType-6]
_ = x[zapi3MinDifferentAPIType-0]
_ = x[zapi6Frr8dot2RedistributeRouteAdd-30]
_ = x[zapi6Frr8dot2RedistributeRouteDel-31]
_ = x[zapi6Frr8dot2VrfLabel-35]
_ = x[zapi6Frr8dot2Ipv4NexthopLookupMRIB-41]
_ = x[zapi6Frr8dot2LabelManagerConnect-50]
_ = x[zapi6Frr8dot2LabelManagerConnectAsync-51]
_ = x[zapi6Frr8dot2GetLabelChunk-52]
_ = x[zapi6Frr8dot2ReleaseLabelChunk-53]
_ = x[zapi6Frr7dot3LabelManagerConnect-50]
_ = x[zapi6Frr7dot3LabelManagerConnectAsync-51]
_ = x[zapi6Frr7dot3GetLabelChunk-52]
_ = x[zapi6Frr7dot3ReleaseLabelChunk-53]
_ = x[zapi6Frr7dot2LabelManagerConnect-49]
_ = x[zapi6Frr7dot2LabelManagerConnectAsync-50]
_ = x[zapi6Frr7dot2GetLabelChunk-51]
_ = x[zapi6Frr7dot2ReleaseLabelChunk-52]
_ = x[zapi6Frr7RouteAdd-7]
_ = x[zapi6Frr7RouteDelete-8]
_ = x[zapi6Frr7RedistributAdd-10]
_ = x[zapi6Frr7RouterIDAdd-14]
_ = x[zapi6Frr7RouterIDUpdate-16]
_ = x[zapi6Frr7Hello-17]
_ = x[zapi6Frr7NexthopRegister-19]
_ = x[zapi6Frr7NexthopUnregister-20]
_ = x[zapi6Frr7NexthopUpdate-21]
_ = x[zapi6Frr7RedistributeRouteAdd-32]
_ = x[zapi6Frr7RedistributeRouteDel-33]
_ = x[zapi6Frr7VrfLabel-37]
_ = x[zapi6Frr7Ipv4NexthopLookupMRIB-43]
_ = x[zapi6Frr7LabelManagerConnect-48]
_ = x[zapi6Frr7LabelManagerConnectAsync-49]
_ = x[zapi6Frr7GetLabelChunk-50]
_ = x[zapi6Frr7ReleaseLabelChunk-51]
_ = x[zapi5ClIpv4NexthopLookupMRIB-42]
_ = x[zapi5ClLabelManagerConnect-47]
_ = x[zapi5ClGetLabelChunk-48]
_ = x[zapi5ClReleaseLabelChunk-49]
_ = x[zapi5RedistributAdd-14]
_ = x[zapi5RouterIDAdd-18]
_ = x[zapi5RouterIDUpdate-20]
_ = x[zapi5Hello-21]
_ = x[zapi5Frr5NexthopRegister-23]
_ = x[zapi5Frr5NexthopUnregister-24]
_ = x[zapi5Frr5NexthopUpdate-25]
_ = x[zapi5Frr5RedistributeRouteAdd-37]
_ = x[zapi5Frr5RedistributeRouteDel-38]
_ = x[zapi5Frr5VrfLabel-42]
_ = x[zapi5Frr5Ipv4NexthopLookupMRIB-47]
_ = x[zapi5Frr5LabelManagerConnect-52]
_ = x[zapi5Frr5LabelManagerConnectAsync-53]
_ = x[zapi5Frr5GetLabelChunk-54]
_ = x[zapi5Frr5ReleaseLabelChunk-55]
_ = x[zapi5Frr4NexthopRegister-22]
_ = x[zapi5Frr4NexthopUnregister-23]
_ = x[zapi5Frr4NexthopUpdate-24]
_ = x[zapi5Frr4RedistributeRouteAdd-36]
_ = x[zapi5Frr4RedistributeRouteDel-37]
_ = x[zapi5Frr4Ipv4NexthopLookupMRIB-45]
_ = x[zapi5Frr4LabelManagerConnect-50]
_ = x[zapi5Frr4GetLabelChunk-51]
_ = x[zapi5Frr4ReleaseLabelChunk-52]
_ = x[zapi4IPv4RouteAdd-6]
_ = x[zapi4IPv4RouteDelete-7]
_ = x[zapi4IPv6RouteAdd-8]
_ = x[zapi4IPv6RouteDelete-9]
_ = x[zapi4RedistributAdd-10]
_ = x[zapi4RouterIDAdd-14]
_ = x[zapi4RouterIDUpdate-16]
_ = x[zapi4Hello-17]
_ = x[zapi4NexthopRegister-18]
_ = x[zapi4NexthopUnregister-19]
_ = x[zapi4NexthopUpdate-20]
_ = x[zapi4RedistributeIPv4Add-32]
_ = x[zapi4RedistributeIPv4Del-33]
_ = x[zapi4RedistributeIPv6Add-34]
_ = x[zapi4RedistributeIPv6Del-35]
_ = x[zapi4LabelManagerConnect-52]
_ = x[zapi4GetLabelChunk-53]
_ = x[zapi4ReleaseLabelChunk-54]
_ = x[zapi3InterfaceAdd-1]
_ = x[zapi3InterfaceDelete-2]
_ = x[zapi3InterfaceAddressAdd-3]
_ = x[zapi3InterfaceAddressDelete-4]
_ = x[zapi3InterfaceUp-5]
_ = x[zapi3InterfaceDown-6]
_ = x[zapi3IPv4RouteAdd-7]
_ = x[zapi3IPv4RouteDelete-8]
_ = x[zapi3IPv6RouteAdd-9]
_ = x[zapi3IPv6RouteDelete-10]
_ = x[zapi3RedistributeAdd-11]
_ = x[zapi3IPv4NexthopLookup-15]
_ = x[zapi3IPv6NexthopLookup-16]
_ = x[zapi3IPv4ImportLookup-17]
_ = x[zapi3RouterIDAdd-20]
_ = x[zapi3RouterIDUpdate-22]
_ = x[zapi3Hello-23]
_ = x[zapi3Ipv4NexthopLookupMRIB-24]
_ = x[zapi3NexthopRegister-27]
_ = x[zapi3NexthopUnregister-28]
_ = x[zapi3NexthopUpdate-29]
}
const _APIType_name = "interfaceAddinterfaceDeleteinterfaceAddressAddinterfaceAddressDeleteinterfaceUpinterfaceDown_interfaceSetMaster_interfaceSetProtoDownRouteAddRouteDelete_routeNotifyOwnerredistributeAdd_redistributeDelete_redistributeDefaultAdd_redistributeDefaultDeleterouterIDAdd_routerIDDeleterouterIDUpdateHello_capabilitiesnexthopRegisternexthopUnregisternexthopUpdate_interfaceNBRAddressAdd_interfaceNBRAddressDelete_interfaceBFDDestUpdate_importRouteRegister_importRouteUnregister_importCheckUpdate_bfdDestRegister_bfdDestDeregister_bfdDestUpdate_bfdDestReplayRedistributeRouteAddRedistributeRouteDel_vrfUnregister_vrfAdd_vrfDeletevrfLabel_interfaceVRFUpdate_bfdClientRegister_bfdClientDeregister_interfaceEnableRADV_interfaceDisableRADVipv4NexthopLookupMRIB_interfaceLinkParams_mplsLabelsAdd_mplsLabelsDelete_mplsLabelsReplace_srPolicySet_srPolicyDelete_srPolicyNotifyStatus_ipmrRouteStatslabelManagerConnectlabelManagerConnectAsyncgetLabelChunkreleaseLabelChunk_fecRegister_fecUnregister_fecUpdate_advertiseDefaultGW_advertiseSviMACIP_advertiseSubnet_advertiseAllVNI_localESAdd_localESDel_remoteESVTEPAdd_remoteESVTEPDel_localESEVIAdd_localESEVIDel_vniAdd_vniDel_l3VNIAdd_l3VNIDel_remoteVTEPAdd_remoteVTEPDel_macIPAdd_macIPDel_ipPrefixRouteAdd_ipPrefixRouteDel_remoteMACIPAdd_remoteMACIPDel_duplicateAddrDetection_pwAdd_pwDelete_pwSet_pwUnset_pwStatusUpdate_ruleAdd_ruleDelete_ruleNotifyOwner_tableManagerConnect_getTableChunk_releaseTableChunk_ipSetCreate_ipSetDestroy_ipSetEntryAdd_ipSetEntryDelete_ipSetNotifyOwner_ipSetEntryNotifyOwner_ipTableAdd_ipTableDelete_ipTableNotifyOwner_vxlanFloodControl_vxlanSgAdd_vxlanSgDel_vxlanSgReplay_mlagProcessUp_mlagProcessDown_mlagClientRegister_mlagClientUnregister_mlagClientForwardMsg_nhgAdd_nhgDel_nhgNotifyOwner_nhgEvpnRemoteNhAdd_nhgEvpnRemoteNhDel_srv6LocatorAdd_srv6LocatorDelete_srv6ManagerGetLocatorChunk_srv6ManagerReleaseLocatorChunkzebraError_clientCapabilities_opaqueMessage_opaqueRegister_opaqueUnregister_neighDiscover_RouteNotifyRequest_ClientCloseNotify_NhrpNeighAdded_NhrpNeighRemoved_NhrpNeighGet_NhrpNeighRegister_NhrpNeighUnregister_NeighIPAdd_NeighIPDel_ConfigureArp_GreGet_GreUpdate_GreSourceSetBackwardIPv6RouteAddBackwardIPv6RouteDelete"
var _APIType_index = [...]uint16{0, 12, 27, 46, 68, 79, 92, 111, 133, 141, 152, 169, 184, 203, 226, 252, 263, 278, 292, 297, 310, 325, 342, 355, 378, 404, 427, 447, 469, 487, 503, 521, 535, 549, 569, 589, 603, 610, 620, 628, 647, 665, 685, 705, 726, 747, 767, 781, 798, 816, 828, 843, 864, 879, 898, 922, 935, 952, 964, 978, 988, 1007, 1025, 1041, 1057, 1068, 1079, 1095, 1111, 1125, 1139, 1146, 1153, 1162, 1171, 1185, 1199, 1208, 1217, 1234, 1251, 1266, 1281, 1304, 1310, 1319, 1325, 1333, 1348, 1356, 1367, 1383, 1403, 1417, 1435, 1447, 1460, 1474, 1491, 1508, 1530, 1541, 1555, 1574, 1592, 1603, 1614, 1628, 1642, 1658, 1677, 1698, 1719, 1726, 1733, 1748, 1767, 1786, 1801, 1819, 1846, 1877, 1887, 1906, 1920, 1935, 1952, 1966, 1985, 2003, 2018, 2035, 2048, 2066, 2086, 2097, 2108, 2121, 2128, 2138, 2151, 2171, 2194}
func (i APIType) String() string {
if i >= APIType(len(_APIType_index)-1) {
return "APIType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _APIType_name[_APIType_index[i]:_APIType_index[i+1]]
}
// Code generated by "stringer -type=linkType"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[linkTypeUnknown-0]
_ = x[linkTypeEther-1]
_ = x[linkTypeEEther-2]
_ = x[linkTypeAX25-3]
_ = x[linkTypePRONET-4]
_ = x[linkTypeIeee802-5]
_ = x[linkTypeARCNET-6]
_ = x[linkTypeAPPLETLK-7]
_ = x[linkTypeDLCI-8]
_ = x[linkTypeATM-9]
_ = x[linkTypeMetricOM-10]
_ = x[linkTypeIeee1394-11]
_ = x[linkTypeEUI64-12]
_ = x[linkTypeINFINIBAND-13]
_ = x[linkTypeSLIP-14]
_ = x[linkTypeCSLIP-15]
_ = x[linkTypeSLIP6-16]
_ = x[linkTypeCSLIP6-17]
_ = x[linkTypeRSRVD-18]
_ = x[linkTypeADAPT-19]
_ = x[linkTypeROSE-20]
_ = x[linkTypeX25-21]
_ = x[linkTypePPP-22]
_ = x[linkTypeCHDLC-23]
_ = x[linkTypeLAPB-24]
_ = x[linkTypeRAWHDLC-25]
_ = x[linkTypeIPIP-26]
_ = x[linkTypeIPIP6-27]
_ = x[linkTypeFRAD-28]
_ = x[linkTypeSKIP-29]
_ = x[linkTypeLOOPBACK-30]
_ = x[linkTypeLOCALTLK-31]
_ = x[linkTypeFDDI-32]
_ = x[linkTypeSIT-33]
_ = x[linkTypeIPDDP-34]
_ = x[linkTypeIPGRE-35]
_ = x[linkTypeIP6GRE-36]
_ = x[linkTypePIMREG-37]
_ = x[linkTypeHIPPI-38]
_ = x[linkTypeECONET-39]
_ = x[linkTypeIRDA-40]
_ = x[linkTypeFCPP-41]
_ = x[linkTypeFCAL-42]
_ = x[linkTypeFCPL-43]
_ = x[linkTypeFCFABRIC-44]
_ = x[linkTypeIeee802Tr-45]
_ = x[linkTypeIeee80211-46]
_ = x[linkTypeIeee80211RadioTap-47]
_ = x[linkTypeIeee802154-48]
_ = x[linkTypeIeee802154Phy-49]
}
const _linkType_name = "linkTypeUnknownlinkTypeEtherlinkTypeEEtherlinkTypeAX25linkTypePRONETlinkTypeIeee802linkTypeARCNETlinkTypeAPPLETLKlinkTypeDLCIlinkTypeATMlinkTypeMetricOMlinkTypeIeee1394linkTypeEUI64linkTypeINFINIBANDlinkTypeSLIPlinkTypeCSLIPlinkTypeSLIP6linkTypeCSLIP6linkTypeRSRVDlinkTypeADAPTlinkTypeROSElinkTypeX25linkTypePPPlinkTypeCHDLClinkTypeLAPBlinkTypeRAWHDLClinkTypeIPIPlinkTypeIPIP6linkTypeFRADlinkTypeSKIPlinkTypeLOOPBACKlinkTypeLOCALTLKlinkTypeFDDIlinkTypeSITlinkTypeIPDDPlinkTypeIPGRElinkTypeIP6GRElinkTypePIMREGlinkTypeHIPPIlinkTypeECONETlinkTypeIRDAlinkTypeFCPPlinkTypeFCALlinkTypeFCPLlinkTypeFCFABRIClinkTypeIeee802TrlinkTypeIeee80211linkTypeIeee80211RadioTaplinkTypeIeee802154linkTypeIeee802154Phy"
var _linkType_index = [...]uint16{0, 15, 28, 42, 54, 68, 83, 97, 113, 125, 136, 152, 168, 181, 199, 211, 224, 237, 251, 264, 277, 289, 300, 311, 324, 336, 351, 363, 376, 388, 400, 416, 432, 444, 455, 468, 481, 495, 509, 522, 536, 548, 560, 572, 584, 600, 617, 634, 659, 677, 698}
func (i linkType) String() string {
if i >= linkType(len(_linkType_index)-1) {
return "linkType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _linkType_name[_linkType_index[i]:_linkType_index[i+1]]
}
// Code generated by "stringer -type=lspTYPE"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[lspNone-0]
_ = x[lspStatic-1]
_ = x[lspLDP-2]
_ = x[lspBGP-3]
_ = x[lspSR-4]
_ = x[lspSHARP-5]
}
const _lspTYPE_name = "lspNonelspStaticlspLDPlspBGPlspSRlspSHARP"
var _lspTYPE_index = [...]uint8{0, 7, 16, 22, 28, 33, 41}
func (i lspTYPE) String() string {
if i >= lspTYPE(len(_lspTYPE_index)-1) {
return "lspTYPE(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _lspTYPE_name[_lspTYPE_index[i]:_lspTYPE_index[i+1]]
}
// Code generated by "stringer -type=nexthopFlag"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[nexthopFlagActive-1]
_ = x[nexthopFlagFIB-2]
_ = x[nexthopFlagRecursive-4]
_ = x[nexthopFlagOnlink-8]
_ = x[nexthopFlagDuplicate-16]
_ = x[nexthopFlagRnhFiltered-32]
_ = x[nexthopFlagHasBackup-64]
_ = x[nexthopFlagSRTE-128]
_ = x[zapi6Frr7dot3nexthopFlagMatched-16]
_ = x[zapi6Frr7dot3nexthopFlagDuplicate-32]
_ = x[zapi6Frr7dot3nexthopFlagRnhFiltered-64]
_ = x[zapi6Frr7nexthopFlagFiltered-32]
_ = x[zapi6Frr7nexthopFlagDuplicate-64]
_ = x[zapi6Frr7nexthopFlagEvpnRvtep-128]
}
const (
_nexthopFlag_name_0 = "nexthopFlagActivenexthopFlagFIB"
_nexthopFlag_name_1 = "nexthopFlagRecursive"
_nexthopFlag_name_2 = "nexthopFlagOnlink"
_nexthopFlag_name_3 = "nexthopFlagDuplicate"
_nexthopFlag_name_4 = "nexthopFlagRnhFiltered"
_nexthopFlag_name_5 = "nexthopFlagHasBackup"
_nexthopFlag_name_6 = "nexthopFlagSRTE"
)
var (
_nexthopFlag_index_0 = [...]uint8{0, 17, 31}
)
func (i nexthopFlag) String() string {
switch {
case 1 <= i && i <= 2:
i -= 1
return _nexthopFlag_name_0[_nexthopFlag_index_0[i]:_nexthopFlag_index_0[i+1]]
case i == 4:
return _nexthopFlag_name_1
case i == 8:
return _nexthopFlag_name_2
case i == 16:
return _nexthopFlag_name_3
case i == 32:
return _nexthopFlag_name_4
case i == 64:
return _nexthopFlag_name_5
case i == 128:
return _nexthopFlag_name_6
default:
return "nexthopFlag(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
// Code generated by "stringer -type=nexthopType"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[nexthopTypeIFIndex-1]
_ = x[nexthopTypeIPv4-2]
_ = x[nexthopTypeIPv4IFIndex-3]
_ = x[nexthopTypeIPv6-4]
_ = x[nexthopTypeIPv6IFIndex-5]
_ = x[nexthopTypeBlackhole-6]
_ = x[nexthopTypeIFName-2]
_ = x[backwardNexthopTypeIPv4-3]
_ = x[backwardNexthopTypeIPv4IFIndex-4]
_ = x[nexthopTypeIPv4IFName-5]
_ = x[backwardNexthopTypeIPv6-6]
_ = x[backwardNexthopTypeIPv6IFIndex-7]
_ = x[nexthopTypeIPv6IFName-8]
_ = x[backwardNexthopTypeBlackhole-9]
}
const _nexthopType_name = "nexthopTypeIFIndexnexthopTypeIPv4nexthopTypeIPv4IFIndexnexthopTypeIPv6nexthopTypeIPv6IFIndexnexthopTypeBlackholebackwardNexthopTypeIPv6IFIndexnexthopTypeIPv6IFNamebackwardNexthopTypeBlackhole"
var _nexthopType_index = [...]uint8{0, 18, 33, 55, 70, 92, 112, 142, 163, 191}
func (i nexthopType) String() string {
i -= 1
if i >= nexthopType(len(_nexthopType_index)-1) {
return "nexthopType(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _nexthopType_name[_nexthopType_index[i]:_nexthopType_index[i+1]]
}
// Code generated by "stringer -type=ptmEnable"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ptmEnableOff-0]
_ = x[ptmEnableOn-1]
_ = x[ptmEnableUnspec-2]
}
const _ptmEnable_name = "ptmEnableOffptmEnableOnptmEnableUnspec"
var _ptmEnable_index = [...]uint8{0, 12, 23, 38}
func (i ptmEnable) String() string {
if i >= ptmEnable(len(_ptmEnable_index)-1) {
return "ptmEnable(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ptmEnable_name[_ptmEnable_index[i]:_ptmEnable_index[i+1]]
}
// Code generated by "stringer -type=ptmStatus"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ptmStatusDown-0]
_ = x[ptmStatusUp-1]
_ = x[ptmStatusUnknown-2]
}
const _ptmStatus_name = "ptmStatusDownptmStatusUpptmStatusUnknown"
var _ptmStatus_index = [...]uint8{0, 13, 24, 40}
func (i ptmStatus) String() string {
if i >= ptmStatus(len(_ptmStatus_index)-1) {
return "ptmStatus(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ptmStatus_name[_ptmStatus_index[i]:_ptmStatus_index[i+1]]
}
// Code generated by "stringer -type=RouteType"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[routeSystem-0]
_ = x[routeKernel-1]
_ = x[routeConnect-2]
_ = x[RouteStatic-3]
_ = x[routeRIP-4]
_ = x[routeRIPNG-5]
_ = x[routeOSPF-6]
_ = x[routeOSPF6-7]
_ = x[routeISIS-8]
_ = x[RouteBGP-9]
_ = x[routePIM-10]
_ = x[routeEIGRP-11]
_ = x[routeNHRP-12]
_ = x[routeHSLS-13]
_ = x[routeOLSR-14]
_ = x[routeTABLE-15]
_ = x[routeLDP-16]
_ = x[routeVNC-17]
_ = x[routeVNCDirect-18]
_ = x[routeVNCDirectRH-19]
_ = x[routeBGPDirect-20]
_ = x[routeBGPDirectEXT-21]
_ = x[routeBABEL-22]
_ = x[routeSHARP-23]
_ = x[routePBR-24]
_ = x[routeBFD-25]
_ = x[routeOpenfabric-26]
_ = x[routeVRRP-27]
_ = x[routeNHG-28]
_ = x[routeSRTE-29]
_ = x[routeAll-30]
_ = x[routeMax-31]
_ = x[zapi5Frr4RouteAll-24]
_ = x[zapi5Frr5RouteAll-25]
_ = x[zapi6Frr6RouteAll-26]
_ = x[zapi6Frr7RouteAll-27]
_ = x[zapi6Frr7dot2RouteAll-28]
_ = x[zapi6Frr7dot3RouteAll-29]
_ = x[zapi4RouteNHRP-11]
_ = x[zapi4RouteHSLS-12]
_ = x[zapi4RouteOLSR-13]
_ = x[zapi4RouteTABLE-14]
_ = x[zapi4RouteLDP-15]
_ = x[zapi4RouteVNC-16]
_ = x[zapi4RouteVNCDirect-17]
_ = x[zapi4RouteVNCDirectRH-18]
_ = x[zapi4RouteBGPDixrect-19]
_ = x[zapi4RouteBGPDirectEXT-20]
_ = x[zapi4RouteAll-21]
_ = x[zapi3RouteHSLS-11]
_ = x[zapi3RouteOLSR-12]
_ = x[zapi3RouteBABEL-13]
_ = x[zapi3RouteNHRP-14]
}
const _RouteType_name = "routeSystemrouteKernelrouteConnectRouteStaticrouteRIProuteRIPNGrouteOSPFrouteOSPF6routeISISRouteBGProutePIMrouteEIGRProuteNHRProuteHSLSrouteOLSRrouteTABLErouteLDProuteVNCrouteVNCDirectrouteVNCDirectRHrouteBGPDirectrouteBGPDirectEXTrouteBABELrouteSHARProutePBRrouteBFDrouteOpenfabricrouteVRRProuteNHGrouteSRTErouteAllrouteMax"
var _RouteType_index = [...]uint16{0, 11, 22, 34, 45, 53, 63, 72, 82, 91, 99, 107, 117, 126, 135, 144, 154, 162, 170, 184, 200, 214, 231, 241, 251, 259, 267, 282, 291, 299, 308, 316, 324}
func (i RouteType) String() string {
if i >= RouteType(len(_RouteType_index)-1) {
return "RouteType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _RouteType_name[_RouteType_index[i]:_RouteType_index[i+1]]
}
// Code generated by "stringer -type=Safi"; DO NOT EDIT.
package zebra
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[safiUnspec-0]
_ = x[SafiUnicast-1]
_ = x[safiMulticast-2]
_ = x[safiMplsVpn-3]
_ = x[safiEncap-4]
_ = x[safiEvpn-5]
_ = x[safiLabeledUnicast-6]
_ = x[safiFlowspec-7]
_ = x[safiMax-8]
_ = x[zapi4SafiMplsVpn-3]
_ = x[zapi3SafiMplsVpn-4]
_ = x[zapi4SafiEncap-5]
_ = x[zapi4SafiEvpn-6]
_ = x[zapi3SafiEncap-7]
}
const _Safi_name = "safiUnspecSafiUnicastsafiMulticastsafiMplsVpnsafiEncapsafiEvpnsafiLabeledUnicastsafiFlowspecsafiMax"
var _Safi_index = [...]uint8{0, 10, 21, 34, 45, 54, 62, 80, 92, 99}
func (i Safi) String() string {
if i >= Safi(len(_Safi_index)-1) {
return "Safi(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Safi_name[_Safi_index[i]:_Safi_index[i+1]]
}
// Copyright (C) 2014, 2015 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 zebra
import (
"encoding/binary"
"errors"
"fmt"
"io"
"log/slog"
"math"
"net"
"net/netip"
"regexp"
"strconv"
"strings"
"syscall"
"github.com/osrg/gobgp/v4/pkg/packet/bgp"
)
const (
// MinZapiVer is minimum zebra api version which is referred in zclient
MinZapiVer uint8 = 2
// MaxZapiVer is maximum zebra api version which is referredd in zclient
MaxZapiVer uint8 = 6
// DefaultVrf is default vrf id is referredd in zclient and server
DefaultVrf = 0
)
var MaxSoftware = NewSoftware(MaxZapiVer, "frr8.2")
const (
headerMarker uint8 = 255
frrHeaderMarker uint8 = 254
interfaceNameSize = 20
osIfNameSize = 16
maxPathNum = 64
maxMplsLabel = 16
)
// Internal Interface Status.
type interfaceStatus uint8
const (
interfaceActive interfaceStatus = 0x01
interfaceSub interfaceStatus = 0x02
interfaceLinkDetection interfaceStatus = 0x04
interfaceVrfLoopback interfaceStatus = 0x08
)
// Interface Link Layer Types.
//
//go:generate stringer -type=linkType
type linkType uint32
const (
linkTypeUnknown linkType = iota
linkTypeEther
linkTypeEEther
linkTypeAX25
linkTypePRONET
linkTypeIeee802
linkTypeARCNET
linkTypeAPPLETLK
linkTypeDLCI
linkTypeATM
linkTypeMetricOM
linkTypeIeee1394
linkTypeEUI64
linkTypeINFINIBAND
linkTypeSLIP
linkTypeCSLIP
linkTypeSLIP6
linkTypeCSLIP6
linkTypeRSRVD
linkTypeADAPT
linkTypeROSE
linkTypeX25
linkTypePPP
linkTypeCHDLC
linkTypeLAPB
linkTypeRAWHDLC
linkTypeIPIP
linkTypeIPIP6
linkTypeFRAD
linkTypeSKIP
linkTypeLOOPBACK
linkTypeLOCALTLK
linkTypeFDDI
linkTypeSIT
linkTypeIPDDP
linkTypeIPGRE
linkTypeIP6GRE
linkTypePIMREG
linkTypeHIPPI
linkTypeECONET
linkTypeIRDA
linkTypeFCPP
linkTypeFCAL
linkTypeFCPL
linkTypeFCFABRIC
linkTypeIeee802Tr
linkTypeIeee80211
linkTypeIeee80211RadioTap
linkTypeIeee802154
linkTypeIeee802154Phy
)
// HeaderSize returns suitable header size from version
func HeaderSize(version uint8) uint16 {
switch version {
case 3, 4:
return 8
case 5, 6:
return 10
}
return 6 // version == 2
}
// HeaderMarker returns suitable header marker from version
func HeaderMarker(version uint8) uint8 {
if version > 3 {
return frrHeaderMarker
}
return headerMarker
}
func (t interfaceStatus) String() string {
ss := make([]string, 0, 3)
if t&interfaceActive > 0 {
ss = append(ss, "Active")
}
if t&interfaceSub > 0 {
ss = append(ss, "Sub")
}
if t&interfaceLinkDetection > 0 {
ss = append(ss, "LinkDetection")
}
if t&interfaceVrfLoopback > 0 {
ss = append(ss, "VrfLoopback")
}
return strings.Join(ss, "|")
}
// Interface Connected Address Flags
type interfaceAddressFlag uint8
const (
interfaceAddressSecondary interfaceAddressFlag = 0x01
interfaceAddressPeer interfaceAddressFlag = 0x02
interfaceAddressUnnumbered interfaceAddressFlag = 0x04
)
func (t interfaceAddressFlag) String() string {
ss := make([]string, 0, 3)
if t&interfaceAddressSecondary > 0 {
ss = append(ss, "SECONDARY")
}
if t&interfaceAddressPeer > 0 {
ss = append(ss, "PEER")
}
if t&interfaceAddressUnnumbered > 0 {
ss = append(ss, "UNNUMBERED")
}
return strings.Join(ss, "|")
}
// Address Family IDentifier.
//
//go:generate stringer -type=afi
type afi uint8
const (
afiIP afi = 1
afiIP6 afi = 2
afiEther afi = 3
afiMax afi = 4
)
// Safi is Subsequent Address Family IDentifier.
//
//go:generate stringer -type=Safi
type Safi uint8
// Safi definition in Zebra of FRRouting 4.x, 5.x, 6.x, 7.x, and 8.x(lib/zebra.h)
const (
safiUnspec Safi = iota // added in FRRouting version 7.2 (Zapi 6)
SafiUnicast
safiMulticast
safiMplsVpn
safiEncap
safiEvpn
safiLabeledUnicast
safiFlowspec // added in FRRouting version 5 (Zapi 5)
safiMax
)
// Safi definition in Zebra of Quagga and FRRouting 3.x
const (
zapi4SafiMplsVpn Safi = iota + safiMulticast + 1 // SafiRESERVED_3 in quagga
zapi3SafiMplsVpn // SafiRESERVED_4 in FRRouting 3.x
zapi4SafiEncap
zapi4SafiEvpn
zapi3SafiEncap // SafiMax in FRRouting 3.x
)
var zapi3SafiMap = map[Safi]Safi{
zapi3SafiMplsVpn: safiMplsVpn,
zapi3SafiEncap: safiEncap,
}
var zapi4SafiMap = map[Safi]Safi{
zapi4SafiMplsVpn: safiMplsVpn,
zapi4SafiEncap: safiEncap,
zapi4SafiEvpn: safiEvpn,
}
var safiFamilyIPv4Map = map[Safi]bgp.Family{
safiUnspec: bgp.RF_OPAQUE,
SafiUnicast: bgp.RF_IPv4_UC,
safiMulticast: bgp.RF_IPv4_MC,
safiMplsVpn: bgp.RF_IPv4_VPN,
safiEncap: bgp.RF_IPv4_ENCAP,
safiLabeledUnicast: bgp.RF_IPv4_MPLS,
safiFlowspec: bgp.RF_FS_IPv4_UC,
}
var safiFamilyIPv6Map = map[Safi]bgp.Family{
safiUnspec: bgp.RF_OPAQUE,
SafiUnicast: bgp.RF_IPv6_UC,
safiMulticast: bgp.RF_IPv6_MC,
safiMplsVpn: bgp.RF_IPv6_VPN,
safiEncap: bgp.RF_IPv6_ENCAP,
safiLabeledUnicast: bgp.RF_IPv6_MPLS,
safiFlowspec: bgp.RF_FS_IPv6_UC,
}
// APIType is referred in zclient_test.
//
//go:generate stringer -type=APIType
type APIType uint16
// For FRRouting version 8.1 (ZAPI version 6)
const (
interfaceAdd APIType = iota // 0 // same ID in frr3, 4, 5, 6, 7.0, 7.1. 7.2. 7.3, 7.4, 7.5, 8.0, 8.1, 8.2
interfaceDelete // same ID in frr3, 4, 5, 6, 7.0, 7.1. 7.2. 7.3, 7.4, 7.5, 8.0, 8.1, 8.2
interfaceAddressAdd // same ID in frr3, 4, 5, 6, 7.0, 7.1. 7.2. 7.3, 7.4, 7.5, 8.0, 8.1, 8.2
interfaceAddressDelete // same ID in frr3, 4, 5, 6, 7.0, 7.1. 7.2. 7.3, 7.4, 7.5, 8.0, 8.1, 8.2
interfaceUp // same ID in frr3, 4, 5, 6, 7.0, 7.1. 7.2. 7.3, 7.4, 7.5, 8.0, 8.1, 8.2
interfaceDown // same ID in frr3, 4, 5, 6, 7.0, 7.1. 7.2. 7.3, 7.4, 7.5, 8.0, 8.1, 8.2
_interfaceSetMaster
_interfaceSetProtoDown // Add in frr 7.2
RouteAdd // RouteAdd is referred in zclient_test
RouteDelete // RouteDelete is referred in zclient_test
_routeNotifyOwner // 10
redistributeAdd
_redistributeDelete
_redistributeDefaultAdd
_redistributeDefaultDelete
routerIDAdd
_routerIDDelete
routerIDUpdate
Hello
_capabilities // added in frr5
nexthopRegister // 20
nexthopUnregister
nexthopUpdate
_interfaceNBRAddressAdd
_interfaceNBRAddressDelete
_interfaceBFDDestUpdate
_importRouteRegister // 25 in frr6, 26 in frr7.x, frr8&8.1, deleted in frr8.2
_importRouteUnregister // 26 in frr6, 27 in frr7.x, frr8&8.1, deleted in frr8.2
_importCheckUpdate // 27 in frr6, 28 in frr7.x, frr8&8.1, deleted in frr8.2
_bfdDestRegister
_bfdDestDeregister // 30
_bfdDestUpdate
_bfdDestReplay
RedistributeRouteAdd // 33 // 30 in frr8.2
RedistributeRouteDel
_vrfUnregister
_vrfAdd
_vrfDelete
vrfLabel // added in frr5
_interfaceVRFUpdate
_bfdClientRegister // 40
_bfdClientDeregister
_interfaceEnableRADV
_interfaceDisableRADV // 43 // 50 in frr8.2
ipv4NexthopLookupMRIB
_interfaceLinkParams
_mplsLabelsAdd
_mplsLabelsDelete
_mplsLabelsReplace // added in frr7.3
_srPolicySet // added in frr7.5
_srPolicyDelete // 50 // added in frr7.5
_srPolicyNotifyStatus // added in frr7.5
_ipmrRouteStats
labelManagerConnect // 53 // 50 in frr8.2
labelManagerConnectAsync // added in frr5
getLabelChunk
releaseLabelChunk
_fecRegister
_fecUnregister
_fecUpdate
_advertiseDefaultGW // 60
_advertiseSviMACIP // added in frr7.1
_advertiseSubnet
_advertiseAllVNI // 63 // 60 in frr8.2
_localESAdd
_localESDel
_remoteESVTEPAdd // added in frr7.5
_remoteESVTEPDel // added in frr7.5
_localESEVIAdd // added in frr7.5
_localESEVIDel // added in frr7.5
_vniAdd // 70
_vniDel
_l3VNIAdd
_l3VNIDel // 73 // 70 in frr8.2
_remoteVTEPAdd
_remoteVTEPDel
_macIPAdd
_macIPDel
_ipPrefixRouteAdd
_ipPrefixRouteDel
_remoteMACIPAdd // 80
_remoteMACIPDel
_duplicateAddrDetection
_pwAdd // 83 // 80 in frr8.2
_pwDelete
_pwSet
_pwUnset
_pwStatusUpdate
_ruleAdd
_ruleDelete
_ruleNotifyOwner // 90
_tableManagerConnect
_getTableChunk
_releaseTableChunk // 93 // 90 in frr8.2
_ipSetCreate
_ipSetDestroy
_ipSetEntryAdd
_ipSetEntryDelete
_ipSetNotifyOwner
_ipSetEntryNotifyOwner
_ipTableAdd // 100
_ipTableDelete
_ipTableNotifyOwner
_vxlanFloodControl // 103 // 100 in frr8.2
_vxlanSgAdd
_vxlanSgDel
_vxlanSgReplay
_mlagProcessUp // added in frr7.3
_mlagProcessDown // added in frr7.3
_mlagClientRegister // added in frr7.3
_mlagClientUnregister // 110 // added in frr7.3
_mlagClientForwardMsg // added in frr7.3
_nhgAdd // added in frr8
_nhgDel // 113 // 110 in frr8.2 // added in frr8
_nhgNotifyOwner // added in frr8
_nhgEvpnRemoteNhAdd // added in frr8
_nhgEvpnRemoteNhDel // added in frr8
_srv6LocatorAdd // added in frr8.1
_srv6LocatorDelete // added in frr8.1
_srv6ManagerGetLocatorChunk // added in frr8.1
_srv6ManagerReleaseLocatorChunk // 120 //added in frr8.1
zebraError // added in frr7.3
_clientCapabilities // added in frr7.4
_opaqueMessage // 123 // 120 in frr8.2 // added in frr7.5
_opaqueRegister // added in frr7.5
_opaqueUnregister // added in frr7.5
_neighDiscover // added in frr7.5
_RouteNotifyRequest // added in frr8
_ClientCloseNotify // added in frr8
_NhrpNeighAdded // added in frr8
_NhrpNeighRemoved // 130 // added in frr8
_NhrpNeighGet // added in frr8
_NhrpNeighRegister // added in frr8
_NhrpNeighUnregister // 133// 130 in frr8.2 // added in frr8
_NeighIPAdd // added in frr8
_NeighIPDel // added in frr8
_ConfigureArp // added in frr8
_GreGet // added in frr8
_GreUpdate // added in frr8
_GreSourceSet // added in frr8
// BackwardIPv6RouteAdd is referred in zclient_test
BackwardIPv6RouteAdd // quagga, frr3, frr4, frr5
// BackwardIPv6RouteDelete is referred in zclient_test
BackwardIPv6RouteDelete // quagga, frr3, frr4, frr5
)
// Difference default version (frr8.1) and older version
const (
zapi6Frr8dot2MinDifferentAPIType APIType = 26 // frr8.2(zapi6)
zapi6Frr7dot3MinDifferentAPIType APIType = 49 // frr7.3(zapi6)
zapi6Frr7dot2MinDifferentAPIType APIType = 48 // frr7.2(zapi6)
zapi6Frr6MinDifferentAPIType APIType = 7 // frr6&7.0&7.1(zapi6)
zapi5ClMinDifferentAPIType APIType = 19 // cumuluslinux3.7.7, zebra4.0+cl3u13(zapi5)
zapi5MinDifferentAPIType APIType = 7 // frr4&5(zapi5), frr6&7.0&7.1(zapi6)
zapi4MinDifferentAPIType APIType = 6
zapi3MinDifferentAPIType APIType = 0
)
func minDifferentAPIType(version uint8, software Software) APIType {
if version < 4 {
return zapi3MinDifferentAPIType
} else if version == 4 {
return zapi4MinDifferentAPIType
} else if version == 5 && software.name == "cumulus" {
return zapi5ClMinDifferentAPIType
} else if version == 5 ||
version == 6 && software.name == "frr" && software.version < 7.2 {
return zapi5MinDifferentAPIType
} else if version == 6 && software.name == "frr" && software.version == 7.2 {
return zapi6Frr7dot2MinDifferentAPIType
} else if version == 6 && software.name == "frr" && software.version < 7.5 {
return zapi6Frr7dot3MinDifferentAPIType
}
return zapi6Frr8dot2MinDifferentAPIType
}
const (
zapi6Frr8dot2RedistributeRouteAdd APIType = 30
zapi6Frr8dot2RedistributeRouteDel APIType = 31
zapi6Frr8dot2VrfLabel APIType = 35
zapi6Frr8dot2Ipv4NexthopLookupMRIB APIType = 41
zapi6Frr8dot2LabelManagerConnect APIType = 50
zapi6Frr8dot2LabelManagerConnectAsync APIType = 51
zapi6Frr8dot2GetLabelChunk APIType = 52
zapi6Frr8dot2ReleaseLabelChunk APIType = 53
)
var apiTypeZapi6Frr8dot2Map = map[APIType]APIType{ // frr8.2
RedistributeRouteAdd: zapi6Frr8dot2RedistributeRouteAdd,
RedistributeRouteDel: zapi6Frr8dot2RedistributeRouteDel,
vrfLabel: zapi6Frr8dot2VrfLabel,
ipv4NexthopLookupMRIB: zapi6Frr8dot2Ipv4NexthopLookupMRIB,
labelManagerConnect: zapi6Frr8dot2LabelManagerConnect,
labelManagerConnectAsync: zapi6Frr8dot2LabelManagerConnectAsync,
getLabelChunk: zapi6Frr8dot2GetLabelChunk,
releaseLabelChunk: zapi6Frr8dot2ReleaseLabelChunk,
}
const (
zapi6Frr7dot3LabelManagerConnect APIType = 50 // difference from frr8.1
zapi6Frr7dot3LabelManagerConnectAsync APIType = 51 // difference from frr8.1
zapi6Frr7dot3GetLabelChunk APIType = 52 // difference from frr8.1
zapi6Frr7dot3ReleaseLabelChunk APIType = 53 // difference from frr8.1
)
var apiTypeZapi6Frr7dot3Map = map[APIType]APIType{
labelManagerConnect: zapi6Frr7dot3LabelManagerConnect,
labelManagerConnectAsync: zapi6Frr7dot3LabelManagerConnectAsync,
getLabelChunk: zapi6Frr7dot3GetLabelChunk,
releaseLabelChunk: zapi6Frr7dot3ReleaseLabelChunk,
}
const (
zapi6Frr7dot2LabelManagerConnect APIType = 49 // difference from frr8.1
zapi6Frr7dot2LabelManagerConnectAsync APIType = 50 // difference from frr8.1
zapi6Frr7dot2GetLabelChunk APIType = 51 // difference from frr8.1
zapi6Frr7dot2ReleaseLabelChunk APIType = 52 // difference from frr8.1
)
var apiTypeZapi6Frr7dot2Map = map[APIType]APIType{
labelManagerConnect: zapi6Frr7dot2LabelManagerConnect,
labelManagerConnectAsync: zapi6Frr7dot2LabelManagerConnectAsync,
getLabelChunk: zapi6Frr7dot2GetLabelChunk,
releaseLabelChunk: zapi6Frr7dot2ReleaseLabelChunk,
}
const ( // frr7.0, 7.1
zapi6Frr7RouteAdd APIType = 7
zapi6Frr7RouteDelete APIType = 8
zapi6Frr7RedistributAdd APIType = 10
zapi6Frr7RouterIDAdd APIType = 14
zapi6Frr7RouterIDUpdate APIType = 16
zapi6Frr7Hello APIType = 17
zapi6Frr7NexthopRegister APIType = 19
zapi6Frr7NexthopUnregister APIType = 20
zapi6Frr7NexthopUpdate APIType = 21
zapi6Frr7RedistributeRouteAdd APIType = 32
zapi6Frr7RedistributeRouteDel APIType = 33
zapi6Frr7VrfLabel APIType = 37
zapi6Frr7Ipv4NexthopLookupMRIB APIType = 43
zapi6Frr7LabelManagerConnect APIType = 48
zapi6Frr7LabelManagerConnectAsync APIType = 49
zapi6Frr7GetLabelChunk APIType = 50
zapi6Frr7ReleaseLabelChunk APIType = 51
)
var apiTypeZapi6Frr7Map = map[APIType]APIType{ // frr7.0, 7.1
RouteAdd: zapi6Frr7RouteAdd,
RouteDelete: zapi6Frr7RouteDelete,
redistributeAdd: zapi6Frr7RedistributAdd,
routerIDAdd: zapi6Frr7RouterIDAdd,
routerIDUpdate: zapi6Frr7RouterIDUpdate,
Hello: zapi6Frr7Hello,
nexthopRegister: zapi6Frr7NexthopRegister,
nexthopUnregister: zapi6Frr7NexthopUnregister,
nexthopUpdate: zapi6Frr7NexthopUpdate,
RedistributeRouteAdd: zapi6Frr7RedistributeRouteAdd,
RedistributeRouteDel: zapi6Frr7RedistributeRouteDel,
vrfLabel: zapi6Frr7VrfLabel,
ipv4NexthopLookupMRIB: zapi6Frr7Ipv4NexthopLookupMRIB,
labelManagerConnect: zapi6Frr7LabelManagerConnect,
labelManagerConnectAsync: zapi6Frr7LabelManagerConnectAsync,
getLabelChunk: zapi6Frr7GetLabelChunk,
releaseLabelChunk: zapi6Frr7ReleaseLabelChunk,
}
var apiTypeZapi6Frr6Map = map[APIType]APIType{
RouteAdd: zapi6Frr7RouteAdd, // same as frr7.0&7.1
RouteDelete: zapi6Frr7RouteDelete, // same as frr7.0&7.1
redistributeAdd: zapi6Frr7RedistributAdd, // same as frr7.0&7.1
routerIDAdd: zapi6Frr7RouterIDAdd, // same as frr7.0&7.1
routerIDUpdate: zapi6Frr7RouterIDUpdate, // same as frr7.0&7.1
Hello: zapi6Frr7Hello, // same as frr7.0&7.1
nexthopRegister: zapi6Frr7NexthopRegister, // same as frr7.0&7.1
nexthopUnregister: zapi6Frr7NexthopUnregister, // same as frr7.0&7.1
nexthopUpdate: zapi6Frr7NexthopUpdate, // same as frr7.0&7.1
RedistributeRouteAdd: RedistributeRouteAdd, // same as frr7.2&7.3
RedistributeRouteDel: RedistributeRouteDel, // same as frr7.2&7.3
vrfLabel: vrfLabel, // same as frr7.2&7.3
ipv4NexthopLookupMRIB: ipv4NexthopLookupMRIB, // same as frr7.2&7.3
labelManagerConnect: zapi6Frr7dot2LabelManagerConnect, // same as frr7.2
labelManagerConnectAsync: zapi6Frr7dot2LabelManagerConnectAsync, // same as frr7.2
getLabelChunk: zapi6Frr7dot2GetLabelChunk, // same as frr7.2
releaseLabelChunk: zapi6Frr7dot2ReleaseLabelChunk, // same as frr7.2
}
const ( // For Cumulus Linux 3.7.7, zebra 4.0+cl3u13 (ZAPI version 5)
zapi5ClIpv4NexthopLookupMRIB APIType = 42
zapi5ClLabelManagerConnect APIType = 47
zapi5ClGetLabelChunk APIType = 48
zapi5ClReleaseLabelChunk APIType = 49
)
var apiTypeZapi5ClMap = map[APIType]APIType{
nexthopRegister: zapi6Frr7NexthopRegister, // same as frr7.0&7.1
nexthopUnregister: zapi6Frr7NexthopUnregister, // same as frr7.0&7.1
nexthopUpdate: zapi6Frr7NexthopUpdate, // same as frr7.0&7.1
RedistributeRouteAdd: zapi6Frr7RedistributeRouteAdd, // same as frr7.0&7.1
RedistributeRouteDel: zapi6Frr7RedistributeRouteDel, // same as frr7.0&7.1
vrfLabel: zapi6Frr7VrfLabel, // same as frr7.0&7.1
labelManagerConnect: zapi5ClLabelManagerConnect,
getLabelChunk: zapi5ClGetLabelChunk,
releaseLabelChunk: zapi5ClReleaseLabelChunk,
}
const (
zapi5RedistributAdd APIType = 14
zapi5RouterIDAdd APIType = 18
zapi5RouterIDUpdate APIType = 20
zapi5Hello APIType = 21
zapi5Frr5NexthopRegister APIType = 23
zapi5Frr5NexthopUnregister APIType = 24
zapi5Frr5NexthopUpdate APIType = 25
zapi5Frr5RedistributeRouteAdd APIType = 37
zapi5Frr5RedistributeRouteDel APIType = 38
zapi5Frr5VrfLabel APIType = 42
zapi5Frr5Ipv4NexthopLookupMRIB APIType = 47
zapi5Frr5LabelManagerConnect APIType = 52
zapi5Frr5LabelManagerConnectAsync APIType = 53
zapi5Frr5GetLabelChunk APIType = 54
zapi5Frr5ReleaseLabelChunk APIType = 55
)
var apiTypeZapi5Frr5Map = map[APIType]APIType{
RouteAdd: zapi6Frr7RouteAdd, // same as frr7.0&7.1
RouteDelete: zapi6Frr7RouteDelete, // same as frr7.0&7.1
redistributeAdd: zapi5RedistributAdd,
routerIDAdd: zapi5RouterIDAdd,
routerIDUpdate: zapi5RouterIDUpdate,
Hello: zapi5Hello,
nexthopRegister: zapi5Frr5NexthopRegister,
nexthopUnregister: zapi5Frr5NexthopUnregister,
nexthopUpdate: zapi5Frr5NexthopUpdate,
RedistributeRouteAdd: zapi5Frr5RedistributeRouteAdd,
RedistributeRouteDel: zapi5Frr5RedistributeRouteDel,
vrfLabel: zapi5Frr5VrfLabel,
ipv4NexthopLookupMRIB: zapi5Frr5Ipv4NexthopLookupMRIB,
labelManagerConnect: zapi5Frr5LabelManagerConnect,
labelManagerConnectAsync: zapi5Frr5LabelManagerConnectAsync,
getLabelChunk: zapi5Frr5GetLabelChunk,
releaseLabelChunk: zapi5Frr5ReleaseLabelChunk,
}
const (
zapi5Frr4NexthopRegister APIType = 22
zapi5Frr4NexthopUnregister APIType = 23
zapi5Frr4NexthopUpdate APIType = 24
zapi5Frr4RedistributeRouteAdd APIType = 36
zapi5Frr4RedistributeRouteDel APIType = 37
zapi5Frr4Ipv4NexthopLookupMRIB APIType = 45
zapi5Frr4LabelManagerConnect APIType = 50
zapi5Frr4GetLabelChunk APIType = 51
zapi5Frr4ReleaseLabelChunk APIType = 52
)
var apiTypeZapi5Frr4Map = map[APIType]APIType{
RouteAdd: zapi6Frr7RouteAdd, // same as frr7.0&7.1
RouteDelete: zapi6Frr7RouteDelete, // same as frr7.0&7.1
redistributeAdd: zapi5RedistributAdd,
routerIDAdd: zapi5RouterIDAdd,
routerIDUpdate: zapi5RouterIDUpdate,
Hello: zapi5Hello,
nexthopRegister: zapi5Frr4NexthopRegister,
nexthopUnregister: zapi5Frr4NexthopUnregister,
nexthopUpdate: zapi5Frr4NexthopUpdate,
RedistributeRouteAdd: zapi5Frr4RedistributeRouteAdd,
RedistributeRouteDel: zapi5Frr4RedistributeRouteDel,
ipv4NexthopLookupMRIB: zapi5Frr4Ipv4NexthopLookupMRIB,
labelManagerConnect: zapi5Frr4LabelManagerConnect,
getLabelChunk: zapi5Frr4GetLabelChunk,
releaseLabelChunk: zapi5Frr4ReleaseLabelChunk,
}
const (
zapi4IPv4RouteAdd APIType = 6 // deleted in zapi6
zapi4IPv4RouteDelete APIType = 7 // deleted in zapi6
zapi4IPv6RouteAdd APIType = 8 // deleted in zapi6
zapi4IPv6RouteDelete APIType = 9 // deleted in zapi6
zapi4RedistributAdd APIType = 10
zapi4RouterIDAdd APIType = 14
zapi4RouterIDUpdate APIType = 16
zapi4Hello APIType = 17
zapi4NexthopRegister APIType = 18
zapi4NexthopUnregister APIType = 19
zapi4NexthopUpdate APIType = 20
zapi4RedistributeIPv4Add APIType = 32 // deleted in zapi6
zapi4RedistributeIPv4Del APIType = 33 // deleted in zapi6
zapi4RedistributeIPv6Add APIType = 34 // deleted in zapi6
zapi4RedistributeIPv6Del APIType = 35 // deleted in zapi6
zapi4LabelManagerConnect APIType = 52
zapi4GetLabelChunk APIType = 53
zapi4ReleaseLabelChunk APIType = 54
)
var apiTypeZapi4Map = map[APIType]APIType{
RouteAdd: zapi4IPv4RouteAdd, // deleted in zapi5
RouteDelete: zapi4IPv4RouteDelete, // deleted in zapi5
redistributeAdd: zapi4RedistributAdd,
routerIDAdd: zapi4RouterIDAdd,
routerIDUpdate: zapi4RouterIDUpdate,
Hello: zapi4Hello,
nexthopRegister: zapi4NexthopRegister,
nexthopUnregister: zapi4NexthopUnregister,
nexthopUpdate: zapi4NexthopUpdate,
RedistributeRouteAdd: zapi4RedistributeIPv4Add, // deleted in zapi5
RedistributeRouteDel: zapi4RedistributeIPv4Del, // deleted in zapi5
ipv4NexthopLookupMRIB: zapi6Frr7Ipv4NexthopLookupMRIB, // same as frr7.0&7.1
labelManagerConnect: zapi4LabelManagerConnect,
getLabelChunk: zapi4GetLabelChunk,
releaseLabelChunk: zapi4ReleaseLabelChunk,
BackwardIPv6RouteAdd: zapi4IPv6RouteAdd,
BackwardIPv6RouteDelete: zapi4IPv6RouteDelete,
}
const (
zapi3InterfaceAdd APIType = 1
zapi3InterfaceDelete APIType = 2
zapi3InterfaceAddressAdd APIType = 3
zapi3InterfaceAddressDelete APIType = 4
zapi3InterfaceUp APIType = 5
zapi3InterfaceDown APIType = 6
zapi3IPv4RouteAdd APIType = 7 // deleted in zapi5
zapi3IPv4RouteDelete APIType = 8 // deleted in zapi5
zapi3IPv6RouteAdd APIType = 9 // deleted in zapi5
zapi3IPv6RouteDelete APIType = 10 // deleted in zapi5
zapi3RedistributeAdd APIType = 11
zapi3IPv4NexthopLookup APIType = 15 // zapi3(quagga) only
zapi3IPv6NexthopLookup APIType = 16 // zapi3(quagga) only
zapi3IPv4ImportLookup APIType = 17 // zapi3(quagga) only
zapi3RouterIDAdd APIType = 20
zapi3RouterIDUpdate APIType = 22
zapi3Hello APIType = 23
zapi3Ipv4NexthopLookupMRIB APIType = 24
zapi3NexthopRegister APIType = 27
zapi3NexthopUnregister APIType = 28
zapi3NexthopUpdate APIType = 29
)
var apiTypeZapi3Map = map[APIType]APIType{
interfaceAdd: zapi3InterfaceAdd,
interfaceDelete: zapi3InterfaceDelete,
interfaceAddressAdd: zapi3InterfaceAddressAdd,
interfaceAddressDelete: zapi3InterfaceAddressDelete,
interfaceUp: zapi3InterfaceUp,
interfaceDown: zapi3InterfaceDown,
RouteAdd: zapi3IPv4RouteAdd, // deleted in zapi5
RouteDelete: zapi3IPv4RouteDelete, // deleted in zapi5
redistributeAdd: zapi3RedistributeAdd,
routerIDAdd: zapi3RouterIDAdd,
routerIDUpdate: zapi3RouterIDUpdate,
Hello: zapi3Hello,
nexthopRegister: zapi3NexthopRegister,
nexthopUnregister: zapi3NexthopUnregister,
nexthopUpdate: zapi3NexthopUpdate,
BackwardIPv6RouteAdd: zapi3IPv6RouteAdd,
BackwardIPv6RouteDelete: zapi3IPv6RouteDelete,
}
func (t APIType) doesNeedConversion(version uint8, software Software) bool {
if version == 6 && software.name == "frr" &&
software.version >= 7.5 && software.version < 8.2 ||
t < minDifferentAPIType(version, software) {
return false
}
return true
}
func apiTypeMap(version uint8, software Software) map[APIType]APIType {
if version == 6 && software.name == "frr" && software.version >= 7.3 && software.version < 7.5 {
return apiTypeZapi6Frr7dot3Map
} else if version == 6 && software.name == "frr" && software.version == 7.2 {
return apiTypeZapi6Frr7dot2Map
} else if version == 6 && software.name == "frr" && software.version >= 7 && software.version < 7.2 {
return apiTypeZapi6Frr7Map
} else if version == 6 && software.name == "frr" && software.version >= 6 && software.version < 7 {
return apiTypeZapi6Frr6Map
} else if version == 5 {
if software.name == "frr" && software.version == 4 {
return apiTypeZapi5Frr4Map
} else if software.name == "cumulus" {
return apiTypeZapi5ClMap
}
return apiTypeZapi5Frr5Map
} else if version == 4 {
return apiTypeZapi4Map
} else if version < 4 {
return apiTypeZapi3Map
}
return apiTypeZapi6Frr8dot2Map
}
// ToEach is referred in zclient_test
func (t APIType) ToEach(version uint8, software Software) APIType {
if !t.doesNeedConversion(version, software) {
return t
}
apiMap := apiTypeMap(version, software)
backward, ok := apiMap[t]
if !ok {
backward = zebraError // fail to convert and error value
}
return backward // success to convert
}
func (t APIType) ToCommon(version uint8, software Software) APIType {
if !t.doesNeedConversion(version, software) {
return t
}
apiMap := apiTypeMap(version, software)
for common, backward := range apiMap {
if backward == t {
return common // success to convert
}
}
return zebraError // fail to convert and error value
}
func (t APIType) addressFamily(version uint8) uint8 {
if version == 4 {
switch t {
case zapi4IPv4RouteAdd, zapi4IPv4RouteDelete, zapi4RedistributeIPv4Add, zapi4RedistributeIPv4Del, zapi6Frr7Ipv4NexthopLookupMRIB:
return syscall.AF_INET
case zapi4IPv6RouteAdd, zapi4IPv6RouteDelete, zapi4RedistributeIPv6Add, zapi4RedistributeIPv6Del:
return syscall.AF_INET6
}
} else if version < 4 {
switch t {
case zapi3IPv4RouteAdd, zapi3IPv4RouteDelete, zapi3IPv4NexthopLookup, zapi3IPv4ImportLookup, zapi3Ipv4NexthopLookupMRIB:
return syscall.AF_INET
case zapi3IPv6RouteAdd, zapi3IPv6RouteDelete, zapi3IPv6NexthopLookup:
return syscall.AF_INET6
}
}
return syscall.AF_UNSPEC
}
// RouteType is referred in zclient.
//
//go:generate stringer -type=RouteType
type RouteType uint8
// For FRRouting version 7 (ZAPI version 6).
const (
routeSystem RouteType = iota // 0
routeKernel
routeConnect
RouteStatic
routeRIP
routeRIPNG
routeOSPF
routeOSPF6
routeISIS
RouteBGP
routePIM // 10
routeEIGRP // FRRRouting version 4 (Zapi5) adds.
routeNHRP
routeHSLS
routeOLSR
routeTABLE
routeLDP
routeVNC
routeVNCDirect
routeVNCDirectRH
routeBGPDirect
routeBGPDirectEXT
routeBABEL
routeSHARP
routePBR // FRRRouting version 5 (Zapi5) adds.
routeBFD // FRRRouting version 6 (Zapi6) adds.
routeOpenfabric // FRRRouting version 7 (Zapi6) adds.
routeVRRP // FRRRouting version 7.2 (Zapi6) adds.
routeNHG // FRRRouting version 7.3 (Zapi6) adds.
routeSRTE // FRRRouting version 7.5 (Zapi6) adds.
routeAll
routeMax // max value for error
)
const (
zapi5Frr4RouteAll RouteType = 24
zapi5Frr5RouteAll RouteType = 25
zapi6Frr6RouteAll RouteType = 26
zapi6Frr7RouteAll RouteType = 27
zapi6Frr7dot2RouteAll RouteType = 28
zapi6Frr7dot3RouteAll RouteType = 29
)
func getRouteAll(version uint8, software Software) RouteType {
switch version {
case 5:
if software.name == "frr" && software.version == 4 {
return zapi5Frr4RouteAll
}
return zapi5Frr5RouteAll
case 6:
if software.name == "frr" && software.version == 6 {
return zapi6Frr6RouteAll
} else if software.name == "frr" && software.version >= 7 && software.version < 7.2 {
return zapi6Frr7RouteAll
} else if software.name == "frr" && software.version >= 7.2 && software.version < 7.3 {
return zapi6Frr7dot2RouteAll
} else if software.name == "frr" && software.version >= 7.3 && software.version < 7.5 {
return zapi6Frr7dot3RouteAll
}
}
return routeAll
}
// For FRRouting version 3.0 except common route type.
const (
zapi4RouteNHRP RouteType = iota + routePIM + 1
zapi4RouteHSLS
zapi4RouteOLSR
zapi4RouteTABLE
zapi4RouteLDP
zapi4RouteVNC
zapi4RouteVNCDirect
zapi4RouteVNCDirectRH
zapi4RouteBGPDixrect
zapi4RouteBGPDirectEXT
zapi4RouteAll
)
var routeTypeZapi4Map = map[RouteType]RouteType{
routeNHRP: zapi4RouteNHRP,
routeHSLS: zapi4RouteHSLS,
routeOLSR: zapi4RouteOLSR,
routeTABLE: zapi4RouteTABLE,
routeLDP: zapi4RouteLDP,
routeVNC: zapi4RouteVNC,
routeVNCDirect: zapi4RouteVNCDirect,
routeVNCDirectRH: zapi4RouteVNCDirectRH,
routeBGPDirect: zapi4RouteBGPDixrect,
routeBGPDirectEXT: zapi4RouteBGPDirectEXT,
routeAll: zapi4RouteAll,
}
// For Quagga except common route type.
const (
zapi3RouteHSLS RouteType = iota + routePIM + 1
zapi3RouteOLSR
zapi3RouteBABEL
zapi3RouteNHRP // quagga 1.2.4
)
var routeTypeZapi3Map = map[RouteType]RouteType{
routeHSLS: zapi3RouteHSLS,
routeOLSR: zapi3RouteOLSR,
routeBABEL: zapi3RouteBABEL,
routeNHRP: zapi3RouteNHRP,
}
func (t RouteType) toEach(version uint8) RouteType {
if t <= routePIM || version > 4 { // not need to convert
return t
}
routeTypeMap := routeTypeZapi4Map
if version < 4 {
routeTypeMap = routeTypeZapi3Map
}
backward, ok := routeTypeMap[t]
if ok {
return backward // success to convert
}
return routeMax // fail to convert and error value
}
var routeTypeValueMap = map[string]RouteType{
"system": routeSystem,
"kernel": routeKernel,
"connect": routeConnect, // hack for backward compatibility
"directly-connected": routeConnect,
"static": RouteStatic,
"rip": routeRIP,
"ripng": routeRIPNG,
"ospf": routeOSPF,
"ospf3": routeOSPF6,
"isis": routeISIS,
"bgp": RouteBGP,
"pim": routePIM,
"eigrp": routeEIGRP, // added in frr4(zapi5)
"nhrp": routeNHRP,
"hsls": routeHSLS,
"olsr": routeOLSR,
"table": routeTABLE,
"ldp": routeLDP,
"vnc": routeVNC,
"vnc-direct": routeVNCDirect,
"vnc-rn": routeVNCDirectRH,
"bgp-direct": routeBGPDirect,
"bgp-direct-to-nve-groups": routeBGPDirectEXT,
"babel": routeBABEL,
"sharp": routeSHARP,
"pbr": routePBR,
"bfd": routeBFD,
"openfabric": routeOpenfabric, // added in frr7.0(zapi6)
"vrrp": routeVRRP, // added in frr7.2(zapi6)
"nhg": routeNHG, // added in frr7.3(zapi6)
"srte": routeSRTE, // added in frr7.5(zapi6)
"wildcard": routeAll,
}
// RouteTypeFromString converts from string to route type
func RouteTypeFromString(typ string, version uint8, software Software) (RouteType, error) {
t, ok := routeTypeValueMap[typ]
if !ok { // failed to lookup RouteType from string
return t, fmt.Errorf("unknown route type: %s in version: %d (%s)", typ, version, software.string())
}
t = t.toEach(version) // when lookup failes return routeMax
if t > getRouteAll(version, software) {
return t, fmt.Errorf("unknown route type: %d in version: %d (%s)", t, version, software.string())
}
return t, nil // Success
}
func addressByteLength(family uint8) (int, error) {
switch family {
case syscall.AF_INET:
return net.IPv4len, nil
case syscall.AF_INET6:
return net.IPv6len, nil
}
return 0, fmt.Errorf("unknown address family: %d", family)
}
func ipFromFamily(family uint8, buf []byte) netip.Addr {
switch family {
case syscall.AF_INET:
b, _ := netip.AddrFromSlice(buf)
return b
case syscall.AF_INET6:
b, _ := netip.AddrFromSlice(buf)
return b
}
return netip.Addr{}
}
// MESSAGE_FLAG is 32bit in frr7.5 and after frr7.5, 8bit in frr 7.4 and before frr7.4
// MessageFlag is the type of API Message Flags.
type MessageFlag uint32
const ( // For FRRouting version 4, 5 and 6 (ZAPI version 5 and 6).
// MessageNexthop is referred in zclient
MessageNexthop MessageFlag = 0x01
// MessageDistance is referred in zclient_test
MessageDistance MessageFlag = 0x02
// MessageMetric is referred in zclient
MessageMetric MessageFlag = 0x04
messageTag MessageFlag = 0x08
// MessageMTU is referred in zclient_test
MessageMTU MessageFlag = 0x10
messageSRCPFX MessageFlag = 0x20
// MessageLabel is referred in zclient
MessageLabel MessageFlag = 0x40 // deleted in frr7.3
messageBackupNexthops MessageFlag = 0x40 // added in frr7.4
messageNhg MessageFlag = 0x80 // added in frr8
messageTableID MessageFlag = 0x100 // frr8: 0x100, frr5&6&7.x: 0x80
messageSRTE MessageFlag = 0x200 // frr8: 0x200, frr7.5: 0x100
messageOpaque MessageFlag = 0x400 // introduced in frr8
)
const ( // For FRRouting.
messageIFIndex MessageFlag = 0x02
zapi4MessageDistance MessageFlag = 0x04
zapi4MessageMetric MessageFlag = 0x08
zapi4MessageTag MessageFlag = 0x10
zapi4MessageMTU MessageFlag = 0x20
zapi4MessageSRCPFX MessageFlag = 0x40
)
const ( // For Quagga.
zapi3MessageMTU MessageFlag = 0x10
zapi3MessageTag MessageFlag = 0x20
)
// ToEach is referred in zclient
func (f MessageFlag) ToEach(version uint8, software Software) MessageFlag {
if version > 4 { // zapi version 5, 6
if f > messageNhg && (version == 5 ||
version == 6 && software.name == "frr" && software.version < 8) { // except frr8
return f >> 1
}
return f
}
if version < 4 { // zapi version 3, 2
switch f {
case MessageMTU:
return zapi3MessageMTU
case messageTag:
return zapi3MessageTag
}
}
switch f { // zapi version 4
case MessageDistance, MessageMetric, messageTag, MessageMTU, messageSRCPFX:
return f << 1
}
return f
}
func (f MessageFlag) string(version uint8, software Software) string {
var ss []string
if f&MessageNexthop > 0 {
ss = append(ss, "NEXTHOP")
}
if version < 4 && f&messageIFIndex > 0 {
ss = append(ss, "IFINDEX")
}
if f&MessageDistance.ToEach(version, software) > 0 {
ss = append(ss, "DISTANCE")
}
if f&MessageMetric.ToEach(version, software) > 0 {
ss = append(ss, "METRIC")
}
if f&MessageMTU.ToEach(version, software) > 0 {
ss = append(ss, "MTU")
}
if f&messageTag.ToEach(version, software) > 0 {
ss = append(ss, "TAG")
}
if version > 3 && f&messageSRCPFX.ToEach(version, software) > 0 {
ss = append(ss, "SRCPFX")
}
if version == 6 && software.name == "frr" && software.version >= 7.4 && f&messageBackupNexthops > 0 { // added in frr7.4, frr7.5
ss = append(ss, "BACKUP_NEXTHOPS")
} else if version > 4 && f&MessageLabel > 0 {
ss = append(ss, "LABEL")
}
if version > 6 && software.name == "frr" && software.version >= 8 && f&messageNhg > 0 { // added in frr8
ss = append(ss, "NHG")
}
if version > 5 && f&messageTableID > 0 {
ss = append(ss, "TABLEID")
}
if version == 6 && software.name == "frr" && software.version >= 7.5 && f&messageSRTE > 0 { // added in frr7.5
ss = append(ss, "SRTE")
}
if version > 6 && software.name == "frr" && software.version >= 8 && f&messageOpaque > 0 { // added in frr8
ss = append(ss, "OPAQUE")
}
return strings.Join(ss, "|")
}
// Flag is Message Flag which is referred in zclient
type Flag uint64
const ( // For FRRouting version 7 (zebra API version 6), these are defined in lib/zclient.h
// FlagAllowRecursion is referred in zclient, and it is renamed from ZEBRA_FLAG_INTERNAL (https://github.com/FRRouting/frr/commit/4e8b02f4df5d6bcfde6390955b8feda2a17dc9bd)
FlagAllowRecursion Flag = 0x01 // quagga, frr3, frr4, frr5, frr6, frr7
flagSelfRoute Flag = 0x02 // quagga, frr3, frr4, frr5, frr6, frr7
// FlagIBGP is referred in zclient
FlagIBGP Flag = 0x04
// FlagSelected referred in zclient_test
FlagSelected Flag = 0x08
flagFIBOverride Flag = 0x10
flagEvpnRoute Flag = 0x20
flagRRUseDistance Flag = 0x40
flagOnlink Flag = 0x80 // frr7.0 only, this vale is deleted in frr7.1
flagTrapped Flag = 0x80 // added in frr8
flagOffloaded Flag = 0x100 // added in frr8
flagOffloadFailed Flag = 0x200 // added in frr8
)
// For Quagga (ZAPI v2, v3), FRR v3 (ZAPI v4), FRR v4, v5 (ZAPI v5), FRR v6 (ZAPI v6) for backward compatibility
const (
flagBlackhole Flag = 0x04 // quagga, frr3
flagStatic Flag = 0x40 // quagga, frr3, frr4, frr5, frr6
flagReject Flag = 0x80 // quagga, frr3
flagScopeLink Flag = 0x100 // frr4, frr5, frr6
)
// ToEach is referred in zclient
func (f Flag) ToEach(version uint8, software Software) Flag {
if version == 6 && software.name == "frr" && software.version >= 7 || f < FlagIBGP || f > flagRRUseDistance {
return f
}
switch f {
case FlagIBGP, FlagSelected: // 0x04->0x08,0x08->0x10(quagga, frr3,4,5,6)
return f << 1
case flagEvpnRoute, flagRRUseDistance: // 0x20->0x400,0x40->0x800(frr4,5,6)
return f << 5
case flagFIBOverride:
if version < 4 {
return f << 1 // 0x10->0x20(quagga)
}
return f << 5 // 0x10->0x200(frr3, frr4, frr5, frr6)
}
return f
}
// String is referred in zclient
func (f Flag) String(version uint8, software Software) string {
var ss []string
// common flag
if f&FlagAllowRecursion > 0 {
ss = append(ss, "FLAG_ALLOW_RECURSION")
}
if f&flagSelfRoute > 0 {
ss = append(ss, "FLAG_SELFROUTE")
}
if f&FlagIBGP.ToEach(version, software) > 0 {
ss = append(ss, "FLAG_IBGP")
}
if f&FlagSelected.ToEach(version, software) > 0 {
ss = append(ss, "FLAG_SELECTED")
}
if f&flagEvpnRoute.ToEach(version, software) > 0 {
ss = append(ss, "FLAG_EVPN_ROUTE")
}
if f&flagRRUseDistance.ToEach(version, software) > 0 {
ss = append(ss, "FLAG_RR_USE_DISTANCE")
}
if f&flagFIBOverride.ToEach(version, software) > 0 {
ss = append(ss, "FLAG_FIB_OVERRIDE")
}
if version == 6 && software.name == "frr" && software.version >= 7 && f&flagOnlink > 0 { // frr7.0 only
ss = append(ss, "FLAG_ONLINK")
}
if version == 6 && software.name == "frr" && software.version >= 8 && f&flagTrapped > 0 { // added in frr8
ss = append(ss, "FLAG_TRAPPED")
}
if f&flagOffloaded > 0 { // added in frr8
ss = append(ss, "FLAG_OFFLOADED")
}
if f&flagOffloadFailed > 0 { // added in frr8
ss = append(ss, "FLAG_OFFLOADFAILED")
}
if (version < 6 || version == 6 && software.name == "frr" && software.version < 7) && f&flagStatic > 0 {
ss = append(ss, "FLAG_STATIC") // quagga, frr3, frr4, frr5, frr6
}
if version < 5 && f&flagBlackhole > 0 { // quagga, frr3
ss = append(ss, "FLAG_BLACKHOLE")
}
if version < 5 && f&flagReject > 0 { // quagga, frr3
ss = append(ss, "FLAG_REJECT")
}
if (version == 5 || version == 6 && software.name == "frr" && software.version < 7) && f&flagScopeLink > 0 {
ss = append(ss, "FLAG_SCOPE_LINK") // frr4, frr5, frr6
}
return strings.Join(ss, "|")
}
// Nexthop Types.
//
//go:generate stringer -type=nexthopType
type nexthopType uint8
// For FRRouting.
const (
_ nexthopType = iota
nexthopTypeIFIndex // 1
nexthopTypeIPv4 // 2
nexthopTypeIPv4IFIndex // 3
nexthopTypeIPv6 // 4
nexthopTypeIPv6IFIndex // 5
nexthopTypeBlackhole // 6
)
// For Quagga.
const (
nexthopTypeIFName nexthopType = iota + 2 // 2
backwardNexthopTypeIPv4 // 3
backwardNexthopTypeIPv4IFIndex // 4
nexthopTypeIPv4IFName // 5
backwardNexthopTypeIPv6 // 6
backwardNexthopTypeIPv6IFIndex // 7
nexthopTypeIPv6IFName // 8
backwardNexthopTypeBlackhole // 9
)
var nexthopTypeMap = map[nexthopType]nexthopType{
nexthopTypeIPv4: backwardNexthopTypeIPv4, // 2 -> 3
nexthopTypeIPv4IFIndex: backwardNexthopTypeIPv4IFIndex, // 3 -> 4
nexthopTypeIPv6: backwardNexthopTypeIPv6, // 4 -> 6
nexthopTypeIPv6IFIndex: backwardNexthopTypeIPv6IFIndex, // 5 -> 7
nexthopTypeBlackhole: backwardNexthopTypeBlackhole, // 6 -> 9
}
func (t nexthopType) toEach(version uint8) nexthopType {
if version > 3 { // frr
return t
}
if t == nexthopTypeIFIndex || t > nexthopTypeBlackhole { // 1 (common), 7, 8, 9 (out of map range)
return t
}
backward, ok := nexthopTypeMap[t]
if ok {
return backward // converted value
}
return nexthopType(0) // error for conversion
}
func (t nexthopType) ipToIPIFIndex() nexthopType {
// process of nexthopTypeIPv[4|6] is same as nexthopTypeIPv[4|6]IFIndex
// in IPRouteBody of frr7.3 and NexthoUpdate of frr
switch t {
case nexthopTypeIPv4:
return nexthopTypeIPv4IFIndex
case nexthopTypeIPv6:
return nexthopTypeIPv6IFIndex
}
return t
}
func (t nexthopType) ifNameToIFIndex() nexthopType { // quagga
switch t {
case nexthopTypeIFName:
return nexthopTypeIFIndex
case nexthopTypeIPv4IFName:
return backwardNexthopTypeIPv4IFIndex
case nexthopTypeIPv6IFName:
return backwardNexthopTypeIPv6IFIndex
}
return t
}
// Nexthop Flags.
//
//go:generate stringer -type=nexthopFlag
type nexthopFlag uint8
const (
nexthopFlagActive nexthopFlag = 0x01 // This nexthop is alive.
nexthopFlagFIB nexthopFlag = 0x02 // FIB nexthop.
nexthopFlagRecursive nexthopFlag = 0x04 // Recursive nexthop.
nexthopFlagOnlink nexthopFlag = 0x08 // Nexthop should be installed onlink.
nexthopFlagDuplicate nexthopFlag = 0x10 // nexthop duplicates (frr8, 7.5, 7.4)
nexthopFlagRnhFiltered nexthopFlag = 0x20 // nexthop duplicates (frr8, 7.5, 7.4)
nexthopFlagHasBackup nexthopFlag = 0x40 // nexthop duplicates (frr8, 7.5, 7.4)
nexthopFlagSRTE nexthopFlag = 0x80 // nexthop duplicates (frr8, 7.5)
)
const (
// Already matched vs a nexthop (frr7.3, 7.2, 7.1, 7, 6, 5, 4, 3) (zapi version >= 4)
zapi6Frr7dot3nexthopFlagMatched nexthopFlag = 0x10
// nexthop duplicates (frr7.3, 7.2, 7.1)
zapi6Frr7dot3nexthopFlagDuplicate nexthopFlag = 0x20
// nexthop duplicates (frr7.3, 7.2, 7.1)
zapi6Frr7dot3nexthopFlagRnhFiltered nexthopFlag = 0x40
)
const (
// rmap filtered (frr7, 6, 5, 4, 3)(zapi version >= 4)
zapi6Frr7nexthopFlagFiltered nexthopFlag = 0x20
// nexthop duplicates (frr7, 6, 5, 4)(version >= 5)
zapi6Frr7nexthopFlagDuplicate nexthopFlag = 0x40
// Evpn remote vtep nexthop (frr7, 6, 5, 4)(version >= 5)
zapi6Frr7nexthopFlagEvpnRvtep nexthopFlag = 0x80
)
// Interface PTM Enable Configuration.
//
//go:generate stringer -type=ptmEnable
type ptmEnable uint8
const (
ptmEnableOff ptmEnable = 0
ptmEnableOn ptmEnable = 1
ptmEnableUnspec ptmEnable = 2
)
// PTM Status.
//
//go:generate stringer -type=ptmStatus
type ptmStatus uint8
const (
ptmStatusDown ptmStatus = 0
ptmStatusUp ptmStatus = 1
ptmStatusUnknown ptmStatus = 2
)
const (
defaultZebraSoftwareName string = "frr"
defaultZapi5SoftwareVersion float64 = 5 // FRR software version for Zapi5
defaultZapi6SoftwareVersion float64 = 8.1 // FRR software version for Zapi6
)
// Software is zebra software (quagga, frr, cumulus) which is referred in zclient
type Software struct {
name string
version float64
}
// string returns combined string with name and version in Software structure
func (software *Software) string() string {
return fmt.Sprintf("%s%f", software.name, software.version)
}
// NewSoftware is constructor of Software strucuture
func NewSoftware(version uint8, softwareName string) Software {
regex := regexp.MustCompile(`([a-z]*)(\d*\.?\d*)`)
regexResult := regex.FindAllStringSubmatch(softwareName, -1)
software := Software{regexResult[0][1], defaultZapi6SoftwareVersion}
var err error
software.version, err = strconv.ParseFloat(regexResult[0][2], 64)
if err != nil || software.name != "cumulus" && version >= 5 {
software.name = defaultZebraSoftwareName
if version == 5 && software.version < 4 && software.version >= 6 {
software.version = defaultZapi5SoftwareVersion
} else if version == 6 && software.version < 6 {
software.version = defaultZapi6SoftwareVersion
}
}
return software
}
// Client is zebra client which is referred in zclient
type Client struct {
outgoing chan *Message
incoming chan *Message
redistDefault RouteType
conn net.Conn
Version uint8
Software Software
logger *slog.Logger
}
func ReceiveSingleMsg(logger *slog.Logger, conn net.Conn, version uint8, software Software, topic string) (*Message, error) {
headerBuf, err := readAll(conn, int(HeaderSize(version)))
if err != nil {
logger.Error("failed to read header",
slog.String("Topic", topic),
slog.String("Error", err.Error()),
)
return nil, err
}
hd := &Header{}
err = hd.decodeFromBytes(headerBuf)
if version != hd.Version {
logger.Warn(fmt.Sprintf("ZAPI version mismatch. configured version: %d, version of received message:%d", version, hd.Version),
slog.String("Topic", topic))
return nil, errors.New("ZAPI version mismatch")
}
if err != nil {
logger.Error("failed to decode header",
slog.String("Topic", topic),
slog.String("Error", err.Error()),
)
return nil, err
}
bodyBuf, err := readAll(conn, int(hd.Len-HeaderSize(version)))
if err != nil {
logger.Error("failed to read body",
slog.String("Topic", topic),
slog.Any("Header", hd),
slog.String("Error", err.Error()),
)
return nil, err
}
m, err := parseMessage(hd, bodyBuf, software)
if err != nil {
// Just outputting warnings (not error message) and ignore this
// error considering the case that body parser is not implemented yet.
logger.Warn("failed to decode body",
slog.String("Topic", topic),
slog.Any("Header", hd),
slog.Any("Data", bodyBuf),
slog.String("Error", err.Error()),
)
return nil, nil
}
logger.Debug("read message from zebra",
slog.String("Topic", topic),
slog.Any("Header", m.Header),
slog.Any("Body", m.Body))
return m, nil
}
// NewClient returns a Client instance (Client constructor)
func NewClient(logger *slog.Logger, network, address string, typ RouteType, version uint8, software Software, mplsLabelRangeSize uint32) (*Client, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
outgoing := make(chan *Message)
incoming := make(chan *Message, 64)
if version < MinZapiVer {
version = MinZapiVer
} else if version > MaxZapiVer {
version = MaxZapiVer
}
c := &Client{
outgoing: outgoing,
incoming: incoming,
redistDefault: typ,
conn: conn,
Version: version,
Software: software,
logger: logger,
}
go func() {
for {
m, more := <-outgoing
if more {
b, err := m.Serialize(software)
if err != nil {
logger.Warn(fmt.Sprintf("failed to serialize: %v", m), slog.String("Topic", "Zebra"))
continue
}
_, err = conn.Write(b)
if err != nil {
logger.Error("failed to write",
slog.String("Topic", "Zebra"),
slog.String("Error", err.Error()),
)
closeChannel(outgoing)
return
}
} else {
logger.Debug("finish outgoing loop",
slog.String("Topic", "Zebra"))
return
}
}
}()
// Send Hello/RouterIDAdd messages to negotiate the Zebra message version.
c.SendHello()
c.SendRouterIDAdd()
if mplsLabelRangeSize > 0 && c.SupportMpls() {
if err := c.sendLabelManagerConnect(true); err != nil {
logger.Warn("failed to send label manager connect",
slog.String("Topic", "Zebra"),
slog.String("Error", err.Error()),
)
}
}
// Try to receive the first message from Zebra.
if m, err := ReceiveSingleMsg(logger, conn, version, software, "Zebra"); err != nil {
c.close()
// Return error explicitly in order to retry connection.
return nil, err
} else if m != nil {
incoming <- m
}
// Start receive loop only when the first message successfully received.
go func() {
defer close(incoming)
for {
if m, err := ReceiveSingleMsg(logger, conn, version, software, "Zebra"); err != nil {
return
} else if m != nil {
incoming <- m
}
}
}()
return c, nil
}
func readAll(conn net.Conn, length int) ([]byte, error) {
buf := make([]byte, length)
_, err := io.ReadFull(conn, buf)
return buf, err
}
// Receive return incoming channel message
func (c *Client) Receive() chan *Message {
return c.incoming
}
func (c *Client) send(m *Message) {
defer func() {
if err := recover(); err != nil {
c.logger.Debug("recovered",
slog.String("Topic", "Zebra"),
slog.Any("Error", err),
)
}
}()
c.logger.Debug("send command to zebra",
slog.String("Topic", "Zebra"),
slog.Any("Header", m.Header),
slog.Any("Body", m.Body))
c.outgoing <- m
}
func (c *Client) sendCommand(command APIType, vrfID uint32, body Body) {
m := &Message{
Header: Header{
Len: HeaderSize(c.Version),
Marker: HeaderMarker(c.Version),
Version: c.Version,
VrfID: vrfID,
Command: command.ToEach(c.Version, c.Software),
},
Body: body,
}
c.send(m)
}
// SendHello sends HELLO message to zebra daemon.
func (c *Client) SendHello() {
if c.redistDefault > 0 {
body := &HelloBody{
redistDefault: c.redistDefault,
instance: 0,
}
c.sendCommand(Hello, DefaultVrf, body)
}
}
// SendRouterIDAdd sends ROUTER_ID_ADD message to zebra daemon.
func (c *Client) SendRouterIDAdd() {
bodies := make([]*routerIDUpdateBody, 0)
for _, afi := range []afi{afiIP, afiIP6} {
bodies = append(bodies, &routerIDUpdateBody{
afi: afi,
})
}
for _, body := range bodies {
c.sendCommand(routerIDAdd, DefaultVrf, body)
}
}
// SendInterfaceAdd sends INTERFACE_ADD message to zebra daemon.
func (c *Client) SendInterfaceAdd() {
c.sendCommand(interfaceAdd, DefaultVrf, nil)
}
// SendRedistribute sends REDISTRIBUTE message to zebra daemon.
func (c *Client) SendRedistribute(t RouteType, vrfID uint32) {
if c.redistDefault != t {
bodies := make([]*redistributeBody, 0)
if c.Version <= 3 {
bodies = append(bodies, &redistributeBody{
redist: t,
})
} else { // Version >= 4
for _, afi := range []afi{afiIP, afiIP6} {
bodies = append(bodies, &redistributeBody{
afi: afi,
redist: t,
instance: 0,
})
}
}
for _, body := range bodies {
c.sendCommand(redistributeAdd, vrfID, body)
}
}
}
// SendIPRoute sends ROUTE message to zebra daemon.
func (c *Client) SendIPRoute(vrfID uint32, body *IPRouteBody, isWithdraw bool) error {
Family := body.Family(c.logger, c.Version, c.Software)
if vrfID == DefaultVrf && (Family == bgp.RF_IPv4_VPN || Family == bgp.RF_IPv6_VPN) {
return fmt.Errorf("RF_IPv4_VPN or RF_IPv6_VPN are not suitable for Default VRF (default forwarding table)")
}
command := RouteAdd
if isWithdraw {
command = RouteDelete
}
if c.Version < 5 && familyFromPrefix(body.Prefix.Prefix) == syscall.AF_INET6 {
command = BackwardIPv6RouteAdd
if isWithdraw {
command = BackwardIPv6RouteDelete
}
}
c.sendCommand(command, vrfID, body)
return nil
}
// SendNexthopRegister sends NEXTHOP_REGISTER message to zebra daemon.
func (c *Client) SendNexthopRegister(vrfID uint32, body *NexthopRegisterBody, isWithdraw bool) error {
// Note: NexthopRegister and NexthopUnregister messages are not
// supported in Zebra protocol version<3.
if c.Version < 3 {
return fmt.Errorf("NexthopRegister/NexthopUnregister are not supported in version: %d", c.Version)
}
command := nexthopRegister
if isWithdraw {
command = nexthopUnregister
}
c.sendCommand(command, vrfID, body)
return nil
}
// SupportMpls is referred in zclient. It returns bool value.
func (c *Client) SupportMpls() bool {
// Note: frr3&4 have LABEL_MANAGER_CONNECT& GET_LABEL_CHUNK. However
// Routes will not be installed via zebra of frr3&4 after call these APIs.
if c.Version < 5 || c.Software.name == "frr" && c.Software.version == 4 {
return false // if frr4 or earlier are used
}
return true // if frr5 or later are used
}
// Ref: zread_label_manager_connect in zebra/zserv.c of FRR3 (ZAPI4)
// Ref: zread_label_manager_connect in zebra/zapi_msg.c of FRR5&6 (ZAPI5&6)
func (c *Client) sendLabelManagerConnect(async bool) error {
if c.Version < 4 {
return fmt.Errorf("LabelManagerConnect is not supported in zebra API version: %d", c.Version)
}
command := labelManagerConnectAsync
// FRR version 4 (ZAPI version 5) and FRR version 3 (ZAPI version 4)
if !async || c.Version == 4 || c.Version == 5 && c.Software.name == "frr" && c.Software.version < 5 {
command = labelManagerConnect
}
c.sendCommand(
command, 0,
&labelManagerConnectBody{
redistDefault: RouteBGP,
instance: 0,
})
return nil
}
// SendGetLabelChunk sends GET_LABEL_CHUNK message to zebra daemon.
func (c *Client) SendGetLabelChunk(body *GetLabelChunkBody) error {
if c.Version < 4 {
return fmt.Errorf("GetLabelChunk is not supported in version: %d", c.Version)
}
body.instance = 0
body.proto = uint8(RouteBGP)
c.sendCommand(getLabelChunk, 0, body)
return nil
}
// SendVrfLabel sends VRF_LABEL message to zebra daemon.
func (c *Client) SendVrfLabel(label uint32, vrfID uint32) error {
// ZAPIv5 has ZEBRA_VRF_LABEL, however frr4 (ZAPIv5) doesn't have it.
if c.Version < 5 || c.Version == 5 && c.Software.name == "frr" && c.Software.version < 5 {
return fmt.Errorf("VrfLabel is not supported in zebra API version: %d software: %s", c.Version, c.Software.string())
}
body := &vrfLabelBody{
label: label,
afi: afiIP,
labelType: lspBGP,
}
c.sendCommand(vrfLabel, vrfID, body)
return nil
}
// for avoiding double close
func closeChannel(ch chan *Message) bool {
select {
case _, ok := <-ch:
if ok {
close(ch)
return true
}
default:
}
return false
}
func (c *Client) close() {
closeChannel(c.outgoing)
c.conn.Close()
}
// SetLabelFlag is referred in zclient, this func sets label flag
func (c Client) SetLabelFlag(msgFlags *MessageFlag, nexthop *Nexthop) {
if c.Version == 6 && c.Software.name == "frr" {
nexthop.flags |= zapiNexthopFlagLabel
} else if c.Version > 4 {
*msgFlags |= MessageLabel
}
}
// Header is header of zebra message.
type Header struct {
Len uint16
Marker uint8
Version uint8
VrfID uint32 // ZAPI v4: 16bits, v5: 32bits
Command APIType
}
func (h *Header) serialize() ([]byte, error) {
buf := make([]byte, HeaderSize(h.Version))
binary.BigEndian.PutUint16(buf[:2], h.Len)
buf[2] = h.Marker
buf[3] = h.Version
switch h.Version {
case 2:
binary.BigEndian.PutUint16(buf[4:6], uint16(h.Command))
case 3, 4:
binary.BigEndian.PutUint16(buf[4:6], uint16(h.VrfID))
binary.BigEndian.PutUint16(buf[6:8], uint16(h.Command))
case 5, 6:
binary.BigEndian.PutUint32(buf[4:8], h.VrfID)
binary.BigEndian.PutUint16(buf[8:10], uint16(h.Command))
default:
return nil, fmt.Errorf("unsupported ZAPI version: %d", h.Version)
}
return buf, nil
}
func (h *Header) decodeFromBytes(data []byte) error {
if uint16(len(data)) < 4 {
return fmt.Errorf("not all ZAPI message header")
}
h.Len = binary.BigEndian.Uint16(data[:2])
h.Marker = data[2]
h.Version = data[3]
if uint16(len(data)) < HeaderSize(h.Version) {
return fmt.Errorf("not all ZAPI message header")
}
switch h.Version {
case 2:
h.Command = APIType(binary.BigEndian.Uint16(data[4:6]))
case 3, 4:
h.VrfID = uint32(binary.BigEndian.Uint16(data[4:6]))
h.Command = APIType(binary.BigEndian.Uint16(data[6:8]))
case 5, 6:
h.VrfID = binary.BigEndian.Uint32(data[4:8])
h.Command = APIType(binary.BigEndian.Uint16(data[8:10]))
default:
return fmt.Errorf("unsupported ZAPI version: %d", h.Version)
}
return nil
}
// Body is an interface for zebra messages.
type Body interface {
decodeFromBytes([]byte, uint8, Software) error
serialize(uint8, Software) ([]byte, error)
string(uint8, Software) string
}
type unknownBody struct {
Data []byte
}
func (b *unknownBody) decodeFromBytes(data []byte, version uint8, software Software) error {
b.Data = data
return nil
}
func (b *unknownBody) serialize(version uint8, software Software) ([]byte, error) {
return b.Data, nil
}
func (b *unknownBody) string(version uint8, software Software) string {
return fmt.Sprintf("data: %v", b.Data)
}
type HelloBody struct {
redistDefault RouteType
instance uint16
sessionID uint32 // frr7.4, 7.5, 8, 8.1, 8.2
receiveNotify uint8
synchronous uint8 // frr7.4, 7.5, 8, 8.1, 8.2
}
// Ref: zread_hello in zebra/zserv.c of Quagga1.2&FRR3 (ZAPI3&4)
// Ref: zread_hello in zebra/zapi_msg.c of FRR5&FRR6&FRR7&FRR7.1&FRR7.2&FRR7.3&FRR7.4&FRR7.5&FRR8 (ZAPI5&6)
func (b *HelloBody) decodeFromBytes(data []byte, version uint8, software Software) error {
if len(data) < 1 {
return errors.New("not all ZAPI message body")
}
b.redistDefault = RouteType(data[0])
if version > 3 { // frr
if len(data) < 3+1 {
return errors.New("not all ZAPI message body")
}
b.instance = binary.BigEndian.Uint16(data[1:3])
if version == 6 && software.name == "frr" && software.version >= 7.4 {
if len(data) < 9 {
return errors.New("not all ZAPI message body")
}
b.sessionID = binary.BigEndian.Uint32(data[3:7])
b.receiveNotify = data[7]
b.synchronous = data[8]
} else if version > 4 {
b.receiveNotify = data[3]
}
}
return nil
}
// Ref: zebra_hello_send in lib/zclient.c of Quagga1.2&FRR3&FRR5&FRR6&FRR7&FRR7.1&FRR7.2&FRR7.3 (ZAPI3&4&5&6)
// Ref: zclient_send_hello in lib/zclient.c of FRR7.4&FRR7.5&FRR8 (ZAPI6)
func (b *HelloBody) serialize(version uint8, software Software) ([]byte, error) {
if version < 4 {
return []byte{uint8(b.redistDefault)}, nil
}
var buf []byte
if version == 6 && software.name == "frr" && software.version >= 7.4 {
buf = make([]byte, 9)
} else if version > 4 {
buf = make([]byte, 4)
} else if version == 4 {
buf = make([]byte, 3)
}
buf[0] = uint8(b.redistDefault)
binary.BigEndian.PutUint16(buf[1:3], b.instance)
if version == 6 && software.name == "frr" && software.version >= 7.4 {
binary.BigEndian.PutUint32(buf[3:7], b.sessionID)
buf[7] = b.receiveNotify
buf[8] = b.synchronous
} else if version > 4 {
buf[3] = b.receiveNotify
}
return buf, nil
}
func (b *HelloBody) string(version uint8, software Software) string {
return fmt.Sprintf(
"route_type: %s, instance :%d, sessionID: %d, receiveNotify: %d, synchronous: %d",
b.redistDefault.String(), b.instance, b.sessionID, b.receiveNotify, b.synchronous)
}
type redistributeBody struct {
afi afi
redist RouteType
instance uint16
}
// Ref: zebra_redistribute_add in zebra/redistribute.c of Quagga1.2&FRR3&FRR4&FRR5&FRR6&FRR7.x&FRR8 (ZAPI3&4&5&6)
func (b *redistributeBody) decodeFromBytes(data []byte, version uint8, software Software) error {
if version < 4 {
if len(data) < 1 {
return errors.New("not all ZAPI message body")
}
b.redist = RouteType(data[0])
} else { // version >= 4
if len(data) < 4 {
return errors.New("not all ZAPI message body")
}
b.afi = afi(data[0])
b.redist = RouteType(data[1])
b.instance = binary.BigEndian.Uint16(data[2:4])
}
return nil
}
// Ref: zebra_redistribute_send in lib/zclient.c of Quagga1.2&FRR3&FRR4&FRR5&FRR6&FRR7.x&FRR8 (ZAPI3&4&5&6)
func (b *redistributeBody) serialize(version uint8, software Software) ([]byte, error) {
if version < 4 {
return []byte{uint8(b.redist)}, nil
}
buf := make([]byte, 4)
buf[0] = uint8(b.afi)
buf[1] = uint8(b.redist)
binary.BigEndian.PutUint16(buf[2:4], b.instance)
return buf, nil
}
func (b *redistributeBody) string(version uint8, software Software) string {
return fmt.Sprintf(
"afi: %s, route_type: %s, instance :%d",
b.afi.String(), b.redist.String(), b.instance)
}
type linkParam struct {
status uint32
teMetric uint32
maxBw float32
maxRsvBw float32
unrsvBw [8]float32
bwClassNum uint32
adminGroup uint32
remoteAS uint32
remoteIP net.IP
aveDelay uint32
minDelay uint32
maxDelay uint32
delayVar uint32
pktLoss float32
residualBw float32
availableBw float32
useBw float32
}
type interfaceUpdateBody struct {
name string
index uint32
status interfaceStatus
flags uint64
ptmEnable ptmEnable
ptmStatus ptmStatus
metric uint32
speed uint32
mtu uint32
mtu6 uint32
bandwidth uint32
linkIfindex uint32
linktype linkType
hardwareAddr net.HardwareAddr
linkParam linkParam
}
// Ref: zebra_interface_if_set_value in lib/zclient.c of Quagga1.2&FRR3&FRR4&FRR5&FRR6&FRR7.x&FRR8 (ZAPI3&4&5&6)
func (b *interfaceUpdateBody) decodeFromBytes(data []byte, version uint8, software Software) error {
ifNameSize := interfaceNameSize
if version == 6 && software.name == "frr" && software.version >= 8.3 {
ifNameSize = osIfNameSize
}
// version 2: index(4)+status(1)+flags(8)+metric(4)+mtu(4)+mtu6(4)+bandwidth(4)+hw_addr_len(4)
necessaryDataSize := ifNameSize + 33
if version > 3 {
necessaryDataSize += 6 // add ptmEnable(1)+ptmStatus(1)+speed(4)
}
if version > 2 {
necessaryDataSize += 4 // add linktype(4)
}
if version == 6 && software.name == "frr" && software.version >= 7.2 {
necessaryDataSize += 4 // add linkIfIndex(4)
}
if len(data) < necessaryDataSize {
return fmt.Errorf("lack of bytes. need %d but %d", necessaryDataSize, len(data))
}
b.name = strings.Trim(string(data[:ifNameSize]), "\u0000")
data = data[ifNameSize:]
b.index = binary.BigEndian.Uint32(data[:4])
b.status = interfaceStatus(data[4])
b.flags = binary.BigEndian.Uint64(data[5:13])
if version > 3 {
b.ptmEnable = ptmEnable(data[13])
b.ptmStatus = ptmStatus(data[14])
b.metric = binary.BigEndian.Uint32(data[15:19])
b.speed = binary.BigEndian.Uint32(data[19:23])
data = data[23:]
} else {
b.metric = binary.BigEndian.Uint32(data[13:17])
data = data[17:]
}
b.mtu = binary.BigEndian.Uint32(data[:4])
b.mtu6 = binary.BigEndian.Uint32(data[4:8])
b.bandwidth = binary.BigEndian.Uint32(data[8:12])
data = data[12:]
// frr 7.2 and later versions have link Ifindex
if version == 6 && software.name == "frr" && software.version >= 7.2 {
b.linkIfindex = binary.BigEndian.Uint32(data[:4])
data = data[4:]
}
if version > 2 {
b.linktype = linkType(binary.BigEndian.Uint32(data[:4]))
data = data[4:]
}
l := binary.BigEndian.Uint32(data[:4]) // STREAM_GETL(s, ifp->hw_addr_len)
if l > 0 {
if len(data) < 4+int(l) {
return fmt.Errorf("lack of bytes in remain data. need %d but %d", 4+l, len(data))
}
b.hardwareAddr = data[4 : 4+l] // STREAM_GET(ifp->hw_addr, s, MIN(ifp->hw_addr_len, INTERFACE_HWADDR_MAX));
}
if version > 2 {
if len(data) < 4+int(l)+1 {
return errors.New("interfaceUpdateBody: lack of data")
}
linkParam := data[4+l] // stream_getc(s)
if linkParam > 0 { // link_params_set_value
data = data[5+l:]
if len(data) < 20 {
return errors.New("interfaceUpdateBody: lack of data")
}
b.linkParam.status = binary.BigEndian.Uint32(data[:4])
b.linkParam.teMetric = binary.BigEndian.Uint32(data[4:8])
b.linkParam.maxBw = math.Float32frombits(binary.BigEndian.Uint32(data[8:12]))
b.linkParam.maxRsvBw = math.Float32frombits(binary.BigEndian.Uint32(data[12:16]))
b.linkParam.bwClassNum = binary.BigEndian.Uint32(data[16:20])
if len(data) < 20+4+int(b.linkParam.bwClassNum)*4 || int(b.linkParam.bwClassNum) >= len(b.linkParam.unrsvBw) {
return errors.New("interfaceUpdateBody: lack or wrong data")
}
for i := range b.linkParam.bwClassNum {
b.linkParam.unrsvBw[i] = math.Float32frombits(binary.BigEndian.Uint32(data[20+i*4 : 24+i*4]))
}
data = data[20+b.linkParam.bwClassNum*4:]
if len(data) < 44 {
return errors.New("interfaceUpdateBody: lack of data")
}
b.linkParam.adminGroup = binary.BigEndian.Uint32(data[:4])
b.linkParam.remoteAS = binary.BigEndian.Uint32(data[4:8])
b.linkParam.remoteIP = data[8:12]
b.linkParam.aveDelay = binary.BigEndian.Uint32(data[12:16])
b.linkParam.minDelay = binary.BigEndian.Uint32(data[16:20])
b.linkParam.maxDelay = binary.BigEndian.Uint32(data[20:24])
b.linkParam.delayVar = binary.BigEndian.Uint32(data[24:28])
b.linkParam.pktLoss = math.Float32frombits(binary.BigEndian.Uint32(data[28:32]))
b.linkParam.residualBw = math.Float32frombits(binary.BigEndian.Uint32(data[32:36]))
b.linkParam.availableBw = math.Float32frombits(binary.BigEndian.Uint32(data[36:40]))
b.linkParam.useBw = math.Float32frombits(binary.BigEndian.Uint32(data[40:44]))
}
}
return nil
}
func (b *interfaceUpdateBody) serialize(version uint8, software Software) ([]byte, error) {
return []byte{}, nil
}
func (b *interfaceUpdateBody) string(version uint8, software Software) string {
s := fmt.Sprintf(
"name: %s, idx: %d, status: %s, flags: %s, ptm_enable: %s, ptm_status: %s, metric: %d, speed: %d, mtu: %d, mtu6: %d, bandwidth: %d, linktype: %s",
b.name, b.index, b.status.String(), intfflag2string(b.flags), b.ptmEnable.String(), b.ptmStatus.String(), b.metric, b.speed, b.mtu, b.mtu6, b.bandwidth, b.linktype.String())
if len(b.hardwareAddr) > 0 {
return s + fmt.Sprintf(", mac: %s", b.hardwareAddr.String())
}
return s
}
type interfaceAddressUpdateBody struct {
index uint32
flags interfaceAddressFlag
prefix net.IP
length uint8
destination net.IP
}
// Ref: zebra_interface_address_read in lib/zclient.c of Quagga1.2&FRR3&FRR4&FRR5&FRR6&FRR7.x&FRR8 (ZAPI3&4&5&6)
func (b *interfaceAddressUpdateBody) decodeFromBytes(data []byte, version uint8, software Software) error {
if len(data) < 6 {
return errors.New("not enough data for interface address update")
}
b.index = binary.BigEndian.Uint32(data[:4]) // STREAM_GETL(s, ifindex)
b.flags = interfaceAddressFlag(data[4]) // STREAM_GETC(s, ifc_flags)
family := data[5] // STREAM_GETC(s, d.family)
addrlen, err := addressByteLength(family)
if err != nil {
return err
}
if len(data) < 7+addrlen*2 {
return errors.New("not enough data for interface address update")
}
b.prefix = data[6 : 6+addrlen] // zclient_stream_get_prefix //STREAM_GET(&p->u.prefix, s, plen);
b.length = data[6+addrlen] // zclient_stream_get_prefix //STREAM_GETC(s, c);
b.destination = data[7+addrlen : 7+addrlen*2] // STREAM_GET(&d.u.prefix, s, plen)
return nil
}
func (b *interfaceAddressUpdateBody) serialize(version uint8, software Software) ([]byte, error) {
return []byte{}, nil
}
func (b *interfaceAddressUpdateBody) string(version uint8, software Software) string {
return fmt.Sprintf(
"idx: %d, flags: %s, addr: %s/%d",
b.index, b.flags.String(), b.prefix.String(), b.length)
}
type routerIDUpdateBody struct {
length uint8
prefix net.IP
afi afi
}
// Ref: zebra_router_id_update_read in lib/zclient.c of Quagga1.2&FRR3&FRR5 (ZAPI3&4&5)
func (b *routerIDUpdateBody) decodeFromBytes(data []byte, version uint8, software Software) error {
if len(data) < 1 {
return errors.New("not enough data for router ID update")
}
family := data[0]
addrlen, err := addressByteLength(family)
if err != nil {
return err
}
if len(data) < 1+addrlen+1 {
return errors.New("not enough data for router ID update")
}
b.prefix = data[1 : 1+addrlen] // zclient_stream_get_prefix
b.length = data[1+addrlen] // zclient_stream_get_prefix
return nil
}
// Ref: zclient_send_router_id_update in lib/zclient.c of FRR7.5
func (b *routerIDUpdateBody) serialize(version uint8, software Software) ([]byte, error) {
if version == 6 && software.name == "frr" && software.version >= 7.5 {
return []byte{0x00, uint8(b.afi)}, nil // stream_putw(s, afi);
}
return []byte{}, nil
}
func (b *routerIDUpdateBody) string(version uint8, software Software) string {
return fmt.Sprintf("id: %s/%d", b.prefix.String(), b.length)
}
// zapiNexthopFlag is defined in lib/zclient.h of FRR
const (
zapiNexthopFlagOnlink uint8 = 0x01 // frr7.1, 7.2, 7.3, 7.4, 7.5, 8.0
zapiNexthopFlagLabel uint8 = 0x02 // frr7.3, 7.4, 7.5, 8.0
zapiNexthopFlagWeight uint8 = 0x04 // frr7.3, 7.4, 7.5, 8.0
zapiNexthopFlagHasBackup uint8 = 0x08 // frr7.4, 7.5, 8.0
zapiNexthopFlagSeg6 uint8 = 0x10 // frr8.1
zapiNexthopFlagSeg6Local uint8 = 0x20 // frr8.1
)
// Flag for nexthop processing. It is gobgp's internal flag.
type nexthopProcessFlag uint8
const (
nexthopHasType nexthopProcessFlag = 0x01
nexthopHasVrfID nexthopProcessFlag = 0x02
nexthopHasFlag nexthopProcessFlag = 0x04
nexthopHasOnlink nexthopProcessFlag = 0x08
nexthopProcessIPToIPIFindex nexthopProcessFlag = 0x10
nexthopProcessIFnameToIFindex nexthopProcessFlag = 0x20 // for quagga
)
func nexthopProcessFlagForIPRouteBody(version uint8, software Software, isDecode bool) nexthopProcessFlag {
if version < 5 {
if isDecode {
return nexthopProcessFlag(0) // frr3&quagga don't have type&vrfid
}
return nexthopHasType // frr3&quagga need type for encode(serialize)
}
processFlag := nexthopHasVrfID | nexthopHasType // frr4, 5, 6, 7
if version == 6 && software.name == "frr" {
if software.version >= 7.3 {
processFlag |= nexthopHasFlag | nexthopProcessIPToIPIFindex
} else if software.version >= 7.1 {
processFlag |= nexthopHasOnlink
}
}
return processFlag
}
// Ref: struct seg6local_context in lib/srv6.h of FRR8.1
type seg6localContext struct {
nh4 net.IP // struct in_addr nh4
nh6 net.IP // struct in_addr nh6
table uint32
}
func (s6lc seg6localContext) encode() []byte {
var buf []byte
buf = append(buf, s6lc.nh4.To4()...)
buf = append(buf, s6lc.nh6.To16()...)
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, s6lc.table)
buf = append(buf, tmpbuf...)
return buf
}
func (s6lc *seg6localContext) decode(data []byte) int {
offset := 0
s6lc.nh4 = net.IP(data[offset : offset+4]).To4()
offset += 4
s6lc.nh6 = net.IP(data[offset : offset+16]).To16()
offset += 16
s6lc.table = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
return offset
}
// Ref: struct zapi_nexthop in lib/zclient.h of FRR5&FRR6&FRR7.x&FRR8, FRR8.1 (ZAPI5&6)
// Nexthop is referred in zclient
type Nexthop struct {
Type nexthopType // FRR5, FRR6, FRR7.x, FRR8, FRR8.1
VrfID uint32 // FRR5, FRR6, FRR7.x, FRR8, FRR8.1
Ifindex uint32 // Ifindex is referred in zclient_test
flags uint8 // FRR7.1, FRR7.2 FRR7.3, FRR7.4, FRR7.5, FRR8, FRR8.1
Gate netip.Addr // union { union g_addr gate;
blackholeType uint8 // enum blackhole_type bh_type;}
LabelNum uint8 // FRR5, FRR6, FRR7.x, FRR8, FRR8.1
MplsLabels []uint32 // FRR5, FRR6, FRR7.x, FRR8, FRR8.1
rmac [6]byte // FRR6, FRR7.x, FRR8, FRR8.1
Weight uint32 // FRR7.3, FRR7.4, FRR7.5, FRR8, FRR8.1
backupNum uint8 // FRR7.4, FRR7.5, FRR8, FRR8.1
backupIndex []uint8 // FRR7.5, FRR8, FRR8.1
srteColor uint32 // FRR7.5, FRR8, FRR8.1
seg6localAction uint32 // FRR8.1
seg6localCtx seg6localContext // FRR8.1
seg6Segs net.IP // strcut in6_addr // FRR8.1
}
func (n Nexthop) string() string {
s := make([]string, 0)
s = append(s, fmt.Sprintf(
"type: %s, vrf_id: %d, ifindex: %d, flags: %d, gate: %s, blackholeType: %d, label_num: %d, weight: %d, backupNum: %d, srteColor: %d",
n.Type.String(), n.VrfID, n.Ifindex, n.flags, n.Gate.String(),
n.blackholeType, n.LabelNum, n.Weight, n.backupNum, n.srteColor))
for i := range n.LabelNum {
s = append(s, fmt.Sprintf(" label[%d]: %d", i, n.MplsLabels[i]))
}
for i := range n.backupNum {
s = append(s, fmt.Sprintf(" backupIndex[%d]: %d", i, n.backupIndex[i]))
}
return strings.Join(s, ", ")
}
func (n Nexthop) gateToType(version uint8) nexthopType {
if n.Gate.Is4() {
if version > 4 && n.Ifindex > 0 {
return nexthopTypeIPv4IFIndex
}
return nexthopTypeIPv4.toEach(version)
} else if n.Gate.Is6() {
if version > 4 && n.Ifindex > 0 {
return nexthopTypeIPv6IFIndex
}
return nexthopTypeIPv6.toEach(version)
} else if n.Ifindex > 0 {
return nexthopTypeIFIndex.toEach(version)
} else if version > 4 {
return nexthopTypeBlackhole
}
return nexthopType(0)
}
// Ref: zapi_nexthop_encode in lib/zclient.h of FRR7.3&FRR7.4&FRR7.5&FRR8
func (n Nexthop) encode(version uint8, software Software, processFlag nexthopProcessFlag, message MessageFlag, apiFlag Flag) []byte {
var buf []byte
if processFlag&nexthopHasVrfID > 0 {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, n.VrfID)
buf = append(buf, tmpbuf...) // frr: stream_putl(s, api_nh->vrf_id);
}
if processFlag&nexthopHasType > 0 {
if n.Type == nexthopType(0) {
n.Type = n.gateToType(version)
}
buf = append(buf, uint8(n.Type)) // frr: stream_putc(s, api_nh->type);
}
if processFlag&nexthopHasFlag > 0 {
if n.LabelNum > 0 {
n.flags |= zapiNexthopFlagLabel
}
if n.Weight > 0 {
n.flags |= zapiNexthopFlagWeight
}
if n.backupNum > 0 {
n.flags |= zapiNexthopFlagHasBackup
}
}
if processFlag&nexthopHasFlag > 0 || processFlag&nexthopHasOnlink > 0 {
// frr7.1, 7.2 has onlink, 7.3 has flag
buf = append(buf, n.flags) // frr: stream_putc(s, nh_flags);
}
nhType := n.Type
if processFlag&nexthopProcessIPToIPIFindex > 0 {
nhType = nhType.ipToIPIFIndex()
}
if processFlag&nexthopProcessIFnameToIFindex > 0 {
nhType = nhType.ifNameToIFIndex()
}
if nhType == nexthopTypeIPv4.toEach(version) ||
nhType == nexthopTypeIPv4IFIndex.toEach(version) {
// frr: stream_put_in_addr(s, &api_nh->gate.ipv4);
buf = append(buf, n.Gate.AsSlice()...)
} else if nhType == nexthopTypeIPv6.toEach(version) ||
nhType == nexthopTypeIPv6IFIndex.toEach(version) {
// frr: stream_write(s, (uint8_t *)&api_nh->gate.ipv6, 16);
buf = append(buf, n.Gate.AsSlice()...)
}
if nhType == nexthopTypeIFIndex ||
nhType == nexthopTypeIPv4IFIndex.toEach(version) ||
nhType == nexthopTypeIPv6IFIndex.toEach(version) {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, n.Ifindex)
buf = append(buf, tmpbuf...) // frr: stream_putl(s, api_nh->ifindex);
}
if nhType == nexthopTypeBlackhole.toEach(version) { // case NEXTHOP_TYPE_BLACKHOLE:
// frr: stream_putc(s, api_nh->bh_type);
buf = append(buf, n.blackholeType)
}
if n.flags&zapiNexthopFlagLabel > 0 || (message&MessageLabel > 0 &&
version == 5 ||
version == 6 && software.name == "frr" &&
software.version >= 6 && software.version < 7.3) {
tmpbuf := make([]byte, 1+4*n.LabelNum)
tmpbuf[0] = n.LabelNum // frr: stream_putc(s, api_nh->label_num);
for i := range n.LabelNum {
// frr uses stream_put for mpls label array.
// stream_put is unaware of byteorder coversion.
// Therefore LittleEndian is used instead of BigEndian.
binary.LittleEndian.PutUint32(tmpbuf[i*4+1:], n.MplsLabels[i])
}
// frr: stream_put(s, &api_nh->labels[0], api_nh->label_num * sizeof(mpls_label_t));
buf = append(buf, tmpbuf...)
}
if n.flags&zapiNexthopFlagWeight > 0 && n.Weight > 0 {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, n.Weight)
buf = append(buf, tmpbuf...) // frr: stream_putl(s, api_nh->Weight);
}
if apiFlag&flagEvpnRoute.ToEach(version, software) > 0 {
// frr: stream_put(s, &(api_nh->rmac), sizeof(struct ethaddr));
buf = append(buf, n.rmac[:]...)
}
// added in frr7.5 (Color for Segment Routing TE.)
if message&messageSRTE > 0 && (version == 6 && software.name == "frr" && software.version >= 7.5) {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, n.srteColor)
buf = append(buf, tmpbuf...) // frr: stream_putl(s, api_nh->srte_color);
}
// added in frr7.4 (Index of backup nexthop)
if n.flags&zapiNexthopFlagHasBackup > 0 {
tmpbuf := make([]byte, 1+1*n.backupNum)
tmpbuf[0] = n.backupNum // frr: stream_putc(s, api_nh->backup_num);
for i := range n.backupNum {
tmpbuf[i+1] = n.backupIndex[i]
}
buf = append(buf, tmpbuf...)
}
// added in frr8.1
if n.flags&zapiNexthopFlagSeg6 > 0 {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, n.seg6localAction)
buf = append(buf, tmpbuf...) // stream_putl(s, api_nh->seg6local_action);
// frr: stream_write(s, &api_nh->seg6local_ctx, sizeof(struct seg6local_context));
buf = append(buf, n.seg6localCtx.encode()...)
}
// added in frr8.1
if n.flags&zapiNexthopFlagSeg6Local > 0 {
// frr: stream_write(s, &api_nh->seg6_segs, sizeof(struct in6_addr));
buf = append(buf, n.seg6Segs.To16()...)
}
return buf
}
// Ref: zapi_nexthop_decode in lib/zclient.h of FRR7.3&FRR7.4&FRR7.5&FRR8
func (n *Nexthop) decode(data []byte, version uint8, software Software, family uint8, processFlag nexthopProcessFlag, message MessageFlag, apiFlag Flag, nhType nexthopType) (int, error) {
offset := 0
if processFlag&nexthopHasVrfID > 0 {
if len(data) < offset+4 {
return 0, fmt.Errorf("lack of bytes for vrf_id. need 4 but %d", len(data)-offset)
}
// frr: STREAM_GETL(s, api_nh->vrf_id);
n.VrfID = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
}
n.Type = nhType // data does not have nexthop type
if processFlag&nexthopHasType > 0 {
if len(data) < offset+1 {
return 0, errors.New("lack of bytes for nexthop type. need 1")
}
n.Type = nexthopType(data[offset]) // frr: STREAM_GETC(s, api_nh->type);
offset++
}
n.flags = uint8(0)
if processFlag&nexthopHasFlag > 0 || processFlag&nexthopHasOnlink > 0 {
if len(data) < offset+1 {
return 0, errors.New("lack of bytes for nexthop flags. need 1")
}
n.flags = data[offset] // frr: STREAM_GETC(s, api_nh->flags);
offset++
}
nhType = n.Type
if processFlag&nexthopProcessIPToIPIFindex > 0 {
nhType = nhType.ipToIPIFIndex()
}
if processFlag&nexthopProcessIFnameToIFindex > 0 {
nhType = nhType.ifNameToIFIndex()
}
switch family {
case syscall.AF_INET:
n.Gate = netip.IPv4Unspecified()
case syscall.AF_INET6:
n.Gate = netip.IPv6Unspecified()
}
if nhType == nexthopTypeIPv4.toEach(version) ||
nhType == nexthopTypeIPv4IFIndex.toEach(version) {
if len(data) < offset+4 {
return 0, fmt.Errorf("lack of bytes for IPv4 gate. need 4 but %d", len(data)-offset)
}
// frr: STREAM_GET(&api_nh->gate.ipv4.s_addr, s, IPV4_MAX_BYTELEN);
n.Gate, _ = netip.AddrFromSlice(data[offset : offset+4])
offset += 4
} else if nhType == nexthopTypeIPv6.toEach(version) ||
nhType == nexthopTypeIPv6IFIndex.toEach(version) {
if len(data) < offset+16 {
return 0, fmt.Errorf("lack of bytes for IPv6 gate. need 16 but %d", len(data)-offset)
}
// frr: STREAM_GET(&api_nh->gate.ipv6, s, 16);
n.Gate, _ = netip.AddrFromSlice(data[offset : offset+16])
offset += 16
}
if nhType == nexthopTypeIFIndex ||
nhType == nexthopTypeIPv4IFIndex.toEach(version) ||
nhType == nexthopTypeIPv6IFIndex.toEach(version) {
if len(data) < offset+4 {
return 0, fmt.Errorf("lack of bytes for ifindex. need 4 but %d", len(data)-offset)
}
// frr: STREAM_GETL(s, api_nh->ifindex);
n.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
}
if nhType == nexthopTypeBlackhole.toEach(version) { // case NEXTHOP_TYPE_BLACKHOLE:
if len(data) < offset+1 {
return 0, errors.New("lack of bytes for blackhole type. need 1")
}
n.blackholeType = data[offset] // frr: STREAM_GETC(s, api_nh->bh_type);
offset++
}
if n.flags&zapiNexthopFlagLabel > 0 || message&MessageLabel > 0 &&
(version == 5 || version == 6 && software.name == "frr" &&
software.version >= 6 && software.version < 7.3) {
if len(data) < offset+1 {
return 0, errors.New("lack of bytes for label_num. need 1")
}
n.LabelNum = data[offset] // frr: STREAM_GETC(s, api_nh->label_num);
offset++
if n.LabelNum > maxMplsLabel {
n.LabelNum = maxMplsLabel
}
if n.LabelNum > 0 {
n.MplsLabels = make([]uint32, n.LabelNum)
for i := range n.LabelNum {
if len(data) < offset+4 {
return 0, fmt.Errorf("lack of bytes for mpls label. need %d but %d", 4, len(data)-offset)
}
// frr uses stream_put which is unaware of byteorder for mpls label array.
// Therefore LittleEndian is used instead of BigEndian.
// frr: STREAM_GET(&api_nh->labels[0], s, api_nh->label_num * sizeof(mpls_label_t));
n.MplsLabels[i] = binary.LittleEndian.Uint32(data[offset : offset+4])
offset += 4
}
}
}
if n.flags&zapiNexthopFlagWeight > 0 {
if len(data) < offset+4 {
return 0, fmt.Errorf("lack of bytes for weight. need 4 but %d", len(data)-offset)
}
// frr: STREAM_GETL(s, api_nh->Weight);
n.Weight = binary.BigEndian.Uint32(data[offset:])
offset += 4
}
if apiFlag&flagEvpnRoute.ToEach(version, software) > 0 {
if len(data) < offset+6 {
return 0, fmt.Errorf("lack of bytes for eVPN route. need 6 but %d", len(data)-offset)
}
// frr: STREAM_GET(&(api_nh->rmac), s, sizeof(struct ethaddr));
copy(n.rmac[0:], data[offset:offset+6])
offset += 6
}
// added in frr7.5 (Color for Segment Routing TE.)
if message&messageSRTE > 0 &&
(version == 6 && software.name == "frr" && software.version >= 7.5) {
if len(data) < offset+4 {
return 0, fmt.Errorf("lack of bytes for srte_color. need 4 but %d", len(data)-offset)
}
// frr: STREAM_GETL(s, api_nh->srte_color);
n.srteColor = binary.BigEndian.Uint32(data[offset:])
offset += 4
}
// added in frr7.4 (Index of backup nexthop)
if n.flags&zapiNexthopFlagHasBackup > 0 {
if len(data) < offset+1 {
return 0, errors.New("lack of bytes for backup_num. need 1")
}
n.backupNum = data[offset] // frr: STREAM_GETC(s, api_nh->backup_num);
offset++
if n.backupNum > 0 {
n.backupIndex = make([]uint8, n.backupNum)
if len(data) < offset+int(n.backupNum) {
return 0, errors.New("lack of bytes for backup_num")
}
for i := range n.backupNum {
// frr STREAM_GETC(s, api_nh->backup_idx[i]);
n.backupIndex[i] = data[offset]
offset++
}
}
}
// added in frr8.1
if n.flags&zapiNexthopFlagSeg6 > 0 {
if len(data) < offset+4+24 {
return 0, fmt.Errorf("lack of bytes for Nexthop Seg6. need 24 but %d", len(data)-offset)
}
n.seg6localAction = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
offset += n.seg6localCtx.decode(data[offset : offset+24])
}
// added in frr8.1
if n.flags&zapiNexthopFlagSeg6Local > 0 {
if len(data) < offset+16 {
return 0, fmt.Errorf("lack of bytes for Nexthop Seg6Local. need 16 but %d", len(data)-offset)
}
n.seg6Segs = net.IP(data[offset : offset+16]).To16()
offset += 16
}
return offset, nil
}
// decodeNexthops is referred from decodeFromBytes of NexthopUpdateBody and IPRouteBody
func decodeNexthops(nexthops *[]Nexthop, data []byte, version uint8, software Software, family uint8, numNexthop uint16, processFlag nexthopProcessFlag, message MessageFlag, apiFlag Flag, nhType nexthopType) (int, error) {
offset := 0
*nexthops = make([]Nexthop, numNexthop)
for i := range numNexthop {
if len(data) < offset {
return 0, fmt.Errorf("lack of bytes in remain data. need %d but %d", offset, len(data))
}
size, err := (&(*nexthops)[i]).decode(data[offset:], version, software, family, processFlag, message, apiFlag, nhType)
if err != nil {
return offset, err
}
offset += size
}
return offset, nil
}
// Prefix referred in zclient is struct for network prefix and relate information
type Prefix struct {
Family uint8
PrefixLen uint8
Prefix netip.Addr
}
func familyFromPrefix(prefix netip.Addr) uint8 {
if prefix.Is4() {
return syscall.AF_INET
} else if prefix.Is6() {
return syscall.AF_INET6
}
return syscall.AF_UNSPEC
}
const messageOpaqueLenth uint16 = 1024
type opaque struct {
length uint16
data [messageOpaqueLenth]uint8
}
// Ref: struct zapi_route in lib/zclient.h of FRR4&FRR5&FRR6&FRR7.x&RR8 (ZAPI5&6)
// IPRouteBody is struct for IPRotue (zapi_route)
type IPRouteBody struct {
Type RouteType // FRR4&FRR5&FRR6&FRR7.x&FRR8
instance uint16 // FRR4&FRR5&FRR6&FRR7.x&FRR8
Flags Flag // FRR4&FRR5&FRR6&FRR7.x&FRR8
Message MessageFlag // FRR4&FRR5&FRR6&FRR7.x&FRR8
Safi Safi // FRR4&FRR5&FRR6&FRR7.x&FRR8
Prefix Prefix // FRR4&FRR5&FRR6&FRR7.x&FRR8
srcPrefix Prefix // FRR4&FRR5&FRR6&FRR7.x&FRR8
Nexthops []Nexthop // FRR4&FRR5&FRR6&FRR7.x&FRR8
backupNexthops []Nexthop // added in frr7.4, FRR7.4&FRR7.5&FRR8
nhgid uint32 // added in frr8
Distance uint8 // FRR4&FRR5&FRR6&FRR7.x&FRR8
Metric uint32 // FRR4&FRR5&FRR6&FRR7.x&FRR8
tag uint32 // FRR4&FRR5&FRR6&FRR7.x&FRR8
Mtu uint32 // FRR4&FRR5&FRR6&FRR7.x&FRR8
tableID uint32 // FRR5&FRR6&FRR7.x&FRR8 (nh_vrf_id in FRR4)
srteColor uint32 // added in frr7.5, FRR7.5&FRR8
opaque opaque // added in frr8
API APIType // API is referred in zclient_test
// vrfID uint32 // lib/zebra.h:typedef uint32_t vrf_id_t;
}
func (b *IPRouteBody) safi(logger *slog.Logger, version uint8, software Software) Safi {
// frr 7.2 and later versions have safiUnspec, older versions don't have safiUnspec
if b.Safi == safiUnspec && (version < 6 ||
version == 6 && software.name == "frr" && software.version < 7.2) {
return SafiUnicast // safiUnspec is regarded as safiUnicast in older versions
}
if b.Safi <= safiMulticast || version > 4 { // not need to convert
return b.Safi
}
safiMap := zapi4SafiMap
if version < 4 {
safiMap = zapi3SafiMap
}
safi, ok := safiMap[b.Safi]
if !ok {
safi = safiUnspec // failed to convert
}
logger.Debug("zebra converts safi",
slog.String("Topic", "Zebra"),
slog.Any("Body", b),
slog.String("Old", b.Safi.String()),
slog.String("New", safi.String()),
)
return safi // success to convert
}
// Family is referred in zclient
func (b *IPRouteBody) Family(logger *slog.Logger, version uint8, software Software) bgp.Family {
if b == nil {
return bgp.RF_OPAQUE // fail
}
safi := b.safi(logger, version, software)
if safi == safiEvpn {
return bgp.RF_EVPN // success
}
family := b.Prefix.Family
if family == syscall.AF_UNSPEC {
family = familyFromPrefix(b.Prefix.Prefix)
}
if family == syscall.AF_UNSPEC { // familyFromPrefix returs AF_UNSPEC
return bgp.RF_OPAQUE // fail
}
safiFamilyMap := safiFamilyIPv4Map // syscall.AF_INET
if family == syscall.AF_INET6 {
safiFamilyMap = safiFamilyIPv6Map
}
rf, ok := safiFamilyMap[safi]
if !ok {
return bgp.RF_OPAQUE // fail
}
logger.Debug("zebra converts safi",
slog.String("Topic", "Zebra"),
slog.Any("Body", b),
slog.String("Safi", safi.String()),
slog.String("Rf", rf.String()))
return rf // success
}
// IsWithdraw is referred in zclient
func (b *IPRouteBody) IsWithdraw(version uint8, software Software) bool {
api := b.API.ToCommon(version, software)
switch api {
case RouteDelete, RedistributeRouteDel, BackwardIPv6RouteDelete:
return true
}
if version == 4 && b.API == zapi4RedistributeIPv6Del {
return true
}
return false
}
// Ref: zapi_ipv4_route in lib/zclient.c of Quagga1.2.x&FRR3.x(ZAPI3&4)
// Ref: zapi_route_encode in lib/zclient.c of FRR4&FRR5&FRR6&FRR7.x&FRR8 (ZAPI5&6)
func (b *IPRouteBody) serialize(version uint8, software Software) ([]byte, error) {
var buf []byte
numNexthop := len(b.Nexthops)
bufInitSize := 12 // type(1)+instance(2)+flags(4)+message(4)+safi(1), frr7.4&newer
switch version {
case 2, 3:
bufInitSize = 5
case 4:
bufInitSize = 10
case 5:
bufInitSize = 9 // type(1)+instance(2)+flags(4)+message(1)+safi(1)
case 6:
if software.name == "frr" && software.version < 7.4 { // frr6, 7, 7.2, 7.3
bufInitSize = 9 // type(1)+instance(2)+flags(4)+message(1)+safi(1)
}
}
buf = make([]byte, bufInitSize)
buf[0] = uint8(b.Type.toEach(version)) // frr: stream_putc(s, api->type);
if version < 4 {
buf[1] = uint8(b.Flags)
buf[2] = uint8(b.Message)
binary.BigEndian.PutUint16(buf[3:5], uint16(b.Safi))
} else { // version >= 4
// frr: stream_putw(s, api->instance);
binary.BigEndian.PutUint16(buf[1:3], b.instance)
// frr: stream_putl(s, api->flags);
binary.BigEndian.PutUint32(buf[3:7], uint32(b.Flags))
if version == 6 && software.name == "frr" && software.version >= 7.5 {
// frr7.5 and newer: stream_putl(s, api->message);
binary.BigEndian.PutUint32(buf[7:11], uint32(b.Message))
buf[11] = uint8(b.Safi) // stream_putc(s, api->safi);
} else {
// frr 7.4 and older: stream_putc(s, api->message);
buf[7] = uint8(b.Message)
if version > 4 {
buf[8] = uint8(b.Safi) // frr: stream_putc(s, api->safi);
} else { // version 2,3 and 4 (quagga, frr3)
binary.BigEndian.PutUint16(buf[8:10], uint16(b.Safi))
}
}
}
// only zapi version 5 (frr4.0.x) have evpn routes
if version == 5 && b.Flags&flagEvpnRoute.ToEach(version, software) > 0 {
// size of struct ethaddr is 6 octets defined by ETH_ALEN
buf = append(buf, b.Nexthops[numNexthop-1].rmac[:6]...)
}
if version > 4 { // version 5, 6 (after frr4)
if b.Prefix.Family == syscall.AF_UNSPEC {
b.Prefix.Family = familyFromPrefix(b.Prefix.Prefix)
}
// frr: stream_putc(s, api->prefix.family);
buf = append(buf, b.Prefix.Family)
}
byteLen := (int(b.Prefix.PrefixLen) + 7) / 8
buf = append(buf, b.Prefix.PrefixLen) // frr: stream_putc(s, api->prefix.prefixlen);
// frr: stream_write(s, (uint8_t *)&api->prefix.u.prefix, psize);
buf = append(buf, b.Prefix.Prefix.AsSlice()[:byteLen]...)
if version > 3 && b.Message&messageSRCPFX.ToEach(version, software) > 0 {
byteLen = (int(b.srcPrefix.PrefixLen) + 7) / 8
// frr: stream_putc(s, api->src_prefix.prefixlen);
buf = append(buf, b.srcPrefix.PrefixLen)
// frr: stream_write(s, (uint8_t *)&api->prefix.u.prefix, psize);
buf = append(buf, b.srcPrefix.Prefix.AsSlice()[:byteLen]...)
}
// NHG(Nexthop Group) is added in frr8
// frr: if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NHG))
if version == 6 && software.name == "frr" && software.version >= 8 &&
b.Message&messageNhg.ToEach(version, software) > 0 {
// frr: stream_putl(s, api->nhgid);
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, b.nhgid)
buf = append(buf, tmpbuf...)
}
processFlag := nexthopProcessFlagForIPRouteBody(version, software, false)
if b.Message&MessageNexthop > 0 {
if version < 5 {
if b.Flags&flagBlackhole > 0 {
buf = append(buf, []byte{1, uint8(nexthopTypeBlackhole.toEach(version))}...)
} else {
buf = append(buf, uint8(numNexthop))
}
} else { // version >= 5
tmpbuf := make([]byte, 2)
binary.BigEndian.PutUint16(tmpbuf, uint16(numNexthop))
buf = append(buf, tmpbuf...) // frr: stream_putw(s, api->nexthop_num);
}
for _, nexthop := range b.Nexthops {
buf = append(buf, nexthop.encode(version, software, processFlag, b.Message, b.Flags)...)
}
}
// MESSAGE_BACKUP_NEXTHOPS is added in frr7.4
if version == 6 && software.name == "frr" && software.version >= 7.4 &&
b.Message&messageBackupNexthops > 0 {
tmpbuf := make([]byte, 2)
binary.BigEndian.PutUint16(tmpbuf, uint16(len(b.backupNexthops)))
buf = append(buf, tmpbuf...) // frr: stream_putw(s, api->backup_nexthop_num);
for _, nexthop := range b.backupNexthops {
buf = append(buf, nexthop.encode(version, software, processFlag, b.Message, b.Flags)...)
}
}
if b.Message&MessageDistance.ToEach(version, software) > 0 {
buf = append(buf, b.Distance)
}
if b.Message&MessageMetric.ToEach(version, software) > 0 {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, b.Metric)
buf = append(buf, tmpbuf...)
}
if b.Message&messageTag.ToEach(version, software) > 0 {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, b.tag)
buf = append(buf, tmpbuf...)
}
if b.Message&MessageMTU.ToEach(version, software) > 0 {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, b.Mtu)
buf = append(buf, tmpbuf...)
}
if b.Message&messageTableID.ToEach(version, software) > 0 {
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, b.tableID)
buf = append(buf, tmpbuf...)
}
if b.Message&messageOpaque.ToEach(version, software) > 0 {
tmpbuf := make([]byte, 2)
binary.BigEndian.PutUint16(tmpbuf, b.opaque.length)
buf = append(buf, tmpbuf...) // frr: stream_putw(s, api->opaque.length);
buf = append(buf, b.opaque.data[:]...) // frr: stream_write(s, api->opaque.data, api->opaque.length);
}
return buf, nil
}
// decodeMessageNexthopFromBytes is referred in IPRouteBody's decodeFromBytes
func (b *IPRouteBody) decodeMessageNexthopFromBytes(data []byte, version uint8, software Software, isBackup bool) (int, error) {
pos := 0
rest := len(data)
message := MessageNexthop
nexthops := &b.Nexthops
messageString := "MessageNexthop"
if isBackup {
message = messageBackupNexthops
nexthops = &b.backupNexthops
messageString = "messageBackupNexthops"
}
if b.Message&message > 0 {
numNexthop := uint16(0)
numNexthopDataSize := 2
processFlag := nexthopProcessFlagForIPRouteBody(version, software, true)
nhType := nexthopType(0)
if message == MessageNexthop && version < 5 { // frr3 and quagga
numNexthopDataSize = 1
nhType = nexthopTypeIPv4.toEach(version)
if b.Prefix.Family == syscall.AF_INET6 {
nhType = nexthopTypeIPv6.toEach(version)
}
}
if pos+numNexthopDataSize > rest {
return pos, fmt.Errorf("%s message length invalid pos:%d rest:%d", messageString, pos, rest)
}
if numNexthopDataSize == 2 {
// frr: STREAM_GETW(s, api->nexthop_num);
numNexthop = binary.BigEndian.Uint16(data[pos : pos+2])
} else if message == MessageNexthop && numNexthopDataSize == 1 {
numNexthop = uint16(data[pos])
}
pos += numNexthopDataSize
nexthopsByteLen, err := decodeNexthops(nexthops, data[pos:], version, software, b.Prefix.Family, numNexthop, processFlag, b.Message, b.Flags, nhType)
if err != nil {
return pos, err
}
pos += nexthopsByteLen
}
return pos, nil
}
// Ref: zebra_read_ipv4 in bgpd/bgp_zebra.c of Quagga1.2.x&FRR3.x(ZAPI3&4)
// Ref: zapi_route_decode in lib/zclient.c of FRR5&FRR6&FRR7.x&FRR8 (ZAPI5&6)
func (b *IPRouteBody) decodeFromBytes(data []byte, version uint8, software Software) error {
if b == nil {
return fmt.Errorf("IPRouteBody is nil")
}
if len(data) < 1 {
return errors.New("IPRouteBody data length is too short")
}
// frr: STREAM_GETC(s, api->type);
b.Type = RouteType(data[0])
if b.Type > getRouteAll(version, software) { // ver5 and later work, fix for older
return fmt.Errorf("unknown route type: %d in version: %d (%s)", b.Type, version, software.string())
}
if version <= 3 {
if len(data) < 2 {
return errors.New("IPRouteBody data length is too short")
}
b.Flags = Flag(data[1])
data = data[2:]
} else { // version >= 4
if len(data) < 7 {
return errors.New("IPRouteBody data length is too short")
}
// frr: STREAM_GETW(s, api->instance);
b.instance = binary.BigEndian.Uint16(data[1:3])
// frr: STREAM_GETL(s, api->flags);
b.Flags = Flag(binary.BigEndian.Uint32(data[3:7]))
data = data[7:]
}
if version == 6 && software.name == "frr" && software.version >= 7.5 {
if len(data) < 4 {
return errors.New("IPRouteBody data length is too short")
}
// frr7.5: STREAM_GETL(s, api->message);
b.Message = MessageFlag(binary.BigEndian.Uint32(data[:4]))
data = data[4:]
} else {
if len(data) < 1 {
return errors.New("IPRouteBody data length is too short")
}
b.Message = MessageFlag(data[0]) // frr: STREAM_GETC(s, api->message);
data = data[1:]
}
b.Safi = SafiUnicast
b.Prefix.Family = b.API.addressFamily(version) // return AF_UNSPEC if version > 4
var evpnNexthop Nexthop
if version > 4 {
if len(data) < 1 {
return errors.New("IPRouteBody safi type data length is too short")
}
b.Safi = Safi(data[0]) // frr: STREAM_GETC(s, api->safi);
if b.Safi > safiMax { // frr5 and later work, ToDo: fix for older version
return fmt.Errorf("unknown safi type: %d in version: %d (%s)", b.Type, version, software.string())
}
data = data[1:]
// zapi version 5 only
if version == 5 && b.Flags&flagEvpnRoute.ToEach(version, software) > 0 {
if len(data) < 6 {
return errors.New("IPRouteBody data length is too short")
}
// size of struct ethaddr is 6 octets defined by ETH_ALEN
copy(evpnNexthop.rmac[:6], data[:6])
data = data[6:]
}
if len(data) < 1 {
return errors.New("IPRouteBody data length is too short")
}
b.Prefix.Family = data[0] // frr: STREAM_GETC(s, api->prefix.family);
data = data[1:]
}
addrByteLen, err := addressByteLength(b.Prefix.Family)
if err != nil {
return err
}
addrBitLen := uint8(addrByteLen * 8)
if len(data) < 1 {
return errors.New("IPRouteBody data length is too short")
}
b.Prefix.PrefixLen = data[0] // frr: STREAM_GETC(s, api->prefix.prefixlen);
if b.Prefix.PrefixLen > addrBitLen {
return fmt.Errorf("prefix length %d is greater than %d", b.Prefix.PrefixLen, addrBitLen)
}
data = data[1:]
pos := 0
rest := len(data)
buf := make([]byte, addrByteLen)
byteLen := (int(b.Prefix.PrefixLen) + 7) / 8
if pos+byteLen > rest || len(buf) < byteLen {
return fmt.Errorf("message length invalid pos:%d rest:%d buflen:%d", pos, rest, len(buf))
}
// frr: STREAM_GET(&api->prefix.u.prefix, s, PSIZE(api->prefix.prefixlen));
copy(buf, data[pos:pos+byteLen])
b.Prefix.Prefix = ipFromFamily(b.Prefix.Family, buf)
pos += byteLen
if version > 3 && b.Message&messageSRCPFX.ToEach(version, software) > 0 {
if pos+1 > rest {
return fmt.Errorf("MessageSRCPFX message length invalid pos:%d rest:%d", pos, rest)
}
// frr: STREAM_GETC(s, api->src_prefix.prefixlen);
b.srcPrefix.PrefixLen = data[pos]
if b.srcPrefix.PrefixLen > addrBitLen {
return fmt.Errorf("prefix length is greater than %d", addrByteLen*8)
}
pos++
buf = make([]byte, addrByteLen)
byteLen = (int(b.srcPrefix.PrefixLen) + 7) / 8
if pos+byteLen > rest || len(buf) < byteLen {
return fmt.Errorf("message length invalid pos:%d rest:%d buflen:%d", pos, rest, len(buf))
}
// frr: STREAM_GET(&api->src_prefix.prefix, s, PSIZE(api->src_prefix.prefixlen));
copy(buf, data[pos:pos+byteLen])
b.srcPrefix.Prefix = ipFromFamily(b.Prefix.Family, buf)
pos += byteLen
}
// NHG(Nexthop Group) is added in frr8
// frr: if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NHG))
if version == 6 && software.name == "frr" && software.version >= 8 { // added in frr8
if b.Message&messageNhg.ToEach(version, software) > 0 {
if len(data) < pos+4 {
return errors.New("IPRouteBody frr8 data length is too short")
}
// frr: STREAM_GETL(s, api->nhgid);
b.nhgid = binary.BigEndian.Uint32(data[pos : pos+4])
pos += 4
}
}
b.Nexthops = []Nexthop{}
if b.Message&MessageNexthop.ToEach(version, software) > 0 {
if rest < pos {
return errors.New("IPRouteBody nexthops data length is too short")
}
offset, err := b.decodeMessageNexthopFromBytes(data[pos:], version, software, false)
if err != nil {
return err
}
pos += offset
}
b.backupNexthops = []Nexthop{} // backupNexthops is added in frr7.4
if b.Message&messageBackupNexthops.ToEach(version, software) > 0 {
if rest < pos {
return errors.New("IPRouteBody backupnexthops data length is too short")
}
offset, err := b.decodeMessageNexthopFromBytes(data[pos:], version, software, true)
if err != nil {
return err
}
pos += offset
}
// version 5 only, In version 6, EvpnRoute is processed in MessageNexthop
if version == 5 && b.Flags&flagEvpnRoute.ToEach(version, software) > 0 {
b.Nexthops = append(b.Nexthops, evpnNexthop)
}
if version < 5 && b.Message&messageIFIndex > 0 { // version 4, 3, 2
if pos+1 > rest {
return fmt.Errorf("MessageIFIndex message length invalid pos:%d rest:%d", pos, rest)
}
numIfIndex := data[pos]
pos++
for range numIfIndex {
if pos+4 > rest {
return fmt.Errorf("MessageIFIndex message length invalid pos:%d rest:%d", pos, rest)
}
var nexthop Nexthop
nexthop.Ifindex = binary.BigEndian.Uint32(data[pos : pos+4])
nexthop.Type = nexthopTypeIFIndex
b.Nexthops = append(b.Nexthops, nexthop)
pos += 4
}
}
if b.Message&MessageDistance.ToEach(version, software) > 0 {
if pos+1 > rest {
return fmt.Errorf("MessageDistance message length invalid pos:%d rest:%d", pos, rest)
}
b.Distance = data[pos] // frr: STREAM_GETC(s, api->distance);
pos++
}
if b.Message&MessageMetric.ToEach(version, software) > 0 {
if pos+4 > rest {
return fmt.Errorf("MessageMetric message length invalid pos:%d rest:%d", pos, rest)
}
// frr: STREAM_GETL(s, api->metric);
b.Metric = binary.BigEndian.Uint32(data[pos : pos+4])
pos += 4
}
if b.Message&messageTag.ToEach(version, software) > 0 {
if pos+4 > rest {
return fmt.Errorf("MessageTag message length invalid pos:%d rest:%d", pos, rest)
}
// frr: STREAM_GETL(s, api->tag);
b.tag = binary.BigEndian.Uint32(data[pos : pos+4])
pos += 4
}
// frr3 and quagga does not have MESSAGE_MTU
if b.Message&MessageMTU.ToEach(version, software) > 0 {
if pos+4 > rest {
return fmt.Errorf("MessageMTU message length invalid pos:%d rest:%d", pos, rest)
}
// frr: STREAM_GETL(s, api->mtu);
b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4])
pos += 4
}
// frr5 and later version have MESSAGE_TABLEID
if b.Message&messageTableID.ToEach(version, software) > 0 {
if pos+4 > rest {
return fmt.Errorf("MessageTableID message length invalid pos:%d rest:%d", pos, rest)
}
// frr: STREAM_GETL(s, api->mtu);
b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4])
pos += 4
}
if version == 6 && software.name == "frr" && software.version >= 8 { // added in frr8
if b.Message&messageOpaque.ToEach(version, software) > 0 {
if len(data) < pos+2 {
return errors.New("IPRouteBody frr message opaque data length is too short")
}
b.opaque.length = binary.BigEndian.Uint16(data[pos : pos+2])
if len(data) < pos+2+int(b.opaque.length) || len(b.opaque.data) < int(b.opaque.length) {
return errors.New("IPRouteBody frr message opaque data length is too short or wrong")
}
copy(b.opaque.data[:b.opaque.length], data[pos+2:pos+2+int(b.opaque.length)])
pos += 2 + int(b.opaque.length)
}
}
if pos != rest {
return fmt.Errorf("message length invalid (last) pos:%d rest:%d, message:%#x", pos, rest, b.Message)
}
return nil
}
func (b *IPRouteBody) string(version uint8, software Software) string {
s := fmt.Sprintf(
"type: %s, instance: %d, flags: %s, message: %d(%s), safi: %s, prefix: %s/%d, src_prefix: %s/%d",
b.Type.String(), b.instance, b.Flags.String(version, software), b.Message,
b.Message.string(version, software), b.Safi.String(), b.Prefix.Prefix.String(), b.Prefix.PrefixLen,
b.srcPrefix.Prefix.String(), b.srcPrefix.PrefixLen)
for i, nh := range b.Nexthops {
s += fmt.Sprintf(", nexthops[%d]: %s", i, nh.string())
}
return s + fmt.Sprintf(
", nhgid:%d, distance: %d, metric: %d, mtu: %d, tag: %d",
b.nhgid, b.Distance, b.Metric, b.Mtu, b.tag)
}
// lookupBody is combination of nexthopLookupBody and imporetLookupBody
type lookupBody struct {
api APIType
prefixLength uint8 // importLookup serialize only
addr netip.Addr // it is same as prefix (it is deleted from importLookup)
distance uint8 // nexthopIPv4LookupMRIB only
metric uint32
nexthops []Nexthop
}
// Quagga only. Ref: zread_ipv4_(nexthop|import_lookup) in zebra/zserv.c
func (b *lookupBody) serialize(version uint8, software Software) ([]byte, error) {
buf := make([]byte, 0)
if b.api == zapi3IPv4ImportLookup {
buf = append(buf, b.prefixLength)
}
switch b.api {
case ipv4NexthopLookupMRIB, zapi3IPv4NexthopLookup, zapi3IPv4ImportLookup:
buf = append(buf, b.addr.AsSlice()...)
case zapi3IPv6NexthopLookup:
buf = append(buf, b.addr.AsSlice()...)
}
return buf, nil
}
// Quagga only(except ipv4NexthopLookupMRIB).
// Ref: zsend_ipv[4|6]_(nexthop|import)_lookup in zebra/zserv.c
func (b *lookupBody) decodeFromBytes(data []byte, version uint8, software Software) error {
family := uint8(syscall.AF_INET)
if b.api == zapi3IPv6NexthopLookup {
family = syscall.AF_INET6
}
addrByteLen, _ := addressByteLength(family)
requiredLen := 5 // metric(4), numNexthop(1)
hasDistance := false
if b.api == ipv4NexthopLookupMRIB.ToEach(version, software) {
requiredLen++ // distance
hasDistance = true
}
if len(data) < addrByteLen+requiredLen {
return fmt.Errorf("message length invalid")
}
buf := make([]byte, addrByteLen)
copy(buf, data[:addrByteLen])
pos := addrByteLen
b.addr = ipFromFamily(family, buf)
if hasDistance {
b.distance = data[pos]
pos++
}
b.metric = binary.BigEndian.Uint32(data[pos : pos+4])
pos += 4
numNexthop := uint16(data[pos])
pos++
b.nexthops = []Nexthop{}
processFlag := nexthopHasType | nexthopProcessIFnameToIFindex
_, err := decodeNexthops(&b.nexthops, data[pos:], version, software, family, numNexthop, processFlag, MessageFlag(0), Flag(0), nexthopType(0))
if err != nil {
return err
}
// pos += nexthopsByteLen
return nil
}
func (b *lookupBody) string(version uint8, software Software) string {
s := fmt.Sprintf(
"addr/prefixLength: %s/%d, distance:%d, metric: %d",
b.addr.String(), b.prefixLength, b.distance, b.metric)
if len(b.nexthops) > 0 {
for _, nh := range b.nexthops {
s = s + fmt.Sprintf(", nexthop:{%s}", nh.string())
}
}
return s
}
// RegisteredNexthop is referred in zclient
type RegisteredNexthop struct {
connected uint8
resolveViaDef uint8 // added in frr8.2
safi uint16 // added in frr8.2
Family uint16
// Note: Ignores PrefixLength (uint8), because this field should be always:
// - 32 if Address Family is AF_INET
// - 128 if Address Family is AF_INET6
Prefix netip.Addr
}
func (n *RegisteredNexthop) len() int {
// Connected (1 byte) + Address Family (2 bytes) + Prefix Length (1 byte) + Prefix (variable)
if n.Family == uint16(syscall.AF_INET) {
return 4 + net.IPv4len
}
return 4 + net.IPv6len
}
// Ref: sendmsg_nexthop in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3)
// Ref: sendmsg_zebra_rnh in bgpd/bgp_nht.c of FRR3.x (ZAPI4)
// Ref: zclient_send_rnh in lib/zclient.c of FRR5&FRR6&FRR7.x&FRR8 (ZAPI5&6)
func (n *RegisteredNexthop) serialize(version uint8, software Software) ([]byte, error) {
bufInitSize := 4
if version == 6 && software.name == "frr" && software.version >= 8.2 {
bufInitSize = 7
}
buf := make([]byte, bufInitSize)
// Connected (1 byte)
buf[0] = n.connected // stream_putc(s, (connected) ? 1 : 0);
pos := 1
if version == 6 && software.name == "frr" && software.version >= 8.2 {
buf[1] = n.resolveViaDef
binary.BigEndian.PutUint16(buf[1:3], uint16(SafiUnicast)) // stream_putw(s, PREFIX_FAMILY(p));
pos += 3
}
// Address Family (2 bytes)
binary.BigEndian.PutUint16(buf[pos:pos+2], n.Family) // stream_putw(s, PREFIX_FAMILY(p));
// pos += 2
// Prefix Length (1 byte)
addrByteLen, err := addressByteLength(uint8(n.Family))
if err != nil {
return nil, err
}
buf[3] = byte(addrByteLen * 8) // stream_putc(s, p->prefixlen);
// pos += 1
// Prefix (variable)
switch n.Family {
case uint16(syscall.AF_INET):
buf = append(buf, n.Prefix.AsSlice()...) // stream_put_in_addr(s, &p->u.prefix4);
case uint16(syscall.AF_INET6):
buf = append(buf, n.Prefix.AsSlice()...) // stream_put(s, &(p->u.prefix6), 16);
default:
return nil, fmt.Errorf("invalid address family: %d", n.Family)
}
return buf, nil
}
// Ref: zserv_nexthop_register in zebra/zserv.c of Quagga1.2.x (ZAPI3)
// Ref: zserv_rnh_register in zebra/zserv.c of FRR3.x (ZAPI4)
// Ref: zread_rnh_register in zebra/zapi_msg.c of FRR5&FRR6&FRR7.x&FRR8 (ZAPI5&6)
func (n *RegisteredNexthop) decodeFromBytes(data []byte, version uint8, software Software) error {
if len(data) < 1 {
return errors.New("RegisteredNexthop data length is too short")
}
// Connected (1 byte)
n.connected = data[0]
data = data[1:]
if version == 6 && software.name == "frr" && software.version >= 8.2 {
if len(data) < 3 {
return errors.New("RegisteredNexthop data length is too short")
}
n.resolveViaDef = data[0] // STREAM_GETC(s, resolve_via_default);
n.safi = binary.BigEndian.Uint16(data[1:3]) // STREAM_GETW(s, safi);
data = data[3:]
}
if len(data) < 3 {
return errors.New("RegisteredNexthop data length is too short")
}
// Address Family (2 bytes)
n.Family = binary.BigEndian.Uint16(data[:2])
// Note: Ignores Prefix Length (1 byte)
addrByteLen := (int(data[2]) + 7) / 8
// Prefix (variable)
if len(data) < 3+addrByteLen {
return errors.New("RegisteredNexthop data length is too short")
}
n.Prefix = ipFromFamily(uint8(n.Family), data[3:3+addrByteLen])
return nil
}
func (n *RegisteredNexthop) string(version uint8, software Software) string {
return fmt.Sprintf(
"connected: %d, resolveViaDef:%d, safi: %d, family: %d, prefix: %s",
n.connected, n.resolveViaDef, n.safi, n.Family, n.Prefix.String())
}
// NexthopRegisterBody us referred in zclient
type NexthopRegisterBody struct {
api APIType
Nexthops []*RegisteredNexthop
}
// Ref: sendmsg_nexthop in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3)
// Ref: sendmsg_zebra_rnh in bgpd/bgp_nht.c of FRR3.x (ZAPI4)
// Ref: zclient_send_rnh in lib/zclient.c of FRR5&FRR6&FRR7.x&FRR8 (ZAPI5&6)
func (b *NexthopRegisterBody) serialize(version uint8, software Software) ([]byte, error) {
buf := make([]byte, 0)
// List of Registered Nexthops
for _, nh := range b.Nexthops {
nhBuf, err := nh.serialize(version, software)
if err != nil {
return nil, err
}
buf = append(buf, nhBuf...)
}
return buf, nil
}
// Ref: zserv_nexthop_register in zebra/zserv.c of Quagga1.2.x (ZAPI3)
// Ref: zserv_rnh_register in zebra/zserv.c of FRR3.x (ZAPI4)
// Ref: zread_rnh_register in zebra/zapi_msg.c of FRR5.x (ZAPI5)
func (b *NexthopRegisterBody) decodeFromBytes(data []byte, version uint8, software Software) error {
offset := 0
// List of Registered Nexthops
b.Nexthops = []*RegisteredNexthop{}
for len(data[offset:]) > 0 {
nh := new(RegisteredNexthop)
err := nh.decodeFromBytes(data[offset:], version, software)
if err != nil {
return err
}
b.Nexthops = append(b.Nexthops, nh)
offset += nh.len()
if len(data) < offset {
break
}
}
return nil
}
func (b *NexthopRegisterBody) string(version uint8, software Software) string {
s := make([]string, 0)
for _, nh := range b.Nexthops {
s = append(s, fmt.Sprintf("nexthop:{%s}", nh.string(version, software)))
}
return strings.Join(s, ", ")
}
// NexthopUpdateBody uses same data structure as IPRoute (zapi_route) after frr4 (Zapi5)
type NexthopUpdateBody IPRouteBody
// Ref: send_client in zebra/zebra_rnh.c of Quagga1.2&FRR3&FRR5(ZAPI3&4$5) and until FRR7.4
// Ref: zebra_send_rnh_update zebra/zebra_rnh.c of FRR7.5&FRR8
func (b *NexthopUpdateBody) serialize(version uint8, software Software) ([]byte, error) {
var buf []byte
offset := 0
// Message (4 bytes) // if (srte_color) stream_putl(s, message);
if version == 6 && software.name == "frr" && software.version >= 7.5 { // since frr7.5
buf = make([]byte, 7)
binary.BigEndian.PutUint32(buf, uint32(b.Message))
offset += 4
} else { // until frr7.4
buf = make([]byte, 3)
}
// Address Family (2 bytes)
binary.BigEndian.PutUint16(buf[offset:], uint16(b.Prefix.Family))
addrByteLen, err := addressByteLength(b.Prefix.Family)
if err != nil {
return nil, err
}
buf[offset+2] = byte(addrByteLen * 8) // stream_putc(s, rn->p.prefixlen);
// Prefix Length (1 byte) + Prefix (variable)
switch b.Prefix.Family {
case syscall.AF_INET:
buf = append(buf, b.Prefix.Prefix.AsSlice()...)
case syscall.AF_INET6:
buf = append(buf, b.Prefix.Prefix.AsSlice()...)
default:
return nil, fmt.Errorf("invalid address family: %d", b.Prefix.Family)
}
// SRTE color // if (srte_color) stream_putl(s, srte_color);
if b.Message&messageSRTE > 0 { // since frr 7.5
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, b.srteColor)
buf = append(buf, tmpbuf...)
}
if version >= 5 {
// Type (1 byte) (if version>=5)
// instance (2 bytes) (if version>=5)
buf = append(buf, byte(b.Type))
tmpbuf := make([]byte, 2)
binary.BigEndian.PutUint16(tmpbuf, b.instance)
buf = append(buf, tmpbuf...)
}
if version >= 4 {
// Distance (1 byte) (if version>=4)
buf = append(buf, b.Distance)
}
// Metric (4 bytes)
tmpbuf := make([]byte, 4)
binary.BigEndian.PutUint32(tmpbuf, b.Metric)
buf = append(buf, tmpbuf...)
// Number of Nexthops (1 byte)
buf = append(buf, uint8(0)) // Temporary code
// ToDo Processing Route Entry
return buf, nil
}
// Ref: bgp_parse_nexthop_update in bgpd/bgp_nht.c of Quagga1.2&FRR3 (ZAPI3&4)
// Ref: zapi_nexthop_update_decode in lib/zclient.c of FRR5.x&FRR6&FRR7.x&FRR8 (ZAPI5&6)
func (b *NexthopUpdateBody) decodeFromBytes(data []byte, version uint8, software Software) error {
if version == 6 && software.name == "frr" && software.version >= 7.5 { // since frr7.5
if len(data) < 4 {
return errors.New("invalid message length: missing message")
}
// Message //frr7.5: STREAM_GETL(s, nhr->message);
b.Message = MessageFlag(binary.BigEndian.Uint32(data[:4]))
data = data[4:]
if software.version >= 8.2 { // added in frr8.2
if len(data) < 5 {
return errors.New("invalid message length: missing safi and prefix")
}
b.Safi = Safi(binary.BigEndian.Uint16(data[:2]))
var match Prefix
match.Family = uint8(binary.BigEndian.Uint16(data[2:4])) // STREAM_GETC(s, match->prefixlen);
match.PrefixLen = data[4] // STREAM_GETC(s, match->prefixlen);
addrByteLen, err := addressByteLength(match.Family)
if err != nil {
return err
}
if len(data) < 5+addrByteLen {
return errors.New("invalid message length: missing match prefix")
}
match.Prefix = ipFromFamily(b.Prefix.Family, data[5:5+addrByteLen])
data = data[5+addrByteLen:]
}
}
if len(data) < 3 {
return errors.New("invalid message length: missing type(1 byte), instance(2 bytes) or prefix family(2 bytes)")
}
// Address Family (2 bytes) and Prefix Length (1 byte)
prefixFamily := binary.BigEndian.Uint16(data[:2])
b.Prefix.Family = uint8(prefixFamily)
b.Prefix.PrefixLen = data[2]
offset := 3
addrByteLen, err := addressByteLength(b.Prefix.Family)
if err != nil {
return err
}
if len(data) < offset+addrByteLen {
return errors.New("invalid message length: missing prefix")
}
b.Prefix.Prefix = ipFromFamily(b.Prefix.Family, data[offset:offset+addrByteLen])
offset += addrByteLen
if b.Message&messageSRTE > 0 { // since frr 7.5
if len(data) < offset+4 {
return errors.New("invalid message length: missing srteColor(4 bytes)")
}
b.srteColor = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
}
if version > 4 {
if len(data) < offset+3 {
return errors.New("invalid message length: missing type(1 byte) and instance(2 bytes)")
}
// Route Type (1 byte) and insrance (2 bytes)
b.Type = RouteType(data[offset])
b.instance = binary.BigEndian.Uint16(data[offset+1 : offset+3])
offset += 3
}
// Distance (1 byte) (if version>=4)
if version > 3 {
if len(data) < offset+1 {
return errors.New("invalid message length: missing distance(1 byte)")
}
b.Distance = data[offset]
offset++
}
// Metric (4 bytes) & Number of Nexthops (1 byte)
if len(data) < offset+5 {
return errors.New("invalid message length: missing metric(4 bytes) or nexthops(1 byte)")
}
b.Metric = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
numNexthop := uint16(data[offset])
offset++
// List of Nexthops
b.Nexthops = []Nexthop{}
processFlag := nexthopHasType
if version == 6 && software.name == "frr" {
if software.version >= 7.3 {
processFlag |= nexthopHasVrfID | nexthopHasFlag | nexthopProcessIPToIPIFindex
} else if software.version >= 7 {
processFlag |= nexthopHasVrfID | nexthopProcessIPToIPIFindex
} else if software.version >= 6 {
processFlag |= nexthopProcessIPToIPIFindex
}
} else if version == 5 && software.name == "frr" && software.version == 5 {
processFlag |= nexthopProcessIPToIPIFindex
} else if version < 4 { // quagga
processFlag |= nexthopProcessIFnameToIFindex
}
// since frr7.3, MessageLabel is deleted
if version == 6 && software.name == "frr" && software.version < 7.3 ||
version == 5 && software.name == "frr" && software.version == 5 {
b.Message |= MessageLabel
}
_, err = decodeNexthops(&b.Nexthops, data[offset:], version, software, b.Prefix.Family, numNexthop, processFlag, b.Message, Flag(0), nexthopType(0))
if err != nil {
return err
}
// offset += nexthopsByteLen
return nil
}
func (b *NexthopUpdateBody) string(version uint8, software Software) string {
s := fmt.Sprintf(
"family: %d, prefix: %s, distance: %d, metric: %d",
b.Prefix.Family, b.Prefix.Prefix.String(), b.Distance, b.Metric)
for _, nh := range b.Nexthops {
s = s + fmt.Sprintf(", nexthop:{%s}", nh.string())
}
return s
}
type labelManagerConnectBody struct {
redistDefault RouteType
instance uint16
// The followings are used in response from Zebra
result uint8 // 0 means success
}
// Ref: lm_label_manager_connect in lib/zclient.c of FRR
func (b *labelManagerConnectBody) serialize(version uint8, software Software) ([]byte, error) {
buf := make([]byte, 3)
buf[0] = uint8(b.redistDefault)
binary.BigEndian.PutUint16(buf[1:3], b.instance)
return buf, nil
}
func (b *labelManagerConnectBody) decodeFromBytes(data []byte, version uint8, software Software) error {
size := 1
if version > 4 && !(software.name == "frr" && software.version == 4) { // FRR4 returns result only.
size = 4
}
if len(data) < size {
return fmt.Errorf("invalid message length for LabelManagerConnect response: %d<%d",
len(data), size)
}
if version > 4 && !(software.name == "frr" && software.version == 4) {
b.redistDefault = RouteType(data[0])
b.instance = binary.BigEndian.Uint16(data[1:3])
data = data[3:]
}
b.result = data[0]
return nil
}
func (b *labelManagerConnectBody) string(version uint8, software Software) string {
return fmt.Sprintf(
"route_type: %s, instance: %d, result: %d",
b.redistDefault.String(), b.instance, b.result)
}
// GetLabelChunkBody is referred in zclient (Ref: zsend_assign_label_chunk_response)
type GetLabelChunkBody struct {
proto uint8 // it is appeared in FRR5.x and 6.x
instance uint16 // it is appeared in FRR5.x and 6.x
keep uint8
ChunkSize uint32
Start uint32 // The followings are used in response from Zebra
End uint32
base uint32 // it is added in FRR7.2
}
// Ref: zread_get_label_chunk in zebra/zserv.c of FRR3.x
// Ref: zread_get_label_chunk in zebra/zapi_msg.c of FRR5.x, 6.x, 7,x, and 8
func (b *GetLabelChunkBody) serialize(version uint8, software Software) ([]byte, error) {
buf := make([]byte, 12)
pos := 0
b.base = 0
if version > 4 && !(software.name == "frr" && software.version == 4) {
buf[pos] = b.proto
binary.BigEndian.PutUint16(buf[pos+1:pos+3], b.instance)
pos += 3
}
buf[pos] = b.keep
binary.BigEndian.PutUint32(buf[pos+1:pos+5], b.ChunkSize)
pos += 5
if version == 6 && software.name == "frr" && software.version >= 7.2 {
binary.BigEndian.PutUint32(buf[pos:pos+4], b.base)
pos += 4
}
return buf[:pos], nil
}
// Ref: zsend_assign_label_chunk_response in zebra/zserv.c of FRR3.x
// Ref: zsend_assign_label_chunk_response in zebra/zapi_msg.c of FRR5.x, 6.x, 7,x, and 8
func (b *GetLabelChunkBody) decodeFromBytes(data []byte, version uint8, software Software) error {
size := 9
if version > 4 && !(software.name == "frr" && software.version == 4) {
size = 12
}
if len(data) < size {
return fmt.Errorf("invalid message length for GetLabelChunk response: %d<%d",
len(data), size)
}
if version > 4 && !(software.name == "frr" && software.version == 4) {
b.proto = data[0]
b.instance = binary.BigEndian.Uint16(data[1:3])
data = data[3:]
}
b.keep = data[0]
b.Start = binary.BigEndian.Uint32(data[1:5])
b.End = binary.BigEndian.Uint32(data[5:9])
return nil
}
func (b *GetLabelChunkBody) string(version uint8, software Software) string {
return fmt.Sprintf(
"keep: %d, chunk_size: %d, start: %d, end: %d",
b.keep, b.ChunkSize, b.Start, b.End)
}
type releaseLabelChunkBody struct {
proto uint8 // it is added in FRR5.x
instance uint16 // it is added in FRR5.x
start uint32
end uint32
}
// Ref: zread_release_label_chunk in zebra/zapi_msg.c of FRR
func (b *releaseLabelChunkBody) serialize(version uint8, software Software) ([]byte, error) {
buf := make([]byte, 11)
pos := 0
if version > 4 && !(software.name == "frr" && software.version == 4) {
buf[pos] = b.proto
binary.BigEndian.PutUint16(buf[pos+1:pos+3], b.instance)
pos += 3
}
binary.BigEndian.PutUint32(buf[pos:pos+4], b.start)
binary.BigEndian.PutUint32(buf[pos+4:pos+8], b.end)
pos += 8
return buf[:pos], nil
}
func (b *releaseLabelChunkBody) decodeFromBytes(data []byte, version uint8, software Software) error {
return nil // No response from Zebra
}
func (b *releaseLabelChunkBody) string(version uint8, software Software) string {
return fmt.Sprintf("start: %d, end: %d", b.start, b.end)
}
//go:generate stringer -type=lspTYPE
type lspTYPE uint8
const (
lspNone lspTYPE = iota // defined in FRR3 and over
lspStatic // defined in FRR3 and over
lspLDP // defined in FRR3 and over
lspBGP // defined in FRR4 and over
lspSR // defined in FRR4 and over
lspSHARP // defined in FRR5 and over
)
type vrfLabelBody struct {
label uint32
afi afi
labelType lspTYPE
}
// Ref: zclient_send_vrf_label in lib/zclient.c of FRR 5.x, 6.x, 7.x, and 8
func (b *vrfLabelBody) serialize(version uint8, software Software) ([]byte, error) {
buf := make([]byte, 6)
binary.BigEndian.PutUint32(buf[:4], b.label)
buf[4] = uint8(b.afi)
buf[5] = uint8(b.labelType)
return buf, nil
}
// Ref: zread_vrf_label in zebra/zapi_msg.c of FRR 5.x, 6.x, 7.x, and 8
func (b *vrfLabelBody) decodeFromBytes(data []byte, version uint8, software Software) error {
if len(data) < 6 {
return fmt.Errorf("invalid message length for VRFLabel message: %d<6", len(data))
}
b.label = binary.BigEndian.Uint32(data[:4])
b.afi = afi(data[4])
b.labelType = lspTYPE(data[5])
return nil
}
func (b *vrfLabelBody) string(version uint8, software Software) string {
return fmt.Sprintf(
"label: %d, afi: %s LSP type: %s",
b.label, b.afi, b.labelType)
}
// Message is referred in zclient
type Message struct {
Header Header
Body Body
}
func (m *Message) Serialize(software Software) ([]byte, error) {
var body []byte
if m.Body != nil {
var err error
body, err = m.Body.serialize(m.Header.Version, software)
if err != nil {
return nil, err
}
}
m.Header.Len = uint16(len(body)) + HeaderSize(m.Header.Version)
hdr, err := m.Header.serialize()
if err != nil {
return nil, err
}
return append(hdr, body...), nil
}
func parseMessage(hdr *Header, data []byte, software Software) (m *Message, err error) {
m = &Message{Header: *hdr}
/* TODO:
InterfaceNBRAddressAdd, InterfaceNBRAddressDelete,
InterfaceBFDDestUpdate, ImportCheckUpdate, BFDDestReplay,
InterfaceVRFUpdate, InterfaceLinkParams, PWStatusUpdate
*/
command := m.Header.Command.ToCommon(m.Header.Version, software)
switch command {
case interfaceAdd, interfaceDelete, interfaceUp, interfaceDown:
m.Body = &interfaceUpdateBody{}
case interfaceAddressAdd, interfaceAddressDelete:
m.Body = &interfaceAddressUpdateBody{}
case routerIDUpdate:
m.Body = &routerIDUpdateBody{}
case nexthopUpdate:
m.Body = &NexthopUpdateBody{}
case RedistributeRouteAdd, RedistributeRouteDel: // for frr
m.Body = &IPRouteBody{API: m.Header.Command}
case labelManagerConnect: // Note: Synchronous message
m.Body = &labelManagerConnectBody{}
case getLabelChunk: // Note: Synchronous message
m.Body = &GetLabelChunkBody{}
case releaseLabelChunk: // Note: Synchronous message
m.Body = &releaseLabelChunkBody{}
case vrfLabel:
m.Body = &vrfLabelBody{}
case RouteAdd, RouteDelete, BackwardIPv6RouteAdd, BackwardIPv6RouteDelete: // for quagga
m.Body = &IPRouteBody{API: m.Header.Command}
case ipv4NexthopLookupMRIB:
m.Body = &lookupBody{api: m.Header.Command}
default:
m.Body = &unknownBody{}
if m.Header.Version == 4 {
switch m.Header.Command {
case zapi4RedistributeIPv6Add, zapi4RedistributeIPv6Del: // for frr3
m.Body = &IPRouteBody{API: m.Header.Command}
}
} else if m.Header.Version < 4 {
switch m.Header.Command {
case zapi3IPv4NexthopLookup, zapi3IPv6NexthopLookup, zapi3IPv4ImportLookup:
m.Body = &lookupBody{api: m.Header.Command}
}
}
}
return m, m.Body.decodeFromBytes(data, m.Header.Version, software)
}
// Copyright (C) 2014, 2015 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 zebra
import (
"strings"
"syscall"
)
func intfflag2string(flag uint64) string {
ss := make([]string, 0, 10)
if flag&syscall.IFF_UP > 0 {
ss = append(ss, "UP")
}
if flag&syscall.IFF_BROADCAST > 0 {
ss = append(ss, "BROADCAST")
}
if flag&syscall.IFF_DEBUG > 0 {
ss = append(ss, "DEBUG")
}
if flag&syscall.IFF_LOOPBACK > 0 {
ss = append(ss, "LOOPBACK")
}
if flag&syscall.IFF_POINTOPOINT > 0 {
ss = append(ss, "POINTOPOINT")
}
if flag&syscall.IFF_NOTRAILERS > 0 {
ss = append(ss, "NOTRAILERS")
}
if flag&syscall.IFF_RUNNING > 0 {
ss = append(ss, "RUNNING")
}
if flag&syscall.IFF_NOARP > 0 {
ss = append(ss, "NOARP")
}
if flag&syscall.IFF_PROMISC > 0 {
ss = append(ss, "PROMISC")
}
if flag&syscall.IFF_ALLMULTI > 0 {
ss = append(ss, "ALLMULTI")
}
if flag&syscall.IFF_MASTER > 0 {
ss = append(ss, "MASTER")
}
if flag&syscall.IFF_SLAVE > 0 {
ss = append(ss, "SLAVE")
}
if flag&syscall.IFF_MULTICAST > 0 {
ss = append(ss, "MULTICAST")
}
if flag&syscall.IFF_PORTSEL > 0 {
ss = append(ss, "PORTSEL")
}
if flag&syscall.IFF_AUTOMEDIA > 0 {
ss = append(ss, "AUTOMEDIA")
}
if flag&syscall.IFF_DYNAMIC > 0 {
ss = append(ss, "DYNAMIC")
}
// if flag&syscall.IFF_LOWER_UP > 0 {
// ss = append(ss, "LOWER_UP")
// }
// if flag&syscall.IFF_DORMANT > 0 {
// ss = append(ss, "DORMANT")
// }
// if flag&syscall.IFF_ECHO > 0 {
// ss = append(ss, "ECHO")
// }
return strings.Join(ss, " | ")
}