summaryrefslogtreecommitdiffhomepage
path: root/packet/bgp
diff options
context:
space:
mode:
Diffstat (limited to 'packet/bgp')
-rw-r--r--packet/bgp/bgp.go6785
-rw-r--r--packet/bgp/bgp_test.go565
-rw-r--r--packet/bgp/bgpattrtype_string.go28
-rw-r--r--packet/bgp/bgpcapabilitycode_string.go40
-rw-r--r--packet/bgp/bmp.go609
-rw-r--r--packet/bgp/bmp_test.go73
-rw-r--r--packet/bgp/constant.go194
-rw-r--r--packet/bgp/esitype_string.go16
-rw-r--r--packet/bgp/fsmstate_string.go16
-rw-r--r--packet/bgp/mrt.go798
-rw-r--r--packet/bgp/mrt_test.go220
-rw-r--r--packet/bgp/rtr.go392
-rw-r--r--packet/bgp/rtr_test.go122
-rw-r--r--packet/bgp/validate.go221
-rw-r--r--packet/bgp/validate_test.go343
15 files changed, 10422 insertions, 0 deletions
diff --git a/packet/bgp/bgp.go b/packet/bgp/bgp.go
new file mode 100644
index 00000000..34b812a0
--- /dev/null
+++ b/packet/bgp/bgp.go
@@ -0,0 +1,6785 @@
+// 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/json"
+ "fmt"
+ "math"
+ "net"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+const (
+ AFI_IP = 1
+ AFI_IP6 = 2
+ AFI_L2VPN = 25
+ AFI_OPAQUE = 16397
+)
+
+const (
+ SAFI_UNICAST = 1
+ SAFI_MULTICAST = 2
+ SAFI_MPLS_LABEL = 4
+ SAFI_ENCAPSULATION = 7
+ SAFI_VPLS = 65
+ SAFI_EVPN = 70
+ SAFI_MPLS_VPN = 128
+ SAFI_MPLS_VPN_MULTICAST = 129
+ SAFI_ROUTE_TARGET_CONSTRTAINS = 132
+ SAFI_FLOW_SPEC_UNICAST = 133
+ SAFI_FLOW_SPEC_VPN = 134
+ SAFI_KEY_VALUE = 241
+)
+
+const (
+ BGP_ORIGIN_ATTR_TYPE_IGP = 0
+ BGP_ORIGIN_ATTR_TYPE_EGP = 1
+ BGP_ORIGIN_ATTR_TYPE_INCOMPLETE = 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
+)
+
+// RFC7153 5.1. Registries for the "Type" Field
+// RANGE REGISTRACTION 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_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_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC ExtendedCommunityAttrType = 0x40
+ 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
+ //draft-ietf-idr-flowspec-redirect-rt-bis-05
+ EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 ExtendedCommunityAttrType = 0x81
+ EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 ExtendedCommunityAttrType = 0x82
+)
+
+// RFC7153 5.2. Registraction for the "Sub-Type" Field
+// RANGE REGISTRACTION 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_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_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_UUID_BASED_RT ExtendedCommunityAttrSubType = 0x11
+)
+
+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
+)
+
+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
+)
+
+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_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_ROUTE_REFRESH_CISCO BGPCapabilityCode = 128
+)
+
+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 {
+ c.CapCode = BGPCapabilityCode(data[0])
+ c.CapLen = data[1]
+ if len(data) < 2+int(c.CapLen) {
+ return fmt.Errorf("Not all OptionParameterCapability bytes available")
+ }
+ if c.CapLen > 0 {
+ c.CapValue = data[2 : 2+c.CapLen]
+ }
+ return nil
+}
+
+func (c *DefaultParameterCapability) Serialize() ([]byte, error) {
+ c.CapLen = uint8(len(c.CapValue))
+ buf := make([]byte, 2)
+ buf[0] = uint8(c.CapCode)
+ buf[1] = c.CapLen
+ buf = append(buf, c.CapValue...)
+ return buf, nil
+}
+
+func (c *DefaultParameterCapability) Len() int {
+ return int(c.CapLen + 2)
+}
+
+type CapMultiProtocol struct {
+ DefaultParameterCapability
+ CapValue RouteFamily
+}
+
+func (c *CapMultiProtocol) DecodeFromBytes(data []byte) error {
+ c.DefaultParameterCapability.DecodeFromBytes(data)
+ data = data[2:]
+ if len(data) < 4 {
+ return fmt.Errorf("Not all CapabilityMultiProtocol bytes available")
+ }
+ c.CapValue = AfiSafiToRouteFamily(binary.BigEndian.Uint16(data[0:2]), data[3])
+ return nil
+}
+
+func (c *CapMultiProtocol) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ afi, safi := RouteFamilyToAfiSafi(c.CapValue)
+ binary.BigEndian.PutUint16(buf[0:], afi)
+ buf[3] = safi
+ c.DefaultParameterCapability.CapValue = buf
+ return c.DefaultParameterCapability.Serialize()
+}
+
+func (c *CapMultiProtocol) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Code BGPCapabilityCode `json:"code"`
+ Value RouteFamily `json:"value"`
+ }{
+ Code: c.Code(),
+ Value: c.CapValue,
+ })
+}
+
+func NewCapMultiProtocol(rf RouteFamily) *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
+}
+
+type CapGracefulRestartTuple struct {
+ AFI uint16
+ SAFI uint8
+ Flags uint8
+}
+
+func (c *CapGracefulRestartTuple) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ RouteFamily RouteFamily `json:"route_family"`
+ Flags uint8 `json:"flags"`
+ }{
+ RouteFamily: AfiSafiToRouteFamily(c.AFI, c.SAFI),
+ Flags: c.Flags,
+ })
+}
+
+func NewCapGracefulRestartTuple(rf RouteFamily, forward bool) *CapGracefulRestartTuple {
+ afi, safi := RouteFamilyToAfiSafi(rf)
+ flags := 0
+ if forward {
+ flags = 0x80
+ }
+ return &CapGracefulRestartTuple{
+ AFI: afi,
+ SAFI: safi,
+ Flags: uint8(flags),
+ }
+}
+
+type CapGracefulRestart struct {
+ DefaultParameterCapability
+ Flags uint8
+ Time uint16
+ Tuples []*CapGracefulRestartTuple
+}
+
+func (c *CapGracefulRestart) DecodeFromBytes(data []byte) error {
+ c.DefaultParameterCapability.DecodeFromBytes(data)
+ data = data[2:]
+ restart := binary.BigEndian.Uint16(data[0:2])
+ c.Flags = uint8(restart >> 12)
+ c.Time = restart & 0xfff
+ data = data[2:]
+ c.Tuples = make([]*CapGracefulRestartTuple, 0, len(data)/4)
+ for len(data) >= 4 {
+ t := &CapGracefulRestartTuple{binary.BigEndian.Uint16(data[0: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)
+ binary.BigEndian.PutUint16(buf[0:], uint16(c.Flags)<<12|c.Time)
+ for _, t := range c.Tuples {
+ tbuf := make([]byte, 4)
+ binary.BigEndian.PutUint16(tbuf[0:2], t.AFI)
+ tbuf[2] = t.SAFI
+ tbuf[3] = t.Flags
+ buf = append(buf, tbuf...)
+ }
+ c.DefaultParameterCapability.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 bool, time uint16, tuples []*CapGracefulRestartTuple) *CapGracefulRestart {
+ flags := 0
+ if restarting {
+ flags = 0x08
+ }
+ 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 {
+ c.DefaultParameterCapability.DecodeFromBytes(data)
+ data = data[2:]
+ if len(data) < 4 {
+ return fmt.Errorf("Not all CapabilityMultiProtocol bytes available")
+ }
+ c.CapValue = binary.BigEndian.Uint32(data[0: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_RECEIVE BGPAddPathMode = 1
+ BGP_ADD_PATH_SEND BGPAddPathMode = 2
+ BGP_ADD_PATH_BOTH BGPAddPathMode = 3
+)
+
+func (m BGPAddPathMode) String() string {
+ switch m {
+ 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 CapAddPath struct {
+ DefaultParameterCapability
+ RouteFamily RouteFamily
+ Mode BGPAddPathMode
+}
+
+func (c *CapAddPath) DecodeFromBytes(data []byte) error {
+ c.DefaultParameterCapability.DecodeFromBytes(data)
+ data = data[2:]
+ if len(data) < 4 {
+ return fmt.Errorf("Not all CapabilityAddPath bytes available")
+ }
+ c.RouteFamily = AfiSafiToRouteFamily(binary.BigEndian.Uint16(data[:2]), data[2])
+ c.Mode = BGPAddPathMode(data[3])
+ return nil
+}
+
+func (c *CapAddPath) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ afi, safi := RouteFamilyToAfiSafi(c.RouteFamily)
+ binary.BigEndian.PutUint16(buf, afi)
+ buf[2] = safi
+ buf[3] = byte(c.Mode)
+ c.DefaultParameterCapability.CapValue = buf
+ return c.DefaultParameterCapability.Serialize()
+}
+
+func (c *CapAddPath) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Code BGPCapabilityCode `json:"code"`
+ Value RouteFamily `json:"value"`
+ Mode BGPAddPathMode `json:"mode"`
+ }{
+ Code: c.Code(),
+ Value: c.RouteFamily,
+ Mode: c.Mode,
+ })
+}
+
+func NewCapAddPath(rf RouteFamily, mode BGPAddPathMode) *CapAddPath {
+ return &CapAddPath{
+ DefaultParameterCapability: DefaultParameterCapability{
+ CapCode: BGP_CAP_ADD_PATH,
+ },
+ RouteFamily: rf,
+ Mode: mode,
+ }
+}
+
+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 CapUnknown struct {
+ DefaultParameterCapability
+}
+
+func DecodeCapability(data []byte) (ParameterCapabilityInterface, error) {
+ if len(data) < 2 {
+ return nil, fmt.Errorf("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_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{}
+ default:
+ c = &CapUnknown{}
+ }
+ err := c.DecodeFromBytes(data)
+ return c, err
+}
+
+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 fmt.Errorf("Not all OptionParameterCapability bytes available")
+ }
+ for len(data) >= 2 {
+ c, err := DecodeCapability(data)
+ if err != nil {
+ return err
+ }
+ o.Capability = append(o.Capability, c)
+ 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)
+ buf[0] = o.ParamType
+ if o.ParamLen == 0 {
+ o.ParamLen = uint8(len(o.Value))
+ }
+ buf[1] = o.ParamLen
+ return append(buf, o.Value...), nil
+}
+
+type BGPOpen struct {
+ Version uint8
+ MyAS uint16
+ HoldTime uint16
+ ID net.IP
+ OptParamLen uint8
+ OptParams []OptionParameterInterface
+}
+
+func (msg *BGPOpen) DecodeFromBytes(data []byte) error {
+ msg.Version = data[0]
+ msg.MyAS = binary.BigEndian.Uint16(data[1:3])
+ msg.HoldTime = binary.BigEndian.Uint16(data[3:5])
+ msg.ID = net.IP(data[5:9]).To4()
+ msg.OptParamLen = data[9]
+ data = data[10:]
+ if len(data) < int(msg.OptParamLen) {
+ return fmt.Errorf("Not all BGP Open message bytes available")
+ }
+
+ msg.OptParams = []OptionParameterInterface{}
+ for rest := msg.OptParamLen; rest > 0; {
+ paramtype := data[0]
+ paramlen := data[1]
+ rest -= paramlen + 2
+
+ if paramtype == BGP_OPT_CAPABILITY {
+ p := &OptionParameterCapability{}
+ p.ParamType = paramtype
+ p.ParamLen = paramlen
+ p.DecodeFromBytes(data[2 : 2+paramlen])
+ 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() ([]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.To4())
+ 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 string, optparams []OptionParameterInterface) *BGPMessage {
+ return &BGPMessage{
+ Header: BGPHeader{Type: BGP_MSG_OPEN},
+ Body: &BGPOpen{4, myas, holdtime, net.ParseIP(id).To4(), 0, optparams},
+ }
+}
+
+type AddrPrefixInterface interface {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+ AFI() uint16
+ SAFI() uint8
+ Len() int
+ String() string
+ MarshalJSON() ([]byte, error)
+}
+
+type IPAddrPrefixDefault struct {
+ Length uint8
+ Prefix net.IP
+}
+
+func (r *IPAddrPrefixDefault) decodePrefix(data []byte, bitlen uint8, addrlen uint8) error {
+ bytelen := (int(bitlen) + 7) / 8
+ if len(data) < bytelen {
+ eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
+ eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
+ return NewMessageError(eCode, eSubCode, nil, "network bytes is short")
+ }
+ b := make([]byte, addrlen)
+ copy(b, data[:bytelen])
+ r.Prefix = b
+ return nil
+}
+
+func (r *IPAddrPrefixDefault) serializePrefix(bitlen uint8) ([]byte, error) {
+ bytelen := (int(bitlen) + 7) / 8
+ buf := make([]byte, bytelen)
+ copy(buf, r.Prefix)
+ // clear trailing bits in the last byte. rfc doesn't require
+ // this though.
+ if bitlen%8 != 0 {
+ mask := 0xff00 >> (bitlen % 8)
+ last_byte_value := buf[bytelen-1] & byte(mask)
+ buf[bytelen-1] = last_byte_value
+ }
+ b := make([]byte, len(r.Prefix))
+ copy(b, buf)
+ copy(r.Prefix, b)
+ return buf, nil
+}
+
+func (r *IPAddrPrefixDefault) Len() int {
+ return 1 + ((int(r.Length) + 7) / 8)
+}
+
+func (r *IPAddrPrefixDefault) String() string {
+ return fmt.Sprintf("%s/%d", r.Prefix.String(), r.Length)
+}
+
+func (r *IPAddrPrefixDefault) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Prefix string `json:"prefix"`
+ }{
+ Prefix: r.String(),
+ })
+}
+
+type IPAddrPrefix struct {
+ IPAddrPrefixDefault
+ addrlen uint8
+}
+
+func (r *IPAddrPrefix) DecodeFromBytes(data []byte) 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")
+ }
+ r.Length = data[0]
+ if r.addrlen == 0 {
+ r.addrlen = 4
+ }
+ return r.decodePrefix(data[1:], r.Length, r.addrlen)
+}
+
+func (r *IPAddrPrefix) Serialize() ([]byte, error) {
+ buf := make([]byte, 1)
+ buf[0] = r.Length
+ pbuf, err := r.serializePrefix(r.Length)
+ if err != nil {
+ return nil, err
+ }
+ return append(buf, pbuf...), nil
+}
+
+func (r *IPAddrPrefix) AFI() uint16 {
+ return AFI_IP
+}
+
+func (r *IPAddrPrefix) SAFI() uint8 {
+ return SAFI_UNICAST
+}
+
+func NewIPAddrPrefix(length uint8, prefix string) *IPAddrPrefix {
+ return &IPAddrPrefix{
+ IPAddrPrefixDefault{length, net.ParseIP(prefix).To4()},
+ 4,
+ }
+}
+
+type IPv6AddrPrefix struct {
+ IPAddrPrefix
+}
+
+func (r *IPv6AddrPrefix) AFI() uint16 {
+ return AFI_IP6
+}
+
+func (r *IPv6AddrPrefix) String() string {
+ isZero := func(p net.IP) bool {
+ for i := 0; i < len(p); i++ {
+ if p[i] != 0 {
+ return false
+ }
+ }
+ return true
+ }(r.Prefix[0:10])
+ if isZero && r.Prefix[10] == 0xff && r.Prefix[11] == 0xff {
+ return fmt.Sprintf("::ffff:%s/%d", r.Prefix.String(), r.Length)
+ }
+ return fmt.Sprintf("%s/%d", r.Prefix.String(), r.Length)
+}
+
+func NewIPv6AddrPrefix(length uint8, prefix string) *IPv6AddrPrefix {
+ return &IPv6AddrPrefix{
+ IPAddrPrefix{
+ IPAddrPrefixDefault{length, net.ParseIP(prefix)},
+ 16,
+ },
+ }
+}
+
+const (
+ BGP_RD_TWO_OCTET_AS = iota
+ BGP_RD_IPV4_ADDRESS
+ BGP_RD_FOUR_OCTET_AS
+)
+
+type RouteDistinguisherInterface interface {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+ Len() int
+ String() string
+ MarshalJSON() ([]byte, error)
+}
+
+type DefaultRouteDistinguisher struct {
+ Type uint16
+ Value []byte
+}
+
+func (rd *DefaultRouteDistinguisher) DecodeFromBytes(data []byte) error {
+ rd.Type = binary.BigEndian.Uint16(data[0:2])
+ rd.Value = data[2:8]
+ return nil
+}
+
+func (rd *DefaultRouteDistinguisher) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ binary.BigEndian.PutUint16(buf, rd.Type)
+ copy(buf[2:], rd.Value)
+ return buf, nil
+}
+
+func (rd *DefaultRouteDistinguisher) String() string {
+ return fmt.Sprintf("%v", rd.Value)
+}
+
+func (rd *DefaultRouteDistinguisher) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type uint16 `json:"type"`
+ Value []byte `json:"value"`
+ }{
+ Type: rd.Type,
+ Value: rd.Value,
+ })
+}
+
+func (rd *DefaultRouteDistinguisher) Len() int { return 8 }
+
+type RouteDistinguisherTwoOctetAS struct {
+ DefaultRouteDistinguisher
+ Admin uint16
+ Assigned uint32
+}
+
+func (rd *RouteDistinguisherTwoOctetAS) Serialize() ([]byte, error) {
+ buf := make([]byte, 6)
+ binary.BigEndian.PutUint16(buf[0:], rd.Admin)
+ binary.BigEndian.PutUint32(buf[2:], rd.Assigned)
+ rd.Value = buf
+ return rd.DefaultRouteDistinguisher.Serialize()
+}
+
+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 net.IP
+ Assigned uint16
+}
+
+func (rd *RouteDistinguisherIPAddressAS) Serialize() ([]byte, error) {
+ buf := make([]byte, 6)
+ copy(buf[0:], rd.Admin.To4())
+ binary.BigEndian.PutUint16(buf[4:], rd.Assigned)
+ rd.Value = buf
+ return rd.DefaultRouteDistinguisher.Serialize()
+}
+
+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 string, assigned uint16) *RouteDistinguisherIPAddressAS {
+ return &RouteDistinguisherIPAddressAS{
+ DefaultRouteDistinguisher: DefaultRouteDistinguisher{
+ Type: BGP_RD_IPV4_ADDRESS,
+ },
+ Admin: net.ParseIP(admin).To4(),
+ Assigned: assigned,
+ }
+}
+
+type RouteDistinguisherFourOctetAS struct {
+ DefaultRouteDistinguisher
+ Admin uint32
+ Assigned uint16
+}
+
+func (rd *RouteDistinguisherFourOctetAS) Serialize() ([]byte, error) {
+ buf := make([]byte, 6)
+ binary.BigEndian.PutUint32(buf[0:], rd.Admin)
+ binary.BigEndian.PutUint16(buf[4:], rd.Assigned)
+ rd.Value = buf
+ return rd.DefaultRouteDistinguisher.Serialize()
+}
+
+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
+}
+
+func GetRouteDistinguisher(data []byte) RouteDistinguisherInterface {
+ rdtype := binary.BigEndian.Uint16(data[0:2])
+ switch rdtype {
+ 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:
+ return NewRouteDistinguisherIPAddressAS(net.IP(data[2:6]).String(), binary.BigEndian.Uint16(data[6:8]))
+ case BGP_RD_FOUR_OCTET_AS:
+ return NewRouteDistinguisherFourOctetAS(binary.BigEndian.Uint32(data[2:6]), binary.BigEndian.Uint16(data[6:8]))
+ }
+ rd := &RouteDistinguisherUnknown{}
+ rd.Type = rdtype
+ return rd
+}
+
+func parseRdAndRt(input string) ([]string, error) {
+ exp := regexp.MustCompile("^((\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)|((\\d+)\\.)?(\\d+)):(\\d+)$")
+ elems := exp.FindStringSubmatch(input)
+ if len(elems) != 10 {
+ return nil, fmt.Errorf("failed to parse")
+ }
+ return elems, nil
+}
+
+func ParseRouteDistinguisher(rd string) (RouteDistinguisherInterface, error) {
+ elems, err := parseRdAndRt(rd)
+ if err != nil {
+ return nil, err
+ }
+ assigned, _ := strconv.Atoi(elems[9])
+ ip := net.ParseIP(elems[1])
+ switch {
+ case ip.To4() != nil:
+ return NewRouteDistinguisherIPAddressAS(elems[1], uint16(assigned)), nil
+ case elems[6] == "" && elems[7] == "":
+ asn, _ := strconv.Atoi(elems[8])
+ return NewRouteDistinguisherTwoOctetAS(uint16(asn), uint32(assigned)), nil
+ default:
+ fst, _ := strconv.Atoi(elems[7])
+ snd, _ := strconv.Atoi(elems[8])
+ asn := fst<<16 | snd
+ return NewRouteDistinguisherFourOctetAS(uint32(asn), uint16(assigned)), nil
+ }
+}
+
+//
+// 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)
+
+type MPLSLabelStack struct {
+ Labels []uint32
+}
+
+func (l *MPLSLabelStack) DecodeFromBytes(data []byte) error {
+ labels := []uint32{}
+ foundBottom := false
+ for len(data) >= 3 {
+ label := uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2])
+ if label == WITHDRAW_LABEL {
+ l.Labels = []uint32{label}
+ return nil
+ }
+ data = data[3:]
+ labels = append(labels, label>>4)
+ if label&1 == 1 {
+ foundBottom = true
+ break
+ }
+ }
+ if foundBottom == false {
+ l.Labels = []uint32{}
+ return nil
+ }
+ l.Labels = labels
+ return nil
+}
+
+func (l *MPLSLabelStack) Serialize() ([]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.Atoi(elem)
+ if err != nil {
+ goto ERR
+ }
+ if i < 0 || i > ((1<<20)-1) {
+ goto ERR
+ }
+ labels = append(labels, uint32(i))
+ }
+ return NewMPLSLabelStack(labels...), nil
+ERR:
+ return nil, fmt.Errorf("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
+ addrlen uint8
+}
+
+func (l *LabeledVPNIPAddrPrefix) DecodeFromBytes(data []byte) error {
+ l.Length = uint8(data[0])
+ data = data[1:]
+ l.Labels.DecodeFromBytes(data)
+ if int(l.Length)-8*(l.Labels.Len()) < 0 {
+ l.Labels.Labels = []uint32{}
+ }
+ data = data[l.Labels.Len():]
+ l.RD = GetRouteDistinguisher(data)
+ data = data[l.RD.Len():]
+ restbits := int(l.Length) - 8*(l.Labels.Len()+l.RD.Len())
+ l.decodePrefix(data, uint8(restbits), l.addrlen)
+ return nil
+}
+
+func (l *LabeledVPNIPAddrPrefix) Serialize() ([]byte, error) {
+ buf := make([]byte, 1)
+ buf[0] = l.Length
+ lbuf, err := l.Labels.Serialize()
+ 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...)
+ restbits := int(l.Length) - 8*(l.Labels.Len()+l.RD.Len())
+ pbuf, err := l.serializePrefix(uint8(restbits))
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, pbuf...)
+ return buf, nil
+}
+
+func (l *LabeledVPNIPAddrPrefix) AFI() uint16 {
+ return AFI_IP
+}
+
+func (l *LabeledVPNIPAddrPrefix) SAFI() uint8 {
+ return SAFI_MPLS_VPN
+}
+
+func (l *LabeledVPNIPAddrPrefix) String() string {
+ masklen := l.IPAddrPrefixDefault.Length - uint8(8*(l.Labels.Len()+l.RD.Len()))
+ return fmt.Sprintf("%s:%s/%d", l.RD, l.IPAddrPrefixDefault.Prefix, masklen)
+}
+
+func (l *LabeledVPNIPAddrPrefix) MarshalJSON() ([]byte, error) {
+ masklen := l.IPAddrPrefixDefault.Length - uint8(8*(l.Labels.Len()+l.RD.Len()))
+ return json.Marshal(struct {
+ Prefix string `json:"prefix"`
+ Labels []uint32 `json:"labels"`
+ RD RouteDistinguisherInterface `json:"rd"`
+ }{
+ Prefix: fmt.Sprintf("%s/%d", l.IPAddrPrefixDefault.Prefix, masklen),
+ Labels: l.Labels.Labels,
+ RD: l.RD,
+ })
+}
+
+func NewLabeledVPNIPAddrPrefix(length uint8, prefix string, label MPLSLabelStack, rd RouteDistinguisherInterface) *LabeledVPNIPAddrPrefix {
+ rdlen := 0
+ if rd != nil {
+ rdlen = rd.Len()
+ }
+ return &LabeledVPNIPAddrPrefix{
+ IPAddrPrefixDefault{length + uint8(8*(label.Len()+rdlen)), net.ParseIP(prefix).To4()},
+ label,
+ rd,
+ 4,
+ }
+}
+
+type LabeledVPNIPv6AddrPrefix struct {
+ LabeledVPNIPAddrPrefix
+}
+
+func (l *LabeledVPNIPv6AddrPrefix) AFI() uint16 {
+ return AFI_IP6
+}
+
+func NewLabeledVPNIPv6AddrPrefix(length uint8, prefix string, label MPLSLabelStack, rd RouteDistinguisherInterface) *LabeledVPNIPv6AddrPrefix {
+ rdlen := 0
+ if rd != nil {
+ rdlen = rd.Len()
+ }
+ return &LabeledVPNIPv6AddrPrefix{
+ LabeledVPNIPAddrPrefix{
+ IPAddrPrefixDefault{length + uint8(8*(label.Len()+rdlen)), net.ParseIP(prefix)},
+ label,
+ rd,
+ 16,
+ },
+ }
+}
+
+type LabeledIPAddrPrefix struct {
+ IPAddrPrefixDefault
+ Labels MPLSLabelStack
+ addrlen uint8
+}
+
+func (r *LabeledIPAddrPrefix) AFI() uint16 {
+ return AFI_IP
+}
+
+func (r *LabeledIPAddrPrefix) SAFI() uint8 {
+ return SAFI_MPLS_LABEL
+}
+
+func (l *LabeledIPAddrPrefix) DecodeFromBytes(data []byte) error {
+ l.Length = uint8(data[0])
+ data = data[1:]
+ l.Labels.DecodeFromBytes(data)
+ if int(l.Length)-8*(l.Labels.Len()) < 0 {
+ l.Labels.Labels = []uint32{}
+ }
+ restbits := int(l.Length) - 8*(l.Labels.Len())
+ data = data[l.Labels.Len():]
+ l.decodePrefix(data, uint8(restbits), l.addrlen)
+ return nil
+}
+
+func (l *LabeledIPAddrPrefix) Serialize() ([]byte, error) {
+ buf := make([]byte, 1)
+ buf[0] = l.Length
+ restbits := int(l.Length) - 8*(l.Labels.Len())
+ lbuf, err := l.Labels.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, lbuf...)
+ pbuf, err := l.serializePrefix(uint8(restbits))
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, pbuf...)
+ return buf, nil
+}
+
+func (l *LabeledIPAddrPrefix) String() string {
+ return fmt.Sprintf("%s/%d", l.Prefix.String(), int(l.Length)-l.Labels.Len()*8)
+}
+
+func NewLabeledIPAddrPrefix(length uint8, prefix string, label MPLSLabelStack) *LabeledIPAddrPrefix {
+ return &LabeledIPAddrPrefix{
+ IPAddrPrefixDefault{length + uint8(label.Len()*8), net.ParseIP(prefix).To4()},
+ label,
+ 4,
+ }
+}
+
+type LabeledIPv6AddrPrefix struct {
+ LabeledIPAddrPrefix
+}
+
+func (l *LabeledIPv6AddrPrefix) AFI() uint16 {
+ return AFI_IP6
+}
+
+func NewLabeledIPv6AddrPrefix(length uint8, prefix string, label MPLSLabelStack) *LabeledIPv6AddrPrefix {
+ return &LabeledIPv6AddrPrefix{
+ LabeledIPAddrPrefix{
+ IPAddrPrefixDefault{length + uint8(label.Len()*8), net.ParseIP(prefix)},
+ label,
+ 16,
+ },
+ }
+}
+
+type RouteTargetMembershipNLRI struct {
+ Length uint8
+ AS uint32
+ RouteTarget ExtendedCommunityInterface
+}
+
+func (n *RouteTargetMembershipNLRI) DecodeFromBytes(data []byte) error {
+ n.Length = data[0]
+ data = data[1:]
+ if len(data) == 0 {
+ return nil
+ } else if len(data) != 12 {
+ return fmt.Errorf("Not all RouteTargetMembershipNLRI bytes available")
+ }
+ n.AS = binary.BigEndian.Uint32(data[0:4])
+ rt, err := ParseExtended(data[4:])
+ n.RouteTarget = rt
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (n *RouteTargetMembershipNLRI) Serialize() ([]byte, error) {
+ if n.RouteTarget == nil {
+ return []byte{0}, nil
+ }
+ buf := make([]byte, 5)
+ buf[0] = 12 * 8
+ binary.BigEndian.PutUint32(buf[1:], n.AS)
+ ebuf, err := n.RouteTarget.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, ebuf...)
+ return buf, nil
+}
+
+func (n *RouteTargetMembershipNLRI) AFI() uint16 {
+ return AFI_IP
+}
+
+func (n *RouteTargetMembershipNLRI) SAFI() uint8 {
+ return SAFI_ROUTE_TARGET_CONSTRTAINS
+}
+
+func (n *RouteTargetMembershipNLRI) Len() int {
+ if n.AS == 0 && n.RouteTarget == nil {
+ return 1
+ }
+ return 13
+}
+
+func (n *RouteTargetMembershipNLRI) String() string {
+ target := "default"
+ if n.RouteTarget != nil {
+ target = n.RouteTarget.String()
+ }
+ return fmt.Sprintf("%d:%s", n.AS, 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 = 1
+ }
+ return &RouteTargetMembershipNLRI{
+ Length: uint8(l),
+ AS: as,
+ RouteTarget: target,
+ }
+}
+
+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 {
+ 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 fmt.Errorf("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 {
+ s := bytes.NewBuffer(make([]byte, 0, 64))
+ s.WriteString(fmt.Sprintf("%s | ", esi.Type.String()))
+ switch esi.Type {
+ case ESI_ARBITRARY:
+ if isZeroBuf(esi.Value) {
+ return "single-homed"
+ }
+ s.WriteString(fmt.Sprintf("%s", esi.Value))
+ case ESI_LACP:
+ s.WriteString(fmt.Sprintf("system mac %s, ", net.HardwareAddr(esi.Value[:6]).String()))
+ s.WriteString(fmt.Sprintf("port key %d", binary.BigEndian.Uint16(esi.Value[6:8])))
+ case ESI_MSTP:
+ s.WriteString(fmt.Sprintf("bridge mac %s, ", net.HardwareAddr(esi.Value[:6]).String()))
+ s.WriteString(fmt.Sprintf("priority %d", binary.BigEndian.Uint16(esi.Value[6:8])))
+ case ESI_MAC:
+ s.WriteString(fmt.Sprintf("system mac %s, ", net.HardwareAddr(esi.Value[:6]).String()))
+ s.WriteString(fmt.Sprintf("local discriminator %d", uint32(esi.Value[6])<<16|uint32(esi.Value[7])<<8|uint32(esi.Value[8])))
+ case ESI_ROUTERID:
+ s.WriteString(fmt.Sprintf("router id %s, ", net.IP(esi.Value[:4])))
+ s.WriteString(fmt.Sprintf("local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8])))
+ case ESI_AS:
+ s.WriteString(fmt.Sprintf("as %d:%d, ", binary.BigEndian.Uint16(esi.Value[:2]), binary.BigEndian.Uint16(esi.Value[2:4])))
+ s.WriteString(fmt.Sprintf("local discriminator %d", binary.BigEndian.Uint32(esi.Value[4:8])))
+ default:
+ s.WriteString(fmt.Sprintf("value %s", esi.Value))
+ }
+ return s.String()
+}
+
+//
+// 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 {
+ return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2])
+}
+
+func labelSerialize(label uint32, buf []byte) {
+ buf[0] = byte((label >> 16) & 0xff)
+ buf[1] = byte((label >> 8) & 0xff)
+ buf[2] = byte(label & 0xff)
+}
+
+type EVPNEthernetAutoDiscoveryRoute struct {
+ RD RouteDistinguisherInterface
+ ESI EthernetSegmentIdentifier
+ ETag uint32
+ Label uint32
+}
+
+func (er *EVPNEthernetAutoDiscoveryRoute) DecodeFromBytes(data []byte) error {
+ er.RD = GetRouteDistinguisher(data)
+ data = data[er.RD.Len():]
+ err := er.ESI.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ data = data[10:]
+ er.ETag = binary.BigEndian.Uint32(data[0:4])
+ data = data[4:]
+ er.Label = labelDecode(data)
+ 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...)
+
+ tbuf = make([]byte, 4)
+ binary.BigEndian.PutUint32(tbuf, er.ETag)
+ buf = append(buf, tbuf...)
+
+ tbuf = make([]byte, 3)
+ labelSerialize(er.Label, tbuf)
+ buf = append(buf, tbuf...)
+
+ return buf, nil
+}
+
+func (er *EVPNEthernetAutoDiscoveryRoute) String() string {
+ return fmt.Sprintf("[type:A-D][rd:%s][esi:%s][etag:%d][label:%d]", er.RD, er.ESI.String(), er.ETag, er.Label)
+}
+
+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
+}
+
+type EVPNMacIPAdvertisementRoute struct {
+ RD RouteDistinguisherInterface
+ ESI EthernetSegmentIdentifier
+ ETag uint32
+ MacAddressLength uint8
+ MacAddress net.HardwareAddr
+ IPAddressLength uint8
+ IPAddress net.IP
+ Labels []uint32
+}
+
+func (er *EVPNMacIPAdvertisementRoute) DecodeFromBytes(data []byte) error {
+ er.RD = GetRouteDistinguisher(data)
+ data = data[er.RD.Len():]
+ err := er.ESI.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ data = data[10:]
+ er.ETag = binary.BigEndian.Uint32(data[0: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 {
+ er.IPAddress = net.IP(data[0:((er.IPAddressLength) / 8)])
+ } else if er.IPAddressLength != 0 {
+ return fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
+ }
+ data = data[(er.IPAddressLength / 8):]
+ label1 := labelDecode(data)
+ er.Labels = append(er.Labels, label1)
+ data = data[3:]
+ if len(data) == 3 {
+ label2 := labelDecode(data)
+ er.Labels = append(er.Labels, label2)
+
+ }
+ 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)
+ }
+
+ tbuf, err := er.ESI.Serialize()
+ if err != nil {
+ return nil, err
+ }
+
+ buf = append(buf, tbuf...)
+ tbuf = make([]byte, 4)
+ binary.BigEndian.PutUint32(tbuf, er.ETag)
+ buf = append(buf, tbuf...)
+ tbuf = make([]byte, 7)
+ tbuf[0] = er.MacAddressLength
+ copy(tbuf[1:], er.MacAddress)
+ buf = append(buf, tbuf...)
+
+ if er.IPAddressLength == 0 {
+ buf = append(buf, 0)
+ } else if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
+ buf = append(buf, er.IPAddressLength)
+ if er.IPAddressLength == 32 {
+ er.IPAddress = er.IPAddress.To4()
+ }
+ buf = append(buf, []byte(er.IPAddress)...)
+ } else {
+ return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
+ }
+
+ for _, l := range er.Labels {
+ tbuf = make([]byte, 3)
+ labelSerialize(l, tbuf)
+ buf = append(buf, tbuf...)
+ }
+ return buf, nil
+}
+
+func (er *EVPNMacIPAdvertisementRoute) String() string {
+ return fmt.Sprintf("[type:macadv][rd:%s][esi:%s][etag:%d][mac:%s][ip:%s][labels:%v]", er.RD, er.ESI.String(), er.ETag, er.MacAddress, er.IPAddress, er.Labels)
+}
+
+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
+}
+
+type EVPNMulticastEthernetTagRoute struct {
+ RD RouteDistinguisherInterface
+ ETag uint32
+ IPAddressLength uint8
+ IPAddress net.IP
+}
+
+func (er *EVPNMulticastEthernetTagRoute) DecodeFromBytes(data []byte) error {
+ er.RD = GetRouteDistinguisher(data)
+ data = data[er.RD.Len():]
+ er.ETag = binary.BigEndian.Uint32(data[0:4])
+ er.IPAddressLength = data[4]
+ data = data[5:]
+ if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
+ er.IPAddress = net.IP(data[:er.IPAddressLength/8])
+ } else {
+ return fmt.Errorf("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)
+ }
+ tbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(tbuf, er.ETag)
+ buf = append(buf, tbuf...)
+ if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
+ buf = append(buf, er.IPAddressLength)
+ if er.IPAddressLength == 32 {
+ er.IPAddress = er.IPAddress.To4()
+ }
+ buf = append(buf, []byte(er.IPAddress)...)
+ } else {
+ return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return buf, nil
+}
+
+func (er *EVPNMulticastEthernetTagRoute) String() string {
+ 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
+}
+
+type EVPNEthernetSegmentRoute struct {
+ RD RouteDistinguisherInterface
+ ESI EthernetSegmentIdentifier
+ IPAddressLength uint8
+ IPAddress net.IP
+}
+
+func (er *EVPNEthernetSegmentRoute) DecodeFromBytes(data []byte) error {
+ er.RD = GetRouteDistinguisher(data)
+ data = data[er.RD.Len():]
+ er.ESI.DecodeFromBytes(data)
+ data = data[10:]
+ er.IPAddressLength = data[0]
+ data = data[1:]
+ if er.IPAddressLength == 32 || er.IPAddressLength == 128 {
+ er.IPAddress = net.IP(data[:er.IPAddressLength/8])
+ } else {
+ return fmt.Errorf("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.IPAddressLength == 32 || er.IPAddressLength == 128 {
+ if er.IPAddressLength == 32 {
+ er.IPAddress = er.IPAddress.To4()
+ }
+ buf = append(buf, []byte(er.IPAddress)...)
+ } else {
+ return nil, fmt.Errorf("Invalid IP address length: %d", er.IPAddressLength)
+ }
+ return buf, nil
+}
+
+type EVPNRouteTypeInterface interface {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+ String() string
+ rd() RouteDistinguisherInterface
+ MarshalJSON() ([]byte, error)
+}
+
+func (er *EVPNEthernetSegmentRoute) String() string {
+ return fmt.Sprintf("[type:esi][rd:%s][esi:%d][ip:%s]", er.RD, er.ESI, 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 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
+ }
+ return nil, fmt.Errorf("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
+)
+
+type EVPNNLRI struct {
+ RouteType uint8
+ Length uint8
+ RouteTypeData EVPNRouteTypeInterface
+}
+
+func (n *EVPNNLRI) DecodeFromBytes(data []byte) error {
+ if len(data) < 2 {
+ return fmt.Errorf("Not all EVPNNLRI bytes available")
+ }
+ n.RouteType = data[0]
+ n.Length = data[1]
+ data = data[2:]
+ if len(data) < int(n.Length) {
+ return fmt.Errorf("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() ([]byte, error) {
+ buf := make([]byte, 2)
+ buf[0] = n.RouteType
+ tbuf, err := n.RouteTypeData.Serialize()
+ n.Length = uint8(len(tbuf))
+ buf[1] = n.Length
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, tbuf...)
+ return buf, nil
+}
+
+func (n *EVPNNLRI) AFI() uint16 {
+ return AFI_L2VPN
+}
+
+func (n *EVPNNLRI) SAFI() uint8 {
+ return SAFI_EVPN
+}
+
+func (n *EVPNNLRI) Len() int {
+ return int(n.Length) + 2
+}
+
+func (n *EVPNNLRI) String() string {
+ if n.RouteTypeData != nil {
+ return n.RouteTypeData.String()
+ }
+ return fmt.Sprintf("%d:%d", n.RouteType, n.Length)
+}
+
+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, length uint8, routetypedata EVPNRouteTypeInterface) *EVPNNLRI {
+ return &EVPNNLRI{
+ routetype,
+ length,
+ routetypedata,
+ }
+}
+
+type EncapNLRI struct {
+ IPAddrPrefixDefault
+}
+
+func (n *EncapNLRI) DecodeFromBytes(data []byte) error {
+ if len(data) < 4 {
+ 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]
+ return n.decodePrefix(data[1:], n.Length, n.Length/8)
+}
+
+func (n *EncapNLRI) Serialize() ([]byte, error) {
+ buf := make([]byte, 1)
+ buf[0] = net.IPv6len * 8
+ if n.Prefix.To4() != nil {
+ buf[0] = net.IPv4len * 8
+ n.Prefix = n.Prefix.To4()
+ }
+ n.Length = buf[0]
+ pbuf, err := n.serializePrefix(n.Length)
+ if err != nil {
+ return nil, err
+ }
+ return append(buf, pbuf...), nil
+}
+
+func (n *EncapNLRI) String() string {
+ return n.Prefix.String()
+}
+
+func (n *EncapNLRI) AFI() uint16 {
+ if n.Prefix.To4() != nil {
+ return AFI_IP
+ }
+ return AFI_IP6
+}
+
+func (n *EncapNLRI) SAFI() uint8 {
+ return SAFI_ENCAPSULATION
+}
+
+func NewEncapNLRI(endpoint string) *EncapNLRI {
+ return &EncapNLRI{
+ IPAddrPrefixDefault{0, net.ParseIP(endpoint)},
+ }
+}
+
+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,
+}
+
+func flowSpecPrefixParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ if len(args) < 2 {
+ return nil, fmt.Errorf("invalid flowspec dst/src prefix")
+ }
+ typ := args[0]
+ ip, net, err := net.ParseCIDR(args[1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid ip prefix")
+ }
+ afi, _ := RouteFamilyToAfiSafi(rf)
+ if afi == AFI_IP && ip.To4() == nil {
+ return nil, fmt.Errorf("invalid ipv4 prefix")
+ } else if afi == AFI_IP6 && !strings.Contains(ip.String(), ":") {
+ return nil, fmt.Errorf("invalid ipv6 prefix")
+ }
+ ones, _ := net.Mask.Size()
+ var offset uint8
+ if len(args) > 2 {
+ o, err := strconv.Atoi(args[2])
+ offset = uint8(o)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ switch typ {
+ case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]:
+ switch rf {
+ case RF_FS_IPv4_UC:
+ return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil
+ case RF_FS_IPv6_UC:
+ return NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil
+ default:
+ return nil, fmt.Errorf("invalid type. only RF_FS_IPv4_UC or RF_FS_IPv6_UC is allowed")
+ }
+ case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]:
+ switch rf {
+ case RF_FS_IPv4_UC:
+ return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil
+ case RF_FS_IPv6_UC:
+ return NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil
+ default:
+ return nil, fmt.Errorf("invalid type. only RF_FS_IPv4_UC or RF_FS_IPv6_UC is allowed")
+ }
+ }
+ return nil, fmt.Errorf("invalid type. only destination or source is allowed")
+}
+
+func flowSpecIpProtoParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ ss := make([]string, 0, len(ProtocolNameMap))
+ for _, v := range ProtocolNameMap {
+ ss = append(ss, v)
+ }
+ protos := strings.Join(ss, "|")
+ exp := regexp.MustCompile(fmt.Sprintf("^%s (((%s) )*)(%s)$", FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO], protos, protos))
+ elems := exp.FindStringSubmatch(strings.Join(args, " "))
+ items := make([]*FlowSpecComponentItem, 0)
+ eq := 0x1
+ if elems[1] != "" {
+ for _, v := range strings.Split(elems[1], " ") {
+ p, ok := ProtocolValueMap[v]
+ if !ok {
+ continue
+ }
+ items = append(items, NewFlowSpecComponentItem(eq, int(p)))
+ }
+ }
+ items = append(items, NewFlowSpecComponentItem(eq, int(ProtocolValueMap[elems[4]])))
+ return NewFlowSpecComponent(FLOW_SPEC_TYPE_IP_PROTO, items), nil
+}
+
+func flowSpecTcpFlagParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ ss := make([]string, 0, len(TCPFlagNameMap))
+ for _, v := range TCPFlagNameMap {
+ ss = append(ss, v)
+ }
+ protos := strings.Join(ss, "|")
+ exp := regexp.MustCompile(fmt.Sprintf("^%s (not )?(match )?((((%s)\\&)*(%s) )*(((%s)\\&)*(%s)))$", FlowSpecNameMap[FLOW_SPEC_TYPE_TCP_FLAG], protos, protos, protos, protos))
+ elems := exp.FindStringSubmatch(strings.Join(args, " "))
+ if len(elems) < 1 {
+ return nil, fmt.Errorf("invalid flag format")
+ }
+ items := make([]*FlowSpecComponentItem, 0)
+ op := 0
+ if elems[2] != "" {
+ op |= 0x1
+ }
+ if elems[1] != "" {
+ op |= 0x2
+ }
+ for _, v := range strings.Split(elems[3], " ") {
+ flag := 0
+ for _, e := range strings.Split(v, "&") {
+ flag |= int(TCPFlagValueMap[e])
+ }
+ items = append(items, NewFlowSpecComponentItem(op, flag))
+ }
+ return NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, items), nil
+}
+
+func flowSpecEtherTypeParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ ss := make([]string, 0, len(EthernetTypeNameMap))
+ for _, v := range EthernetTypeNameMap {
+ ss = append(ss, v)
+ }
+ protos := strings.Join(ss, "|")
+ exp := regexp.MustCompile(fmt.Sprintf("^%s (((%s) )*)(%s)$", FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE], protos, protos))
+ elems := exp.FindStringSubmatch(strings.Join(args, " "))
+ items := make([]*FlowSpecComponentItem, 0)
+ eq := 0x1
+ if elems[1] != "" {
+ for _, v := range strings.Split(elems[1], " ") {
+ p, ok := EthernetTypeValueMap[v]
+ if !ok {
+ continue
+ }
+ items = append(items, NewFlowSpecComponentItem(eq, int(p)))
+ }
+ }
+ items = append(items, NewFlowSpecComponentItem(eq, int(EthernetTypeValueMap[elems[4]])))
+ return NewFlowSpecComponent(FLOW_SPEC_TYPE_ETHERNET_TYPE, items), nil
+}
+
+func doFlowSpecNumericParser(rf RouteFamily, args []string, validationFunc func(int) error) (FlowSpecComponentInterface, error) {
+ if afi, _ := RouteFamilyToAfiSafi(rf); afi == AFI_IP && FlowSpecValueMap[args[0]] == FLOW_SPEC_TYPE_LABEL {
+ return nil, fmt.Errorf("flow label spec is only allowed for ipv6")
+ }
+ exp := regexp.MustCompile("^((<=|>=|[<>=])(\\d+)&)?(<=|>=|[<>=])?(\\d+)$")
+ items := make([]*FlowSpecComponentItem, 0)
+
+ f := func(and bool, o, v string) (*FlowSpecComponentItem, error) {
+ op := 0
+ if and {
+ op |= 0x40
+ }
+ if len(o) == 0 {
+ op |= 0x1
+ }
+ for _, oo := range o {
+ switch oo {
+ case '>':
+ op |= 0x2
+ case '<':
+ op |= 0x4
+ case '=':
+ op |= 0x1
+ }
+ }
+ value, err := strconv.Atoi(v)
+ if err != nil {
+ return nil, err
+ }
+ err = validationFunc(value)
+ if err != nil {
+ return nil, err
+ }
+ return NewFlowSpecComponentItem(op, value), nil
+ }
+
+ for _, arg := range args[1:] {
+ var and bool
+ elems := exp.FindStringSubmatch(arg)
+ if len(elems) == 0 {
+ return nil, fmt.Errorf("invalid flowspec numeric item")
+ }
+ if elems[1] != "" {
+ and = true
+ item, err := f(false, elems[2], elems[3])
+ if err != nil {
+ return nil, err
+ }
+ items = append(items, item)
+ }
+ item, err := f(and, elems[4], elems[5])
+ if err != nil {
+ return nil, err
+ }
+ items = append(items, item)
+ }
+
+ return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil
+}
+
+func flowSpecNumericParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ f := func(i int) error {
+ return nil
+ }
+ return doFlowSpecNumericParser(rf, args, f)
+}
+
+func flowSpecPortParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ f := func(i int) error {
+ if 0 < i && i < 65536 {
+ return nil
+ }
+ return fmt.Errorf("port range exceeded")
+ }
+ return doFlowSpecNumericParser(rf, args, f)
+}
+
+func flowSpecDscpParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ f := func(i int) error {
+ if 0 < i && i < 64 {
+ return nil
+ }
+ return fmt.Errorf("dscp value range exceeded")
+ }
+ return doFlowSpecNumericParser(rf, args, f)
+}
+
+func flowSpecFragmentParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ if len(args) < 2 {
+ return nil, fmt.Errorf("invalid flowspec fragment specifier")
+ }
+ items := make([]*FlowSpecComponentItem, 0)
+ for _, a := range args[1:] {
+ value := 0
+ switch a {
+ case "dont-fragment":
+ if afi, _ := RouteFamilyToAfiSafi(rf); afi == AFI_IP6 {
+ return nil, fmt.Errorf("can't specify dont-fragment for ipv6")
+ }
+ value = 0x1
+ case "is-fragment":
+ value = 0x2
+ case "first-fragment":
+ value = 0x4
+ case "last-fragment":
+ value = 0x8
+ case "not-a-fragment":
+ value = 0x0
+ default:
+ return nil, fmt.Errorf("invalid flowspec fragment specifier")
+ }
+ items = append(items, NewFlowSpecComponentItem(0, value))
+ }
+ return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil
+}
+
+func flowSpecMacParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) {
+ if len(args) < 2 {
+ return nil, fmt.Errorf("invalid flowspec dst/src mac")
+ }
+ if rf != RF_FS_L2_VPN {
+ return nil, fmt.Errorf("invalid family")
+ }
+ typ := args[0]
+ mac, err := net.ParseMAC(args[1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid mac")
+ }
+ switch typ {
+ case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]:
+ return NewFlowSpecDestinationMac(mac), nil
+ case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]:
+ return NewFlowSpecSourceMac(mac), nil
+ }
+ return nil, fmt.Errorf("invalid type. only %s or %s allowed", FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC], FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC])
+}
+
+var flowSpecParserMap = map[BGPFlowSpecType]func(RouteFamily, []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: flowSpecPortParser,
+ FLOW_SPEC_TYPE_DST_PORT: flowSpecPortParser,
+ FLOW_SPEC_TYPE_SRC_PORT: flowSpecPortParser,
+ FLOW_SPEC_TYPE_ICMP_TYPE: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_ICMP_CODE: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_TCP_FLAG: flowSpecTcpFlagParser,
+ FLOW_SPEC_TYPE_PKT_LEN: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_DSCP: flowSpecDscpParser,
+ FLOW_SPEC_TYPE_FRAGMENT: flowSpecFragmentParser,
+ FLOW_SPEC_TYPE_LABEL: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_ETHERNET_TYPE: flowSpecEtherTypeParser,
+ FLOW_SPEC_TYPE_DST_MAC: flowSpecMacParser,
+ FLOW_SPEC_TYPE_SRC_MAC: flowSpecMacParser,
+ FLOW_SPEC_TYPE_LLC_DSAP: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_LLC_SSAP: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_LLC_CONTROL: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_SNAP: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_VID: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_COS: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_INNER_VID: flowSpecNumericParser,
+ FLOW_SPEC_TYPE_INNER_COS: flowSpecNumericParser,
+}
+
+func ParseFlowSpecComponents(rf RouteFamily, input string) ([]FlowSpecComponentInterface, error) {
+ idxs := make([]struct {
+ t BGPFlowSpecType
+ i int
+ }, 0, 8)
+ args := strings.Split(input, " ")
+ for idx, v := range args {
+ if t, ok := FlowSpecValueMap[v]; ok {
+ idxs = append(idxs, struct {
+ t BGPFlowSpecType
+ i int
+ }{t, idx})
+ }
+ }
+ if len(idxs) == 0 {
+ return nil, fmt.Errorf("failed to parse: %s", input)
+ }
+ cmps := make([]FlowSpecComponentInterface, 0, len(idxs))
+ for i, idx := range idxs {
+ var a []string
+ f := flowSpecParserMap[idx.t]
+ if i < len(idxs)-1 {
+ a = args[idx.i:idxs[i+1].i]
+ } else {
+ a = args[idx.i:]
+ }
+ cmp, err := f(rf, a)
+ if err != nil {
+ return nil, err
+ }
+ cmps = append(cmps, cmp)
+ }
+ return cmps, 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) error
+ Serialize() ([]byte, error)
+ Len() int
+ Type() BGPFlowSpecType
+ String() string
+}
+
+type flowSpecPrefix struct {
+ Prefix AddrPrefixInterface
+ type_ BGPFlowSpecType
+}
+
+func (p *flowSpecPrefix) DecodeFromBytes(data []byte) error {
+ p.type_ = BGPFlowSpecType(data[0])
+ return p.Prefix.DecodeFromBytes(data[1:])
+}
+
+func (p *flowSpecPrefix) Serialize() ([]byte, error) {
+ buf := []byte{byte(p.Type())}
+ bbuf, err := p.Prefix.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ return append(buf, bbuf...), nil
+}
+
+func (p *flowSpecPrefix) Len() int {
+ buf, _ := p.Serialize()
+ return len(buf)
+}
+
+func (p *flowSpecPrefix) Type() BGPFlowSpecType {
+ return p.type_
+}
+
+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 AddrPrefixInterface `json:"value"`
+ }{
+ Type: p.Type(),
+ Value: p.Prefix,
+ })
+}
+
+type flowSpecPrefix6 struct {
+ Prefix AddrPrefixInterface
+ Offset uint8
+ type_ 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) error {
+ p.type_ = BGPFlowSpecType(data[0])
+ p.Offset = data[2]
+ prefix := append([]byte{data[1]}, data[3:]...)
+ return p.Prefix.DecodeFromBytes(prefix)
+}
+
+func (p *flowSpecPrefix6) Serialize() ([]byte, error) {
+ buf := []byte{byte(p.Type())}
+ bbuf, err := p.Prefix.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, bbuf[0])
+ buf = append(buf, p.Offset)
+ return append(buf, bbuf[1:]...), nil
+}
+
+func (p *flowSpecPrefix6) Len() int {
+ buf, _ := p.Serialize()
+ return len(buf)
+}
+
+func (p *flowSpecPrefix6) Type() BGPFlowSpecType {
+ return p.type_
+}
+
+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 AddrPrefixInterface `json:"value"`
+ Offset uint8 `json:"offset"`
+ }{
+ Type: p.Type(),
+ Value: p.Prefix,
+ Offset: p.Offset,
+ })
+}
+
+type FlowSpecDestinationPrefix struct {
+ flowSpecPrefix
+}
+
+func NewFlowSpecDestinationPrefix(prefix AddrPrefixInterface) *FlowSpecDestinationPrefix {
+ return &FlowSpecDestinationPrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_DST_PREFIX}}
+}
+
+type FlowSpecSourcePrefix struct {
+ flowSpecPrefix
+}
+
+func NewFlowSpecSourcePrefix(prefix AddrPrefixInterface) *FlowSpecSourcePrefix {
+ return &FlowSpecSourcePrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_SRC_PREFIX}}
+}
+
+type FlowSpecDestinationPrefix6 struct {
+ flowSpecPrefix6
+}
+
+func NewFlowSpecDestinationPrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecDestinationPrefix6 {
+ return &FlowSpecDestinationPrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_DST_PREFIX}}
+}
+
+type FlowSpecSourcePrefix6 struct {
+ flowSpecPrefix6
+}
+
+func NewFlowSpecSourcePrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecSourcePrefix6 {
+ return &FlowSpecSourcePrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_SRC_PREFIX}}
+}
+
+type flowSpecMac struct {
+ Mac net.HardwareAddr
+ type_ BGPFlowSpecType
+}
+
+func (p *flowSpecMac) DecodeFromBytes(data []byte) error {
+ if len(data) < 2 || len(data) < 2+int(data[1]) {
+ return fmt.Errorf("not all mac bits available")
+ }
+ p.type_ = BGPFlowSpecType(data[0])
+ p.Mac = net.HardwareAddr(data[2 : 2+int(data[1])])
+ return nil
+}
+
+func (p *flowSpecMac) Serialize() ([]byte, error) {
+ if len(p.Mac) == 0 {
+ return nil, fmt.Errorf("mac unset")
+ }
+ buf := []byte{byte(p.Type()), byte(len(p.Mac))}
+ return append(buf, []byte(p.Mac)...), nil
+}
+
+func (p *flowSpecMac) Len() int {
+ return 2 + len(p.Mac)
+}
+
+func (p *flowSpecMac) Type() BGPFlowSpecType {
+ return p.type_
+}
+
+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, type_: FLOW_SPEC_TYPE_SRC_MAC}}
+}
+
+type FlowSpecDestinationMac struct {
+ flowSpecMac
+}
+
+func NewFlowSpecDestinationMac(mac net.HardwareAddr) *FlowSpecDestinationMac {
+ return &FlowSpecDestinationMac{flowSpecMac{Mac: mac, type_: FLOW_SPEC_TYPE_DST_MAC}}
+}
+
+type FlowSpecComponentItem struct {
+ Op int `json:"op"`
+ Value int `json:"value"`
+}
+
+func (v *FlowSpecComponentItem) Len() int {
+ return 1 << ((uint32(v.Op) >> 4) & 0x3)
+}
+
+func (v *FlowSpecComponentItem) Serialize() ([]byte, error) {
+ if v.Value < 0 {
+ return nil, fmt.Errorf("invalid value size(too small): %d", v.Value)
+ }
+ if v.Op < 0 || v.Op > math.MaxUint8 {
+ return nil, fmt.Errorf("invalid op size: %d", v.Op)
+
+ }
+ 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:], uint64(v.Value))
+ default:
+ return nil, fmt.Errorf("invalid value size(too big): %d", v.Value)
+ }
+ return buf, nil
+}
+
+func NewFlowSpecComponentItem(op int, value int) *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 := 0; i < 3; i++ {
+ if v.Value < (1 << ((1 << uint(i)) * 8)) {
+ return uint32(i)
+ }
+ }
+ // return invalid order
+ return 4
+ }()
+ }
+ if order > 3 {
+ return nil
+ }
+ v.Op = int(uint32(v.Op) | order<<4)
+ return v
+}
+
+type FlowSpecComponent struct {
+ Items []*FlowSpecComponentItem
+ type_ BGPFlowSpecType
+}
+
+func (p *FlowSpecComponent) DecodeFromBytes(data []byte) error {
+ p.type_ = BGPFlowSpecType(data[0])
+ data = data[1:]
+ p.Items = make([]*FlowSpecComponentItem, 0)
+ for {
+ if len(data) < 2 {
+ return fmt.Errorf("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)
+ copy(v[8-l:], data[1:1+l])
+ i := int(binary.BigEndian.Uint64(v))
+ item := &FlowSpecComponentItem{int(op), i}
+ p.Items = append(p.Items, item)
+ if end > 0 {
+ break
+ }
+ data = data[1+l:]
+ }
+ return nil
+}
+
+func (p *FlowSpecComponent) Serialize() ([]byte, error) {
+ buf := []byte{byte(p.Type())}
+ for i, v := range p.Items {
+ //set end-of-list bit
+ if i == (len(p.Items) - 1) {
+ v.Op |= 0x80
+ } else {
+ v.Op &^= 0x80
+ }
+ bbuf, err := v.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, bbuf...)
+ }
+ return buf, nil
+}
+
+func (p *FlowSpecComponent) Len() int {
+ l := 1
+ for _, item := range p.Items {
+ l += (item.Len() + 1)
+ }
+ return l
+}
+
+func (p *FlowSpecComponent) Type() BGPFlowSpecType {
+ return p.type_
+}
+
+func formatRaw(op int, value int) string {
+ return fmt.Sprintf("op: %b, value: %d", op, value)
+}
+
+func formatNumericOp(op int) string {
+ var opstr string
+ if op&0x40 > 0 {
+ opstr = "&"
+ } else {
+ opstr = " "
+ }
+ if op&0x2 > 0 {
+ opstr += ">"
+ }
+ if op&0x4 > 0 {
+ opstr += "<"
+ }
+ if op&0x1 > 0 {
+ opstr += "="
+ }
+ return opstr
+}
+
+func formatNumeric(op int, value int) string {
+ return fmt.Sprintf("%s%d", formatNumericOp(op), value)
+}
+
+func formatProto(op int, value int) string {
+ return fmt.Sprintf(" %s", Protocol(value).String())
+}
+
+func formatFlag(op int, value int) string {
+ and := " "
+ ss := make([]string, 0, 2)
+ if op&0x40 > 0 {
+ and = "&"
+ }
+ if op&0x1 > 0 {
+ ss = append(ss, "match")
+ }
+ if op&0x2 > 0 {
+ ss = append(ss, "not")
+ }
+ if len(ss) > 0 {
+ return fmt.Sprintf("%s(%s)%s", and, strings.Join(ss, "|"), TCPFlag(value).String())
+ }
+ return fmt.Sprintf("%s%s", and, TCPFlag(value).String())
+}
+
+func formatFragment(op int, value int) string {
+ ss := make([]string, 0)
+ if value == 0 {
+ ss = append(ss, "not-a-fragment")
+ }
+ if value&0x1 > 0 {
+ ss = append(ss, "dont-fragment")
+ }
+ if value&0x2 > 0 {
+ ss = append(ss, "is-fragment")
+ }
+ if value&0x4 > 0 {
+ ss = append(ss, "first-fragment")
+ }
+ if value&0x8 > 0 {
+ ss = append(ss, "last-fragment")
+ }
+ if len(ss) > 1 {
+ return fmt.Sprintf("%s(%s)", formatNumericOp(op), strings.Join(ss, "|"))
+ }
+ return fmt.Sprintf("%s%s", formatNumericOp(op), ss[0])
+}
+
+func formatEtherType(op int, value int) string {
+ return fmt.Sprintf(" %s", EthernetType(value).String())
+}
+
+var flowSpecFormatMap = map[BGPFlowSpecType]func(op int, value int) 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: formatFlag,
+ 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.Type()]; ok {
+ f = flowSpecFormatMap[p.Type()]
+ }
+ buf := bytes.NewBuffer(make([]byte, 0, 32))
+ for _, i := range p.Items {
+ buf.WriteString(f(i.Op, i.Value))
+ }
+ return fmt.Sprintf("[%s:%s]", p.type_, buf.String())
+}
+
+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(type_ BGPFlowSpecType, items []*FlowSpecComponentItem) *FlowSpecComponent {
+ return &FlowSpecComponent{
+ Items: items,
+ type_: type_,
+ }
+}
+
+type FlowSpecUnknown struct {
+ Value []byte
+}
+
+func (p *FlowSpecUnknown) DecodeFromBytes(data []byte) error {
+ p.Value = data
+ return nil
+}
+
+func (p *FlowSpecUnknown) Serialize() ([]byte, error) {
+ return p.Value, nil
+}
+
+func (p *FlowSpecUnknown) Len() 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)
+}
+
+type FlowSpecNLRI struct {
+ Value []FlowSpecComponentInterface
+ rf RouteFamily
+}
+
+func (n *FlowSpecNLRI) decodeFromBytes(rf RouteFamily, data []byte) error {
+ var length int
+ if (data[0]>>4) == 0xf && len(data) > 2 {
+ length = int(binary.BigEndian.Uint16(data[0:2]))
+ data = data[2:]
+ } else if len(data) > 1 {
+ length = int(data[0])
+ data = data[1:]
+ } else {
+ return fmt.Errorf("not all flowspec component bytes available")
+ }
+
+ n.rf = rf
+
+ for l := length; l > 0; {
+ if len(data) == 0 {
+ return fmt.Errorf("not all flowspec component bytes available")
+ }
+ t := BGPFlowSpecType(data[0])
+ var i FlowSpecComponentInterface
+ switch t {
+ case FLOW_SPEC_TYPE_DST_PREFIX:
+ switch rf {
+ case RF_FS_IPv4_UC:
+ i = NewFlowSpecDestinationPrefix(NewIPAddrPrefix(0, ""))
+ case RF_FS_IPv4_VPN:
+ i = NewFlowSpecDestinationPrefix(NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil))
+ case RF_FS_IPv6_UC:
+ i = NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(0, ""), 0)
+ case RF_FS_IPv6_VPN:
+ i = NewFlowSpecDestinationPrefix6(NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil), 0)
+ default:
+ return fmt.Errorf("Invalid RF: %v", rf)
+ }
+ case FLOW_SPEC_TYPE_SRC_PREFIX:
+ switch rf {
+ case RF_FS_IPv4_UC:
+ i = NewFlowSpecSourcePrefix(NewIPAddrPrefix(0, ""))
+ case RF_FS_IPv4_VPN:
+ i = NewFlowSpecSourcePrefix(NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil))
+ case RF_FS_IPv6_UC:
+ i = NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(0, ""), 0)
+ case RF_FS_IPv6_VPN:
+ i = NewFlowSpecSourcePrefix6(NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil), 0)
+ default:
+ return fmt.Errorf("Invalid RF: %v", rf)
+ }
+ case FLOW_SPEC_TYPE_SRC_MAC:
+ switch rf {
+ case RF_FS_L2_VPN:
+ i = NewFlowSpecSourceMac(nil)
+ default:
+ return fmt.Errorf("invalid family: %v", rf)
+ }
+ case FLOW_SPEC_TYPE_DST_MAC:
+ switch rf {
+ case RF_FS_L2_VPN:
+ i = NewFlowSpecDestinationMac(nil)
+ default:
+ return fmt.Errorf("invalid family: %v", 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)
+ if err != nil {
+ i = &FlowSpecUnknown{data}
+ }
+ l -= i.Len()
+ data = data[i.Len():]
+ n.Value = append(n.Value, i)
+ }
+
+ return nil
+}
+
+func (n *FlowSpecNLRI) Serialize() ([]byte, error) {
+ buf := make([]byte, 0, 32)
+ for _, v := range n.Value {
+ b, err := v.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, b...)
+ }
+ length := n.Len()
+ 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() int {
+ l := 0
+ for _, v := range n.Value {
+ l += v.Len()
+ }
+ if l < 0xf0 {
+ return l + 1
+ } else {
+ return l + 2
+ }
+}
+
+func (n *FlowSpecNLRI) String() string {
+ buf := bytes.NewBuffer(make([]byte, 0, 32))
+ for _, v := range n.Value {
+ buf.WriteString(v.String())
+ }
+ return buf.String()
+}
+
+func (n *FlowSpecNLRI) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Value []FlowSpecComponentInterface `json:"value"`
+ }{
+ Value: n.Value,
+ })
+}
+
+type FlowSpecIPv4Unicast struct {
+ FlowSpecNLRI
+}
+
+func (n *FlowSpecIPv4Unicast) DecodeFromBytes(data []byte) error {
+ return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
+}
+
+func (n *FlowSpecIPv4Unicast) AFI() uint16 {
+ return AFI_IP
+}
+
+func (n *FlowSpecIPv4Unicast) SAFI() uint8 {
+ return SAFI_FLOW_SPEC_UNICAST
+}
+
+func NewFlowSpecIPv4Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv4Unicast {
+ return &FlowSpecIPv4Unicast{FlowSpecNLRI{value, RF_FS_IPv4_UC}}
+}
+
+type FlowSpecIPv4VPN struct {
+ FlowSpecNLRI
+}
+
+func (n *FlowSpecIPv4VPN) DecodeFromBytes(data []byte) error {
+ return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
+}
+
+func (n *FlowSpecIPv4VPN) AFI() uint16 {
+ return AFI_IP
+}
+
+func (n *FlowSpecIPv4VPN) SAFI() uint8 {
+ return SAFI_FLOW_SPEC_VPN
+}
+
+func NewFlowSpecIPv4VPN(value []FlowSpecComponentInterface) *FlowSpecIPv4VPN {
+ return &FlowSpecIPv4VPN{FlowSpecNLRI{value, RF_FS_IPv4_VPN}}
+}
+
+type FlowSpecIPv6Unicast struct {
+ FlowSpecNLRI
+}
+
+func (n *FlowSpecIPv6Unicast) DecodeFromBytes(data []byte) error {
+ return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
+}
+
+func (n *FlowSpecIPv6Unicast) AFI() uint16 {
+ return AFI_IP6
+}
+
+func (n *FlowSpecIPv6Unicast) SAFI() uint8 {
+ return SAFI_FLOW_SPEC_UNICAST
+}
+
+func NewFlowSpecIPv6Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv6Unicast {
+ return &FlowSpecIPv6Unicast{FlowSpecNLRI{
+ Value: value,
+ rf: RF_FS_IPv6_UC,
+ }}
+}
+
+type FlowSpecIPv6VPN struct {
+ FlowSpecNLRI
+}
+
+func (n *FlowSpecIPv6VPN) DecodeFromBytes(data []byte) error {
+ return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
+}
+
+func (n *FlowSpecIPv6VPN) AFI() uint16 {
+ return AFI_IP6
+}
+
+func (n *FlowSpecIPv6VPN) SAFI() uint8 {
+ return SAFI_FLOW_SPEC_VPN
+}
+
+func NewFlowSpecIPv6VPN(value []FlowSpecComponentInterface) *FlowSpecIPv6VPN {
+ return &FlowSpecIPv6VPN{FlowSpecNLRI{
+ Value: value,
+ rf: RF_FS_IPv6_VPN,
+ }}
+}
+
+type FlowSpecL2VPN struct {
+ FlowSpecNLRI
+}
+
+func (n *FlowSpecL2VPN) DecodeFromBytes(data []byte) error {
+ return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data)
+}
+
+func (n *FlowSpecL2VPN) AFI() uint16 {
+ return AFI_L2VPN
+}
+
+func (n *FlowSpecL2VPN) SAFI() uint8 {
+ return SAFI_FLOW_SPEC_VPN
+}
+
+func NewFlowSpecL2VPN(value []FlowSpecComponentInterface) *FlowSpecL2VPN {
+ return &FlowSpecL2VPN{FlowSpecNLRI{
+ Value: value,
+ rf: RF_FS_L2_VPN,
+ }}
+}
+
+type OpaqueNLRI struct {
+ Length uint8
+ Key []byte
+}
+
+func (n *OpaqueNLRI) DecodeFromBytes(data []byte) error {
+ n.Length = data[0]
+ if len(data)-1 < int(n.Length) {
+ return fmt.Errorf("Not all OpaqueNLRI bytes available")
+ }
+ n.Key = data[1 : 1+n.Length]
+ return nil
+}
+
+func (n *OpaqueNLRI) Serialize() ([]byte, error) {
+ if len(n.Key) > math.MaxUint8 {
+ return nil, fmt.Errorf("Key length too big")
+ }
+ return append([]byte{byte(len(n.Key))}, n.Key...), nil
+}
+
+func (n *OpaqueNLRI) AFI() uint16 {
+ return AFI_OPAQUE
+}
+
+func (n *OpaqueNLRI) SAFI() uint8 {
+ return SAFI_KEY_VALUE
+}
+
+func (n *OpaqueNLRI) Len() int {
+ return 1 + len(n.Key)
+}
+
+func (n *OpaqueNLRI) String() string {
+ return string(n.Key)
+}
+
+func (n *OpaqueNLRI) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Key string `json:"key"`
+ }{
+ Key: n.String(),
+ })
+}
+
+func NewOpaqueNLRI(key []byte) *OpaqueNLRI {
+ return &OpaqueNLRI{
+ Key: key,
+ }
+}
+
+func AfiSafiToRouteFamily(afi uint16, safi uint8) RouteFamily {
+ return RouteFamily(int(afi)<<16 | int(safi))
+}
+
+func RouteFamilyToAfiSafi(rf RouteFamily) (uint16, uint8) {
+ return uint16(int(rf) >> 16), uint8(int(rf) & 0xff)
+}
+
+type RouteFamily int
+
+func (f RouteFamily) String() string {
+ if n, y := AddressFamilyNameMap[f]; y {
+ return n
+ }
+ return fmt.Sprintf("UnknownFamily(%d)", f)
+}
+
+const (
+ RF_IPv4_UC RouteFamily = AFI_IP<<16 | SAFI_UNICAST
+ RF_IPv6_UC RouteFamily = AFI_IP6<<16 | SAFI_UNICAST
+ RF_IPv4_MC RouteFamily = AFI_IP<<16 | SAFI_MULTICAST
+ RF_IPv6_MC RouteFamily = AFI_IP6<<16 | SAFI_MULTICAST
+ RF_IPv4_VPN RouteFamily = AFI_IP<<16 | SAFI_MPLS_VPN
+ RF_IPv6_VPN RouteFamily = AFI_IP6<<16 | SAFI_MPLS_VPN
+ RF_IPv4_VPN_MC RouteFamily = AFI_IP<<16 | SAFI_MPLS_VPN_MULTICAST
+ RF_IPv6_VPN_MC RouteFamily = AFI_IP6<<16 | SAFI_MPLS_VPN_MULTICAST
+ RF_IPv4_MPLS RouteFamily = AFI_IP<<16 | SAFI_MPLS_LABEL
+ RF_IPv6_MPLS RouteFamily = AFI_IP6<<16 | SAFI_MPLS_LABEL
+ RF_VPLS RouteFamily = AFI_L2VPN<<16 | SAFI_VPLS
+ RF_EVPN RouteFamily = AFI_L2VPN<<16 | SAFI_EVPN
+ RF_RTC_UC RouteFamily = AFI_IP<<16 | SAFI_ROUTE_TARGET_CONSTRTAINS
+ RF_ENCAP RouteFamily = AFI_IP<<16 | SAFI_ENCAPSULATION
+ RF_FS_IPv4_UC RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_UNICAST
+ RF_FS_IPv4_VPN RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_VPN
+ RF_FS_IPv6_UC RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_UNICAST
+ RF_FS_IPv6_VPN RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_VPN
+ RF_FS_L2_VPN RouteFamily = AFI_L2VPN<<16 | SAFI_FLOW_SPEC_VPN
+ RF_OPAQUE RouteFamily = AFI_OPAQUE<<16 | SAFI_KEY_VALUE
+)
+
+var AddressFamilyNameMap = map[RouteFamily]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_ENCAP: "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",
+}
+
+var AddressFamilyValueMap = map[string]RouteFamily{
+ 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_ENCAP]: RF_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,
+}
+
+func GetRouteFamily(name string) (RouteFamily, error) {
+ if v, ok := AddressFamilyValueMap[name]; ok {
+ return v, nil
+ }
+ return RouteFamily(0), fmt.Errorf("%s isn't a valid route family name", name)
+}
+
+func NewPrefixFromRouteFamily(afi uint16, safi uint8) (prefix AddrPrefixInterface, err error) {
+ switch AfiSafiToRouteFamily(afi, safi) {
+ case RF_IPv4_UC, RF_IPv4_MC:
+ prefix = NewIPAddrPrefix(0, "")
+ case RF_IPv6_UC, RF_IPv6_MC:
+ prefix = NewIPv6AddrPrefix(0, "")
+ case RF_IPv4_VPN:
+ prefix = NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil)
+ case RF_IPv6_VPN:
+ prefix = NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil)
+ case RF_IPv4_MPLS:
+ prefix = NewLabeledIPAddrPrefix(0, "", *NewMPLSLabelStack())
+ case RF_IPv6_MPLS:
+ prefix = NewLabeledIPv6AddrPrefix(0, "", *NewMPLSLabelStack())
+ case RF_EVPN:
+ prefix = NewEVPNNLRI(0, 0, nil)
+ case RF_RTC_UC:
+ prefix = &RouteTargetMembershipNLRI{}
+ case RF_ENCAP:
+ prefix = NewEncapNLRI("")
+ case RF_FS_IPv4_UC:
+ prefix = &FlowSpecIPv4Unicast{}
+ case RF_FS_IPv4_VPN:
+ prefix = &FlowSpecIPv4VPN{}
+ case RF_FS_IPv6_UC:
+ prefix = &FlowSpecIPv6Unicast{}
+ case RF_FS_IPv6_VPN:
+ prefix = &FlowSpecIPv6VPN{}
+ case RF_FS_L2_VPN:
+ prefix = &FlowSpecL2VPN{}
+ case RF_OPAQUE:
+ prefix = &OpaqueNLRI{}
+ default:
+ return nil, fmt.Errorf("unknown route family. AFI: %d, SAFI: %d", afi, safi)
+ }
+ return prefix, nil
+}
+
+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, "|")
+}
+
+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_AIGP // = 26
+ BGP_ATTR_TYPE_OPAQUE_VALUE BGPAttrType = 41
+)
+
+// 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
+)
+
+// 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_AUTHENTICATION_FAILURE
+ BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME
+)
+
+// 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_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_FSM_ERROR
+)
+
+// 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_RESET
+ BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
+ BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
+ BGP_ERROR_SUB_OUT_OF_RESOURCES
+)
+
+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_AIGP: BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_TYPE_OPAQUE_VALUE: BGP_ATTR_FLAG_TRANSITIVE | BGP_ATTR_FLAG_OPTIONAL,
+}
+
+type PathAttributeInterface interface {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+ Len() int
+ getFlags() BGPAttrFlag
+ GetType() BGPAttrType
+ String() string
+ MarshalJSON() ([]byte, error)
+}
+
+type PathAttribute struct {
+ Flags BGPAttrFlag
+ Type BGPAttrType
+ Length uint16
+ Value []byte
+}
+
+func (p *PathAttribute) Len() int {
+ if p.Length == 0 {
+ p.Length = uint16(len(p.Value))
+ }
+ l := 2 + p.Length
+ if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
+ l += 2
+ } else {
+ l += 1
+ }
+ return int(l)
+}
+
+func (p *PathAttribute) getFlags() BGPAttrFlag {
+ return p.Flags
+}
+
+func (p *PathAttribute) GetType() BGPAttrType {
+ return p.Type
+}
+
+func (p *PathAttribute) DecodeFromBytes(data []byte) error {
+ odata := data
+ eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
+ eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
+ if len(data) < 2 {
+ return 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 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 NewMessageError(eCode, eSubCode, data, "attribute header length is short")
+ }
+ p.Length = uint16(data[2])
+ data = data[3:]
+ }
+ if len(data) < int(p.Length) {
+ return NewMessageError(eCode, eSubCode, data, "attribute value length is short")
+ }
+ if len(data[:p.Length]) > 0 {
+ p.Value = data[:p.Length]
+ }
+
+ ok, eMsg := ValidateFlags(p.Type, p.Flags)
+ if !ok {
+ return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, odata[:p.Len()], eMsg)
+ }
+ return nil
+}
+
+func (p *PathAttribute) Serialize() ([]byte, error) {
+ p.Length = uint16(len(p.Value))
+ if p.Length > 255 {
+ p.Flags |= BGP_ATTR_FLAG_EXTENDED_LENGTH
+ } else {
+ p.Flags &^= BGP_ATTR_FLAG_EXTENDED_LENGTH
+ }
+ buf := make([]byte, p.Len())
+ buf[0] = uint8(p.Flags)
+ buf[1] = uint8(p.Type)
+ if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
+ binary.BigEndian.PutUint16(buf[2:4], p.Length)
+ copy(buf[4:], p.Value)
+ } else {
+ buf[2] = byte(p.Length)
+ copy(buf[3:], p.Value)
+ }
+ return buf, nil
+}
+
+func (p *PathAttribute) String() string {
+ return fmt.Sprintf("%s %s %s", p.Type.String(), p.Flags, []byte(p.Value))
+}
+
+func (p *PathAttribute) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type BGPAttrType `json:"type"`
+ Value []byte `json:"value"`
+ }{
+ Type: p.GetType(),
+ Value: p.Value,
+ })
+}
+
+type PathAttributeOrigin struct {
+ PathAttribute
+}
+
+func (p *PathAttributeOrigin) String() string {
+ typ := "-"
+ switch p.Value[0] {
+ case BGP_ORIGIN_ATTR_TYPE_IGP:
+ typ = "i"
+ case BGP_ORIGIN_ATTR_TYPE_EGP:
+ typ = "e"
+ case BGP_ORIGIN_ATTR_TYPE_INCOMPLETE:
+ typ = "?"
+ }
+ return fmt.Sprintf("{Origin: %s}", 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[0],
+ })
+}
+
+func NewPathAttributeOrigin(value uint8) *PathAttributeOrigin {
+ t := BGP_ATTR_TYPE_ORIGIN
+ return &PathAttributeOrigin{
+
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ Value: []byte{byte(value)},
+ },
+ }
+}
+
+type AsPathParamFormat struct {
+ start string
+ end string
+ separator string
+}
+
+var asPathParamFormatMap = map[uint8]*AsPathParamFormat{
+ BGP_ASPATH_ATTR_TYPE_SET: &AsPathParamFormat{"{", "}", ","},
+ BGP_ASPATH_ATTR_TYPE_SEQ: &AsPathParamFormat{"", "", " "},
+ BGP_ASPATH_ATTR_TYPE_CONFED_SET: &AsPathParamFormat{"(", ")", " "},
+ BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: &AsPathParamFormat{"[", "]", ","},
+}
+
+type AsPathParamInterface interface {
+ Serialize() ([]byte, error)
+ DecodeFromBytes([]byte) error
+ Len() int
+ ASLen() int
+ MarshalJSON() ([]byte, error)
+ String() string
+}
+
+type AsPathParam struct {
+ Type uint8
+ Num uint8
+ AS []uint16
+}
+
+func (a *AsPathParam) Serialize() ([]byte, error) {
+ buf := make([]byte, 2+len(a.AS)*2)
+ buf[0] = uint8(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 i := 0; i < int(a.Num); i++ {
+ 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, fmt.Sprintf("%d", asn))
+ }
+ 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) 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 i := 0; i < int(a.Num); i++ {
+ 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, fmt.Sprintf("%d", asn))
+ }
+ 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 DefaultAsPath struct {
+}
+
+func (p *DefaultAsPath) isValidAspath(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 == true {
+ segLength *= 4
+ } else {
+ segLength *= 2
+ }
+ if int(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")
+}
+
+type PathAttributeAsPath struct {
+ DefaultAsPath
+ PathAttribute
+ Value []AsPathParamInterface
+}
+
+func (p *PathAttributeAsPath) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if p.PathAttribute.Length == 0 {
+ // ibgp or something
+ return nil
+ }
+ as4Bytes, err := p.DefaultAsPath.isValidAspath(p.PathAttribute.Value)
+ if err != nil {
+ err.(*MessageError).Data = data[:p.Len()]
+ return err
+ }
+ v := p.PathAttribute.Value
+ for len(v) > 0 {
+ var tuple AsPathParamInterface
+ if as4Bytes == true {
+ tuple = &As4PathParam{}
+ } else {
+ tuple = &AsPathParam{}
+ }
+ err := tuple.DecodeFromBytes(v)
+ if err != nil {
+ return err
+ }
+ p.Value = append(p.Value, tuple)
+ if tuple.Len() > len(v) {
+
+ }
+ v = v[tuple.Len():]
+ }
+ return nil
+}
+
+func (p *PathAttributeAsPath) Serialize() ([]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...)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func (p *PathAttributeAsPath) 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 *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 {
+ t := BGP_ATTR_TYPE_AS_PATH
+ return &PathAttributeAsPath{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Value: value,
+ }
+}
+
+type PathAttributeNextHop struct {
+ PathAttribute
+ Value net.IP
+}
+
+func (p *PathAttributeNextHop) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value) != 4 && len(p.PathAttribute.Value) != 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 = p.PathAttribute.Value
+ return nil
+}
+
+func (p *PathAttributeNextHop) Serialize() ([]byte, error) {
+ p.PathAttribute.Value = p.Value
+ return p.PathAttribute.Serialize()
+}
+
+func (p *PathAttributeNextHop) String() string {
+ return fmt.Sprintf("{Nexthop: %s}", p.Value)
+}
+
+func (p *PathAttributeNextHop) MarshalJSON() ([]byte, error) {
+ value := "0.0.0.0"
+ if p.Value != nil {
+ value = p.Value.String()
+ }
+ return json.Marshal(struct {
+ Type BGPAttrType `json:"type"`
+ Value string `json:"nexthop"`
+ }{
+ Type: p.GetType(),
+ Value: value,
+ })
+}
+
+func NewPathAttributeNextHop(value string) *PathAttributeNextHop {
+ t := BGP_ATTR_TYPE_NEXT_HOP
+ return &PathAttributeNextHop{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Value: net.ParseIP(value).To4(),
+ }
+}
+
+type PathAttributeMultiExitDisc struct {
+ PathAttribute
+ Value uint32
+}
+
+func (p *PathAttributeMultiExitDisc) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value) != 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(p.PathAttribute.Value)
+ return nil
+}
+
+func (p *PathAttributeMultiExitDisc) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, p.Value)
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func (p *PathAttributeMultiExitDisc) String() string {
+ return fmt.Sprintf("{Med: %d}", p.Value)
+}
+
+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,
+ },
+ Value: value,
+ }
+}
+
+type PathAttributeLocalPref struct {
+ PathAttribute
+ Value uint32
+}
+
+func (p *PathAttributeLocalPref) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value) != 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(p.PathAttribute.Value)
+ return nil
+}
+
+func (p *PathAttributeLocalPref) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, p.Value)
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func (p *PathAttributeLocalPref) String() string {
+ return fmt.Sprintf("{LocalPref: %d}", p.Value)
+}
+
+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,
+ },
+ Value: value,
+ }
+}
+
+type PathAttributeAtomicAggregate struct {
+ PathAttribute
+}
+
+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,
+ },
+ }
+}
+
+type PathAttributeAggregatorParam struct {
+ AS uint32
+ askind reflect.Kind
+ Address net.IP
+}
+
+type PathAttributeAggregator struct {
+ PathAttribute
+ Value PathAttributeAggregatorParam
+}
+
+func (p *PathAttributeAggregator) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value) != 6 && len(p.PathAttribute.Value) != 8 {
+ 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")
+ }
+ if len(p.PathAttribute.Value) == 6 {
+ p.Value.AS = uint32(binary.BigEndian.Uint16(p.PathAttribute.Value[0:2]))
+ p.Value.Address = p.PathAttribute.Value[2:]
+ p.Value.askind = reflect.Uint16
+ } else {
+ p.Value.AS = binary.BigEndian.Uint32(p.PathAttribute.Value[0:4])
+ p.Value.Address = p.PathAttribute.Value[4:]
+ p.Value.askind = reflect.Uint32
+ }
+ return nil
+}
+
+func (p *PathAttributeAggregator) Serialize() ([]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)
+ case reflect.Uint32:
+ buf = make([]byte, 8)
+ binary.BigEndian.PutUint32(buf, p.Value.AS)
+ copy(buf[4:], p.Value.Address)
+ }
+
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func (p *PathAttributeAggregator) String() string {
+ return fmt.Sprintf("{Aggregate: {AS: %d, Address: %s}}", p.Value.AS, p.Value.Address)
+}
+
+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 interface{}, address string) *PathAttributeAggregator {
+ v := reflect.ValueOf(as)
+ t := BGP_ATTR_TYPE_AGGREGATOR
+ return &PathAttributeAggregator{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Value: PathAttributeAggregatorParam{
+ AS: uint32(v.Uint()),
+ askind: v.Kind(),
+ Address: net.ParseIP(address).To4(),
+ },
+ }
+}
+
+type PathAttributeCommunities struct {
+ PathAttribute
+ Value []uint32
+}
+
+func (p *PathAttributeCommunities) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value)%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")
+ }
+ value := p.PathAttribute.Value
+ for len(value) >= 4 {
+ p.Value = append(p.Value, binary.BigEndian.Uint32(value))
+ value = value[4:]
+ }
+ return nil
+}
+
+func (p *PathAttributeCommunities) Serialize() ([]byte, error) {
+ buf := make([]byte, len(p.Value)*4)
+ for i, v := range p.Value {
+ binary.BigEndian.PutUint32(buf[i*4:], v)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+type WellKnownCommunity uint32
+
+const (
+ COMMUNITY_INTERNET WellKnownCommunity = 0x00000000
+ COMMUNITY_PLANNED_SHUT = 0xffff0000
+ COMMUNITY_ACCEPT_OWN = 0xffff0001
+ COMMUNITY_ROUTE_FILTER_TRANSLATED_v4 = 0xffff0002
+ COMMUNITY_ROUTE_FILTER_v4 = 0xffff0003
+ COMMUNITY_ROUTE_FILTER_TRANSLATED_v6 = 0xffff0004
+ COMMUNITY_ROUTE_FILTER_v6 = 0xffff0005
+ COMMUNITY_LLGR_STALE = 0xffff0006
+ COMMUNITY_NO_LLGR = 0xffff0007
+ COMMUNITY_NO_EXPORT = 0xffffff01
+ COMMUNITY_NO_ADVERTISE = 0xffffff02
+ COMMUNITY_NO_EXPORT_SUBCONFED = 0xffffff03
+ COMMUNITY_NO_PEER = 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_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_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 := []string{}
+ for _, v := range p.Value {
+ n, ok := WellKnownCommunityNameMap[WellKnownCommunity(v)]
+ if ok {
+ l = append(l, n)
+ } else {
+ l = append(l, fmt.Sprintf("%d:%d", (0xffff0000&v)>>16, 0xffff&v))
+ }
+ }
+ return fmt.Sprintf("{Communities: %s}", 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 {
+ t := BGP_ATTR_TYPE_COMMUNITIES
+ return &PathAttributeCommunities{
+ PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ Length: 0,
+ Value: nil},
+ value,
+ }
+}
+
+type PathAttributeOriginatorId struct {
+ PathAttribute
+ Value net.IP
+}
+
+func (p *PathAttributeOriginatorId) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value) != 4 && len(p.PathAttribute.Value) != 16 {
+ eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
+ eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
+ return NewMessageError(eCode, eSubCode, nil, "originatorid length isn't correct")
+ }
+ p.Value = p.PathAttribute.Value
+ return nil
+}
+
+func (p *PathAttributeOriginatorId) String() string {
+ return fmt.Sprintf("{Originator: %s}", p.Value)
+}
+
+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() ([]byte, error) {
+ buf := make([]byte, 4)
+ copy(buf, p.Value)
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func NewPathAttributeOriginatorId(value string) *PathAttributeOriginatorId {
+ t := BGP_ATTR_TYPE_ORIGINATOR_ID
+ return &PathAttributeOriginatorId{
+ PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ Length: 0,
+ Value: nil},
+ net.ParseIP(value).To4(),
+ }
+}
+
+type PathAttributeClusterList struct {
+ PathAttribute
+ Value []net.IP
+}
+
+func (p *PathAttributeClusterList) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ value := p.PathAttribute.Value
+ if len(p.PathAttribute.Value)%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 {
+ p.Value = append(p.Value, value[:4])
+ value = value[4:]
+ }
+ return nil
+}
+
+func (p *PathAttributeClusterList) Serialize() ([]byte, error) {
+ buf := make([]byte, len(p.Value)*4)
+ for i, v := range p.Value {
+ copy(buf[i*4:], v)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+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 []string) *PathAttributeClusterList {
+ l := make([]net.IP, len(value))
+ for i, v := range value {
+ l[i] = net.ParseIP(v).To4()
+ }
+ t := BGP_ATTR_TYPE_CLUSTER_LIST
+ return &PathAttributeClusterList{
+ PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ Length: 0,
+ Value: nil},
+ l,
+ }
+}
+
+type PathAttributeMpReachNLRI struct {
+ PathAttribute
+ Nexthop net.IP
+ LinkLocalNexthop net.IP
+ AFI uint16
+ SAFI uint8
+ Value []AddrPrefixInterface
+}
+
+func (p *PathAttributeMpReachNLRI) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
+ eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
+
+ value := p.PathAttribute.Value
+ if len(value) < 3 {
+ return NewMessageError(eCode, eSubCode, value, "mpreach header length is short")
+ }
+ afi := binary.BigEndian.Uint16(value[0:2])
+ safi := value[2]
+ p.AFI = afi
+ p.SAFI = safi
+ _, err = NewPrefixFromRouteFamily(afi, safi)
+ if err != nil {
+ return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
+ }
+ nexthopLen := value[3]
+ if len(value) < 4+int(nexthopLen) {
+ return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is short")
+ }
+ nexthopbin := value[4 : 4+nexthopLen]
+ value = value[4+nexthopLen:]
+ if nexthopLen > 0 {
+ offset := 0
+ if safi == SAFI_MPLS_VPN {
+ offset = 8
+ }
+ addrlen := 4
+ hasLinkLocal := false
+
+ if afi == AFI_IP6 {
+ addrlen = 16
+ hasLinkLocal = len(nexthopbin) == offset+2*addrlen
+ }
+
+ isValid := len(nexthopbin) == offset+addrlen || hasLinkLocal
+
+ if !isValid {
+ return NewMessageError(eCode, eSubCode, value, "mpreach nexthop length is incorrect")
+ }
+ p.Nexthop = nexthopbin[offset : +offset+addrlen]
+ if hasLinkLocal {
+ p.LinkLocalNexthop = nexthopbin[offset+addrlen : offset+2*addrlen]
+ }
+ }
+ // skip reserved
+ if len(value) == 0 {
+ return NewMessageError(eCode, eSubCode, value, "no skip byte")
+ }
+ value = value[1:]
+ for len(value) > 0 {
+ prefix, err := NewPrefixFromRouteFamily(afi, safi)
+ if err != nil {
+ return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
+ }
+ err = prefix.DecodeFromBytes(value)
+ if err != nil {
+ return err
+ }
+ if prefix.Len() > len(value) {
+ return NewMessageError(eCode, eSubCode, value, "prefix length is incorrect")
+ }
+ value = value[prefix.Len():]
+ p.Value = append(p.Value, prefix)
+ }
+ return nil
+}
+
+func (p *PathAttributeMpReachNLRI) Serialize() ([]byte, error) {
+ afi := p.AFI
+ safi := p.SAFI
+ nexthoplen := 4
+ if afi == AFI_IP6 {
+ nexthoplen = 16
+ if p.LinkLocalNexthop != nil {
+ nexthoplen += 16
+ }
+ }
+ offset := 0
+ switch safi {
+ case SAFI_MPLS_VPN:
+ offset = 8
+ nexthoplen += 8
+ case SAFI_FLOW_SPEC_VPN, SAFI_FLOW_SPEC_UNICAST:
+ nexthoplen = 0
+ }
+ buf := make([]byte, 4+nexthoplen)
+ binary.BigEndian.PutUint16(buf[0:], afi)
+ buf[2] = safi
+ buf[3] = uint8(nexthoplen)
+ copy(buf[4+offset:], p.Nexthop)
+ if p.LinkLocalNexthop != nil {
+ copy(buf[4+offset+len(p.Nexthop):], p.LinkLocalNexthop)
+ }
+ buf = append(buf, make([]byte, 1)...)
+ for _, prefix := range p.Value {
+ pbuf, err := prefix.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, pbuf...)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func (p *PathAttributeMpReachNLRI) MarshalJSON() ([]byte, error) {
+ nexthop := p.Nexthop.String()
+ if p.Nexthop == nil {
+ 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 []AddrPrefixInterface `json:"value"`
+ }{
+ Type: p.GetType(),
+ Nexthop: nexthop,
+ AFI: p.AFI,
+ SAFI: p.SAFI,
+ Value: p.Value,
+ })
+}
+
+func NewPathAttributeMpReachNLRI(nexthop string, nlri []AddrPrefixInterface) *PathAttributeMpReachNLRI {
+ t := BGP_ATTR_TYPE_MP_REACH_NLRI
+ ip := net.ParseIP(nexthop)
+ if ip.To4() != nil {
+ ip = ip.To4()
+ }
+ p := &PathAttributeMpReachNLRI{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Nexthop: ip,
+ Value: nlri,
+ }
+ if len(nlri) > 0 {
+ p.AFI = nlri[0].AFI()
+ p.SAFI = nlri[0].SAFI()
+ }
+ return p
+}
+
+type PathAttributeMpUnreachNLRI struct {
+ PathAttribute
+ AFI uint16
+ SAFI uint8
+ Value []AddrPrefixInterface
+}
+
+func (p *PathAttributeMpUnreachNLRI) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
+ eSubCode := uint8(BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR)
+
+ value := p.PathAttribute.Value
+ if len(value) < 3 {
+ return NewMessageError(eCode, eSubCode, value, "unreach header length is incorrect")
+ }
+ afi := binary.BigEndian.Uint16(value[0:2])
+ safi := value[2]
+ _, err = NewPrefixFromRouteFamily(afi, safi)
+ if err != nil {
+ return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
+ }
+ value = value[3:]
+ p.AFI = afi
+ p.SAFI = safi
+ for len(value) > 0 {
+ prefix, err := NewPrefixFromRouteFamily(afi, safi)
+ if err != nil {
+ return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, data[:p.PathAttribute.Len()], err.Error())
+ }
+ err = prefix.DecodeFromBytes(value)
+ if err != nil {
+ return err
+ }
+ if prefix.Len() > len(value) {
+ return NewMessageError(eCode, eSubCode, data[:p.PathAttribute.Len()], "prefix length is incorrect")
+ }
+ value = value[prefix.Len():]
+ p.Value = append(p.Value, prefix)
+ }
+ return nil
+}
+
+func (p *PathAttributeMpUnreachNLRI) Serialize() ([]byte, error) {
+ buf := make([]byte, 3)
+ binary.BigEndian.PutUint16(buf, p.AFI)
+ buf[2] = p.SAFI
+ for _, prefix := range p.Value {
+ pbuf, err := prefix.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, pbuf...)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func NewPathAttributeMpUnreachNLRI(nlri []AddrPrefixInterface) *PathAttributeMpUnreachNLRI {
+ t := BGP_ATTR_TYPE_MP_UNREACH_NLRI
+ p := &PathAttributeMpUnreachNLRI{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ Length: 0,
+ },
+ Value: nlri,
+ }
+ if len(nlri) > 0 {
+ p.AFI = nlri[0].AFI()
+ p.SAFI = nlri[0].SAFI()
+ }
+ return p
+}
+
+type ExtendedCommunityInterface interface {
+ Serialize() ([]byte, error)
+ String() string
+ GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType)
+ MarshalJSON() ([]byte, error)
+}
+
+type TwoOctetAsSpecificExtended struct {
+ SubType ExtendedCommunityAttrSubType
+ AS uint16
+ LocalAdmin uint32
+ IsTransitive bool
+}
+
+func (e *TwoOctetAsSpecificExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ 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 fmt.Sprintf("%d:%d", e.AS, e.LocalAdmin)
+}
+
+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 net.IP
+ LocalAdmin uint16
+ IsTransitive bool
+}
+
+func (e *IPv4AddressSpecificExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ 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)
+ binary.BigEndian.PutUint16(buf[6:], e.LocalAdmin)
+ return buf, nil
+}
+
+func (e *IPv4AddressSpecificExtended) String() string {
+ return fmt.Sprintf("%s:%d", e.IPv4.String(), e.LocalAdmin)
+}
+
+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 string, localAdmin uint16, isTransitive bool) *IPv4AddressSpecificExtended {
+ ipv4 := net.ParseIP(ip)
+ if ipv4.To4() == nil {
+ return nil
+ }
+ return &IPv4AddressSpecificExtended{
+ SubType: subtype,
+ IPv4: ipv4.To4(),
+ LocalAdmin: localAdmin,
+ IsTransitive: isTransitive,
+ }
+}
+
+type FourOctetAsSpecificExtended struct {
+ SubType ExtendedCommunityAttrSubType
+ AS uint32
+ LocalAdmin uint16
+ IsTransitive bool
+}
+
+func (e *FourOctetAsSpecificExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ 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 {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, e.AS)
+ asUpper := binary.BigEndian.Uint16(buf[0:2])
+ asLower := binary.BigEndian.Uint16(buf[2:])
+ return fmt.Sprintf("%d.%d:%d", asUpper, asLower, e.LocalAdmin)
+}
+
+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_ORIGIN_VALIDATION {
+ var value ValidationState
+ switch com {
+ case VALIDATION_STATE_VALID.String():
+ value = VALIDATION_STATE_VALID
+ case VALIDATION_STATE_NOT_FOUND.String():
+ value = VALIDATION_STATE_NOT_FOUND
+ case VALIDATION_STATE_INVALID.String():
+ value = VALIDATION_STATE_INVALID
+ default:
+ return nil, fmt.Errorf("invalid validation state")
+ }
+ return &OpaqueExtended{
+ SubType: EC_SUBTYPE_ORIGIN_VALIDATION,
+ Value: &ValidationExtended{
+ Value: value,
+ },
+ }, nil
+ }
+ elems, err := parseRdAndRt(com)
+ if err != nil {
+ return nil, err
+ }
+ localAdmin, _ := strconv.Atoi(elems[9])
+ ip := net.ParseIP(elems[1])
+ isTransitive := true
+ switch {
+ case ip.To4() != nil:
+ return NewIPv4AddressSpecificExtended(subtype, elems[1], uint16(localAdmin), isTransitive), nil
+ case elems[6] == "" && elems[7] == "":
+ asn, _ := strconv.Atoi(elems[8])
+ return NewTwoOctetAsSpecificExtended(subtype, uint16(asn), uint32(localAdmin), isTransitive), nil
+ default:
+ fst, _ := strconv.Atoi(elems[7])
+ snd, _ := strconv.Atoi(elems[8])
+ 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)
+}
+
+type OpaqueExtendedValueInterface interface {
+ Serialize() ([]byte, error)
+ String() string
+}
+
+type DefaultOpaqueExtendedValue struct {
+ Value []byte
+}
+
+func (v *DefaultOpaqueExtendedValue) Serialize() ([]byte, error) {
+ v.Value = v.Value[:7]
+ return v.Value[:7], nil
+}
+
+func (v *DefaultOpaqueExtendedValue) String() string {
+ buf := make([]byte, 8)
+ copy(buf[1:], v.Value)
+ d := binary.BigEndian.Uint64(buf)
+ return fmt.Sprintf("%d", d)
+}
+
+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 validatation state(%d)", s)
+}
+
+type ValidationExtended struct {
+ Value ValidationState
+}
+
+func (e *ValidationExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 7)
+ buf[0] = byte(EC_SUBTYPE_ORIGIN_VALIDATION)
+ buf[6] = byte(e.Value)
+ return buf, nil
+}
+
+func (e *ValidationExtended) String() string {
+ return e.Value.String()
+}
+
+type ColorExtended struct {
+ Value uint32
+}
+
+func (e *ColorExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 7)
+ buf[0] = byte(EC_SUBTYPE_COLOR)
+ binary.BigEndian.PutUint32(buf[3:], uint32(e.Value))
+ return buf, nil
+}
+
+func (e *ColorExtended) String() string {
+ return fmt.Sprintf("%d", e.Value)
+}
+
+type EncapExtended struct {
+ TunnelType TunnelType
+}
+
+func (e *EncapExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 7)
+ buf[0] = byte(EC_SUBTYPE_ENCAPSULATION)
+ binary.BigEndian.PutUint16(buf[5:], 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"
+ default:
+ return fmt.Sprintf("tunnel: %d", e.TunnelType)
+ }
+}
+
+type OpaqueExtended struct {
+ IsTransitive bool
+ Value OpaqueExtendedValueInterface
+ SubType ExtendedCommunityAttrSubType
+}
+
+func (e *OpaqueExtended) DecodeFromBytes(data []byte) error {
+ if len(data) != 7 {
+ return fmt.Errorf("Invalid OpaqueExtended bytes len: %d", len(data))
+ }
+ e.SubType = ExtendedCommunityAttrSubType(data[0])
+
+ if e.IsTransitive {
+ switch e.SubType {
+ case EC_SUBTYPE_COLOR:
+ v := binary.BigEndian.Uint32(data[3:7])
+ e.Value = &ColorExtended{
+ Value: v,
+ }
+ case EC_SUBTYPE_ENCAPSULATION:
+ t := TunnelType(binary.BigEndian.Uint16(data[5:7]))
+ e.Value = &EncapExtended{
+ TunnelType: t,
+ }
+ default:
+ e.Value = &DefaultOpaqueExtendedValue{
+ Value: data, //7byte
+ }
+ }
+ } else {
+ switch e.SubType {
+ case EC_SUBTYPE_ORIGIN_VALIDATION:
+ e.Value = &ValidationExtended{
+ Value: ValidationState(data[6]),
+ }
+ default:
+ e.Value = &DefaultOpaqueExtendedValue{
+ Value: data, //7byte
+ }
+ }
+ }
+ return nil
+}
+
+func (e *OpaqueExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 1, 7)
+ if e.IsTransitive {
+ buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
+ } else {
+ buf[0] = byte(EC_TYPE_NON_TRANSITIVE_OPAQUE)
+ }
+ bbuf, err := e.Value.Serialize()
+ e.SubType = ExtendedCommunityAttrSubType(bbuf[0])
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, bbuf...)
+ return buf, nil
+}
+
+func (e *OpaqueExtended) String() string {
+ return e.Value.String()
+}
+
+func (e *OpaqueExtended) MarshalJSON() ([]byte, error) {
+ t, s := e.GetTypes()
+ return json.Marshal(struct {
+ Type ExtendedCommunityAttrType `json:"type"`
+ Subtype ExtendedCommunityAttrSubType `json:"subtype"`
+ Value OpaqueExtendedValueInterface `json:"value"`
+ }{
+ Type: t,
+ Subtype: s,
+ Value: e.Value,
+ })
+}
+
+func (e *OpaqueExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
+ t := EC_TYPE_TRANSITIVE_OPAQUE
+ if !e.IsTransitive {
+ t = EC_TYPE_NON_TRANSITIVE_OPAQUE
+ }
+ return t, e.SubType
+}
+
+func NewOpaqueExtended(isTransitive bool) *OpaqueExtended {
+ return &OpaqueExtended{
+ IsTransitive: isTransitive,
+ }
+}
+
+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(fmt.Sprintf("esi-label: %d", e.Label))
+ 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 fmt.Sprintf("es-import rt: %s", 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(fmt.Sprintf("mac-mobility: %d", e.Sequence))
+ 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,
+ }
+}
+
+func parseEvpnExtended(data []byte) (ExtendedCommunityInterface, error) {
+ if ExtendedCommunityAttrType(data[0]) != EC_TYPE_EVPN {
+ return nil, fmt.Errorf("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
+ }
+ return nil, fmt.Errorf("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(fmt.Sprintf("rate: %f", e.Rate))
+ }
+ if e.AS != 0 {
+ buf.WriteString(fmt.Sprintf("(as: %d)", e.AS))
+ }
+ 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, 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 fmt.Sprintf("action: %s", 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, 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 fmt.Sprintf("redirect: %s", 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 fmt.Sprintf("redirect: %s", 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 string, localAdmin uint16) *RedirectIPv4AddressSpecificExtended {
+ return &RedirectIPv4AddressSpecificExtended{*NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, ipv4, localAdmin, false)}
+}
+
+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 fmt.Sprintf("redirect: %s", 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] = byte(e.DSCP)
+ return buf, nil
+}
+
+func (e *TrafficRemarkExtended) String() string {
+ return fmt.Sprintf("remark: %d", e.DSCP)
+}
+
+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}
+}
+
+func parseFlowSpecExtended(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, fmt.Errorf("ext comm type is not EC_TYPE_FLOWSPEC: %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:
+ //draft-ietf-idr-flowspec-redirect-rt-bis-05
+ 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 := net.IP(data[2:6]).String()
+ localAdmin := binary.BigEndian.Uint16(data[6:8])
+ return NewRedirectIPv4AddressSpecificExtended(ipv4, localAdmin), nil
+ 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
+ }
+ return &UnknownExtended{
+ Type: ExtendedCommunityAttrType(data[0]),
+ Value: data[1:8],
+ }, nil
+}
+
+type UnknownExtended struct {
+ Type ExtendedCommunityAttrType
+ Value []byte
+}
+
+func (e *UnknownExtended) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ buf[0] = uint8(e.Type)
+ copy(buf[1:], e.Value)
+ e.Value = buf[1:]
+ return buf, nil
+}
+
+func (e *UnknownExtended) String() string {
+ buf := make([]byte, 8)
+ copy(buf[1:], e.Value)
+ v := binary.BigEndian.Uint64(buf)
+ return fmt.Sprintf("%d", v)
+}
+
+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) {
+ return ExtendedCommunityAttrType(0xFF), ExtendedCommunityAttrSubType(0xFF)
+}
+
+type PathAttributeExtendedCommunities struct {
+ PathAttribute
+ Value []ExtendedCommunityInterface
+}
+
+func ParseExtended(data []byte) (ExtendedCommunityInterface, error) {
+ if len(data) < 8 {
+ return nil, fmt.Errorf("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])
+ return NewTwoOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil
+ case EC_TYPE_TRANSITIVE_IP4_SPECIFIC:
+ transitive = true
+ fallthrough
+ case EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC:
+ ipv4 := net.IP(data[2:6]).String()
+ localAdmin := binary.BigEndian.Uint16(data[6:8])
+ return NewIPv4AddressSpecificExtended(subtype, ipv4, localAdmin, transitive), nil
+ 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:
+ e := NewOpaqueExtended(transitive)
+ err := e.DecodeFromBytes(data[1:8])
+ return e, err
+ case EC_TYPE_EVPN:
+ return parseEvpnExtended(data)
+ case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3:
+ return parseFlowSpecExtended(data)
+ default:
+ return &UnknownExtended{
+ Type: ExtendedCommunityAttrType(data[0]),
+ Value: data[1:8],
+ }, nil
+ }
+}
+
+func (p *PathAttributeExtendedCommunities) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value)%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")
+ }
+ value := p.PathAttribute.Value
+ 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() ([]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...)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+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 fmt.Sprintf("{Extcomms: %s}", 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 {
+ t := BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
+ return &PathAttributeExtendedCommunities{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Value: value,
+ }
+}
+
+type PathAttributeAs4Path struct {
+ PathAttribute
+ Value []*As4PathParam
+ DefaultAsPath
+}
+
+func (p *PathAttributeAs4Path) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
+ eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
+ v := p.PathAttribute.Value
+ as4Bytes, err := p.DefaultAsPath.isValidAspath(p.PathAttribute.Value)
+ if err != nil {
+ return err
+ }
+ if as4Bytes == false {
+ return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed")
+ }
+ for len(v) > 0 {
+ tuple := &As4PathParam{}
+ tuple.DecodeFromBytes(v)
+ p.Value = append(p.Value, tuple)
+ if len(v) < tuple.Len() {
+ return NewMessageError(eCode, eSubCode, nil, "AS4 PATH param is malformed")
+ }
+ v = v[tuple.Len():]
+ }
+ return nil
+}
+
+func (p *PathAttributeAs4Path) Serialize() ([]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...)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+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 NewPathAttributeAs4Path(value []*As4PathParam) *PathAttributeAs4Path {
+ t := BGP_ATTR_TYPE_AS4_PATH
+ return &PathAttributeAs4Path{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Value: value,
+ }
+}
+
+type PathAttributeAs4Aggregator struct {
+ PathAttribute
+ Value PathAttributeAggregatorParam
+}
+
+func (p *PathAttributeAs4Aggregator) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value) != 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(p.PathAttribute.Value[0:4])
+ p.Value.Address = p.PathAttribute.Value[4:]
+ return nil
+}
+
+func (p *PathAttributeAs4Aggregator) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ binary.BigEndian.PutUint32(buf[0:], p.Value.AS)
+ copy(buf[4:], p.Value.Address.To4())
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func NewPathAttributeAs4Aggregator(as uint32, address string) *PathAttributeAs4Aggregator {
+ t := BGP_ATTR_TYPE_AS4_AGGREGATOR
+ return &PathAttributeAs4Aggregator{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Value: PathAttributeAggregatorParam{
+ AS: as,
+ Address: net.ParseIP(address).To4(),
+ },
+ }
+}
+
+type TunnelEncapSubTLVValue interface {
+ Serialize() ([]byte, error)
+}
+
+type TunnelEncapSubTLVDefault struct {
+ Value []byte
+}
+
+func (t *TunnelEncapSubTLVDefault) Serialize() ([]byte, error) {
+ return t.Value, nil
+}
+
+type TunnelEncapSubTLVEncapuslation struct {
+ Key uint32 // this represent both SessionID for L2TPv3 case and GRE-key for GRE case (RFC5512 4.)
+ Cookie []byte
+}
+
+func (t *TunnelEncapSubTLVEncapuslation) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, t.Key)
+ return append(buf, t.Cookie...), nil
+}
+
+type TunnelEncapSubTLVProtocol struct {
+ Protocol uint16
+}
+
+func (t *TunnelEncapSubTLVProtocol) Serialize() ([]byte, error) {
+ buf := make([]byte, 2)
+ binary.BigEndian.PutUint16(buf, t.Protocol)
+ return buf, nil
+}
+
+type TunnelEncapSubTLVColor struct {
+ Color uint32
+}
+
+func (t *TunnelEncapSubTLVColor) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
+ buf[1] = byte(EC_SUBTYPE_COLOR)
+ binary.BigEndian.PutUint32(buf[4:], t.Color)
+ return buf, nil
+}
+
+type TunnelEncapSubTLV struct {
+ Type EncapSubTLVType
+ Len int
+ Value TunnelEncapSubTLVValue
+}
+
+func (p *TunnelEncapSubTLV) Serialize() ([]byte, error) {
+ buf := make([]byte, 2)
+ bbuf, err := p.Value.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, bbuf...)
+ buf[0] = byte(p.Type)
+ p.Len = len(buf) - 2
+ buf[1] = byte(p.Len)
+ return buf, nil
+}
+
+func (p *TunnelEncapSubTLV) DecodeFromBytes(data []byte) error {
+ switch p.Type {
+ case ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+ if len(data) < 4 {
+ return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
+ }
+ key := binary.BigEndian.Uint32(data[:4])
+ p.Value = &TunnelEncapSubTLVEncapuslation{
+ Key: key,
+ Cookie: data[4:],
+ }
+ case ENCAP_SUBTLV_TYPE_PROTOCOL:
+ if len(data) < 2 {
+ return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
+ }
+ protocol := binary.BigEndian.Uint16(data[:2])
+ p.Value = &TunnelEncapSubTLVProtocol{protocol}
+ case ENCAP_SUBTLV_TYPE_COLOR:
+ if len(data) < 8 {
+ return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
+ }
+ color := binary.BigEndian.Uint32(data[4:])
+ p.Value = &TunnelEncapSubTLVColor{color}
+ default:
+ p.Value = &TunnelEncapSubTLVDefault{data}
+ }
+ return nil
+}
+
+type TunnelEncapTLV struct {
+ Type TunnelType
+ Len int
+ Value []*TunnelEncapSubTLV
+}
+
+func (t *TunnelEncapTLV) DecodeFromBytes(data []byte) error {
+ curr := 0
+ for {
+ if len(data) < curr+2 {
+ break
+ }
+ subType := EncapSubTLVType(data[curr])
+ l := int(data[curr+1])
+ if len(data) < curr+2+l {
+ return fmt.Errorf("Not all TunnelEncapSubTLV bytes available")
+ }
+ v := data[curr+2 : curr+2+l]
+ subTlv := &TunnelEncapSubTLV{
+ Type: subType,
+ }
+ err := subTlv.DecodeFromBytes(v)
+ if err != nil {
+ return err
+ }
+ t.Value = append(t.Value, subTlv)
+ curr += 2 + l
+ }
+ return nil
+}
+
+func (p *TunnelEncapTLV) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ for _, s := range p.Value {
+ bbuf, err := s.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, bbuf...)
+ }
+ binary.BigEndian.PutUint16(buf, uint16(p.Type))
+ p.Len = len(buf) - 4
+ binary.BigEndian.PutUint16(buf[2:], uint16(p.Len))
+ return buf, nil
+}
+
+type PathAttributeTunnelEncap struct {
+ PathAttribute
+ Value []*TunnelEncapTLV
+}
+
+func (p *PathAttributeTunnelEncap) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ curr := 0
+ for {
+ if len(p.PathAttribute.Value) < curr+4 {
+ break
+ }
+ t := binary.BigEndian.Uint16(p.PathAttribute.Value[curr : curr+2])
+ tunnelType := TunnelType(t)
+ l := int(binary.BigEndian.Uint16(p.PathAttribute.Value[curr+2 : curr+4]))
+ if len(p.PathAttribute.Value) < curr+4+l {
+ return fmt.Errorf("Not all TunnelEncapTLV bytes available. %d < %d", len(p.PathAttribute.Value), curr+4+l)
+ }
+ v := p.PathAttribute.Value[curr+4 : curr+4+l]
+ tlv := &TunnelEncapTLV{
+ Type: tunnelType,
+ Len: l,
+ }
+ err = tlv.DecodeFromBytes(v)
+ if err != nil {
+ return err
+ }
+ p.Value = append(p.Value, tlv)
+ curr += 4 + l
+ }
+ return nil
+}
+
+func (p *PathAttributeTunnelEncap) Serialize() ([]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...)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func NewPathAttributeTunnelEncap(value []*TunnelEncapTLV) *PathAttributeTunnelEncap {
+ t := BGP_ATTR_TYPE_TUNNEL_ENCAP
+ return &PathAttributeTunnelEncap{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Value: value,
+ }
+}
+
+type PmsiTunnelIDInterface interface {
+ Serialize() ([]byte, error)
+ String() string
+}
+
+type DefaultPmsiTunnelID struct {
+ Value []byte
+}
+
+func (i *DefaultPmsiTunnelID) Serialize() ([]byte, error) {
+ return i.Value, nil
+}
+
+func (i *DefaultPmsiTunnelID) String() string {
+ return string(i.Value)
+}
+
+type IngressReplTunnelID struct {
+ Value net.IP
+}
+
+func (i *IngressReplTunnelID) Serialize() ([]byte, error) {
+ if i.Value.To4() != nil {
+ return []byte(i.Value.To4()), nil
+ }
+ return []byte(i.Value), nil
+}
+
+func (i *IngressReplTunnelID) String() string {
+ return i.Value.String()
+}
+
+type PathAttributePmsiTunnel struct {
+ PathAttribute
+ IsLeafInfoRequired bool
+ TunnelType PmsiTunnelType
+ Label uint32
+ TunnelID PmsiTunnelIDInterface
+}
+
+func (p *PathAttributePmsiTunnel) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(p.PathAttribute.Value) < 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 (p.PathAttribute.Value[0] & 0x01) > 0 {
+ p.IsLeafInfoRequired = true
+ }
+ p.TunnelType = PmsiTunnelType(p.PathAttribute.Value[1])
+ p.Label = labelDecode(p.PathAttribute.Value[2:5])
+
+ switch p.TunnelType {
+ case PMSI_TUNNEL_TYPE_INGRESS_REPL:
+ p.TunnelID = &IngressReplTunnelID{net.IP(p.PathAttribute.Value[5:])}
+ default:
+ p.TunnelID = &DefaultPmsiTunnelID{p.PathAttribute.Value[5:]}
+ }
+ return nil
+}
+
+func (p *PathAttributePmsiTunnel) Serialize() ([]byte, error) {
+ buf := make([]byte, 2)
+ if p.IsLeafInfoRequired {
+ buf[0] = 0x01
+ }
+ buf[1] = byte(p.TunnelType)
+ lbuf := make([]byte, 3)
+ labelSerialize(p.Label, lbuf)
+ buf = append(buf, lbuf...)
+ ibuf, err := p.TunnelID.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, ibuf...)
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+func (p *PathAttributePmsiTunnel) String() string {
+ buf := bytes.NewBuffer(make([]byte, 0, 32))
+ buf.WriteString(fmt.Sprintf("{Pmsi: type: %s,", p.TunnelType))
+ if p.IsLeafInfoRequired {
+ buf.WriteString(" leaf-info-required,")
+ }
+ buf.WriteString(fmt.Sprintf(" 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 {
+ t := BGP_ATTR_TYPE_PMSI_TUNNEL
+ return &PathAttributePmsiTunnel{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ IsLeafInfoRequired: isLeafInfoRequired,
+ TunnelType: typ,
+ Label: label,
+ TunnelID: id,
+ }
+}
+
+type AigpTLVType uint8
+
+const (
+ AIGP_TLV_UNKNOWN AigpTLVType = iota
+ AIGP_TLV_IGP_METRIC
+)
+
+type AigpTLV interface {
+ Serialize() ([]byte, error)
+ String() string
+ MarshalJSON() ([]byte, error)
+ Type() AigpTLVType
+}
+
+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
+}
+
+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
+}
+
+type PathAttributeAigp struct {
+ PathAttribute
+ Values []AigpTLV
+}
+
+func (p *PathAttributeAigp) DecodeFromBytes(data []byte) error {
+ err := p.PathAttribute.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ rest := p.PathAttribute.Value
+ values := make([]AigpTLV, 0)
+
+ for {
+ if len(rest) < 3 {
+ break
+ }
+ typ := rest[0]
+ length := binary.BigEndian.Uint16(rest[1:3])
+ if len(rest) < int(length) {
+ break
+ }
+ v := rest[3:length]
+ switch AigpTLVType(typ) {
+ case AIGP_TLV_IGP_METRIC:
+ if len(v) < 8 {
+ break
+ }
+ metric := binary.BigEndian.Uint64(v)
+ values = append(values, NewAigpTLVIgpMetric(metric))
+ default:
+ values = append(values, &AigpTLVDefault{AigpTLVType(typ), v})
+ }
+ rest = rest[length:]
+ if len(rest) == 0 {
+ p.Values = values
+ return nil
+ }
+ }
+ eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR)
+ eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST)
+ return NewMessageError(eCode, eSubCode, nil, "Aigp length is incorrect")
+}
+
+func (p *PathAttributeAigp) Serialize() ([]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...)
+ }
+ p.PathAttribute.Value = buf
+ return p.PathAttribute.Serialize()
+}
+
+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 []AigpTLV `json:"value"`
+ }{
+ Type: p.GetType(),
+ Value: p.Values,
+ })
+}
+
+func NewPathAttributeAigp(values []AigpTLV) *PathAttributeAigp {
+ t := BGP_ATTR_TYPE_AIGP
+ return &PathAttributeAigp{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ },
+ Values: values,
+ }
+}
+
+type PathAttributeOpaqueValue struct {
+ PathAttribute
+}
+
+func (p *PathAttributeOpaqueValue) String() string {
+ return fmt.Sprintf("{Value: %s}", string(p.Value))
+}
+
+func (p *PathAttributeOpaqueValue) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Type BGPAttrType `json:"type"`
+ Value string `json:"value"`
+ }{
+ Type: p.GetType(),
+ Value: string(p.Value),
+ })
+}
+
+func NewPathAttributeOpaqueValue(value []byte) *PathAttributeOpaqueValue {
+ t := BGP_ATTR_TYPE_OPAQUE_VALUE
+ return &PathAttributeOpaqueValue{
+ PathAttribute: PathAttribute{
+ Flags: pathAttrFlags[t],
+ Type: t,
+ Value: value,
+ },
+ }
+}
+
+type PathAttributeUnknown struct {
+ PathAttribute
+}
+
+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_AIGP:
+ return &PathAttributeAigp{}, nil
+ case BGP_ATTR_TYPE_OPAQUE_VALUE:
+ return &PathAttributeOpaqueValue{}, nil
+ }
+ return &PathAttributeUnknown{}, nil
+}
+
+type BGPUpdate struct {
+ WithdrawnRoutesLen uint16
+ WithdrawnRoutes []*IPAddrPrefix
+ TotalPathAttributeLen uint16
+ PathAttributes []PathAttributeInterface
+ NLRI []*IPAddrPrefix
+}
+
+func (msg *BGPUpdate) DecodeFromBytes(data []byte) 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[0:2])
+ data = data[2:]
+
+ // check withdrawn route
+ if len(data) < int(msg.WithdrawnRoutesLen) {
+ return NewMessageError(eCode, eSubCode, nil, "withdrawn route length exceeds message length")
+ }
+
+ msg.WithdrawnRoutes = make([]*IPAddrPrefix, 0, msg.WithdrawnRoutesLen)
+ for routelen := msg.WithdrawnRoutesLen; routelen > 0; {
+ w := &IPAddrPrefix{}
+ err := w.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ routelen -= uint16(w.Len())
+ if len(data) < w.Len() {
+ return NewMessageError(eCode, eSubCode, nil, "Withdrawn route length is short")
+ }
+ data = data[w.Len():]
+ msg.WithdrawnRoutes = append(msg.WithdrawnRoutes, w)
+ }
+
+ // 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[0: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")
+ }
+
+ msg.PathAttributes = []PathAttributeInterface{}
+ for pathlen := msg.TotalPathAttributeLen; pathlen > 0; {
+ p, err := GetPathAttribute(data)
+ if err != nil {
+ return err
+ }
+ err = p.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ pathlen -= uint16(p.Len())
+ if len(data) < p.Len() {
+ return NewMessageError(eCode, BGP_ERROR_SUB_ATTRIBUTE_LENGTH_ERROR, data, "attribute length is short")
+ }
+ data = data[p.Len():]
+ msg.PathAttributes = append(msg.PathAttributes, p)
+ }
+
+ msg.NLRI = make([]*IPAddrPrefix, 0)
+ for restlen := len(data); restlen > 0; {
+ n := &IPAddrPrefix{}
+ err := n.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ restlen -= n.Len()
+ if len(data) < n.Len() {
+ return NewMessageError(eCode, BGP_ERROR_SUB_INVALID_NETWORK_FIELD, nil, "NLRI length is short")
+ }
+ data = data[n.Len():]
+ msg.NLRI = append(msg.NLRI, n)
+ }
+
+ return nil
+}
+
+func (msg *BGPUpdate) Serialize() ([]byte, error) {
+ wbuf := make([]byte, 2)
+ for _, w := range msg.WithdrawnRoutes {
+ onewbuf, err := w.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ wbuf = append(wbuf, onewbuf...)
+ }
+ msg.WithdrawnRoutesLen = uint16(len(wbuf) - 2)
+ binary.BigEndian.PutUint16(wbuf, msg.WithdrawnRoutesLen)
+
+ pbuf := make([]byte, 2)
+ for _, p := range msg.PathAttributes {
+ onepbuf, err := p.Serialize()
+ 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.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, nbuf...)
+ }
+ return buf, nil
+}
+
+func (msg *BGPUpdate) IsEndOfRib() (bool, RouteFamily) {
+ 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)
+ return true, AfiSafiToRouteFamily(unreach.AFI, unreach.SAFI)
+ }
+ }
+ return false, RouteFamily(0)
+}
+
+func NewBGPUpdateMessage(withdrawnRoutes []*IPAddrPrefix, pathattrs []PathAttributeInterface, nlri []*IPAddrPrefix) *BGPMessage {
+ return &BGPMessage{
+ Header: BGPHeader{Type: BGP_MSG_UPDATE},
+ Body: &BGPUpdate{0, withdrawnRoutes, 0, pathattrs, nlri},
+ }
+}
+
+func NewEndOfRib(family RouteFamily) *BGPMessage {
+ if family == RF_IPv4_UC {
+ return NewBGPUpdateMessage(nil, nil, nil)
+ } else {
+ afi, safi := RouteFamilyToAfiSafi(family)
+ unreach := &PathAttributeMpUnreachNLRI{
+ AFI: afi,
+ SAFI: safi,
+ }
+ return NewBGPUpdateMessage(nil, []PathAttributeInterface{unreach}, nil)
+ }
+}
+
+type BGPNotification struct {
+ ErrorCode uint8
+ ErrorSubcode uint8
+ Data []byte
+}
+
+func (msg *BGPNotification) DecodeFromBytes(data []byte) error {
+ if len(data) < 2 {
+ return fmt.Errorf("Not all Notificaiton bytes available")
+ }
+ msg.ErrorCode = data[0]
+ msg.ErrorSubcode = data[1]
+ if len(data) > 2 {
+ msg.Data = data[2:]
+ }
+ return nil
+}
+
+func (msg *BGPNotification) Serialize() ([]byte, error) {
+ buf := make([]byte, 2)
+ 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},
+ }
+}
+
+type BGPKeepAlive struct {
+}
+
+func (msg *BGPKeepAlive) DecodeFromBytes(data []byte) error {
+ return nil
+}
+
+func (msg *BGPKeepAlive) Serialize() ([]byte, error) {
+ return nil, nil
+}
+
+func NewBGPKeepAliveMessage() *BGPMessage {
+ return &BGPMessage{
+ Header: BGPHeader{Len: 19, Type: BGP_MSG_KEEPALIVE},
+ Body: &BGPKeepAlive{},
+ }
+}
+
+type BGPRouteRefresh struct {
+ AFI uint16
+ Demarcation uint8
+ SAFI uint8
+}
+
+func (msg *BGPRouteRefresh) DecodeFromBytes(data []byte) error {
+ if len(data) < 4 {
+ return fmt.Errorf("Not all RouteRefresh bytes available")
+ }
+ msg.AFI = binary.BigEndian.Uint16(data[0:2])
+ msg.Demarcation = data[2]
+ msg.SAFI = data[3]
+ return nil
+}
+
+func (msg *BGPRouteRefresh) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint16(buf[0: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) error
+ Serialize() ([]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) error {
+ // minimum BGP message length
+ if uint16(len(data)) < BGP_HEADER_LENGTH {
+ return fmt.Errorf("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() ([]byte, error) {
+ buf := make([]byte, 19)
+ 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) (*BGPMessage, error) {
+ if len(data) < int(h.Len)-BGP_HEADER_LENGTH {
+ return nil, fmt.Errorf("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)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
+
+func ParseBGPMessage(data []byte) (*BGPMessage, error) {
+ h := &BGPHeader{}
+ err := h.DecodeFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ return parseBody(h, data[19:h.Len])
+}
+
+func ParseBGPBody(h *BGPHeader, data []byte) (*BGPMessage, error) {
+ return parseBody(h, data)
+}
+
+func (msg *BGPMessage) Serialize() ([]byte, error) {
+ b, err := msg.Body.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ if msg.Header.Len == 0 {
+ if 19+len(b) > BGP_MAX_MESSAGE_LENGTH {
+ return nil, NewMessageError(0, 0, nil, fmt.Sprintf("too long message length %d", 19+len(b)))
+ }
+ msg.Header.Len = 19 + uint16(len(b))
+ }
+ h, err := msg.Header.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ return append(h, b...), nil
+}
diff --git a/packet/bgp/bgp_test.go b/packet/bgp/bgp_test.go
new file mode 100644
index 00000000..29aa4e69
--- /dev/null
+++ b/packet/bgp/bgp_test.go
@@ -0,0 +1,565 @@
+package bgp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "github.com/stretchr/testify/assert"
+ "net"
+ "reflect"
+ "testing"
+)
+
+func keepalive() *BGPMessage {
+ return NewBGPKeepAliveMessage()
+}
+
+func notification() *BGPMessage {
+ return NewBGPNotificationMessage(1, 2, nil)
+}
+
+func refresh() *BGPMessage {
+ return NewBGPRouteRefreshMessage(1, 2, 10)
+}
+
+func open() *BGPMessage {
+ p1 := NewOptionParameterCapability(
+ []ParameterCapabilityInterface{NewCapRouteRefresh()})
+ p2 := NewOptionParameterCapability(
+ []ParameterCapabilityInterface{NewCapMultiProtocol(RF_IPv4_UC)})
+ g := &CapGracefulRestartTuple{4, 2, 3}
+ p3 := NewOptionParameterCapability(
+ []ParameterCapabilityInterface{NewCapGracefulRestart(false, 100,
+ []*CapGracefulRestartTuple{g})})
+ p4 := NewOptionParameterCapability(
+ []ParameterCapabilityInterface{NewCapFourOctetASNumber(100000)})
+ p5 := NewOptionParameterCapability(
+ []ParameterCapabilityInterface{NewCapAddPath(RF_IPv4_UC, BGP_ADD_PATH_BOTH)})
+ return NewBGPOpenMessage(11033, 303, "100.4.10.3",
+ []OptionParameterInterface{p1, p2, p3, p4, p5})
+}
+
+func update() *BGPMessage {
+ w1 := NewIPAddrPrefix(23, "121.1.3.2")
+ w2 := NewIPAddrPrefix(17, "100.33.3.0")
+ w := []*IPAddrPrefix{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
+
+ ecommunities := []ExtendedCommunityInterface{
+ NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, 10003, 3<<20, isTransitive),
+ NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, 1<<20, 300, isTransitive),
+ NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, "192.2.1.2", 3000, isTransitive),
+ &OpaqueExtended{
+ Value: &DefaultOpaqueExtendedValue{[]byte{255, 1, 2, 3, 4, 5, 6, 7}},
+ },
+ &OpaqueExtended{
+ Value: &ValidationExtended{Value: VALIDATION_STATE_INVALID},
+ },
+ &UnknownExtended{Type: 99, Value: []byte{0, 1, 2, 3, 4, 5, 6, 7}},
+ NewESILabelExtended(1000, true),
+ NewESImportRouteTarget("11:22:33:44:55:66"),
+ NewMacMobilityExtended(123, false),
+ }
+
+ mp_nlri := []AddrPrefixInterface{
+ NewLabeledVPNIPAddrPrefix(20, "192.0.9.0", *NewMPLSLabelStack(1, 2, 3),
+ NewRouteDistinguisherTwoOctetAS(256, 10000)),
+ NewLabeledVPNIPAddrPrefix(26, "192.10.8.192", *NewMPLSLabelStack(5, 6, 7, 8),
+ NewRouteDistinguisherIPAddressAS("10.0.1.1", 10001)),
+ }
+
+ mp_nlri2 := []AddrPrefixInterface{NewIPv6AddrPrefix(100,
+ "fe80:1234:1234:5667:8967:af12:8912:1023")}
+
+ mp_nlri3 := []AddrPrefixInterface{NewLabeledVPNIPv6AddrPrefix(100,
+ "fe80:1234:1234:5667:8967:af12:1203:33a1", *NewMPLSLabelStack(5, 6),
+ NewRouteDistinguisherFourOctetAS(5, 6))}
+
+ mp_nlri4 := []AddrPrefixInterface{NewLabeledIPAddrPrefix(25, "192.168.0.0",
+ *NewMPLSLabelStack(5, 6, 7))}
+
+ mac, _ := net.ParseMAC("01:23:45:67:89:ab")
+ mp_nlri5 := []AddrPrefixInterface{
+ NewEVPNNLRI(EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY, 0,
+ &EVPNEthernetAutoDiscoveryRoute{NewRouteDistinguisherFourOctetAS(5, 6),
+ EthernetSegmentIdentifier{ESI_ARBITRARY, make([]byte, 9)}, 2, 2}),
+ NewEVPNNLRI(EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, 0,
+ &EVPNMacIPAdvertisementRoute{NewRouteDistinguisherFourOctetAS(5, 6),
+ EthernetSegmentIdentifier{ESI_ARBITRARY, make([]byte, 9)}, 3, 48,
+ mac, 32, net.ParseIP("192.2.1.2"),
+ []uint32{3, 4}}),
+ NewEVPNNLRI(EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, 0,
+ &EVPNMulticastEthernetTagRoute{NewRouteDistinguisherFourOctetAS(5, 6), 3, 32, net.ParseIP("192.2.1.2")}),
+ NewEVPNNLRI(EVPN_ETHERNET_SEGMENT_ROUTE, 0,
+ &EVPNEthernetSegmentRoute{NewRouteDistinguisherFourOctetAS(5, 6),
+ EthernetSegmentIdentifier{ESI_ARBITRARY, make([]byte, 9)},
+ 32, net.ParseIP("192.2.1.1")}),
+ }
+
+ p := []PathAttributeInterface{
+ NewPathAttributeOrigin(3),
+ NewPathAttributeAsPath(aspath1),
+ NewPathAttributeAsPath(aspath2),
+ NewPathAttributeNextHop("129.1.1.2"),
+ NewPathAttributeMultiExitDisc(1 << 20),
+ NewPathAttributeLocalPref(1 << 22),
+ NewPathAttributeAtomicAggregate(),
+ NewPathAttributeAggregator(uint16(30002), "129.0.2.99"),
+ NewPathAttributeAggregator(uint32(30002), "129.0.2.99"),
+ NewPathAttributeAggregator(uint32(300020), "129.0.2.99"),
+ NewPathAttributeCommunities([]uint32{1, 3}),
+ NewPathAttributeOriginatorId("10.10.0.1"),
+ NewPathAttributeClusterList([]string{"10.10.0.2", "10.10.0.3"}),
+ NewPathAttributeExtendedCommunities(ecommunities),
+ NewPathAttributeAs4Path(aspath3),
+ NewPathAttributeAs4Aggregator(10000, "112.22.2.1"),
+ NewPathAttributeMpReachNLRI("112.22.2.0", mp_nlri),
+ NewPathAttributeMpReachNLRI("1023::", mp_nlri2),
+ NewPathAttributeMpReachNLRI("fe80::", mp_nlri3),
+ NewPathAttributeMpReachNLRI("129.1.1.1", mp_nlri4),
+ NewPathAttributeMpReachNLRI("129.1.1.1", mp_nlri5),
+ NewPathAttributeMpUnreachNLRI(mp_nlri),
+ //NewPathAttributeMpReachNLRI("112.22.2.0", []AddrPrefixInterface{}),
+ //NewPathAttributeMpUnreachNLRI([]AddrPrefixInterface{}),
+ &PathAttributeUnknown{
+ PathAttribute: PathAttribute{
+ Flags: BGP_ATTR_FLAG_TRANSITIVE,
+ Type: 100,
+ Value: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ },
+ },
+ }
+ n := []*IPAddrPrefix{NewIPAddrPrefix(24, "13.2.3.1")}
+ return NewBGPUpdateMessage(w, p, n)
+}
+
+func Test_Message(t *testing.T) {
+ l := []*BGPMessage{keepalive(), notification(), refresh(), open(), update()}
+ for _, m1 := range l {
+ buf1, _ := m1.Serialize()
+ t.Log("LEN =", len(buf1))
+ m2, err := ParseBGPMessage(buf1)
+ if err != nil {
+ t.Error(err)
+ }
+ // FIXME: shouldn't but workaround for some structs.
+ buf2, _ := m2.Serialize()
+
+ if reflect.DeepEqual(m1, m2) == true {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), m1, buf1)
+ t.Error(len(buf2), m2, buf2)
+ }
+ }
+}
+
+func Test_IPAddrPrefixString(t *testing.T) {
+ ipv4 := NewIPAddrPrefix(24, "129.6.10.0")
+ assert.Equal(t, "129.6.10.0/24", ipv4.String())
+ ipv6 := NewIPv6AddrPrefix(18, "3343:faba:3903::1")
+ assert.Equal(t, "3343:faba:3903::1/18", ipv6.String())
+ ipv6 = NewIPv6AddrPrefix(18, "3343:faba:3903::0")
+ assert.Equal(t, "3343:faba:3903::/18", ipv6.String())
+}
+
+func Test_RouteTargetMembershipNLRIString(t *testing.T) {
+ assert := assert.New(t)
+
+ // TwoOctetAsSpecificExtended
+ buf := make([]byte, 13)
+ buf[0] = 12
+ binary.BigEndian.PutUint32(buf[1:5], 65546)
+ buf[5] = byte(EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC) // typehigh
+ binary.BigEndian.PutUint16(buf[7:9], 65000)
+ binary.BigEndian.PutUint32(buf[9:], 65546)
+ r := &RouteTargetMembershipNLRI{}
+ err := r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:65000:65546", r.String())
+ buf, err = r.Serialize()
+ assert.Equal(nil, err)
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:65000:65546", r.String())
+
+ // IPv4AddressSpecificExtended
+ buf = make([]byte, 13)
+ binary.BigEndian.PutUint32(buf[1:5], 65546)
+ buf[5] = byte(EC_TYPE_TRANSITIVE_IP4_SPECIFIC) // typehigh
+ ip := net.ParseIP("10.0.0.1").To4()
+ copy(buf[7:11], []byte(ip))
+ binary.BigEndian.PutUint16(buf[11:], 65000)
+ r = &RouteTargetMembershipNLRI{}
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:10.0.0.1:65000", r.String())
+ buf, err = r.Serialize()
+ assert.Equal(nil, err)
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:10.0.0.1:65000", r.String())
+
+ // FourOctetAsSpecificExtended
+ buf = make([]byte, 13)
+ binary.BigEndian.PutUint32(buf[1:5], 65546)
+ buf[5] = byte(EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC) // typehigh
+ buf[6] = byte(EC_SUBTYPE_ROUTE_TARGET) // subtype
+ binary.BigEndian.PutUint32(buf[7:], 65546)
+ binary.BigEndian.PutUint16(buf[11:], 65000)
+ r = &RouteTargetMembershipNLRI{}
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:1.10:65000", r.String())
+ buf, err = r.Serialize()
+ assert.Equal(nil, err)
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:1.10:65000", r.String())
+
+ // OpaqueExtended
+ buf = make([]byte, 13)
+ binary.BigEndian.PutUint32(buf[1:5], 65546)
+ buf[5] = byte(EC_TYPE_TRANSITIVE_OPAQUE) // typehigh
+ binary.BigEndian.PutUint32(buf[9:], 1000000)
+ r = &RouteTargetMembershipNLRI{}
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:1000000", r.String())
+ buf, err = r.Serialize()
+ assert.Equal(nil, err)
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:1000000", r.String())
+
+ // Unknown
+ buf = make([]byte, 13)
+ binary.BigEndian.PutUint32(buf[1:5], 65546)
+ buf[5] = 0x04 // typehigh
+ binary.BigEndian.PutUint32(buf[9:], 1000000)
+ r = &RouteTargetMembershipNLRI{}
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:1000000", r.String())
+ buf, err = r.Serialize()
+ assert.Equal(nil, err)
+ err = r.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal("65546:1000000", r.String())
+
+}
+
+func Test_RFC5512(t *testing.T) {
+ assert := assert.New(t)
+
+ buf := make([]byte, 8)
+ buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
+ buf[1] = byte(EC_SUBTYPE_COLOR)
+ binary.BigEndian.PutUint32(buf[4:], 1000000)
+ ec, err := ParseExtended(buf)
+ assert.Equal(nil, err)
+ assert.Equal("1000000", ec.String())
+ buf, err = ec.Serialize()
+ assert.Equal(nil, err)
+ assert.Equal([]byte{0x3, 0xb, 0x0, 0x0, 0x0, 0xf, 0x42, 0x40}, buf)
+
+ buf = make([]byte, 8)
+ buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE)
+ buf[1] = byte(EC_SUBTYPE_ENCAPSULATION)
+ binary.BigEndian.PutUint16(buf[6:], uint16(TUNNEL_TYPE_VXLAN))
+ ec, err = ParseExtended(buf)
+ assert.Equal(nil, err)
+ assert.Equal("VXLAN", ec.String())
+ buf, err = ec.Serialize()
+ assert.Equal(nil, err)
+ assert.Equal([]byte{0x3, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, buf)
+
+ subTlv := &TunnelEncapSubTLV{
+ Type: ENCAP_SUBTLV_TYPE_COLOR,
+ Value: &TunnelEncapSubTLVColor{10},
+ }
+
+ tlv := &TunnelEncapTLV{
+ Type: TUNNEL_TYPE_VXLAN,
+ Value: []*TunnelEncapSubTLV{subTlv},
+ }
+
+ attr := NewPathAttributeTunnelEncap([]*TunnelEncapTLV{tlv})
+
+ buf1, err := attr.Serialize()
+ assert.Equal(nil, err)
+
+ p, err := GetPathAttribute(buf1)
+ assert.Equal(nil, err)
+
+ err = p.DecodeFromBytes(buf1)
+ assert.Equal(nil, err)
+
+ buf2, err := p.Serialize()
+ assert.Equal(nil, err)
+ assert.Equal(buf1, buf2)
+
+ n1 := NewEncapNLRI("10.0.0.1")
+ buf1, err = n1.Serialize()
+ assert.Equal(nil, err)
+
+ n2 := NewEncapNLRI("")
+ err = n2.DecodeFromBytes(buf1)
+ assert.Equal(nil, err)
+ assert.Equal("10.0.0.1", n2.String())
+
+ n1 = NewEncapNLRI("2001::1")
+ buf1, err = n1.Serialize()
+ assert.Equal(nil, err)
+
+ n2 = NewEncapNLRI("")
+ err = n2.DecodeFromBytes(buf1)
+ assert.Equal(nil, err)
+ assert.Equal("2001::1", n2.String())
+}
+
+func Test_ASLen(t *testing.T) {
+ assert := assert.New(t)
+
+ aspath := AsPathParam{
+ Num: 2,
+ AS: []uint16{65000, 65001},
+ }
+ aspath.Type = BGP_ASPATH_ATTR_TYPE_SEQ
+ assert.Equal(2, aspath.ASLen())
+
+ aspath.Type = BGP_ASPATH_ATTR_TYPE_SET
+ assert.Equal(1, aspath.ASLen())
+
+ aspath.Type = BGP_ASPATH_ATTR_TYPE_CONFED_SEQ
+ assert.Equal(0, aspath.ASLen())
+
+ aspath.Type = BGP_ASPATH_ATTR_TYPE_CONFED_SET
+ assert.Equal(0, aspath.ASLen())
+
+ as4path := As4PathParam{
+ Num: 2,
+ AS: []uint32{65000, 65001},
+ }
+ as4path.Type = BGP_ASPATH_ATTR_TYPE_SEQ
+ assert.Equal(2, as4path.ASLen())
+
+ as4path.Type = BGP_ASPATH_ATTR_TYPE_SET
+ assert.Equal(1, as4path.ASLen())
+
+ as4path.Type = BGP_ASPATH_ATTR_TYPE_CONFED_SEQ
+ assert.Equal(0, as4path.ASLen())
+
+ as4path.Type = BGP_ASPATH_ATTR_TYPE_CONFED_SET
+ assert.Equal(0, as4path.ASLen())
+
+}
+
+func Test_MPLSLabelStack(t *testing.T) {
+ assert := assert.New(t)
+ mpls := NewMPLSLabelStack()
+ buf, err := mpls.Serialize()
+ assert.Nil(err)
+ assert.Equal(true, bytes.Equal(buf, []byte{0, 0, 1}))
+
+ mpls = &MPLSLabelStack{}
+ assert.Nil(mpls.DecodeFromBytes(buf))
+ assert.Equal(1, len(mpls.Labels))
+ assert.Equal(uint32(0), mpls.Labels[0])
+
+ mpls = NewMPLSLabelStack(WITHDRAW_LABEL)
+ buf, err = mpls.Serialize()
+ assert.Nil(err)
+ assert.Equal(true, bytes.Equal(buf, []byte{128, 0, 0}))
+
+ mpls = &MPLSLabelStack{}
+ assert.Nil(mpls.DecodeFromBytes(buf))
+ assert.Equal(1, len(mpls.Labels))
+ assert.Equal(WITHDRAW_LABEL, mpls.Labels[0])
+}
+
+func Test_FlowSpecNlri(t *testing.T) {
+ assert := assert.New(t)
+ cmp := make([]FlowSpecComponentInterface, 0)
+ cmp = append(cmp, NewFlowSpecDestinationPrefix(NewIPAddrPrefix(24, "10.0.0.0")))
+ cmp = append(cmp, NewFlowSpecSourcePrefix(NewIPAddrPrefix(24, "10.0.0.0")))
+ eq := 0x1
+ gt := 0x2
+ lt := 0x4
+ and := 0x40
+ not := 0x2
+ item1 := NewFlowSpecComponentItem(eq, TCP)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_IP_PROTO, []*FlowSpecComponentItem{item1}))
+ item2 := NewFlowSpecComponentItem(gt|eq, 20)
+ item3 := NewFlowSpecComponentItem(and|lt|eq, 30)
+ item4 := NewFlowSpecComponentItem(eq, 10)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_PORT, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_DST_PORT, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_SRC_PORT, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ICMP_TYPE, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ICMP_CODE, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_PKT_LEN, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_DSCP, []*FlowSpecComponentItem{item2, item3, item4}))
+ isFlagment := 0x02
+ item5 := NewFlowSpecComponentItem(isFlagment, 0)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_FRAGMENT, []*FlowSpecComponentItem{item5}))
+ item6 := NewFlowSpecComponentItem(0, TCP_FLAG_ACK)
+ item7 := NewFlowSpecComponentItem(and|not, TCP_FLAG_URGENT)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, []*FlowSpecComponentItem{item6, item7}))
+ n1 := NewFlowSpecIPv4Unicast(cmp)
+ buf1, err := n1.Serialize()
+ assert.Nil(err)
+ n2, err := NewPrefixFromRouteFamily(RouteFamilyToAfiSafi(RF_FS_IPv4_UC))
+ assert.Nil(err)
+ err = n2.DecodeFromBytes(buf1)
+ assert.Nil(err)
+ buf2, _ := n2.Serialize()
+ if reflect.DeepEqual(n1, n2) == true {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), n1, buf1)
+ t.Error(len(buf2), n2, buf2)
+ t.Log(bytes.Equal(buf1, buf2))
+ }
+}
+
+func Test_FlowSpecExtended(t *testing.T) {
+ assert := assert.New(t)
+ exts := make([]ExtendedCommunityInterface, 0)
+ exts = append(exts, NewTrafficRateExtended(100, 9600.0))
+ exts = append(exts, NewTrafficActionExtended(true, false))
+ exts = append(exts, NewRedirectTwoOctetAsSpecificExtended(1000, 1000))
+ exts = append(exts, NewRedirectIPv4AddressSpecificExtended("10.0.0.1", 1000))
+ exts = append(exts, NewRedirectFourOctetAsSpecificExtended(10000000, 1000))
+ exts = append(exts, NewTrafficRemarkExtended(10))
+ m1 := NewPathAttributeExtendedCommunities(exts)
+ buf1, err := m1.Serialize()
+ assert.Nil(err)
+ m2 := NewPathAttributeExtendedCommunities(nil)
+ err = m2.DecodeFromBytes(buf1)
+ assert.Nil(err)
+ buf2, _ := m2.Serialize()
+ if reflect.DeepEqual(m1, m2) == true {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), m1, buf1)
+ t.Error(len(buf2), m2, buf2)
+ }
+}
+
+func Test_FlowSpecNlriv6(t *testing.T) {
+ assert := assert.New(t)
+ cmp := make([]FlowSpecComponentInterface, 0)
+ cmp = append(cmp, NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(64, "2001::"), 12))
+ cmp = append(cmp, NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(64, "2001::"), 12))
+ eq := 0x1
+ gt := 0x2
+ lt := 0x4
+ and := 0x40
+ not := 0x2
+ item1 := NewFlowSpecComponentItem(eq, TCP)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_IP_PROTO, []*FlowSpecComponentItem{item1}))
+ item2 := NewFlowSpecComponentItem(gt|eq, 20)
+ item3 := NewFlowSpecComponentItem(and|lt|eq, 30)
+ item4 := NewFlowSpecComponentItem(eq, 10)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_PORT, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_DST_PORT, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_SRC_PORT, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ICMP_TYPE, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ICMP_CODE, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_PKT_LEN, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_DSCP, []*FlowSpecComponentItem{item2, item3, item4}))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_LABEL, []*FlowSpecComponentItem{item2, item3, item4}))
+ isFlagment := 0x02
+ item5 := NewFlowSpecComponentItem(isFlagment, 0)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_FRAGMENT, []*FlowSpecComponentItem{item5}))
+ item6 := NewFlowSpecComponentItem(0, TCP_FLAG_ACK)
+ item7 := NewFlowSpecComponentItem(and|not, TCP_FLAG_URGENT)
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, []*FlowSpecComponentItem{item6, item7}))
+ n1 := NewFlowSpecIPv6Unicast(cmp)
+ buf1, err := n1.Serialize()
+ assert.Nil(err)
+ n2, err := NewPrefixFromRouteFamily(RouteFamilyToAfiSafi(RF_FS_IPv6_UC))
+ assert.Nil(err)
+ err = n2.DecodeFromBytes(buf1)
+ assert.Nil(err)
+ buf2, _ := n2.Serialize()
+ if reflect.DeepEqual(n1, n2) == true {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), n1, buf1)
+ t.Error(len(buf2), n2, buf2)
+ t.Log(bytes.Equal(buf1, buf2))
+ }
+}
+
+func Test_Aigp(t *testing.T) {
+ assert := assert.New(t)
+ m := NewAigpTLVIgpMetric(1000)
+ a1 := NewPathAttributeAigp([]AigpTLV{m})
+ buf1, err := a1.Serialize()
+ assert.Nil(err)
+ a2 := NewPathAttributeAigp(nil)
+ err = a2.DecodeFromBytes(buf1)
+ assert.Nil(err)
+ buf2, _ := a2.Serialize()
+ if reflect.DeepEqual(a1, a2) == true {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), a1, buf1)
+ t.Error(len(buf2), a2, buf2)
+ t.Log(bytes.Equal(buf1, buf2))
+ }
+}
+
+func Test_FlowSpecNlriL2(t *testing.T) {
+ assert := assert.New(t)
+ mac, _ := net.ParseMAC("01:23:45:67:89:ab")
+ cmp := make([]FlowSpecComponentInterface, 0)
+ cmp = append(cmp, NewFlowSpecDestinationMac(mac))
+ cmp = append(cmp, NewFlowSpecSourceMac(mac))
+ eq := 0x1
+ item1 := NewFlowSpecComponentItem(eq, int(IPv4))
+ cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ETHERNET_TYPE, []*FlowSpecComponentItem{item1}))
+ n1 := NewFlowSpecL2VPN(cmp)
+ buf1, err := n1.Serialize()
+ assert.Nil(err)
+ n2, err := NewPrefixFromRouteFamily(RouteFamilyToAfiSafi(RF_FS_L2_VPN))
+ assert.Nil(err)
+ err = n2.DecodeFromBytes(buf1)
+ assert.Nil(err)
+ buf2, _ := n2.Serialize()
+ if reflect.DeepEqual(n1, n2) == true {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), n1, buf1)
+ t.Error(len(buf2), n2, buf2)
+ t.Log(bytes.Equal(buf1, buf2))
+ }
+ fmt.Println(n1, n2)
+}
diff --git a/packet/bgp/bgpattrtype_string.go b/packet/bgp/bgpattrtype_string.go
new file mode 100644
index 00000000..1a2cf1d0
--- /dev/null
+++ b/packet/bgp/bgpattrtype_string.go
@@ -0,0 +1,28 @@
+// generated by stringer -type BGPAttrType bgp.go; DO NOT EDIT
+
+package bgp
+
+import "fmt"
+
+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"
+)
+
+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}
+)
+
+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]]
+ default:
+ return fmt.Sprintf("BGPAttrType(%d)", i)
+ }
+}
diff --git a/packet/bgp/bgpcapabilitycode_string.go b/packet/bgp/bgpcapabilitycode_string.go
new file mode 100644
index 00000000..0dc70a87
--- /dev/null
+++ b/packet/bgp/bgpcapabilitycode_string.go
@@ -0,0 +1,40 @@
+// generated by stringer -type=BGPCapabilityCode; DO NOT EDIT
+
+package bgp
+
+import "fmt"
+
+const (
+ _BGPCapabilityCode_name_0 = "BGP_CAP_MULTIPROTOCOLBGP_CAP_ROUTE_REFRESH"
+ _BGPCapabilityCode_name_1 = "BGP_CAP_CARRYING_LABEL_INFO"
+ _BGPCapabilityCode_name_2 = "BGP_CAP_GRACEFUL_RESTARTBGP_CAP_FOUR_OCTET_AS_NUMBER"
+ _BGPCapabilityCode_name_3 = "BGP_CAP_ENHANCED_ROUTE_REFRESH"
+ _BGPCapabilityCode_name_4 = "BGP_CAP_ROUTE_REFRESH_CISCO"
+)
+
+var (
+ _BGPCapabilityCode_index_0 = [...]uint8{0, 21, 42}
+ _BGPCapabilityCode_index_1 = [...]uint8{0, 27}
+ _BGPCapabilityCode_index_2 = [...]uint8{0, 24, 52}
+ _BGPCapabilityCode_index_3 = [...]uint8{0, 30}
+ _BGPCapabilityCode_index_4 = [...]uint8{0, 27}
+)
+
+func (i BGPCapabilityCode) String() string {
+ switch {
+ case 1 <= i && i <= 2:
+ i -= 1
+ return _BGPCapabilityCode_name_0[_BGPCapabilityCode_index_0[i]:_BGPCapabilityCode_index_0[i+1]]
+ case i == 4:
+ return _BGPCapabilityCode_name_1
+ case 64 <= i && i <= 65:
+ i -= 64
+ return _BGPCapabilityCode_name_2[_BGPCapabilityCode_index_2[i]:_BGPCapabilityCode_index_2[i+1]]
+ case i == 70:
+ return _BGPCapabilityCode_name_3
+ case i == 128:
+ return _BGPCapabilityCode_name_4
+ default:
+ return fmt.Sprintf("BGPCapabilityCode(%d)", i)
+ }
+}
diff --git a/packet/bgp/bmp.go b/packet/bgp/bmp.go
new file mode 100644
index 00000000..4813aff9
--- /dev/null
+++ b/packet/bgp/bmp.go
@@ -0,0 +1,609 @@
+// 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 bgp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+ "net"
+)
+
+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
+)
+
+func (h *BMPHeader) DecodeFromBytes(data []byte) error {
+ 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
+ IsPostPolicy bool
+ PeerDistinguisher uint64
+ PeerAddress net.IP
+ PeerAS uint32
+ PeerBGPID net.IP
+ Timestamp float64
+ Flags uint8
+}
+
+func NewBMPPeerHeader(t uint8, policy bool, dist uint64, address string, as uint32, id string, stamp float64) *BMPPeerHeader {
+ h := &BMPPeerHeader{
+ PeerType: t,
+ IsPostPolicy: policy,
+ PeerDistinguisher: dist,
+ PeerAS: as,
+ PeerBGPID: net.ParseIP(id).To4(),
+ Timestamp: stamp,
+ }
+ if policy == true {
+ h.Flags |= (1 << 6)
+ }
+ if net.ParseIP(address).To4() != nil {
+ h.PeerAddress = net.ParseIP(address).To4()
+ } else {
+ h.PeerAddress = net.ParseIP(address).To16()
+ h.Flags |= (1 << 7)
+ }
+ return h
+}
+
+func (h *BMPPeerHeader) DecodeFromBytes(data []byte) error {
+ h.PeerType = data[0]
+ h.Flags = data[1]
+ if h.Flags&(1<<6) != 0 {
+ h.IsPostPolicy = true
+ } else {
+ h.IsPostPolicy = false
+ }
+ h.PeerDistinguisher = binary.BigEndian.Uint64(data[2:10])
+ if h.Flags&(1<<7) != 0 {
+ h.PeerAddress = net.IP(data[10:26]).To16()
+ } else {
+ h.PeerAddress = net.IP(data[22:26]).To4()
+ }
+ h.PeerAS = binary.BigEndian.Uint32(data[26:30])
+ h.PeerBGPID = 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&(1<<7) != 0 {
+ copy(buf[10:26], h.PeerAddress)
+ } else {
+ copy(buf[22:26], h.PeerAddress.To4())
+ }
+ binary.BigEndian.PutUint32(buf[26:30], h.PeerAS)
+ copy(buf[30:34], h.PeerBGPID)
+ 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 *BGPMessage
+ BGPUpdatePayload []byte
+}
+
+func NewBMPRouteMonitoring(p BMPPeerHeader, update *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) error {
+ update, err := ParseBGPMessage(data)
+ if err != nil {
+ return err
+ }
+ body.BGPUpdate = update
+ return nil
+}
+
+func (body *BMPRouteMonitoring) Serialize() ([]byte, error) {
+ if body.BGPUpdatePayload != nil {
+ return body.BGPUpdatePayload, nil
+ }
+ return body.BGPUpdate.Serialize()
+}
+
+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
+)
+
+type BMPStatsTLV struct {
+ Type uint16
+ Length uint16
+ Value uint64
+}
+
+type BMPStatisticsReport struct {
+ Count uint32
+ Stats []BMPStatsTLV
+}
+
+const (
+ BMP_PEER_DOWN_REASON_UNKNOWN = 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
+)
+
+type BMPPeerDownNotification struct {
+ Reason uint8
+ BGPNotification *BGPMessage
+ Data []byte
+}
+
+func NewBMPPeerDownNotification(p BMPPeerHeader, reason uint8, notification *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
+ default:
+ b.Data = data
+ }
+ 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) 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 := ParseBGPMessage(data)
+ if err != nil {
+ return err
+ }
+ body.BGPNotification = notification
+ } else {
+ body.Data = data
+ }
+ return nil
+}
+
+func (body *BMPPeerDownNotification) Serialize() ([]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()
+ 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 net.IP
+ LocalPort uint16
+ RemotePort uint16
+ SentOpenMsg *BGPMessage
+ ReceivedOpenMsg *BGPMessage
+}
+
+func NewBMPPeerUpNotification(p BMPPeerHeader, lAddr string, lPort, rPort uint16, sent, recv *BGPMessage) *BMPMessage {
+ b := &BMPPeerUpNotification{
+ LocalPort: lPort,
+ RemotePort: rPort,
+ SentOpenMsg: sent,
+ ReceivedOpenMsg: recv,
+ }
+ addr := net.ParseIP(lAddr)
+ if addr.To4() != nil {
+ b.LocalAddress = addr.To4()
+ } else {
+ b.LocalAddress = addr.To16()
+ }
+ 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) error {
+ if msg.PeerHeader.Flags&(1<<7) != 0 {
+ body.LocalAddress = net.IP(data[:16]).To16()
+ } else {
+ body.LocalAddress = net.IP(data[12:16]).To4()
+ }
+
+ body.LocalPort = binary.BigEndian.Uint16(data[16:18])
+ body.RemotePort = binary.BigEndian.Uint16(data[18:20])
+
+ data = data[20:]
+ sentopen, err := ParseBGPMessage(data)
+ if err != nil {
+ return err
+ }
+ body.SentOpenMsg = sentopen
+ data = data[body.SentOpenMsg.Header.Len:]
+ body.ReceivedOpenMsg, err = ParseBGPMessage(data)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (body *BMPPeerUpNotification) Serialize() ([]byte, error) {
+ buf := make([]byte, 20)
+ if body.LocalAddress.To4() != nil {
+ copy(buf[12:16], body.LocalAddress.To4())
+ } else {
+ copy(buf[:16], body.LocalAddress.To16())
+ }
+
+ binary.BigEndian.PutUint16(buf[16:18], body.LocalPort)
+ binary.BigEndian.PutUint16(buf[18:20], body.RemotePort)
+
+ m, _ := body.SentOpenMsg.Serialize()
+ buf = append(buf, m...)
+ m, _ = body.ReceivedOpenMsg.Serialize()
+ buf = append(buf, m...)
+ return buf, nil
+}
+
+func (body *BMPStatisticsReport) ParseBody(msg *BMPMessage, data []byte) error {
+ body.Count = binary.BigEndian.Uint32(data[0:4])
+ data = data[4:]
+ for len(data) >= 4 {
+ s := BMPStatsTLV{}
+ s.Type = binary.BigEndian.Uint16(data[0:2])
+ s.Length = binary.BigEndian.Uint16(data[2:4])
+ data = data[4:]
+ if len(data) < int(s.Length) {
+ break
+ }
+ if s.Type == BMP_STAT_TYPE_ADJ_RIB_IN || s.Type == BMP_STAT_TYPE_LOC_RIB {
+ if s.Length < 8 {
+ break
+ }
+ s.Value = binary.BigEndian.Uint64(data[:8])
+ } else {
+ if s.Length < 4 {
+ break
+ }
+ s.Value = uint64(binary.BigEndian.Uint32(data[:4]))
+ }
+ body.Stats = append(body.Stats, s)
+ data = data[s.Length:]
+ }
+ return nil
+}
+
+func (body *BMPStatisticsReport) Serialize() ([]byte, error) {
+ // TODO
+ buf := make([]byte, 4)
+ body.Count = uint32(len(body.Stats))
+ binary.BigEndian.PutUint32(buf[0:4], body.Count)
+
+ return buf, nil
+}
+
+type BMPTLV struct {
+ Type uint16
+ Length uint16
+ Value []byte
+}
+
+func NewBMPTLV(t uint16, v []byte) *BMPTLV {
+ return &BMPTLV{
+ Type: t,
+ Length: uint16(len(v)),
+ Value: v,
+ }
+}
+
+func (tlv *BMPTLV) DecodeFromBytes(data []byte) error {
+ //TODO: check data length
+ tlv.Type = binary.BigEndian.Uint16(data[0:2])
+ tlv.Length = binary.BigEndian.Uint16(data[2:4])
+ tlv.Value = data[4 : 4+tlv.Length]
+ return nil
+}
+
+func (tlv *BMPTLV) Serialize() ([]byte, error) {
+ if tlv.Length == 0 {
+ tlv.Length = uint16(len(tlv.Value))
+ }
+ buf := make([]byte, 4+tlv.Length)
+ binary.BigEndian.PutUint16(buf[0:2], tlv.Type)
+ binary.BigEndian.PutUint16(buf[2:4], tlv.Length)
+ copy(buf[4:], tlv.Value)
+ return buf, nil
+}
+
+func (tlv *BMPTLV) Len() int {
+ return 4 + int(tlv.Length)
+}
+
+type BMPInitiation struct {
+ Info []BMPTLV
+}
+
+func NewBMPInitiation(info []BMPTLV) *BMPMessage {
+ return &BMPMessage{
+ Header: BMPHeader{
+ Version: BMP_VERSION,
+ Type: BMP_MSG_INITIATION,
+ },
+ Body: &BMPInitiation{
+ Info: info,
+ },
+ }
+}
+
+func (body *BMPInitiation) ParseBody(msg *BMPMessage, data []byte) error {
+ for len(data) > 0 {
+ tlv := BMPTLV{}
+ tlv.DecodeFromBytes(data)
+ body.Info = append(body.Info, tlv)
+ data = data[tlv.Len():]
+ }
+ return nil
+}
+
+func (body *BMPInitiation) Serialize() ([]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 BMPTermination struct {
+ Info []BMPTLV
+}
+
+func NewBMPTermination(info []BMPTLV) *BMPMessage {
+ return &BMPMessage{
+ Header: BMPHeader{
+ Version: BMP_VERSION,
+ Type: BMP_MSG_TERMINATION,
+ },
+ Body: &BMPTermination{
+ Info: info,
+ },
+ }
+}
+
+func (body *BMPTermination) ParseBody(msg *BMPMessage, data []byte) error {
+ for len(data) > 0 {
+ tlv := BMPTLV{}
+ tlv.DecodeFromBytes(data)
+ body.Info = append(body.Info, tlv)
+ data = data[tlv.Len():]
+ }
+ return nil
+}
+
+func (body *BMPTermination) Serialize() ([]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) error
+ Serialize() ([]byte, error)
+}
+
+type BMPMessage struct {
+ Header BMPHeader
+ PeerHeader BMPPeerHeader
+ Body BMPBody
+}
+
+func (msg *BMPMessage) Serialize() ([]byte, error) {
+ buf := make([]byte, 0)
+ if msg.Header.Type != BMP_MSG_INITIATION {
+ p, err := msg.PeerHeader.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, p...)
+ }
+
+ b, err := msg.Body.Serialize()
+ 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
+)
+
+func ParseBMPMessage(data []byte) (*BMPMessage, error) {
+ 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{}
+ }
+
+ if msg.Header.Type != BMP_MSG_INITIATION {
+ msg.PeerHeader.DecodeFromBytes(data)
+ data = data[BMP_PEER_HEADER_SIZE:]
+ }
+
+ err = msg.Body.ParseBody(msg, data)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
+
+type MessageError struct {
+ TypeCode uint8
+ SubTypeCode uint8
+ Data []byte
+ Message string
+}
+
+func NewMessageError(typeCode, subTypeCode uint8, data []byte, msg string) error {
+ return &MessageError{
+ TypeCode: typeCode,
+ SubTypeCode: subTypeCode,
+ Data: data,
+ Message: msg,
+ }
+}
+
+func (e *MessageError) Error() string {
+ return e.Message
+}
+
+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
+ }
+
+ msg := &BMPMessage{}
+ msg.Header.DecodeFromBytes(data)
+ if uint32(len(data)) < msg.Header.Length {
+ return 0, nil, nil
+ }
+
+ return int(msg.Header.Length), data[0:msg.Header.Length], nil
+}
diff --git a/packet/bgp/bmp_test.go b/packet/bgp/bmp_test.go
new file mode 100644
index 00000000..e07c2455
--- /dev/null
+++ b/packet/bgp/bmp_test.go
@@ -0,0 +1,73 @@
+// 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 bgp
+
+import (
+ "github.com/stretchr/testify/assert"
+ "reflect"
+ "testing"
+)
+
+func verify(t *testing.T, m1 *BMPMessage) {
+ buf1, _ := m1.Serialize()
+ m2, err := ParseBMPMessage(buf1)
+ if err != nil {
+ t.Error(err)
+ }
+ buf2, _ := m2.Serialize()
+
+ if reflect.DeepEqual(m1, m2) == true {
+ t.Log("OK")
+ } else {
+ t.Error("Something wrong")
+ t.Error(len(buf1), m1, buf1)
+ t.Error(len(buf2), m2, buf2)
+ }
+}
+
+func Test_Initiation(t *testing.T) {
+ verify(t, NewBMPInitiation(nil))
+ tlv := NewBMPTLV(1, []byte{0x3, 0xb, 0x0, 0x0, 0x0, 0xf, 0x42, 0x40})
+ m := NewBMPInitiation([]BMPTLV{*tlv})
+ verify(t, m)
+}
+
+func Test_PeerUpNotification(t *testing.T) {
+ m := open()
+ p0 := NewBMPPeerHeader(0, false, 1000, "10.0.0.1", 70000, "10.0.0.2", 1)
+ verify(t, NewBMPPeerUpNotification(*p0, "10.0.0.3", 10, 100, m, m))
+ p1 := NewBMPPeerHeader(0, false, 1000, "fe80::6e40:8ff:feab:2c2a", 70000, "10.0.0.2", 1)
+ verify(t, NewBMPPeerUpNotification(*p1, "fe80::6e40:8ff:feab:2c2a", 10, 100, m, m))
+}
+
+func Test_PeerDownNotification(t *testing.T) {
+ p0 := NewBMPPeerHeader(0, false, 1000, "10.0.0.1", 70000, "10.0.0.2", 1)
+ verify(t, NewBMPPeerDownNotification(*p0, BMP_PEER_DOWN_REASON_UNKNOWN, nil, []byte{0x3, 0xb}))
+ m := NewBGPNotificationMessage(1, 2, nil)
+ verify(t, NewBMPPeerDownNotification(*p0, BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION, m, nil))
+}
+
+func Test_RouteMonitoring(t *testing.T) {
+ m := update()
+ p0 := NewBMPPeerHeader(0, false, 1000, "fe80::6e40:8ff:feab:2c2a", 70000, "10.0.0.2", 1)
+ verify(t, NewBMPRouteMonitoring(*p0, m))
+}
+
+func Test_BogusHeader(t *testing.T) {
+ h, err := ParseBMPMessage(make([]byte, 10))
+ assert.Nil(t, h)
+ assert.NotNil(t, err)
+}
diff --git a/packet/bgp/constant.go b/packet/bgp/constant.go
new file mode 100644
index 00000000..aa9b17b9
--- /dev/null
+++ b/packet/bgp/constant.go
@@ -0,0 +1,194 @@
+// 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 (
+ "fmt"
+ "strings"
+)
+
+const AS_TRANS = 23456
+
+const BGP_PORT = 179
+
+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",
+}
+
+var ProtocolValueMap = map[string]Protocol{
+ ProtocolNameMap[ICMP]: ICMP,
+ ProtocolNameMap[IGMP]: IGMP,
+ ProtocolNameMap[TCP]: TCP,
+ ProtocolNameMap[EGP]: EGP,
+ ProtocolNameMap[IGP]: IGP,
+ ProtocolNameMap[UDP]: UDP,
+ ProtocolNameMap[RSVP]: RSVP,
+ ProtocolNameMap[GRE]: GRE,
+ ProtocolNameMap[OSPF]: OSPF,
+ ProtocolNameMap[IPIP]: IPIP,
+ ProtocolNameMap[PIM]: PIM,
+ ProtocolNameMap[SCTP]: SCTP,
+}
+
+func (p Protocol) String() string {
+ name, ok := ProtocolNameMap[p]
+ if !ok {
+ return fmt.Sprintf("%d", p)
+ }
+ return name
+}
+
+type TCPFlag int
+
+const (
+ TCP_FLAG_FIN = 0x01
+ TCP_FLAG_SYN = 0x02
+ TCP_FLAG_RST = 0x04
+ TCP_FLAG_PUSH = 0x08
+ TCP_FLAG_ACK = 0x10
+ TCP_FLAG_URGENT = 0x20
+)
+
+var TCPFlagNameMap = map[TCPFlag]string{
+ TCP_FLAG_FIN: "fin",
+ TCP_FLAG_SYN: "syn",
+ TCP_FLAG_RST: "rst",
+ TCP_FLAG_PUSH: "push",
+ TCP_FLAG_ACK: "ack",
+ TCP_FLAG_URGENT: "urgent",
+}
+
+var TCPFlagValueMap = map[string]TCPFlag{
+ TCPFlagNameMap[TCP_FLAG_FIN]: TCP_FLAG_FIN,
+ TCPFlagNameMap[TCP_FLAG_SYN]: TCP_FLAG_SYN,
+ TCPFlagNameMap[TCP_FLAG_RST]: TCP_FLAG_RST,
+ TCPFlagNameMap[TCP_FLAG_PUSH]: TCP_FLAG_PUSH,
+ TCPFlagNameMap[TCP_FLAG_ACK]: TCP_FLAG_ACK,
+ TCPFlagNameMap[TCP_FLAG_URGENT]: TCP_FLAG_URGENT,
+}
+
+func (f TCPFlag) String() string {
+ ss := make([]string, 0, 6)
+ for _, v := range []TCPFlag{TCP_FLAG_FIN, TCP_FLAG_SYN, TCP_FLAG_RST, TCP_FLAG_PUSH, TCP_FLAG_ACK, TCP_FLAG_URGENT} {
+ if f&v > 0 {
+ ss = append(ss, TCPFlagNameMap[v])
+ }
+ }
+ return strings.Join(ss, "|")
+}
+
+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",
+}
+
+var EthernetTypeValueMap = map[string]EthernetType{
+ EthernetTypeNameMap[IPv4]: IPv4,
+ EthernetTypeNameMap[ARP]: ARP,
+ EthernetTypeNameMap[RARP]: RARP,
+ EthernetTypeNameMap[VMTP]: VMTP,
+ EthernetTypeNameMap[APPLE_TALK]: APPLE_TALK,
+ EthernetTypeNameMap[AARP]: AARP,
+ EthernetTypeNameMap[IPX]: IPX,
+ EthernetTypeNameMap[SNMP]: SNMP,
+ EthernetTypeNameMap[NET_BIOS]: NET_BIOS,
+ EthernetTypeNameMap[XTP]: XTP,
+ EthernetTypeNameMap[IPv6]: IPv6,
+ EthernetTypeNameMap[PPPoE_DISCOVERY]: PPPoE_DISCOVERY,
+ EthernetTypeNameMap[PPPoE_SESSION]: PPPoE_SESSION,
+ EthernetTypeNameMap[LOOPBACK]: LOOPBACK,
+}
+
+func (t EthernetType) String() string {
+ n, ok := EthernetTypeNameMap[t]
+ if !ok {
+ return fmt.Sprintf("%d", t)
+ }
+ return n
+}
diff --git a/packet/bgp/esitype_string.go b/packet/bgp/esitype_string.go
new file mode 100644
index 00000000..5651bda8
--- /dev/null
+++ b/packet/bgp/esitype_string.go
@@ -0,0 +1,16 @@
+// generated by stringer -type=ESIType bgp.go validate.go; DO NOT EDIT
+
+package bgp
+
+import "fmt"
+
+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+1 >= ESIType(len(_ESIType_index)) {
+ return fmt.Sprintf("ESIType(%d)", i)
+ }
+ return _ESIType_name[_ESIType_index[i]:_ESIType_index[i+1]]
+}
diff --git a/packet/bgp/fsmstate_string.go b/packet/bgp/fsmstate_string.go
new file mode 100644
index 00000000..4416afc1
--- /dev/null
+++ b/packet/bgp/fsmstate_string.go
@@ -0,0 +1,16 @@
+// generated by stringer -type=FSMState -output=fsmstate_string.go bgp.go validate.go mrt.go rtr.go constant.go bmp.go esitype_string.go bgpattrtype_string.go; DO NOT EDIT
+
+package bgp
+
+import "fmt"
+
+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 fmt.Sprintf("FSMState(%d)", i)
+ }
+ return _FSMState_name[_FSMState_index[i]:_FSMState_index[i+1]]
+}
diff --git a/packet/bgp/mrt.go b/packet/bgp/mrt.go
new file mode 100644
index 00000000..15dbc839
--- /dev/null
+++ b/packet/bgp/mrt.go
@@ -0,0 +1,798 @@
+// 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 bgp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math"
+ "net"
+ "time"
+)
+
+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
+)
+
+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
+)
+
+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
+)
+
+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 []interface{}) ([]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
+}
+
+func (h *MRTHeader) DecodeFromBytes(data []byte) error {
+ if len(data) < MRT_COMMON_HEADER_LEN {
+ return fmt.Errorf("not all MRTHeader bytes are available. expected: %d, actual: %d", MRT_COMMON_HEADER_LEN, len(data))
+ }
+ 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])
+ return nil
+}
+
+func (h *MRTHeader) Serialize() ([]byte, error) {
+ return packValues([]interface{}{h.Timestamp, h.Type, h.SubType, h.Len})
+}
+
+func NewMRTHeader(timestamp uint32, t MRTType, subtype MRTSubTyper, l uint32) (*MRTHeader, error) {
+ return &MRTHeader{
+ Timestamp: timestamp,
+ Type: t,
+ SubType: subtype.ToUint16(),
+ Len: l,
+ }, nil
+}
+
+func (h *MRTHeader) GetTime() time.Time {
+ t := int64(h.Timestamp)
+ return time.Unix(t, 0)
+}
+
+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 uint32, 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 {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+}
+
+type Peer struct {
+ Type uint8
+ BgpId net.IP
+ IpAddress net.IP
+ AS uint32
+}
+
+func (p *Peer) DecodeFromBytes(data []byte) ([]byte, error) {
+ notAllBytesAvail := fmt.Errorf("not all Peer bytes are available")
+ if len(data) < 5 {
+ return nil, notAllBytesAvail
+ }
+ p.Type = uint8(data[0])
+ p.BgpId = net.IP(data[1:5])
+ data = data[5:]
+
+ if p.Type&1 > 0 {
+ if len(data) < 16 {
+ return nil, notAllBytesAvail
+ }
+ p.IpAddress = net.IP(data[:16])
+ data = data[16:]
+ } else {
+ if len(data) < 4 {
+ return nil, notAllBytesAvail
+ }
+ p.IpAddress = net.IP(data[:4])
+ data = data[4:]
+ }
+
+ if p.Type&(1<<1) > 0 {
+ if len(data) < 4 {
+ return nil, notAllBytesAvail
+ }
+ p.AS = binary.BigEndian.Uint32(data[:4])
+ data = data[4:]
+ } else {
+ if len(data) < 2 {
+ return nil, notAllBytesAvail
+ }
+ 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] = uint8(p.Type)
+ copy(buf[1:], p.BgpId.To4())
+ if p.Type&1 > 0 {
+ buf = append(buf, p.IpAddress.To16()...)
+ } else {
+ buf = append(buf, p.IpAddress.To4()...)
+ }
+ if p.Type&(1<<1) > 0 {
+ bbuf, err = packValues([]interface{}{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([]interface{}{uint16(p.AS)})
+ }
+ if err != nil {
+ return nil, err
+ }
+ return append(buf, bbuf...), nil
+}
+
+func NewPeer(bgpid string, ipaddr string, asn uint32, isAS4 bool) *Peer {
+ t := 0
+ addr := net.ParseIP(ipaddr).To4()
+ if addr == nil {
+ t |= 1
+ addr = net.ParseIP(ipaddr).To16()
+ }
+ if isAS4 {
+ t |= (1 << 1)
+ }
+ return &Peer{
+ Type: uint8(t),
+ BgpId: net.ParseIP(bgpid).To4(),
+ IpAddress: addr,
+ 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 net.IP
+ ViewName string
+ Peers []*Peer
+}
+
+func (t *PeerIndexTable) DecodeFromBytes(data []byte) error {
+ notAllBytesAvail := fmt.Errorf("not all PeerIndexTable bytes are available")
+ if len(data) < 6 {
+ return notAllBytesAvail
+ }
+ t.CollectorBgpId = net.IP(data[:4])
+ viewLen := binary.BigEndian.Uint16(data[4:6])
+ if len(data) < 6+int(viewLen) {
+ return notAllBytesAvail
+ }
+ t.ViewName = string(data[6 : 6+viewLen])
+
+ data = data[6+viewLen:]
+
+ if len(data) < 2 {
+ return notAllBytesAvail
+ }
+ peerNum := binary.BigEndian.Uint16(data[:2])
+ data = data[2:]
+ t.Peers = make([]*Peer, 0, peerNum)
+ var err error
+ for i := 0; i < int(peerNum); i++ {
+ p := &Peer{}
+ data, err = p.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ t.Peers = append(t.Peers, p)
+ }
+
+ return nil
+}
+
+func (t *PeerIndexTable) Serialize() ([]byte, error) {
+ buf := make([]byte, 8+len(t.ViewName))
+ copy(buf, t.CollectorBgpId.To4())
+ 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 string, viewname string, peers []*Peer) *PeerIndexTable {
+ return &PeerIndexTable{
+ CollectorBgpId: net.ParseIP(bgpid).To4(),
+ 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
+ PathAttributes []PathAttributeInterface
+}
+
+func (e *RibEntry) DecodeFromBytes(data []byte) ([]byte, error) {
+ notAllBytesAvail := fmt.Errorf("not all RibEntry bytes are available")
+ if len(data) < 8 {
+ return nil, notAllBytesAvail
+ }
+ e.PeerIndex = binary.BigEndian.Uint16(data[:2])
+ e.OriginatedTime = binary.BigEndian.Uint32(data[2:6])
+ totalLen := binary.BigEndian.Uint16(data[6:8])
+ data = data[8:]
+ for attrLen := totalLen; attrLen > 0; {
+ p, err := GetPathAttribute(data)
+ if err != nil {
+ return nil, err
+ }
+ err = p.DecodeFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ attrLen -= uint16(p.Len())
+ if len(data) < p.Len() {
+ return nil, notAllBytesAvail
+ }
+ data = data[p.Len():]
+ e.PathAttributes = append(e.PathAttributes, p)
+ }
+ return data, nil
+}
+
+func (e *RibEntry) Serialize() ([]byte, error) {
+ buf := make([]byte, 8)
+ binary.BigEndian.PutUint16(buf, e.PeerIndex)
+ binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime)
+ totalLen := 0
+ binary.BigEndian.PutUint16(buf[6:], uint16(totalLen))
+ for _, pattr := range e.PathAttributes {
+ // TODO special modification is needed for MP_REACH_NLRI
+ // but also Quagga doesn't implement this.
+ //
+ // RFC 6396 4.3.4
+ // There is one exception to the encoding of BGP attributes for the BGP
+ // MP_REACH_NLRI attribute (BGP Type Code 14).
+ // Since the AFI, SAFI, and NLRI information is already encoded
+ // in the RIB Entry Header or RIB_GENERIC Entry Header,
+ // only the Next Hop Address Length and Next Hop Address fields are included.
+
+ bbuf, err := pattr.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, bbuf...)
+ totalLen += len(bbuf)
+ }
+ binary.BigEndian.PutUint16(buf[6:], uint16(totalLen))
+ return buf, nil
+}
+
+func NewRibEntry(index uint16, time uint32, pathattrs []PathAttributeInterface) *RibEntry {
+ return &RibEntry{
+ PeerIndex: index,
+ OriginatedTime: time,
+ PathAttributes: pathattrs,
+ }
+}
+
+func (e *RibEntry) String() string {
+ return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathAttrs [%v]", e.PeerIndex, e.OriginatedTime, e.PathAttributes)
+}
+
+type Rib struct {
+ SequenceNumber uint32
+ Prefix AddrPrefixInterface
+ Entries []*RibEntry
+ RouteFamily RouteFamily
+}
+
+func (u *Rib) DecodeFromBytes(data []byte) error {
+ if len(data) < 4 {
+ return fmt.Errorf("Not all RibIpv4Unicast message bytes available")
+ }
+ u.SequenceNumber = binary.BigEndian.Uint32(data[:4])
+ data = data[4:]
+ afi, safi := RouteFamilyToAfiSafi(u.RouteFamily)
+ if afi == 0 && safi == 0 {
+ afi = binary.BigEndian.Uint16(data[:2])
+ safi = data[2]
+ data = data[3:]
+ }
+ prefix, err := NewPrefixFromRouteFamily(afi, safi)
+ if err != nil {
+ return err
+ }
+ err = prefix.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ u.Prefix = prefix
+ data = data[prefix.Len():]
+ entryNum := binary.BigEndian.Uint16(data[:2])
+ data = data[2:]
+ u.Entries = make([]*RibEntry, 0, entryNum)
+ for i := 0; i < int(entryNum); i++ {
+ e := &RibEntry{}
+ data, err = e.DecodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ u.Entries = append(u.Entries, e)
+ }
+ return nil
+}
+
+func (u *Rib) Serialize() ([]byte, error) {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, u.SequenceNumber)
+ rf := AfiSafiToRouteFamily(u.Prefix.AFI(), u.Prefix.SAFI())
+ switch rf {
+ case RF_IPv4_UC, RF_IPv4_MC, RF_IPv6_UC, RF_IPv6_MC:
+ default:
+ bbuf := make([]byte, 0, 2)
+ binary.BigEndian.PutUint16(bbuf, u.Prefix.AFI())
+ buf = append(buf, bbuf...)
+ buf = append(buf, u.Prefix.SAFI())
+ }
+ bbuf, err := u.Prefix.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, bbuf...)
+ bbuf, err = packValues([]interface{}{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, prefix AddrPrefixInterface, entries []*RibEntry) *Rib {
+ rf := AfiSafiToRouteFamily(prefix.AFI(), prefix.SAFI())
+ return &Rib{
+ SequenceNumber: seq,
+ Prefix: prefix,
+ Entries: entries,
+ RouteFamily: rf,
+ }
+}
+
+func (u *Rib) String() string {
+ return fmt.Sprintf("RIB: Seq [%d] Prefix [%s] Entries [%s]", u.SequenceNumber, u.Prefix, u.Entries)
+}
+
+type BGP4MPHeader struct {
+ PeerAS uint32
+ LocalAS uint32
+ InterfaceIndex uint16
+ AddressFamily uint16
+ PeerIpAddress net.IP
+ LocalIpAddress net.IP
+ isAS4 bool
+}
+
+func (m *BGP4MPHeader) decodeFromBytes(data []byte) ([]byte, error) {
+ if m.isAS4 && len(data) < 8 {
+ return nil, fmt.Errorf("Not all BGP4MPMessageAS4 bytes available")
+ } else if !m.isAS4 && len(data) < 4 {
+ return nil, fmt.Errorf("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 AFI_IP:
+ m.PeerIpAddress = net.IP(data[4:8]).To4()
+ m.LocalIpAddress = net.IP(data[8:12]).To4()
+ data = data[12:]
+ case AFI_IP6:
+ m.PeerIpAddress = net.IP(data[4:20])
+ m.LocalIpAddress = net.IP(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 []interface{}
+ if m.isAS4 {
+ values = []interface{}{m.PeerAS, m.LocalAS, m.InterfaceIndex, m.AddressFamily}
+ } else {
+ values = []interface{}{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 AFI_IP:
+ bbuf = make([]byte, 8)
+ copy(bbuf, m.PeerIpAddress.To4())
+ copy(bbuf[4:], m.LocalIpAddress.To4())
+ case AFI_IP6:
+ bbuf = make([]byte, 32)
+ copy(bbuf, m.PeerIpAddress)
+ copy(bbuf[16:], m.LocalIpAddress)
+ 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 string, isAS4 bool) (*BGP4MPHeader, error) {
+ var af uint16
+ paddr := net.ParseIP(peerip).To4()
+ laddr := net.ParseIP(localip).To4()
+ if paddr != nil && laddr != nil {
+ af = AFI_IP
+ } else {
+ paddr = net.ParseIP(peerip).To16()
+ laddr = net.ParseIP(localip).To16()
+ if paddr != nil && laddr != nil {
+ af = 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: paddr,
+ LocalIpAddress: laddr,
+ isAS4: isAS4,
+ }, nil
+}
+
+type BGP4MPStateChange struct {
+ *BGP4MPHeader
+ OldState BGPState
+ NewState BGPState
+}
+
+func (m *BGP4MPStateChange) DecodeFromBytes(data []byte) error {
+ rest, err := m.decodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+ if len(rest) < 4 {
+ return 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 nil
+}
+
+func (m *BGP4MPStateChange) Serialize() ([]byte, error) {
+ buf, err := m.serialize()
+ if err != nil {
+ return nil, err
+ }
+ bbuf, err := packValues([]interface{}{m.OldState, m.NewState})
+ if err != nil {
+ return nil, err
+ }
+ return append(buf, bbuf...), nil
+}
+
+func NewBGP4MPStateChange(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, oldstate, newstate BGPState) *BGP4MPStateChange {
+ header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
+ return &BGP4MPStateChange{
+ BGP4MPHeader: header,
+ OldState: oldstate,
+ NewState: newstate,
+ }
+}
+
+type BGP4MPMessage struct {
+ *BGP4MPHeader
+ BGPMessage *BGPMessage
+ BGPMessagePayload []byte
+ isLocal bool
+}
+
+func (m *BGP4MPMessage) DecodeFromBytes(data []byte) error {
+ rest, err := m.decodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if len(rest) < BGP_HEADER_LENGTH {
+ return fmt.Errorf("Not all BGP4MPMessageAS4 bytes available")
+ }
+
+ msg, err := ParseBGPMessage(rest)
+ if err != nil {
+ return err
+ }
+ m.BGPMessage = msg
+ return 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 string, isAS4 bool, msg *BGPMessage) *BGP4MPMessage {
+ header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
+ return &BGP4MPMessage{
+ BGP4MPHeader: header,
+ BGPMessage: msg,
+ }
+}
+
+func NewBGP4MPMessageLocal(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, msg *BGPMessage) *BGP4MPMessage {
+ header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
+ return &BGP4MPMessage{
+ BGP4MPHeader: header,
+ BGPMessage: msg,
+ isLocal: true,
+ }
+}
+
+func (m *BGP4MPMessage) String() string {
+ title := "BGP4MP_MSG"
+ if m.isAS4 {
+ title += "_AS4"
+ }
+ if m.isLocal {
+ title += "_LOCAL"
+ }
+ 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
+ }
+ //this reads the data
+ hdr := &MRTHeader{}
+ errh := hdr.DecodeFromBytes(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[0:totlen], nil
+}
+
+func ParseMRTBody(h *MRTHeader, data []byte) (*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))
+ }
+ msg := &MRTMessage{Header: *h}
+ switch h.Type {
+ case TABLE_DUMPv2:
+ subType := MRTSubTypeTableDumpv2(h.SubType)
+ rf := RouteFamily(0)
+ switch subType {
+ case PEER_INDEX_TABLE:
+ msg.Body = &PeerIndexTable{}
+ case RIB_IPV4_UNICAST:
+ rf = RF_IPv4_UC
+ case RIB_IPV4_MULTICAST:
+ rf = RF_IPv4_MC
+ case RIB_IPV6_UNICAST:
+ rf = RF_IPv6_UC
+ case RIB_IPV6_MULTICAST:
+ rf = RF_IPv6_MC
+ case RIB_GENERIC:
+ default:
+ return nil, fmt.Errorf("unsupported table dumpv2 subtype: %v\n", subType)
+ }
+
+ if subType != PEER_INDEX_TABLE {
+ msg.Body = &Rib{
+ RouteFamily: rf,
+ }
+ }
+ case BGP4MP:
+ subType := MRTSubTypeBGP4MP(h.SubType)
+ isAS4 := true
+ switch subType {
+ case STATE_CHANGE:
+ isAS4 = false
+ fallthrough
+ case STATE_CHANGE_AS4:
+ msg.Body = &BGP4MPStateChange{
+ BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4},
+ }
+ case MESSAGE:
+ isAS4 = false
+ fallthrough
+ case MESSAGE_AS4:
+ msg.Body = &BGP4MPMessage{
+ BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4},
+ }
+ case MESSAGE_LOCAL:
+ isAS4 = false
+ fallthrough
+ case MESSAGE_AS4_LOCAL:
+ msg.Body = &BGP4MPMessage{
+ BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4},
+ isLocal: true,
+ }
+ default:
+ return nil, fmt.Errorf("unsupported bgp4mp subtype: %v\n", subType)
+ }
+ default:
+ return nil, fmt.Errorf("unsupported type: %v\n", h.Type)
+ }
+ err := msg.Body.DecodeFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
diff --git a/packet/bgp/mrt_test.go b/packet/bgp/mrt_test.go
new file mode 100644
index 00000000..11a233ec
--- /dev/null
+++ b/packet/bgp/mrt_test.go
@@ -0,0 +1,220 @@
+// 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 bgp
+
+import (
+ "bufio"
+ "bytes"
+ "github.com/stretchr/testify/assert"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestMrtHdr(t *testing.T) {
+ h1, err := NewMRTHeader(10, TABLE_DUMPv2, RIB_IPV4_MULTICAST, 20)
+ if err != nil {
+ t.Fatal(err)
+ }
+ b1, err := h1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ h2 := &MRTHeader{}
+ err = h2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(h1, h2), true)
+}
+
+func TestMrtHdrTime(t *testing.T) {
+ h1, err := NewMRTHeader(10, TABLE_DUMPv2, RIB_IPV4_MULTICAST, 20)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ttime := time.Unix(10, 0)
+ htime := h1.GetTime()
+ t.Logf("this timestamp should be 10s after epoch:%v", htime)
+ assert.Equal(t, h1.GetTime(), ttime)
+}
+
+func testPeer(t *testing.T, p1 *Peer) {
+ b1, err := p1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ p2 := &Peer{}
+ rest, err := p2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, len(rest), 0)
+ assert.Equal(t, reflect.DeepEqual(p1, p2), true)
+}
+
+func TestMrtPeer(t *testing.T) {
+ p := NewPeer("192.168.0.1", "10.0.0.1", 65000, false)
+ testPeer(t, p)
+}
+
+func TestMrtPeerv6(t *testing.T) {
+ p := NewPeer("192.168.0.1", "2001::1", 65000, false)
+ testPeer(t, p)
+}
+
+func TestMrtPeerAS4(t *testing.T) {
+ p := NewPeer("192.168.0.1", "2001::1", 135500, true)
+ testPeer(t, p)
+}
+
+func TestMrtPeerIndexTable(t *testing.T) {
+ p1 := NewPeer("192.168.0.1", "10.0.0.1", 65000, false)
+ p2 := NewPeer("192.168.0.1", "2001::1", 65000, false)
+ p3 := NewPeer("192.168.0.1", "2001::1", 135500, true)
+ pt1 := NewPeerIndexTable("192.168.0.1", "test", []*Peer{p1, p2, p3})
+ b1, err := pt1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ pt2 := &PeerIndexTable{}
+ err = pt2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(pt1, pt2), true)
+}
+
+func TestMrtRibEntry(t *testing.T) {
+ aspath1 := []AsPathParamInterface{
+ NewAsPathParam(2, []uint16{1000}),
+ NewAsPathParam(1, []uint16{1001, 1002}),
+ NewAsPathParam(2, []uint16{1003, 1004}),
+ }
+
+ p := []PathAttributeInterface{
+ NewPathAttributeOrigin(3),
+ NewPathAttributeAsPath(aspath1),
+ NewPathAttributeNextHop("129.1.1.2"),
+ NewPathAttributeMultiExitDisc(1 << 20),
+ NewPathAttributeLocalPref(1 << 22),
+ }
+
+ e1 := NewRibEntry(1, uint32(time.Now().Unix()), p)
+ b1, err := e1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ e2 := &RibEntry{}
+ rest, err := e2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, len(rest), 0)
+ assert.Equal(t, reflect.DeepEqual(e1, e2), true)
+}
+
+func TestMrtRib(t *testing.T) {
+ aspath1 := []AsPathParamInterface{
+ NewAsPathParam(2, []uint16{1000}),
+ NewAsPathParam(1, []uint16{1001, 1002}),
+ NewAsPathParam(2, []uint16{1003, 1004}),
+ }
+
+ p := []PathAttributeInterface{
+ NewPathAttributeOrigin(3),
+ NewPathAttributeAsPath(aspath1),
+ NewPathAttributeNextHop("129.1.1.2"),
+ NewPathAttributeMultiExitDisc(1 << 20),
+ NewPathAttributeLocalPref(1 << 22),
+ }
+
+ e1 := NewRibEntry(1, uint32(time.Now().Unix()), p)
+ e2 := NewRibEntry(2, uint32(time.Now().Unix()), p)
+ e3 := NewRibEntry(3, uint32(time.Now().Unix()), p)
+
+ r1 := NewRib(1, NewIPAddrPrefix(24, "192.168.0.0"), []*RibEntry{e1, e2, e3})
+ b1, err := r1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ r2 := &Rib{
+ RouteFamily: RF_IPv4_UC,
+ }
+ err = r2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(r1, r2), true)
+}
+
+func TestMrtBgp4mpStateChange(t *testing.T) {
+ c1 := NewBGP4MPStateChange(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, ACTIVE, ESTABLISHED)
+ b1, err := c1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ c2 := &BGP4MPStateChange{BGP4MPHeader: &BGP4MPHeader{}}
+ err = c2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = c2.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(c1, c2), true)
+}
+
+func TestMrtBgp4mpMessage(t *testing.T) {
+ msg := NewBGPKeepAliveMessage()
+ m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg)
+ b1, err := m1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ m2 := &BGP4MPMessage{BGP4MPHeader: &BGP4MPHeader{}}
+ err = m2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(m1, m2), true)
+}
+
+func TestMrtSplit(t *testing.T) {
+ var b bytes.Buffer
+ numwrite, numread := 10, 0
+ for i := 0; i < numwrite; i++ {
+ msg := NewBGPKeepAliveMessage()
+ m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg)
+ mm, _ := NewMRTMessage(1234, BGP4MP, MESSAGE, m1)
+ b1, err := mm.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ b.Write(b1)
+ }
+ t.Logf("wrote %d serialized MRT keepalives in the buffer", numwrite)
+ r := bytes.NewReader(b.Bytes())
+ scanner := bufio.NewScanner(r)
+ scanner.Split(SplitMrt)
+ for scanner.Scan() {
+ numread += 1
+ }
+ t.Logf("scanner scanned %d serialized keepalives from the buffer", numread)
+ assert.Equal(t, numwrite, numread)
+}
diff --git a/packet/bgp/rtr.go b/packet/bgp/rtr.go
new file mode 100644
index 00000000..30657e15
--- /dev/null
+++ b/packet/bgp/rtr.go
@@ -0,0 +1,392 @@
+// 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 bgp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+)
+
+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 {
+ 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 {
+ 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 {
+ 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 net.IP
+ AS uint32
+}
+
+func (m *RTRIPPrefix) DecodeFromBytes(data []byte) error {
+ 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 = net.IP(data[12:16]).To4()
+ m.AS = binary.BigEndian.Uint32(data[16:20])
+ } else {
+ m.Prefix = net.IP(data[12:28]).To16()
+ 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.To4())
+ binary.BigEndian.PutUint32(data[16:20], m.AS)
+ } else {
+ copy(data[12:28], m.Prefix.To16())
+ binary.BigEndian.PutUint32(data[28:32], m.AS)
+ }
+ return data, nil
+}
+
+func NewRTRIPPrefix(prefix net.IP, prefixLen, maxLen uint8, as uint32, flags uint8) *RTRIPPrefix {
+ var pduType uint8
+ var pduLen uint32
+ if prefix.To4() != nil && prefixLen <= 32 {
+ pduType = RTR_IPV4_PREFIX
+ pduLen = RTR_IPV4_PREFIX_LEN
+ } else {
+ pduType = RTR_IPV6_PREFIX
+ pduLen = RTR_IPV6_PREFIX_LEN
+ }
+
+ 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 {
+ 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])
+ 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 SplitRTR(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 || len(data) < RTR_MIN_LEN {
+ return 0, nil, nil
+ }
+
+ totalLen := binary.BigEndian.Uint32(data[4:8])
+ if totalLen < RTR_MIN_LEN {
+ return 0, nil, fmt.Errorf("Invalid length: %d", totalLen)
+ }
+ if uint32(len(data)) < totalLen {
+ return 0, nil, nil
+ }
+ return int(totalLen), data[0:totalLen], nil
+}
+
+func ParseRTR(data []byte) (RTRMessage, error) {
+ 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
+}
diff --git a/packet/bgp/rtr_test.go b/packet/bgp/rtr_test.go
new file mode 100644
index 00000000..0f9b9527
--- /dev/null
+++ b/packet/bgp/rtr_test.go
@@ -0,0 +1,122 @@
+// 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 bgp
+
+import (
+ "encoding/hex"
+ "math/rand"
+ "net"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func verifyRTRMessage(t *testing.T, m1 RTRMessage) {
+ buf1, _ := m1.Serialize()
+ m2, err := ParseRTR(buf1)
+ if err != nil {
+ t.Error(err)
+ }
+ buf2, _ := m2.Serialize()
+
+ if reflect.DeepEqual(buf1, buf2) == true {
+ t.Log("OK")
+ } else {
+ t.Errorf("Something wrong")
+ t.Error(len(buf1), m1, hex.EncodeToString(buf1))
+ t.Error(len(buf2), m2, hex.EncodeToString(buf2))
+ }
+}
+
+func randUint32() uint32 {
+ rand.Seed(time.Now().UnixNano())
+ return rand.Uint32()
+}
+
+func Test_RTRSerialNotify(t *testing.T) {
+ id := uint16(time.Now().Unix())
+ sn := randUint32()
+ verifyRTRMessage(t, NewRTRSerialNotify(id, sn))
+}
+
+func Test_RTRSerialQuery(t *testing.T) {
+ id := uint16(time.Now().Unix())
+ sn := randUint32()
+ verifyRTRMessage(t, NewRTRSerialQuery(id, sn))
+}
+
+func Test_RTRResetQuery(t *testing.T) {
+ verifyRTRMessage(t, NewRTRResetQuery())
+}
+
+func Test_RTRCacheResponse(t *testing.T) {
+ id := uint16(time.Now().Unix())
+ verifyRTRMessage(t, NewRTRCacheResponse(id))
+}
+
+type rtrIPPrefixTestCase struct {
+ pString string
+ pLen uint8
+ mLen uint8
+ asn uint32
+ flags uint8
+}
+
+var rtrIPPrefixTestCases = []rtrIPPrefixTestCase{
+ {"192.168.0.0", 16, 32, 65001, ANNOUNCEMENT},
+ {"192.168.0.0", 16, 32, 65001, WITHDRAWAL},
+ {"2001:db8::", 32, 128, 65001, ANNOUNCEMENT},
+ {"2001:db8::", 32, 128, 65001, WITHDRAWAL},
+ {"::ffff:0.0.0.0", 96, 128, 65001, ANNOUNCEMENT},
+ {"::ffff:0.0.0.0", 96, 128, 65001, WITHDRAWAL},
+}
+
+func Test_RTRIPPrefix(t *testing.T) {
+ for i := range rtrIPPrefixTestCases {
+ test := &rtrIPPrefixTestCases[i]
+ addr := net.ParseIP(test.pString)
+ verifyRTRMessage(t, NewRTRIPPrefix(addr, test.pLen, test.mLen, test.asn, test.flags))
+ }
+}
+
+func Test_RTREndOfData(t *testing.T) {
+ id := uint16(time.Now().Unix())
+ sn := randUint32()
+ verifyRTRMessage(t, NewRTREndOfData(id, sn))
+}
+
+func Test_RTRCacheReset(t *testing.T) {
+ verifyRTRMessage(t, NewRTRCacheReset())
+}
+
+func Test_RTRErrorReport(t *testing.T) {
+ errPDU, _ := NewRTRResetQuery().Serialize()
+ errText1 := []byte("Couldn't send CacheResponce PDU")
+ errText2 := []byte("Wrong Length of PDU: 10 bytes")
+
+ // See 5.10 ErrorReport in RFC6810
+ // when it doesn't have both "erroneous PDU" and "Arbitrary Text"
+ verifyRTRMessage(t, NewRTRErrorReport(NO_DATA_AVAILABLE, nil, nil))
+
+ // when it has "erroneous PDU"
+ verifyRTRMessage(t, NewRTRErrorReport(UNSUPPORTED_PROTOCOL_VERSION, errPDU, nil))
+
+ // when it has "ArbitaryText"
+ verifyRTRMessage(t, NewRTRErrorReport(INTERNAL_ERROR, nil, errText1))
+
+ // when it has both "erroneous PDU" and "Arbitrary Text"
+ verifyRTRMessage(t, NewRTRErrorReport(CORRUPT_DATA, errPDU, errText2))
+}
diff --git a/packet/bgp/validate.go b/packet/bgp/validate.go
new file mode 100644
index 00000000..73697c4d
--- /dev/null
+++ b/packet/bgp/validate.go
@@ -0,0 +1,221 @@
+package bgp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+ "strconv"
+)
+
+// Validator for BGPUpdate
+func ValidateUpdateMsg(m *BGPUpdate, rfs map[RouteFamily]bool, doConfedCheck bool) (bool, 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 avalible for session", RF_IPv4_UC))
+ }
+ }
+
+ seen := make(map[BGPAttrType]PathAttributeInterface)
+ // check path attribute
+ for _, a := range m.PathAttributes {
+ // check duplication
+ if _, ok := seen[a.GetType()]; !ok {
+ seen[a.GetType()] = a
+ } else {
+ eMsg := "the path attribute apears twice. Type : " + strconv.Itoa(int(a.GetType()))
+ return false, NewMessageError(eCode, eSubCodeAttrList, nil, eMsg)
+ }
+
+ //check specific path attribute
+ ok, e := ValidateAttribute(a, rfs, doConfedCheck)
+ if !ok {
+ return false, e
+ }
+ }
+
+ if 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, 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)}
+ return false, NewMessageError(eCode, eSubCodeMissing, data, eMsg)
+ }
+ }
+ return true, nil
+}
+
+func ValidateAttribute(a PathAttributeInterface, rfs map[RouteFamily]bool, doConfedCheck bool) (bool, 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(l []AddrPrefixInterface) bool {
+ for _, prefix := range l {
+ rf := AfiSafiToRouteFamily(prefix.AFI(), prefix.SAFI())
+ if _, ok := rfs[rf]; !ok {
+ return false
+ }
+ }
+ return true
+ }
+
+ switch p := a.(type) {
+ case *PathAttributeMpUnreachNLRI:
+ rf := AfiSafiToRouteFamily(p.AFI, p.SAFI)
+ if _, ok := rfs[rf]; !ok {
+ return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not avalible for session", rf))
+ }
+ if checkPrefix(p.Value) == false {
+ return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not avalible for session", rf))
+ }
+ case *PathAttributeMpReachNLRI:
+ rf := AfiSafiToRouteFamily(p.AFI, p.SAFI)
+ if _, ok := rfs[rf]; !ok {
+ return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not avalible for session", rf))
+ }
+ if checkPrefix(p.Value) == false {
+ return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not avalible for session", rf))
+ }
+ case *PathAttributeOrigin:
+ v := uint8(p.Value[0])
+ 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))
+ return false, NewMessageError(eCode, eSubCodeBadOrigin, data, eMsg)
+ }
+ case *PathAttributeNextHop:
+
+ isZero := func(ip net.IP) bool {
+ res := ip[0] & 0xff
+ return res == 0x00
+ }
+
+ isClassDorE := func(ip net.IP) bool {
+ res := ip[0] & 0xe0
+ return res == 0xe0
+ }
+
+ //check IP address represents host address
+ if p.Value.IsLoopback() || isZero(p.Value) || isClassDorE(p.Value) {
+ eMsg := "invalid nexthop address"
+ data, _ := a.Serialize()
+ return false, NewMessageError(eCode, eSubCodeBadNextHop, data, eMsg)
+ }
+ case *PathAttributeAsPath:
+ if doConfedCheck {
+ for _, paramIf := range p.Value {
+ var segType uint8
+ asParam, y := paramIf.(*As4PathParam)
+ if y {
+ segType = asParam.Type
+ } else {
+ segType = paramIf.(*AsPathParam).Type
+ }
+
+ if segType == BGP_ASPATH_ATTR_TYPE_CONFED_SET || segType == BGP_ASPATH_ATTR_TYPE_CONFED_SEQ {
+ return false, NewMessageError(eCode, eSubCodeMalformedAspath, nil, fmt.Sprintf("segment type confederation(%d) found", segType))
+ }
+ }
+ }
+
+ 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 true, nil
+
+}
+
+// validator for PathAttribute
+func ValidateFlags(t BGPAttrType, flags BGPAttrFlag) (bool, 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 false, 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 false, 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 false, 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 false, eMsg
+ }
+ }
+ return true, ""
+}
+
+func ValidateBGPMessage(m *BGPMessage) error {
+ if m.Header.Len > BGP_MAX_MESSAGE_LENGTH {
+ buf := make([]byte, 2)
+ binary.BigEndian.PutUint16(buf, m.Header.Len)
+ return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, buf, "too long length")
+ }
+
+ return nil
+}
+
+func ValidateOpenMsg(m *BGPOpen, expectedAS uint32) error {
+ if m.Version != 4 {
+ return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER, nil, fmt.Sprintf("upsuppored 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
+ }
+ }
+ }
+ if as != expectedAS {
+ return 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 NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME, nil, fmt.Sprintf("unacceptable hold time %d", m.HoldTime))
+ }
+ return nil
+}
diff --git a/packet/bgp/validate_test.go b/packet/bgp/validate_test.go
new file mode 100644
index 00000000..6309c74d
--- /dev/null
+++ b/packet/bgp/validate_test.go
@@ -0,0 +1,343 @@
+package bgp
+
+import (
+ "encoding/binary"
+ "github.com/stretchr/testify/assert"
+ "net"
+ "testing"
+)
+
+func bgpupdate() *BGPMessage {
+ aspath := []AsPathParamInterface{
+ NewAsPathParam(2, []uint16{65001}),
+ }
+
+ p := []PathAttributeInterface{
+ NewPathAttributeOrigin(1),
+ NewPathAttributeAsPath(aspath),
+ NewPathAttributeNextHop("192.168.1.1"),
+ }
+
+ n := []*IPAddrPrefix{NewIPAddrPrefix(24, "10.10.10.0")}
+ return NewBGPUpdateMessage(nil, p, n)
+}
+
+func bgpupdateV6() *BGPMessage {
+ aspath := []AsPathParamInterface{
+ NewAsPathParam(2, []uint16{65001}),
+ }
+
+ mp_nlri := []AddrPrefixInterface{NewIPv6AddrPrefix(100,
+ "fe80:1234:1234:5667:8967:af12:8912:1023")}
+
+ p := []PathAttributeInterface{
+ NewPathAttributeOrigin(1),
+ NewPathAttributeAsPath(aspath),
+ NewPathAttributeMpReachNLRI("1023::", mp_nlri),
+ }
+ return NewBGPUpdateMessage(nil, p, nil)
+}
+
+func Test_Validate_CapV4(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv6_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+
+ res, err = ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(true, res)
+}
+
+func Test_Validate_CapV6(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdateV6().Body.(*BGPUpdate)
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv6_UC: true}, false)
+ assert.Equal(true, res)
+ assert.NoError(err)
+
+ res, err = ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+}
+
+func Test_Validate_OK(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(true, res)
+ assert.NoError(err)
+
+}
+
+// func Test_Validate_wellknown_but_nontransitive(t *testing.T) {
+// assert := assert.New(t)
+// message := bgpupdate().Body.(*BGPUpdate)
+
+// originBytes := []byte{0, 1, 1, 1} // 0 means Flags
+// origin := &PathAttributeOrigin{}
+// origin.DecodeFromBytes(originBytes)
+// message.PathAttributes[0] = origin
+
+// res, err := ValidateUpdateMsg(message, []RouteFamily{RF_IPv4_UC,})
+// assert.Equal(false, res)
+// assert.Error(err)
+// e := err.(*MessageError)
+// assert.Equal(BGP_ERROR_UPDATE_MESSAGE_ERROR, e.TypeCode)
+// assert.Equal(BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, e.SubTypeCode)
+// assert.Equal(originBytes, e.Data)
+// }
+
+// func Test_Validate_wellknown_but_partial(t *testing.T) {
+// assert := assert.New(t)
+// message := bgpupdate().Body.(*BGPUpdate)
+
+// originBytes := []byte{BGP_ATTR_FLAG_PARTIAL, 1, 1, 1}
+// origin := &PathAttributeOrigin{}
+// origin.DecodeFromBytes(originBytes)
+// message.PathAttributes[0] = origin
+
+// res, err := ValidateUpdateMsg(message, []RouteFamily{RF_IPv4_UC,})
+// assert.Equal(false, res)
+// assert.Error(err)
+// e := err.(*MessageError)
+// assert.Equal(BGP_ERROR_UPDATE_MESSAGE_ERROR, e.TypeCode)
+// assert.Equal(BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, e.SubTypeCode)
+// assert.Equal(originBytes, e.Data)
+// }
+
+// func Test_Validate_optional_nontransitive_but_partial(t *testing.T) {
+// assert := assert.New(t)
+// message := bgpupdate().Body.(*BGPUpdate)
+// f := BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_PARTIAL
+// originBytes := []byte{byte(f), 1, 1, 1}
+// origin := &PathAttributeOrigin{}
+// origin.DecodeFromBytes(originBytes)
+// message.PathAttributes[0] = origin
+
+// res, err := ValidateUpdateMsg(message, []RouteFamily{RF_IPv4_UC,})
+// assert.Equal(false, res)
+// assert.Error(err)
+// e := err.(*MessageError)
+// assert.Equal(BGP_ERROR_UPDATE_MESSAGE_ERROR, e.TypeCode)
+// assert.Equal(BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, e.SubTypeCode)
+// assert.Equal(originBytes, e.Data)
+// }
+
+// func Test_Validate_flag_mismatch(t *testing.T) {
+// assert := assert.New(t)
+// message := bgpupdate().Body.(*BGPUpdate)
+// f := BGP_ATTR_FLAG_OPTIONAL
+// // origin needs to be well-known
+// originBytes := []byte{byte(f), 1, 1, 1}
+// origin := &PathAttributeOrigin{}
+// origin.DecodeFromBytes(originBytes)
+// message.PathAttributes[0] = origin
+
+// res, err := ValidateUpdateMsg(message, []RouteFamily{RF_IPv4_UC,})
+// assert.Equal(false, res)
+// assert.Error(err)
+// e := err.(*MessageError)
+// assert.Equal(BGP_ERROR_UPDATE_MESSAGE_ERROR, e.TypeCode)
+// assert.Equal(BGP_ERROR_SUB_ATTRIBUTE_FLAGS_ERROR, e.SubTypeCode)
+// assert.Equal(originBytes, e.Data)
+// }
+
+func Test_Validate_duplicate_attribute(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+ // duplicate origin path attribute
+ originBytes := []byte{byte(pathAttrFlags[BGP_ATTR_TYPE_ORIGIN]), 1, 1, 1}
+ origin := &PathAttributeOrigin{}
+ origin.DecodeFromBytes(originBytes)
+ message.PathAttributes = append(message.PathAttributes, origin)
+
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST), e.SubTypeCode)
+ assert.Nil(e.Data)
+}
+
+func Test_Validate_mandatory_missing(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+ message.PathAttributes = message.PathAttributes[1:]
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE), e.SubTypeCode)
+ missing, _ := binary.Uvarint(e.Data)
+ assert.Equal(uint64(1), missing)
+}
+
+func Test_Validate_mandatory_missing_nocheck(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+ message.PathAttributes = message.PathAttributes[1:]
+ message.NLRI = nil
+
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(true, res)
+ assert.NoError(err)
+}
+
+func Test_Validate_invalid_origin(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+ // origin needs to be well-known
+ originBytes := []byte{byte(pathAttrFlags[BGP_ATTR_TYPE_ORIGIN]), 1, 1, 5}
+ origin := &PathAttributeOrigin{}
+ origin.DecodeFromBytes(originBytes)
+ message.PathAttributes[0] = origin
+
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE), e.SubTypeCode)
+ assert.Equal(originBytes, e.Data)
+}
+
+func Test_Validate_invalid_nexthop_zero(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+
+ // invalid nexthop
+ addr := net.ParseIP("0.0.0.1").To4()
+ nexthopBytes := []byte{byte(pathAttrFlags[BGP_ATTR_TYPE_NEXT_HOP]), 3, 4}
+ nexthopBytes = append(nexthopBytes, addr...)
+ nexthop := &PathAttributeNextHop{}
+ nexthop.DecodeFromBytes(nexthopBytes)
+ message.PathAttributes[2] = nexthop
+
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE), e.SubTypeCode)
+ assert.Equal(nexthopBytes, e.Data)
+}
+
+func Test_Validate_invalid_nexthop_lo(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+
+ // invalid nexthop
+ addr := net.ParseIP("127.0.0.1").To4()
+ nexthopBytes := []byte{byte(pathAttrFlags[BGP_ATTR_TYPE_NEXT_HOP]), 3, 4}
+ nexthopBytes = append(nexthopBytes, addr...)
+ nexthop := &PathAttributeNextHop{}
+ nexthop.DecodeFromBytes(nexthopBytes)
+ message.PathAttributes[2] = nexthop
+
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE), e.SubTypeCode)
+ assert.Equal(nexthopBytes, e.Data)
+}
+
+func Test_Validate_invalid_nexthop_de(t *testing.T) {
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+
+ // invalid nexthop
+ addr := net.ParseIP("224.0.0.1").To4()
+ nexthopBytes := []byte{byte(pathAttrFlags[BGP_ATTR_TYPE_NEXT_HOP]), 3, 4}
+ nexthopBytes = append(nexthopBytes, addr...)
+ nexthop := &PathAttributeNextHop{}
+ nexthop.DecodeFromBytes(nexthopBytes)
+ message.PathAttributes[2] = nexthop
+
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE), e.SubTypeCode)
+ assert.Equal(nexthopBytes, e.Data)
+
+}
+
+func Test_Validate_unrecognized_well_known(t *testing.T) {
+
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+ f := BGP_ATTR_FLAG_TRANSITIVE
+ unknownBytes := []byte{byte(f), 30, 1, 1}
+ unknown := &PathAttributeUnknown{}
+ unknown.DecodeFromBytes(unknownBytes)
+ message.PathAttributes = append(message.PathAttributes, unknown)
+
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, false)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE), e.SubTypeCode)
+ assert.Equal(unknownBytes, e.Data)
+}
+
+func Test_Validate_aspath(t *testing.T) {
+
+ assert := assert.New(t)
+ message := bgpupdate().Body.(*BGPUpdate)
+
+ // VALID AS_PATH
+ res, err := ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, true)
+ assert.Equal(true, res)
+
+ // CONFED_SET
+ newAttrs := make([]PathAttributeInterface, 0)
+ attrs := message.PathAttributes
+ for _, attr := range attrs {
+ if _, y := attr.(*PathAttributeAsPath); y {
+ aspath := []AsPathParamInterface{
+ NewAsPathParam(BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65001}),
+ }
+ newAttrs = append(newAttrs, NewPathAttributeAsPath(aspath))
+ } else {
+ newAttrs = append(newAttrs, attr)
+ }
+ }
+
+ message.PathAttributes = newAttrs
+ res, err = ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, true)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e := err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH), e.SubTypeCode)
+ assert.Nil(e.Data)
+
+ // CONFED_SEQ
+ newAttrs = make([]PathAttributeInterface, 0)
+ attrs = message.PathAttributes
+ for _, attr := range attrs {
+ if _, y := attr.(*PathAttributeAsPath); y {
+ aspath := []AsPathParamInterface{
+ NewAsPathParam(BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65001}),
+ }
+ newAttrs = append(newAttrs, NewPathAttributeAsPath(aspath))
+ } else {
+ newAttrs = append(newAttrs, attr)
+ }
+ }
+
+ message.PathAttributes = newAttrs
+ res, err = ValidateUpdateMsg(message, map[RouteFamily]bool{RF_IPv4_UC: true}, true)
+ assert.Equal(false, res)
+ assert.Error(err)
+ e = err.(*MessageError)
+ assert.Equal(uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR), e.TypeCode)
+ assert.Equal(uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH), e.SubTypeCode)
+ assert.Nil(e.Data)
+}