summaryrefslogtreecommitdiffhomepage
path: root/packet/bgp.go
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2014-09-16 07:26:52 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2014-09-16 07:26:52 +0900
commitcf78fbdf952ffd981710268580f87b677fa3dda4 (patch)
tree5d2ae91081b74dd31985ab1353908735b37d4ae3 /packet/bgp.go
parentbd30509c17f19de0512149c4980f060a74b38772 (diff)
add BGP packet parser/serializer
For now, only parsing is supported. Serializing will be supported shortly. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'packet/bgp.go')
-rw-r--r--packet/bgp.go1574
1 files changed, 1574 insertions, 0 deletions
diff --git a/packet/bgp.go b/packet/bgp.go
new file mode 100644
index 00000000..5b927050
--- /dev/null
+++ b/packet/bgp.go
@@ -0,0 +1,1574 @@
+// 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 (
+ "encoding/binary"
+ "fmt"
+ "math"
+ "net"
+ "reflect"
+ "strings"
+)
+
+// move somewhere else
+
+const (
+ AFI_IP = 1
+ AFI_IP6 = 2
+)
+
+const (
+ SAFI_UNICAST = 1
+ SAFI_MULTICAST = 2
+ SAFI_MPLS_LABEL = 4
+ SAFI_MPLS_VPN = 128
+ SAFI_ROUTE_TARGET_CONSTRTAINS = 132
+)
+
+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 = 2
+ BGP_CAP_CARRYING_LABEL_INFO = 4
+ BGP_CAP_GRACEFUL_RESTART = 64
+ BGP_CAP_FOUR_OCTET_AS_NUMBER = 65
+ BGP_CAP_ENHANCED_ROUTE_REFRESH = 70
+ BGP_CAP_ROUTE_REFRESH_CISCO = 128
+)
+
+func (c BGPCapabilityCode) String() string {
+ switch c {
+ case BGP_CAP_MULTIPROTOCOL:
+ return "MultiProtocol"
+ case BGP_CAP_ROUTE_REFRESH:
+ return "RouteRefresh"
+ case BGP_CAP_CARRYING_LABEL_INFO:
+ return "CarryingLabelInfo"
+ case BGP_CAP_GRACEFUL_RESTART:
+ return "GracefulRestart"
+ case BGP_CAP_FOUR_OCTET_AS_NUMBER:
+ return "FourOctetASNumber"
+ case BGP_CAP_ENHANCED_ROUTE_REFRESH:
+ return "EnhancedRouteRefresh"
+ case BGP_CAP_ROUTE_REFRESH_CISCO:
+ return "RouteRefreshCisco"
+ }
+ return "Unknown"
+}
+
+func structName(m interface{}) string {
+ r := reflect.TypeOf(m)
+ return strings.TrimLeft(r.String(), "*bgp.")
+}
+
+type ParameterCapabilityInterface interface {
+ DecodeFromBytes([]byte) error
+ Len() int
+}
+
+type DefaultParameterCapability struct {
+ CapCode BGPCapabilityCode
+ CapLen uint8
+ CapValue []byte
+}
+
+func (c *DefaultParameterCapability) DecodeFromBytes(data []byte) error {
+ c.CapCode = BGPCapabilityCode(data[0])
+ c.CapLen = data[1]
+ if uint8(len(data)) < 2+c.CapLen {
+ return fmt.Errorf("Not all OptionParameterCapability bytes available")
+ }
+ c.CapValue = data[2 : 2+c.CapLen]
+ return nil
+}
+
+func (c *DefaultParameterCapability) Len() int {
+ return int(c.CapLen + 2)
+}
+
+type CapMultiProtocolValue struct {
+ AFI uint16
+ SAFI uint8
+}
+
+type CapMultiProtocol struct {
+ DefaultParameterCapability
+ CapValue CapMultiProtocolValue
+}
+
+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.AFI = binary.BigEndian.Uint16(data[0:2])
+ c.CapValue.SAFI = data[3]
+ return nil
+}
+
+type CapRouteRefresh struct {
+ DefaultParameterCapability
+}
+
+type CapCarryingLabelInfo struct {
+ DefaultParameterCapability
+}
+
+type CapGracefulRestartTuples struct {
+ AFI uint16
+ SAFI uint8
+ Flags uint8
+}
+
+type CapGracefulRestartValue struct {
+ Flags uint8
+ Time uint16
+ Tuples []CapGracefulRestartTuples
+}
+
+type CapGracefulRestart struct {
+ DefaultParameterCapability
+ CapValue CapGracefulRestartValue
+}
+
+func (c *CapGracefulRestart) DecodeFromBytes(data []byte) error {
+ c.DefaultParameterCapability.DecodeFromBytes(data)
+ data = data[2:]
+ restart := binary.BigEndian.Uint16(data[0:2])
+ c.CapValue.Flags = uint8(restart >> 12)
+ c.CapValue.Time = restart & 0xfff
+ data = data[2:]
+ for len(data) >= 4 {
+ t := CapGracefulRestartTuples{binary.BigEndian.Uint16(data[0:2]),
+ data[2], data[3]}
+ c.CapValue.Tuples = append(c.CapValue.Tuples, t)
+ data = data[4:]
+ }
+ return nil
+}
+
+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
+}
+
+type CapEnhancedRouteRefresh struct {
+ DefaultParameterCapability
+}
+
+type CapRouteRefreshCisco struct {
+ DefaultParameterCapability
+}
+
+type CapUnknown struct {
+ DefaultParameterCapability
+}
+
+type OptionParameterInterface interface {
+}
+
+type OptionParameterCapability struct {
+ ParamType uint8
+ ParamLen uint8
+ Capability []ParameterCapabilityInterface
+}
+
+type OptionParameterUnknown struct {
+ ParamType uint8
+ ParamLen uint8
+ Value []byte
+}
+
+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 {
+ 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_ENHANCED_ROUTE_REFRESH:
+ c = &CapEnhancedRouteRefresh{}
+ case BGP_CAP_ROUTE_REFRESH_CISCO:
+ c = &CapRouteRefreshCisco{}
+ default:
+ c = &CapUnknown{}
+ }
+ err := c.DecodeFromBytes(data)
+ if err != nil {
+ return nil
+ }
+ o.Capability = append(o.Capability, c)
+ data = data[c.Len():]
+ }
+ return 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 = data[5:9]
+ msg.OptParamLen = data[9]
+ data = data[10:]
+ if uint8(len(data)) < msg.OptParamLen {
+ return fmt.Errorf("Not all BGP Open message bytes available")
+ }
+
+ 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
+}
+
+type AddrPrefixInterface interface {
+ DecodeFromBytes([]byte) error
+ Len() int
+}
+
+type IPAddrPrefixDefault struct {
+ Length uint8
+ Prefix net.IP
+}
+
+func (r *IPAddrPrefixDefault) decodePrefix(data []byte, bitlen uint8, addrlen uint8) error {
+ bytelen := (bitlen + 7) / 8
+ b := make([]byte, addrlen)
+ copy(b, data[:bytelen])
+ r.Prefix = b
+ return nil
+}
+
+func (r *IPAddrPrefixDefault) Len() int {
+ return int(1 + ((r.Length + 7) / 8))
+}
+
+type IPAddrPrefix struct {
+ IPAddrPrefixDefault
+ addrlen uint8
+}
+
+func (r *IPAddrPrefix) DecodeFromBytes(data []byte) error {
+ r.Length = data[0]
+ if r.addrlen == 0 {
+ r.addrlen = 4
+ }
+ r.decodePrefix(data[1:], r.Length, r.addrlen)
+ return nil
+}
+
+type IPv6AddrPrefix struct {
+ IPAddrPrefix
+}
+
+func NewIPv6AddrPrefix() *IPv6AddrPrefix {
+ p := &IPv6AddrPrefix{}
+ p.addrlen = 16
+ return p
+}
+
+type WithdrawnRoute struct {
+ IPAddrPrefix
+}
+
+const (
+ BGP_RD_TWO_OCTET_AS = iota
+ BGP_RD_IPV4_ADDRESS
+ BGP_RD_FOUR_OCTET_AS
+)
+
+type RouteDistinguisherInterface interface {
+ DecodeFromBytes([]byte) error
+ Len() int
+}
+
+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) Len() int { return 8 }
+
+type RouteDistinguisherTwoOctetASValue struct {
+ Admin uint16
+ Assigned uint32
+}
+
+type RouteDistinguisherTwoOctetAS struct {
+ DefaultRouteDistinguisher
+ Value RouteDistinguisherTwoOctetASValue
+}
+
+type RouteDistinguisherIPAddressASValue struct {
+ Admin net.IP
+ Assigned uint16
+}
+
+type RouteDistinguisherIPAddressAS struct {
+ DefaultRouteDistinguisher
+ Value RouteDistinguisherIPAddressASValue
+}
+
+type RouteDistinguisherFourOctetASValue struct {
+ Admin uint32
+ Assigned uint16
+}
+
+type RouteDistinguisherFourOctetAS struct {
+ DefaultRouteDistinguisher
+ Value RouteDistinguisherFourOctetASValue
+}
+
+type RouteDistinguisherUnknown struct {
+ DefaultRouteDistinguisher
+}
+
+func getRouteDistinguisher(data []byte) RouteDistinguisherInterface {
+ switch binary.BigEndian.Uint16(data[0:2]) {
+ case BGP_RD_TWO_OCTET_AS:
+ rd := &RouteDistinguisherTwoOctetAS{}
+ rd.Value.Admin = binary.BigEndian.Uint16(data[2:4])
+ rd.Value.Assigned = binary.BigEndian.Uint32(data[4:8])
+ return rd
+ case BGP_RD_IPV4_ADDRESS:
+ rd := &RouteDistinguisherIPAddressAS{}
+ rd.Value.Admin = data[2:6]
+ rd.Value.Assigned = binary.BigEndian.Uint16(data[6:8])
+ return rd
+ case BGP_RD_FOUR_OCTET_AS:
+ rd := &RouteDistinguisherFourOctetAS{}
+ rd.Value.Admin = binary.BigEndian.Uint32(data[2:6])
+ rd.Value.Assigned = binary.BigEndian.Uint16(data[6:8])
+ return rd
+ }
+
+ return &RouteDistinguisherUnknown{}
+}
+
+type Label struct {
+ Labels []uint32
+}
+
+func (l *Label) DecodeFromBytes(data []byte) error {
+ labels := []uint32{}
+ foundBottom := false
+ for len(data) >= 4 {
+ label := uint32(data[0]<<16 | data[1]<<8 | data[2])
+ 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 *Label) Len() int { return 3 * len(l.Labels) }
+
+type LabelledVPNIPAddrPrefix struct {
+ IPAddrPrefixDefault
+ Labels Label
+ RD RouteDistinguisherInterface
+ addrlen uint8
+}
+
+func (l *LabelledVPNIPAddrPrefix) 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 NewLabelledVPNIPAddrPrefix() *LabelledVPNIPAddrPrefix {
+ p := &LabelledVPNIPAddrPrefix{}
+ p.addrlen = 4
+ return p
+}
+
+type LabelledVPNIPv6AddrPrefix struct {
+ LabelledVPNIPAddrPrefix
+}
+
+func NewLabelledVPNIPv6AddrPrefix() *LabelledVPNIPv6AddrPrefix {
+ p := &LabelledVPNIPv6AddrPrefix{}
+ p.addrlen = 16
+ return p
+}
+
+type LabelledIPAddrPrefix struct {
+ IPAddrPrefixDefault
+ Labels Label
+ addrlen uint8
+}
+
+func (r *IPAddrPrefix) decodeNextHop(data []byte) net.IP {
+ if r.addrlen == 0 {
+ r.addrlen = 4
+ }
+ var next net.IP = data[0:r.addrlen]
+ return next
+}
+
+func (r *LabelledVPNIPAddrPrefix) decodeNextHop(data []byte) net.IP {
+ // skip rd
+ var next net.IP = data[8 : 8+r.addrlen]
+ return next
+}
+
+func (r *LabelledIPAddrPrefix) decodeNextHop(data []byte) net.IP {
+ var next net.IP = data[0:r.addrlen]
+ return next
+}
+
+func (l *LabelledIPAddrPrefix) 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 NewLabelledIPAddrPrefix() *LabelledIPAddrPrefix {
+ p := &LabelledIPAddrPrefix{}
+ p.addrlen = 4
+ return p
+}
+
+type LabelledIPv6AddrPrefix struct {
+ LabelledIPAddrPrefix
+}
+
+func NewLabelledIPv6AddrPrefix() *LabelledIPv6AddrPrefix {
+ p := &LabelledIPv6AddrPrefix{}
+ p.addrlen = 16
+ return p
+}
+
+type RouteTargetMembershipNLRI struct {
+ AS uint32
+ RouteTarget ExtendedCommunityInterface
+}
+
+func (n *RouteTargetMembershipNLRI) DecodeFromBytes(data []byte) error {
+ n.AS = binary.BigEndian.Uint32(data[0:4])
+ n.RouteTarget = parseExtended(data[4:])
+ return nil
+}
+
+func (n *RouteTargetMembershipNLRI) Len() int { return 12 }
+
+func rfshift(afi uint16, safi uint8) int {
+ return int(afi)<<16 | int(safi)
+}
+
+const (
+ RF_IPv4_UC = AFI_IP<<16 | SAFI_UNICAST
+ RF_IPv6_UC = AFI_IP6<<16 | SAFI_UNICAST
+ RF_IPv4_VPN = AFI_IP<<16 | SAFI_MPLS_VPN
+ RF_IPv6_VPN = AFI_IP6<<16 | SAFI_MPLS_VPN
+ RF_IPv4_MPLS = AFI_IP<<16 | SAFI_MPLS_LABEL
+ RF_IPv6_MPLS = AFI_IP6<<16 | SAFI_MPLS_LABEL
+ RF_RTC_UC = AFI_IP<<16 | SAFI_ROUTE_TARGET_CONSTRTAINS
+)
+
+func routeFamilyPrefix(afi uint16, safi uint8) (prefix AddrPrefixInterface) {
+ switch rfshift(afi, safi) {
+ case RF_IPv4_UC:
+ prefix = &IPAddrPrefix{}
+ case RF_IPv6_UC:
+ prefix = NewIPv6AddrPrefix()
+ case RF_IPv4_VPN:
+ prefix = NewLabelledVPNIPAddrPrefix()
+ case RF_IPv6_VPN:
+ prefix = NewLabelledVPNIPv6AddrPrefix()
+ case RF_IPv4_MPLS:
+ prefix = NewLabelledIPAddrPrefix()
+ case RF_IPv6_MPLS:
+ prefix = NewLabelledIPv6AddrPrefix()
+ case RF_RTC_UC:
+ prefix = &RouteTargetMembershipNLRI{}
+ }
+ return prefix
+}
+
+const (
+ BGP_ATTR_FLAG_EXTENDED_LENGTH = 1 << 4
+ BGP_ATTR_FLAG_PARTIAL = 1 << 5
+ BGP_ATTR_FLAG_TRANSITIVE = 1 << 6
+ BGP_ATTR_FLAG_OPTIONAL = 1 << 7
+)
+
+const (
+ _ = 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
+ BGP_ATTR_TYPE_MP_UNREACH_NLRI
+ BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
+ BGP_ATTR_TYPE_AS4_PATH
+ BGP_ATTR_TYPE_AS4_AGGREGATOR
+)
+
+// 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
+)
+
+type PathAttributeInterface interface {
+ DecodeFromBytes([]byte) error
+ Len() int
+}
+
+type PathAttribute struct {
+ Flags uint8
+ Type uint8
+ Length uint16
+ Value []byte
+}
+
+func (p *PathAttribute) Len() int {
+ l := 2 + p.Length
+ if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
+ l += 2
+ } else {
+ l += 1
+ }
+ return int(l)
+}
+
+func (p *PathAttribute) DecodeFromBytes(data []byte) error {
+ p.Flags = data[0]
+ p.Type = data[1]
+
+ if p.Flags&BGP_ATTR_FLAG_EXTENDED_LENGTH != 0 {
+ p.Length = binary.BigEndian.Uint16(data[2:4])
+ data = data[4:]
+ } else {
+ p.Length = uint16(data[2])
+ data = data[3:]
+ }
+ p.Value = data[:p.Length]
+
+ return nil
+}
+
+type PathAttributeOrigin struct {
+ PathAttribute
+}
+
+type AsPathParam struct {
+ Type uint8
+ Num uint8
+ AS []uint32
+}
+
+type DefaultAsPath struct {
+}
+
+func (p *DefaultAsPath) isValidAspath(data []byte) bool {
+ for len(data) > 0 {
+ segType := data[0]
+ asNum := data[1]
+ if segType == 0 || segType > 4 {
+ return false
+ }
+ data = data[2:]
+ if len(data) < int(asNum)*2 {
+ return false
+ }
+ data = data[2*asNum:]
+ }
+ return true
+}
+
+// TODO: could marge two functions nicely with reflect?
+func (p *DefaultAsPath) decodeAspath(data []byte) []AsPathParam {
+ var param []AsPathParam
+
+ for len(data) > 0 {
+ a := AsPathParam{}
+ a.Type = data[0]
+ a.Num = data[1]
+ data = data[2:]
+ for i := 0; i < int(a.Num); i++ {
+ a.AS = append(a.AS, uint32(binary.BigEndian.Uint16(data)))
+ data = data[2:]
+ }
+ param = append(param, a)
+ }
+ return param
+}
+
+func (p *DefaultAsPath) decodeAs4path(data []byte) []AsPathParam {
+ var param []AsPathParam
+
+ for len(data) > 0 {
+ a := AsPathParam{}
+ a.Type = data[0]
+ a.Num = data[1]
+ data = data[2:]
+ for i := 0; i < int(a.Num); i++ {
+ a.AS = append(a.AS, binary.BigEndian.Uint32(data))
+ data = data[4:]
+ }
+ param = append(param, a)
+ }
+ return param
+}
+
+type PathAttributeAsPath struct {
+ DefaultAsPath
+ PathAttribute
+ Value []AsPathParam
+}
+
+func (p *PathAttributeAsPath) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ if p.DefaultAsPath.isValidAspath(p.PathAttribute.Value) {
+ p.Value = p.DefaultAsPath.decodeAspath(p.PathAttribute.Value)
+ } else {
+ p.Value = p.DefaultAsPath.decodeAs4path(p.PathAttribute.Value)
+ }
+ return nil
+}
+
+type PathAttributeNextHop struct {
+ PathAttribute
+ Value net.IP
+}
+
+func (p *PathAttributeNextHop) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ p.Value = p.PathAttribute.Value
+ return nil
+}
+
+type PathAttributeMultiExitDisc struct {
+ PathAttribute
+ Value uint32
+}
+
+func (p *PathAttributeMultiExitDisc) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ p.Value = binary.BigEndian.Uint32(p.PathAttribute.Value)
+ return nil
+}
+
+type PathAttributeLocalPref struct {
+ PathAttribute
+ Value uint32
+}
+
+func (p *PathAttributeLocalPref) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ p.Value = binary.BigEndian.Uint32(p.PathAttribute.Value)
+ return nil
+}
+
+type PathAttributeAtomicAggregate struct {
+ PathAttribute
+}
+
+type PathAttributeAggregatorParam struct {
+ AS uint32
+ Address net.IP
+}
+
+type PathAttributeAggregator struct {
+ PathAttribute
+ Value PathAttributeAggregatorParam
+}
+
+func (p *PathAttributeAggregator) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ 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:]
+ } else {
+ p.Value.AS = binary.BigEndian.Uint32(p.PathAttribute.Value[0:4])
+ p.Value.Address = p.PathAttribute.Value[4:]
+ }
+ return nil
+}
+
+type PathAttributeCommunities struct {
+ PathAttribute
+ Value []uint32
+}
+
+func (p *PathAttributeCommunities) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ value := p.PathAttribute.Value
+ for len(value) >= 4 {
+ p.Value = append(p.Value, binary.BigEndian.Uint32(value))
+ value = value[4:]
+ }
+ return nil
+}
+
+type PathAttributeOriginatorId struct {
+ PathAttribute
+ Value net.IP
+}
+
+func (p *PathAttributeOriginatorId) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ p.Value = p.PathAttribute.Value
+ return nil
+}
+
+type PathAttributeClusterList struct {
+ PathAttribute
+ Value []net.IP
+}
+
+func (p *PathAttributeClusterList) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ value := p.PathAttribute.Value
+ for len(value) >= 4 {
+ p.Value = append(p.Value, value[:4])
+ value = value[4:]
+ }
+ return nil
+}
+
+type PathAttributeMpReachNLRI struct {
+ PathAttribute
+ Nexthop net.IP
+ Value []AddrPrefixInterface
+}
+
+func (p *PathAttributeMpReachNLRI) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+
+ value := p.PathAttribute.Value
+ afi := binary.BigEndian.Uint16(value[0:2])
+ safi := value[2]
+ nexthopLen := value[3]
+ nexthopbin := value[4 : 4+nexthopLen]
+ value = value[4+nexthopLen:]
+ if nexthopLen > 0 {
+ offset := 0
+ if safi == SAFI_MPLS_VPN {
+ offset = 8
+ }
+ addrlen := 4
+ if afi == AFI_IP6 {
+ addrlen = 16
+ }
+ p.Nexthop = nexthopbin[offset : +offset+addrlen]
+ }
+ // skip reserved
+ value = value[1:]
+ for len(value) > 0 {
+ prefix := routeFamilyPrefix(afi, safi)
+ prefix.DecodeFromBytes(value)
+ value = value[prefix.Len():]
+ p.Value = append(p.Value, prefix)
+ }
+ return nil
+}
+
+type PathAttributeMpUnreachNLRI struct {
+ PathAttribute
+ Value []AddrPrefixInterface
+}
+
+func (p *PathAttributeMpUnreachNLRI) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+
+ value := p.PathAttribute.Value
+ afi := binary.BigEndian.Uint16(value[0:2])
+ safi := value[2]
+ addrprefix := routeFamilyPrefix(afi, safi)
+ value = value[3:]
+ for len(value) > 0 {
+ prefix := addrprefix
+ prefix.DecodeFromBytes(value)
+ value = value[prefix.Len():]
+ p.Value = append(p.Value, prefix)
+ }
+ return nil
+}
+
+type ExtendedCommunityInterface interface {
+}
+
+type TwoOctetAsSpecificExtended struct {
+ AS uint16
+ LocalAdmin uint32
+}
+
+type IPv4AddressSpecificExtended struct {
+ SubType uint8
+ IPv4 net.IP
+ LocalAdmin uint16
+}
+
+type FourOctetAsSpecificExtended struct {
+ AS net.IP
+ LocalAdmin uint16
+}
+
+type OpaqueExtended struct {
+ Value []byte
+}
+
+type UnknownExtended struct {
+ Value []byte
+}
+
+type PathAttributeExtendedCommunities struct {
+ PathAttribute
+ Value []ExtendedCommunityInterface
+}
+
+func parseExtended(data []byte) ExtendedCommunityInterface {
+ typehigh := data[0] & ^uint8(0x40)
+ switch typehigh {
+ case 0:
+ e := &TwoOctetAsSpecificExtended{}
+ e.AS = binary.BigEndian.Uint16(data[2:4])
+ e.LocalAdmin = binary.BigEndian.Uint32(data[4:8])
+ return e
+ case 1:
+ e := &IPv4AddressSpecificExtended{}
+ e.SubType = data[1]
+ e.IPv4 = data[2:6]
+ e.LocalAdmin = binary.BigEndian.Uint16(data[6:8])
+ return e
+ case 2:
+ e := &FourOctetAsSpecificExtended{}
+ e.AS = data[2:6]
+ e.LocalAdmin = binary.BigEndian.Uint16(data[6:8])
+ return e
+ case 3:
+ e := &OpaqueExtended{}
+ e.Value = data[2:8]
+ return e
+ }
+ e := &UnknownExtended{}
+ e.Value = data[2:8]
+ return e
+}
+
+func (p *PathAttributeExtendedCommunities) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+
+ value := p.PathAttribute.Value
+ for len(value) >= 8 {
+ e := parseExtended(value)
+ p.Value = append(p.Value, e)
+ value = value[8:]
+ }
+ return nil
+}
+
+type PathAttributeAs4Path struct {
+ PathAttribute
+ Value []AsPathParam
+ DefaultAsPath
+}
+
+func (p *PathAttributeAs4Path) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+ p.Value = p.DefaultAsPath.decodeAs4path(p.PathAttribute.Value)
+ return nil
+}
+
+type PathAttributeAs4Aggregator struct {
+ PathAttribute
+ Value PathAttributeAggregatorParam
+}
+
+func (p *PathAttributeAs4Aggregator) DecodeFromBytes(data []byte) error {
+ p.PathAttribute.DecodeFromBytes(data)
+
+ p.Value.AS = binary.BigEndian.Uint32(p.PathAttribute.Value[0:4])
+ p.Value.Address = p.PathAttribute.Value[4:]
+ return nil
+}
+
+type PathAttributeUnknown struct {
+ PathAttribute
+}
+
+func getPathAttribute(data []byte) PathAttributeInterface {
+ switch data[1] {
+ case BGP_ATTR_TYPE_ORIGIN:
+ return &PathAttributeOrigin{}
+ case BGP_ATTR_TYPE_AS_PATH:
+ return &PathAttributeAsPath{}
+ case BGP_ATTR_TYPE_NEXT_HOP:
+ return &PathAttributeNextHop{}
+ case BGP_ATTR_TYPE_MULTI_EXIT_DISC:
+ return &PathAttributeMultiExitDisc{}
+ case BGP_ATTR_TYPE_LOCAL_PREF:
+ return &PathAttributeLocalPref{}
+ case BGP_ATTR_TYPE_ATOMIC_AGGREGATE:
+ return &PathAttributeAtomicAggregate{}
+ case BGP_ATTR_TYPE_AGGREGATOR:
+ return &PathAttributeAggregator{}
+ case BGP_ATTR_TYPE_COMMUNITIES:
+ return &PathAttributeCommunities{}
+ case BGP_ATTR_TYPE_ORIGINATOR_ID:
+ return &PathAttributeOriginatorId{}
+ case BGP_ATTR_TYPE_CLUSTER_LIST:
+ return &PathAttributeClusterList{}
+ case BGP_ATTR_TYPE_MP_REACH_NLRI:
+ return &PathAttributeMpReachNLRI{}
+ case BGP_ATTR_TYPE_MP_UNREACH_NLRI:
+ return &PathAttributeMpUnreachNLRI{}
+ case BGP_ATTR_TYPE_EXTENDED_COMMUNITIES:
+ return &PathAttributeExtendedCommunities{}
+ case BGP_ATTR_TYPE_AS4_PATH:
+ return &PathAttributeAs4Path{}
+ case BGP_ATTR_TYPE_AS4_AGGREGATOR:
+ return &PathAttributeAs4Aggregator{}
+ }
+ return &PathAttributeUnknown{}
+}
+
+type NLRInfo struct {
+ IPAddrPrefix
+}
+
+type BGPUpdate struct {
+ WithdrawnRoutesLen uint16
+ WithdrawnRoutes []WithdrawnRoute
+ TotalPathAttributeLen uint16
+ PathAttributes []PathAttributeInterface
+ NLRI []NLRInfo
+}
+
+func (msg *BGPUpdate) DecodeFromBytes(data []byte) error {
+ msg.WithdrawnRoutesLen = binary.BigEndian.Uint16(data[0:2])
+ data = data[2:]
+ for routelen := msg.WithdrawnRoutesLen; routelen > 0; {
+ w := WithdrawnRoute{}
+ w.DecodeFromBytes(data)
+ routelen -= uint16(w.Len())
+ data = data[w.Len():]
+ msg.WithdrawnRoutes = append(msg.WithdrawnRoutes, w)
+ }
+ msg.TotalPathAttributeLen = binary.BigEndian.Uint16(data[0:2])
+ data = data[2:]
+ for pathlen := msg.TotalPathAttributeLen; pathlen > 0; {
+ p := getPathAttribute(data)
+ p.DecodeFromBytes(data)
+ pathlen -= uint16(p.Len())
+ data = data[p.Len():]
+ msg.PathAttributes = append(msg.PathAttributes, p)
+ }
+
+ for restlen := len(data); restlen > 0; {
+ n := NLRInfo{}
+ n.DecodeFromBytes(data)
+ restlen -= n.Len()
+ data = data[n.Len():]
+ msg.NLRI = append(msg.NLRI, n)
+ }
+
+ return 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
+}
+
+type BGPKeepAlive struct {
+}
+
+func (msg *BGPKeepAlive) DecodeFromBytes(data []byte) error {
+ return nil
+}
+
+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
+}
+
+type BGPBody interface {
+ DecodeFromBytes([]byte) error
+}
+
+type BGPHeader struct {
+ Marker []byte
+ Len uint16
+ Type uint8
+}
+
+func (msg *BGPHeader) DecodeFromBytes(data []byte) error {
+ // minimum BGP message length
+ if uint16(len(data)) < 19 {
+ return fmt.Errorf("Not all BGP message header")
+ }
+ msg.Len = binary.BigEndian.Uint16(data[16:18])
+ msg.Type = data[18]
+ if len(data) < int(msg.Len) {
+ return fmt.Errorf("Not all BGP message bytes available")
+ }
+ return nil
+}
+
+type BGPMessage struct {
+ Header BGPHeader
+ Body BGPBody
+}
+
+func ParseBGPMessage(data []byte) (*BGPMessage, error) {
+ msg := &BGPMessage{}
+ err := msg.Header.DecodeFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ data = data[19:msg.Header.Len]
+ 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{}
+ }
+ err = msg.Body.DecodeFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
+
+// BMP
+
+type BMPHeader struct {
+ Version uint8
+ Length uint32
+ Type uint8
+}
+
+const (
+ BMP_HEADER_SIZE = 6
+)
+
+func (msg *BMPHeader) DecodeFromBytes(data []byte) error {
+ msg.Version = data[0]
+ if data[0] != 3 {
+ return fmt.Errorf("error version")
+ }
+ msg.Length = binary.BigEndian.Uint32(data[1:5])
+ msg.Type = data[5]
+ return nil
+}
+
+func (msg *BMPHeader) Len() int {
+ return int(msg.Length)
+}
+
+type BMPPeerHeader struct {
+ PeerType uint8
+ IsPostPolicy bool
+ PeerDistinguisher uint64
+ PeerAddress net.IP
+ PeerAS uint32
+ PeerBGPID net.IP
+ Timestamp float64
+ flags uint8
+}
+
+func (msg *BMPPeerHeader) DecodeFromBytes(data []byte) error {
+ data = data[6:]
+
+ msg.PeerType = data[0]
+ flags := data[1]
+ msg.flags = flags
+ if flags&1<<6 == 1 {
+ msg.IsPostPolicy = true
+ } else {
+ msg.IsPostPolicy = false
+ }
+ msg.PeerDistinguisher = binary.BigEndian.Uint64(data[2:10])
+ if flags&1<<7 == 1 {
+ msg.PeerAddress = data[10:26]
+ } else {
+ msg.PeerAddress = data[10:14]
+ }
+ msg.PeerAS = binary.BigEndian.Uint32(data[26:30])
+ msg.PeerBGPID = data[30:34]
+
+ timestamp1 := binary.BigEndian.Uint32(data[34:38])
+ timestamp2 := binary.BigEndian.Uint32(data[38:42])
+ msg.Timestamp = float64(timestamp1) + float64(timestamp2)*math.Pow(10, -6)
+
+ return nil
+}
+
+type BMPRouteMonitoring struct {
+ BGPUpdate *BGPMessage
+}
+
+func (body *BMPRouteMonitoring) ParseBody(msg *BMPMessage, data []byte) error {
+ update, err := ParseBGPMessage(data)
+ if err != nil {
+ return err
+ }
+ body.BGPUpdate = update
+ return nil
+}
+
+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 {
+ 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 (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
+}
+
+type BMPPeerUpNotification struct {
+ LocalAddress net.IP
+ LocalPort uint16
+ RemotePort uint16
+ SentOpenMsg *BGPMessage
+ ReceivedOpenMsg *BGPMessage
+}
+
+func (body *BMPPeerUpNotification) ParseBody(msg *BMPMessage, data []byte) error {
+ if msg.PeerHeader.flags&1<<7 == 1 {
+ body.LocalAddress = data[:16]
+ } else {
+ body.LocalAddress = data[:4]
+ }
+
+ 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 *BMPStatisticsReport) ParseBody(msg *BMPMessage, data []byte) error {
+ _ = 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])
+
+ if s.Type == BMP_STAT_TYPE_ADJ_RIB_IN || s.Type == BMP_STAT_TYPE_LOC_RIB {
+ s.Value = binary.BigEndian.Uint64(data[4:12])
+ } else {
+ s.Value = uint64(binary.BigEndian.Uint32(data[4:8]))
+ }
+ body.Stats = append(body.Stats, s)
+ data = data[4+s.Length:]
+ }
+ return nil
+}
+
+type BMPTLV struct {
+ Type uint16
+ Length uint16
+ Value []byte
+}
+
+type BMPInitiation struct {
+ Info []BMPTLV
+}
+
+func (body *BMPInitiation) ParseBody(msg *BMPMessage, data []byte) error {
+ for len(data) >= 4 {
+ tlv := BMPTLV{}
+ tlv.Type = binary.BigEndian.Uint16(data[0:2])
+ tlv.Length = binary.BigEndian.Uint16(data[2:4])
+ tlv.Value = data[4 : 4+tlv.Length]
+
+ body.Info = append(body.Info, tlv)
+ data = data[4+tlv.Length:]
+ }
+ return nil
+}
+
+type BMPTermination struct {
+ Info []BMPTLV
+}
+
+func (body *BMPTermination) ParseBody(msg *BMPMessage, data []byte) error {
+ for len(data) >= 4 {
+ tlv := BMPTLV{}
+ tlv.Type = binary.BigEndian.Uint16(data[0:2])
+ tlv.Length = binary.BigEndian.Uint16(data[2:4])
+ tlv.Value = data[4 : 4+tlv.Length]
+
+ body.Info = append(body.Info, tlv)
+ data = data[4+tlv.Length:]
+ }
+ return nil
+}
+
+type BMPBody interface {
+ ParseBody(*BMPMessage, []byte) error
+}
+
+type BMPMessage struct {
+ Header BMPHeader
+ PeerHeader BMPPeerHeader
+ Body BMPBody
+}
+
+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
+)
+
+// move somewhere else
+func ReadBMPMessage(conn net.Conn) (*BMPMessage, error) {
+ buf := make([]byte, BMP_HEADER_SIZE)
+ for offset := 0; offset < BMP_HEADER_SIZE; {
+ rlen, err := conn.Read(buf[offset:])
+ if err != nil {
+ return nil, err
+ }
+ offset += rlen
+ }
+
+ h := BMPHeader{}
+ err := h.DecodeFromBytes(buf)
+ if err != nil {
+ return nil, err
+ }
+
+ data := make([]byte, h.Len())
+ copy(data, buf)
+ data = data[BMP_HEADER_SIZE:]
+ for offset := 0; offset < h.Len()-BMP_HEADER_SIZE; {
+ rlen, err := conn.Read(data[offset:])
+ if err != nil {
+ return nil, err
+ }
+ offset += rlen
+ }
+ msg := &BMPMessage{Header: h}
+
+ 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.Header.Type != BMP_MSG_INITIATION {
+ msg.PeerHeader.DecodeFromBytes(data)
+ data = data[42:]
+ }
+
+ err = msg.Body.ParseBody(msg, data)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
+
+func ParseBMPMessage(data []byte) (*BMPMessage, error) {
+ msg := &BMPMessage{}
+ msg.Header.DecodeFromBytes(data)
+ data = data[6: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.Header.Type != BMP_MSG_INITIATION {
+ msg.PeerHeader.DecodeFromBytes(data)
+ data = data[42:]
+ }
+
+ err := msg.Body.ParseBody(msg, data)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}