diff options
Diffstat (limited to 'packet/bmp')
-rw-r--r-- | packet/bmp/bmp.go | 590 | ||||
-rw-r--r-- | packet/bmp/bmp_test.go | 74 |
2 files changed, 664 insertions, 0 deletions
diff --git a/packet/bmp/bmp.go b/packet/bmp/bmp.go new file mode 100644 index 00000000..084a0d16 --- /dev/null +++ b/packet/bmp/bmp.go @@ -0,0 +1,590 @@ +// Copyright (C) 2014,2015 Nippon Telegraph and Telephone Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bmp + +import ( + "encoding/binary" + "fmt" + "github.com/osrg/gobgp/packet/bgp" + "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 *bgp.BGPMessage + BGPUpdatePayload []byte +} + +func NewBMPRouteMonitoring(p BMPPeerHeader, update *bgp.BGPMessage) *BMPMessage { + return &BMPMessage{ + Header: BMPHeader{ + Version: BMP_VERSION, + Type: BMP_MSG_ROUTE_MONITORING, + }, + PeerHeader: p, + Body: &BMPRouteMonitoring{ + BGPUpdate: update, + }, + } +} + +func (body *BMPRouteMonitoring) ParseBody(msg *BMPMessage, data []byte) error { + update, err := bgp.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 *bgp.BGPMessage + Data []byte +} + +func NewBMPPeerDownNotification(p BMPPeerHeader, reason uint8, notification *bgp.BGPMessage, data []byte) *BMPMessage { + b := &BMPPeerDownNotification{ + Reason: reason, + } + switch reason { + case BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION, BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION: + b.BGPNotification = notification + 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 := bgp.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 *bgp.BGPMessage + ReceivedOpenMsg *bgp.BGPMessage +} + +func NewBMPPeerUpNotification(p BMPPeerHeader, lAddr string, lPort, rPort uint16, sent, recv *bgp.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 := bgp.ParseBGPMessage(data) + if err != nil { + return err + } + body.SentOpenMsg = sentopen + data = data[body.SentOpenMsg.Header.Len:] + body.ReceivedOpenMsg, err = bgp.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 +} + +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/bmp/bmp_test.go b/packet/bmp/bmp_test.go new file mode 100644 index 00000000..dd7391aa --- /dev/null +++ b/packet/bmp/bmp_test.go @@ -0,0 +1,74 @@ +// 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 bmp + +import ( + "github.com/osrg/gobgp/packet/bgp" + "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 := bgp.NewTestBGPOpenMessage() + 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 := bgp.NewBGPNotificationMessage(1, 2, nil) + verify(t, NewBMPPeerDownNotification(*p0, BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION, m, nil)) +} + +func Test_RouteMonitoring(t *testing.T) { + m := bgp.NewTestBGPUpdateMessage() + 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) +} |