diff options
-rw-r--r-- | packet/bmp.go | 359 | ||||
-rw-r--r-- | packet/bmp_test.go | 66 |
2 files changed, 378 insertions, 47 deletions
diff --git a/packet/bmp.go b/packet/bmp.go index d7758120..4da37779 100644 --- a/packet/bmp.go +++ b/packet/bmp.go @@ -29,21 +29,27 @@ type BMPHeader struct { } const ( - BMP_HEADER_SIZE = 6 + BMP_VERSION = 3 + BMP_HEADER_SIZE = 6 + BMP_PEER_HEADER_SIZE = 42 ) -func (msg *BMPHeader) DecodeFromBytes(data []byte) error { - msg.Version = data[0] +func (h *BMPHeader) DecodeFromBytes(data []byte) error { + h.Version = data[0] if data[0] != 3 { return fmt.Errorf("error version") } - msg.Length = binary.BigEndian.Uint32(data[1:5]) - msg.Type = data[5] + h.Length = binary.BigEndian.Uint32(data[1:5]) + h.Type = data[5] return nil } -func (msg *BMPHeader) Len() int { - return int(msg.Length) +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 { @@ -57,37 +63,86 @@ type BMPPeerHeader struct { flags uint8 } -func (msg *BMPPeerHeader) DecodeFromBytes(data []byte) error { - data = data[6:] +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 +} - msg.PeerType = data[0] - flags := data[1] - msg.flags = flags - if flags&1<<6 == 1 { - msg.IsPostPolicy = true +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 { - msg.IsPostPolicy = false + h.IsPostPolicy = false } - msg.PeerDistinguisher = binary.BigEndian.Uint64(data[2:10]) - if flags&1<<7 == 1 { - msg.PeerAddress = data[10:26] + h.PeerDistinguisher = binary.BigEndian.Uint64(data[2:10]) + if h.flags&(1<<7) != 0 { + h.PeerAddress = net.IP(data[10:26]).To16() } else { - msg.PeerAddress = data[10:14] + h.PeerAddress = net.IP(data[10:14]).To4() } - msg.PeerAS = binary.BigEndian.Uint32(data[26:30]) - msg.PeerBGPID = data[30:34] + 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]) - msg.Timestamp = float64(timestamp1) + float64(timestamp2)*math.Pow(10, -6) - + 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[10:14], 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 } +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 { @@ -97,6 +152,10 @@ func (body *BMPRouteMonitoring) ParseBody(msg *BMPMessage, data []byte) error { return nil } +func (body *BMPRouteMonitoring) Serialize() ([]byte, error) { + return body.BGPUpdate.Serialize() +} + const ( BMP_STAT_TYPE_REJECTED = iota BMP_STAT_TYPE_DUPLICATE_PREFIX @@ -116,6 +175,7 @@ type BMPStatsTLV struct { } type BMPStatisticsReport struct { + Count uint32 Stats []BMPStatsTLV } @@ -133,6 +193,26 @@ type BMPPeerDownNotification struct { 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:] @@ -148,6 +228,25 @@ func (body *BMPPeerDownNotification) ParseBody(msg *BMPMessage, data []byte) err 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: + buf = append(buf, body.Data...) + } + return buf, nil +} + type BMPPeerUpNotification struct { LocalAddress net.IP LocalPort uint16 @@ -156,11 +255,34 @@ type BMPPeerUpNotification struct { 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 == 1 { - body.LocalAddress = data[:16] + if msg.PeerHeader.flags&(1<<7) != 0 { + body.LocalAddress = net.IP(data[:16]).To16() } else { - body.LocalAddress = data[:4] + body.LocalAddress = net.IP(data[:4]).To4() } body.LocalPort = binary.BigEndian.Uint16(data[16:18]) @@ -180,67 +302,180 @@ func (body *BMPPeerUpNotification) ParseBody(msg *BMPMessage, data []byte) error return nil } +func (body *BMPPeerUpNotification) Serialize() ([]byte, error) { + buf := make([]byte, 20) + if body.LocalAddress.To4() != nil { + copy(buf[:4], 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 { - _ = binary.BigEndian.Uint32(data[0:4]) + 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 { - s.Value = binary.BigEndian.Uint64(data[4:12]) + if s.Length < 8 { + break + } + s.Value = binary.BigEndian.Uint64(data[:8]) } else { - s.Value = uint64(binary.BigEndian.Uint32(data[4:8])) + if s.Length < 4 { + break + } + s.Value = uint64(binary.BigEndian.Uint32(data[:4])) } body.Stats = append(body.Stats, s) - data = data[4+s.Length:] + 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) >= 4 { + for len(data) > 0 { 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] - + tlv.DecodeFromBytes(data) body.Info = append(body.Info, tlv) - data = data[4+tlv.Length:] + 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) >= 4 { + for len(data) > 0 { 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] - + tlv.DecodeFromBytes(data) body.Info = append(body.Info, tlv) - data = data[4+tlv.Length:] + 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 { @@ -253,6 +488,9 @@ func (msg *BMPMessage) Len() int { return int(msg.Header.Length) } +//func (msg *BMPMessage) Serialize() ([]byte, error) { +//} + const ( BMP_MSG_ROUTE_MONITORING = iota BMP_MSG_STATISTICS_REPORT @@ -279,10 +517,10 @@ func ReadBMPMessage(conn net.Conn) (*BMPMessage, error) { return nil, err } - data := make([]byte, h.Len()) + data := make([]byte, h.Length) copy(data, buf) data = data[BMP_HEADER_SIZE:] - for offset := 0; offset < h.Len()-BMP_HEADER_SIZE; { + for offset := 0; offset < int(h.Length)-BMP_HEADER_SIZE; { rlen, err := conn.Read(data[offset:]) if err != nil { return nil, err @@ -321,7 +559,7 @@ func ReadBMPMessage(conn net.Conn) (*BMPMessage, error) { func ParseBMPMessage(data []byte) (*BMPMessage, error) { msg := &BMPMessage{} msg.Header.DecodeFromBytes(data) - data = data[6:msg.Header.Length] + data = data[BMP_HEADER_SIZE:msg.Header.Length] switch msg.Header.Type { case BMP_MSG_ROUTE_MONITORING: @@ -340,7 +578,7 @@ func ParseBMPMessage(data []byte) (*BMPMessage, error) { if msg.Header.Type != BMP_MSG_INITIATION && msg.Header.Type != BMP_MSG_INITIATION { msg.PeerHeader.DecodeFromBytes(data) - data = data[42:] + data = data[BMP_PEER_HEADER_SIZE:] } err := msg.Body.ParseBody(msg, data) @@ -350,6 +588,33 @@ func ParseBMPMessage(data []byte) (*BMPMessage, error) { return msg, nil } +func (msg *BMPMessage) Serialize() ([]byte, error) { + buf := make([]byte, 0) + if msg.Header.Type != BMP_MSG_INITIATION && 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 +} + type MessageError struct { TypeCode uint8 SubTypeCode uint8 diff --git a/packet/bmp_test.go b/packet/bmp_test.go new file mode 100644 index 00000000..c1226088 --- /dev/null +++ b/packet/bmp_test.go @@ -0,0 +1,66 @@ +// 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 ( + "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)) +} |