diff options
Diffstat (limited to 'packet/bmp.go')
-rw-r--r-- | packet/bmp.go | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/packet/bmp.go b/packet/bmp.go new file mode 100644 index 00000000..d7758120 --- /dev/null +++ b/packet/bmp.go @@ -0,0 +1,371 @@ +// 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_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 +} + +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 +} |