diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2016-03-31 13:19:17 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2016-03-31 13:19:17 +0900 |
commit | 311324fab8bf7d9e39365d53f7e810af90476d85 (patch) | |
tree | 6a32784d484f30bb0abebbc393c7f8b9f270da97 /packet/bgp | |
parent | 051621bf3aecbe0fd6292a3b4ee41ab42169eea1 (diff) |
move packet/*.go to packet/bgp/*.go for Go's convention
Later, we move non-bgp protocol stuff like mrt under their own
direcotries.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'packet/bgp')
-rw-r--r-- | packet/bgp/bgp.go | 6785 | ||||
-rw-r--r-- | packet/bgp/bgp_test.go | 565 | ||||
-rw-r--r-- | packet/bgp/bgpattrtype_string.go | 28 | ||||
-rw-r--r-- | packet/bgp/bgpcapabilitycode_string.go | 40 | ||||
-rw-r--r-- | packet/bgp/bmp.go | 609 | ||||
-rw-r--r-- | packet/bgp/bmp_test.go | 73 | ||||
-rw-r--r-- | packet/bgp/constant.go | 194 | ||||
-rw-r--r-- | packet/bgp/esitype_string.go | 16 | ||||
-rw-r--r-- | packet/bgp/fsmstate_string.go | 16 | ||||
-rw-r--r-- | packet/bgp/mrt.go | 798 | ||||
-rw-r--r-- | packet/bgp/mrt_test.go | 220 | ||||
-rw-r--r-- | packet/bgp/rtr.go | 392 | ||||
-rw-r--r-- | packet/bgp/rtr_test.go | 122 | ||||
-rw-r--r-- | packet/bgp/validate.go | 221 | ||||
-rw-r--r-- | packet/bgp/validate_test.go | 343 |
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) +} |