summaryrefslogtreecommitdiffhomepage
path: root/pkg/packet/mrt
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 13:48:38 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 20:44:25 +0900
commitc4775c42510d1f1ddd55036dc19e982712fa6a0b (patch)
tree6ec8b61d4338c809e239e3003a2d32d480898e22 /pkg/packet/mrt
parentb3079759aa13172fcb548a83da9a9653d8d5fed4 (diff)
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout Now you can see clearly what are private and public library code. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'pkg/packet/mrt')
-rw-r--r--pkg/packet/mrt/mrt.go1006
-rw-r--r--pkg/packet/mrt/mrt_test.go302
2 files changed, 1308 insertions, 0 deletions
diff --git a/pkg/packet/mrt/mrt.go b/pkg/packet/mrt/mrt.go
new file mode 100644
index 00000000..dc07ba9b
--- /dev/null
+++ b/pkg/packet/mrt/mrt.go
@@ -0,0 +1,1006 @@
+// 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 mrt
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math"
+ "net"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+)
+
+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
+ GEO_PEER_TABLE MRTSubTypeTableDumpv2 = 7 // RFC6397
+ RIB_IPV4_UNICAST_ADDPATH MRTSubTypeTableDumpv2 = 8 // RFC8050
+ RIB_IPV4_MULTICAST_ADDPATH MRTSubTypeTableDumpv2 = 9 // RFC8050
+ RIB_IPV6_UNICAST_ADDPATH MRTSubTypeTableDumpv2 = 10 // RFC8050
+ RIB_IPV6_MULTICAST_ADDPATH MRTSubTypeTableDumpv2 = 11 // RFC8050
+ RIB_GENERIC_ADDPATH MRTSubTypeTableDumpv2 = 12 // RFC8050
+)
+
+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
+ MESSAGE_ADDPATH MRTSubTypeBGP4MP = 8 // RFC8050
+ MESSAGE_AS4_ADDPATH MRTSubTypeBGP4MP = 9 // RFC8050
+ MESSAGE_LOCAL_ADDPATH MRTSubTypeBGP4MP = 10 // RFC8050
+ MESSAGE_AS4_LOCAL_ADDPATH MRTSubTypeBGP4MP = 11 // RFC8050
+)
+
+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
+ PathIdentifier uint32
+ PathAttributes []bgp.PathAttributeInterface
+ isAddPath bool
+}
+
+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])
+ if e.isAddPath {
+ e.PathIdentifier = binary.BigEndian.Uint32(data[6:10])
+ data = data[10:]
+ } else {
+ data = data[6:]
+ }
+ totalLen := binary.BigEndian.Uint16(data[:2])
+ data = data[2:]
+ for attrLen := totalLen; attrLen > 0; {
+ p, err := bgp.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) {
+ pbuf := make([]byte, 0)
+ totalLen := 0
+ 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.
+
+ pb, err := pattr.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ pbuf = append(pbuf, pb...)
+ totalLen += len(pb)
+ }
+ var buf []byte
+ if e.isAddPath {
+ buf = make([]byte, 12)
+ binary.BigEndian.PutUint16(buf, e.PeerIndex)
+ binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime)
+ binary.BigEndian.PutUint32(buf[6:], e.PathIdentifier)
+ binary.BigEndian.PutUint16(buf[10:], uint16(totalLen))
+ } else {
+ buf = make([]byte, 8)
+ binary.BigEndian.PutUint16(buf, e.PeerIndex)
+ binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime)
+ binary.BigEndian.PutUint16(buf[6:], uint16(totalLen))
+ }
+ buf = append(buf, pbuf...)
+ return buf, nil
+}
+
+func NewRibEntry(index uint16, time uint32, pathId uint32, pathAttrs []bgp.PathAttributeInterface, isAddPath bool) *RibEntry {
+ return &RibEntry{
+ PeerIndex: index,
+ OriginatedTime: time,
+ PathIdentifier: pathId,
+ PathAttributes: pathAttrs,
+ isAddPath: isAddPath,
+ }
+}
+
+func (e *RibEntry) String() string {
+ if e.isAddPath {
+ return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathIdentifier[%d] PathAttributes [%v]", e.PeerIndex, e.OriginatedTime, e.PathIdentifier, e.PathAttributes)
+ } else {
+ return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathAttributes [%v]", e.PeerIndex, e.OriginatedTime, e.PathAttributes)
+ }
+
+}
+
+type Rib struct {
+ SequenceNumber uint32
+ Prefix bgp.AddrPrefixInterface
+ Entries []*RibEntry
+ RouteFamily bgp.RouteFamily
+ isAddPath bool
+}
+
+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 := bgp.RouteFamilyToAfiSafi(u.RouteFamily)
+ if afi == 0 && safi == 0 {
+ afi = binary.BigEndian.Uint16(data[:2])
+ safi = data[2]
+ data = data[3:]
+ }
+ prefix, err := bgp.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{
+ isAddPath: u.isAddPath,
+ }
+ 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 := bgp.AfiSafiToRouteFamily(u.Prefix.AFI(), u.Prefix.SAFI())
+ switch rf {
+ case bgp.RF_IPv4_UC, bgp.RF_IPv4_MC, bgp.RF_IPv6_UC, bgp.RF_IPv6_MC:
+ default:
+ bbuf := make([]byte, 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 bgp.AddrPrefixInterface, entries []*RibEntry) *Rib {
+ rf := bgp.AfiSafiToRouteFamily(prefix.AFI(), prefix.SAFI())
+ return &Rib{
+ SequenceNumber: seq,
+ Prefix: prefix,
+ Entries: entries,
+ RouteFamily: rf,
+ isAddPath: entries[0].isAddPath,
+ }
+}
+
+func (u *Rib) String() string {
+ return fmt.Sprintf("RIB: Seq [%d] Prefix [%s] Entries [%s]", u.SequenceNumber, u.Prefix, u.Entries)
+}
+
+type GeoPeer struct {
+ Type uint8
+ BgpId net.IP
+ Latitude float32
+ Longitude float32
+}
+
+func (p *GeoPeer) DecodeFromBytes(data []byte) ([]byte, error) {
+ if len(data) < 13 {
+ return nil, fmt.Errorf("not all GeoPeer bytes are available")
+ }
+ // Peer IP Address and Peer AS should not be included
+ p.Type = uint8(data[0])
+ if p.Type != uint8(0) {
+ return nil, fmt.Errorf("unsupported peer type for GeoPeer: %d", p.Type)
+ }
+ p.BgpId = net.IP(data[1:5])
+ p.Latitude = math.Float32frombits(binary.BigEndian.Uint32(data[5:9]))
+ p.Longitude = math.Float32frombits(binary.BigEndian.Uint32(data[9:13]))
+ return data[13:], nil
+}
+
+func (p *GeoPeer) Serialize() ([]byte, error) {
+ buf := make([]byte, 13)
+ buf[0] = uint8(0) // Peer IP Address and Peer AS should not be included
+ bgpId := p.BgpId.To4()
+ if bgpId == nil {
+ return nil, fmt.Errorf("invalid BgpId: %s", p.BgpId)
+ }
+ copy(buf[1:5], bgpId)
+ binary.BigEndian.PutUint32(buf[5:9], math.Float32bits(p.Latitude))
+ binary.BigEndian.PutUint32(buf[9:13], math.Float32bits(p.Longitude))
+ return buf, nil
+}
+
+func NewGeoPeer(bgpid string, latitude float32, longitude float32) *GeoPeer {
+ return &GeoPeer{
+ Type: 0, // Peer IP Address and Peer AS should not be included
+ BgpId: net.ParseIP(bgpid).To4(),
+ Latitude: latitude,
+ Longitude: longitude,
+ }
+}
+
+func (p *GeoPeer) String() string {
+ return fmt.Sprintf("PEER ENTRY: ID [%s] Latitude [%f] Longitude [%f]", p.BgpId, p.Latitude, p.Longitude)
+}
+
+type GeoPeerTable struct {
+ CollectorBgpId net.IP
+ CollectorLatitude float32
+ CollectorLongitude float32
+ Peers []*GeoPeer
+}
+
+func (t *GeoPeerTable) DecodeFromBytes(data []byte) error {
+ if len(data) < 14 {
+ return fmt.Errorf("not all GeoPeerTable bytes are available")
+ }
+ t.CollectorBgpId = net.IP(data[0:4])
+ t.CollectorLatitude = math.Float32frombits(binary.BigEndian.Uint32(data[4:8]))
+ t.CollectorLongitude = math.Float32frombits(binary.BigEndian.Uint32(data[8:12]))
+ peerCount := binary.BigEndian.Uint16(data[12:14])
+ data = data[14:]
+ t.Peers = make([]*GeoPeer, 0, peerCount)
+ var err error
+ for i := 0; i < int(peerCount); i++ {
+ p := &GeoPeer{}
+ if data, err = p.DecodeFromBytes(data); err != nil {
+ return err
+ }
+ t.Peers = append(t.Peers, p)
+ }
+ return nil
+}
+
+func (t *GeoPeerTable) Serialize() ([]byte, error) {
+ buf := make([]byte, 14)
+ collectorBgpId := t.CollectorBgpId.To4()
+ if collectorBgpId == nil {
+ return nil, fmt.Errorf("invalid CollectorBgpId: %s", t.CollectorBgpId)
+ }
+ copy(buf[0:4], collectorBgpId)
+ binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(t.CollectorLatitude))
+ binary.BigEndian.PutUint32(buf[8:12], math.Float32bits(t.CollectorLongitude))
+ binary.BigEndian.PutUint16(buf[12:14], uint16(len(t.Peers)))
+ for _, peer := range t.Peers {
+ pbuf, err := peer.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, pbuf...)
+ }
+ return buf, nil
+}
+
+func NewGeoPeerTable(bgpid string, latitude float32, longitude float32, peers []*GeoPeer) *GeoPeerTable {
+ return &GeoPeerTable{
+ CollectorBgpId: net.ParseIP(bgpid).To4(),
+ CollectorLatitude: latitude,
+ CollectorLongitude: longitude,
+ Peers: peers,
+ }
+}
+
+func (t *GeoPeerTable) String() string {
+ return fmt.Sprintf("GEO_PEER_TABLE: CollectorBgpId [%s] CollectorLatitude [%f] CollectorLongitude [%f] Peers [%s]", t.CollectorBgpId, t.CollectorLatitude, t.CollectorLongitude, t.Peers)
+}
+
+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 bgp.AFI_IP:
+ m.PeerIpAddress = net.IP(data[4:8]).To4()
+ m.LocalIpAddress = net.IP(data[8:12]).To4()
+ data = data[12:]
+ case bgp.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 bgp.AFI_IP:
+ bbuf = make([]byte, 8)
+ copy(bbuf, m.PeerIpAddress.To4())
+ copy(bbuf[4:], m.LocalIpAddress.To4())
+ case bgp.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 = bgp.AFI_IP
+ } else {
+ paddr = net.ParseIP(peerip).To16()
+ laddr = net.ParseIP(localip).To16()
+ if paddr != nil && laddr != nil {
+ af = bgp.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 *bgp.BGPMessage
+ BGPMessagePayload []byte
+ isLocal bool
+ isAddPath bool
+}
+
+func (m *BGP4MPMessage) DecodeFromBytes(data []byte) error {
+ rest, err := m.decodeFromBytes(data)
+ if err != nil {
+ return err
+ }
+
+ if len(rest) < bgp.BGP_HEADER_LENGTH {
+ return fmt.Errorf("Not all BGP4MPMessageAS4 bytes available")
+ }
+
+ msg, err := bgp.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 *bgp.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 *bgp.BGPMessage) *BGP4MPMessage {
+ header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
+ return &BGP4MPMessage{
+ BGP4MPHeader: header,
+ BGPMessage: msg,
+ isLocal: true,
+ }
+}
+
+func NewBGP4MPMessageAddPath(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, msg *bgp.BGPMessage) *BGP4MPMessage {
+ header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
+ return &BGP4MPMessage{
+ BGP4MPHeader: header,
+ BGPMessage: msg,
+ isAddPath: true,
+ }
+}
+
+func NewBGP4MPMessageLocalAddPath(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, msg *bgp.BGPMessage) *BGP4MPMessage {
+ header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4)
+ return &BGP4MPMessage{
+ BGP4MPHeader: header,
+ BGPMessage: msg,
+ isLocal: true,
+ isAddPath: true,
+ }
+}
+
+func (m *BGP4MPMessage) String() string {
+ title := "BGP4MP_MSG"
+ if m.isAS4 {
+ title += "_AS4"
+ }
+ if m.isLocal {
+ title += "_LOCAL"
+ }
+ if m.isAddPath {
+ title += "_ADDPATH"
+ }
+ 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 := bgp.RouteFamily(0)
+ isAddPath := false
+ switch subType {
+ case PEER_INDEX_TABLE:
+ msg.Body = &PeerIndexTable{}
+ case RIB_IPV4_UNICAST:
+ rf = bgp.RF_IPv4_UC
+ case RIB_IPV4_MULTICAST:
+ rf = bgp.RF_IPv4_MC
+ case RIB_IPV6_UNICAST:
+ rf = bgp.RF_IPv6_UC
+ case RIB_IPV6_MULTICAST:
+ rf = bgp.RF_IPv6_MC
+ case RIB_GENERIC:
+ case GEO_PEER_TABLE:
+ msg.Body = &GeoPeerTable{}
+ case RIB_IPV4_UNICAST_ADDPATH:
+ rf = bgp.RF_IPv4_UC
+ isAddPath = true
+ case RIB_IPV4_MULTICAST_ADDPATH:
+ rf = bgp.RF_IPv4_MC
+ isAddPath = true
+ case RIB_IPV6_UNICAST_ADDPATH:
+ rf = bgp.RF_IPv6_UC
+ isAddPath = true
+ case RIB_IPV6_MULTICAST_ADDPATH:
+ rf = bgp.RF_IPv6_MC
+ isAddPath = true
+ case RIB_GENERIC_ADDPATH:
+ isAddPath = true
+ default:
+ return nil, fmt.Errorf("unsupported table dumpv2 subtype: %v\n", subType)
+ }
+
+ if msg.Body == nil {
+ msg.Body = &Rib{
+ RouteFamily: rf,
+ isAddPath: isAddPath,
+ }
+ }
+ 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,
+ }
+ case MESSAGE_ADDPATH:
+ isAS4 = false
+ fallthrough
+ case MESSAGE_AS4_ADDPATH:
+ msg.Body = &BGP4MPMessage{
+ BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4},
+ isAddPath: true,
+ }
+ case MESSAGE_LOCAL_ADDPATH:
+ isAS4 = false
+ fallthrough
+ case MESSAGE_AS4_LOCAL_ADDPATH:
+ msg.Body = &BGP4MPMessage{
+ BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4},
+ isLocal: true,
+ isAddPath: 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/pkg/packet/mrt/mrt_test.go b/pkg/packet/mrt/mrt_test.go
new file mode 100644
index 00000000..8a710758
--- /dev/null
+++ b/pkg/packet/mrt/mrt_test.go
@@ -0,0 +1,302 @@
+// 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 mrt
+
+import (
+ "bufio"
+ "bytes"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ "github.com/stretchr/testify/assert"
+)
+
+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 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{1000}),
+ bgp.NewAsPathParam(1, []uint16{1001, 1002}),
+ bgp.NewAsPathParam(2, []uint16{1003, 1004}),
+ }
+
+ p := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(3),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("129.1.1.2"),
+ bgp.NewPathAttributeMultiExitDisc(1 << 20),
+ bgp.NewPathAttributeLocalPref(1 << 22),
+ }
+
+ e1 := NewRibEntry(1, uint32(time.Now().Unix()), 0, p, false)
+ 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 TestMrtRibEntryWithAddPath(t *testing.T) {
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{1000}),
+ bgp.NewAsPathParam(1, []uint16{1001, 1002}),
+ bgp.NewAsPathParam(2, []uint16{1003, 1004}),
+ }
+
+ p := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(3),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("129.1.1.2"),
+ bgp.NewPathAttributeMultiExitDisc(1 << 20),
+ bgp.NewPathAttributeLocalPref(1 << 22),
+ }
+ e1 := NewRibEntry(1, uint32(time.Now().Unix()), 200, p, true)
+ b1, err := e1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ e2 := &RibEntry{isAddPath: true}
+ 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 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{1000}),
+ bgp.NewAsPathParam(1, []uint16{1001, 1002}),
+ bgp.NewAsPathParam(2, []uint16{1003, 1004}),
+ }
+
+ p := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(3),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("129.1.1.2"),
+ bgp.NewPathAttributeMultiExitDisc(1 << 20),
+ bgp.NewPathAttributeLocalPref(1 << 22),
+ }
+
+ e1 := NewRibEntry(1, uint32(time.Now().Unix()), 0, p, false)
+ e2 := NewRibEntry(2, uint32(time.Now().Unix()), 0, p, false)
+ e3 := NewRibEntry(3, uint32(time.Now().Unix()), 0, p, false)
+
+ r1 := NewRib(1, bgp.NewIPAddrPrefix(24, "192.168.0.0"), []*RibEntry{e1, e2, e3})
+ b1, err := r1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ r2 := &Rib{
+ RouteFamily: bgp.RF_IPv4_UC,
+ }
+ err = r2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(r1, r2), true)
+}
+
+func TestMrtRibWithAddPath(t *testing.T) {
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{1000}),
+ bgp.NewAsPathParam(1, []uint16{1001, 1002}),
+ bgp.NewAsPathParam(2, []uint16{1003, 1004}),
+ }
+
+ p := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(3),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("129.1.1.2"),
+ bgp.NewPathAttributeMultiExitDisc(1 << 20),
+ bgp.NewPathAttributeLocalPref(1 << 22),
+ }
+
+ e1 := NewRibEntry(1, uint32(time.Now().Unix()), 100, p, true)
+ e2 := NewRibEntry(2, uint32(time.Now().Unix()), 200, p, true)
+ e3 := NewRibEntry(3, uint32(time.Now().Unix()), 300, p, true)
+
+ r1 := NewRib(1, bgp.NewIPAddrPrefix(24, "192.168.0.0"), []*RibEntry{e1, e2, e3})
+ b1, err := r1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ r2 := &Rib{
+ RouteFamily: bgp.RF_IPv4_UC,
+ isAddPath: true,
+ }
+ err = r2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(r1, r2), true)
+}
+
+func TestMrtGeoPeerTable(t *testing.T) {
+ p1 := NewGeoPeer("192.168.0.1", 28.031157, 86.899684)
+ p2 := NewGeoPeer("192.168.0.1", 35.360556, 138.727778)
+ pt1 := NewGeoPeerTable("192.168.0.1", 12.345678, 98.765432, []*GeoPeer{p1, p2})
+ b1, err := pt1.Serialize()
+ if err != nil {
+ t.Fatal(err)
+ }
+ pt2 := &GeoPeerTable{}
+ err = pt2.DecodeFromBytes(b1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, reflect.DeepEqual(pt1, pt2), 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 := bgp.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 := bgp.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)
+}