813 lines
21 KiB
Go
813 lines
21 KiB
Go
package mrt
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/cloudflare/fgbgp/messages"
|
|
"io"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
TYPE_OSPFV2 = 11
|
|
TYPE_TABLE_DUMP = 12
|
|
TYPE_TABLE_DUMPV2 = 13
|
|
|
|
TYPE_BGP4MP = 16
|
|
TYPE_BGP4MP_ET = 17
|
|
|
|
TYPE_ISIS = 32
|
|
TYPE_ISIS_ET = 33
|
|
|
|
TYPE_OSPFV3 = 48
|
|
TYPE_OSPFV3_ET = 49
|
|
|
|
SUBT_TABLE_DUMP_AFI_IPV4 = 1
|
|
SUBT_TABLE_DUMP_AFI_IPV6 = 2
|
|
|
|
SUBT_TABLE_DUMPV2_PEER_INDEX_TABLE = 1
|
|
SUBT_TABLE_DUMPV2_RIB_IPV4_UNICAST = 2
|
|
SUBT_TABLE_DUMPV2_RIB_IPV4_MULTICAST = 3
|
|
SUBT_TABLE_DUMPV2_RIB_IPV6_UNICAST = 4
|
|
SUBT_TABLE_DUMPV2_RIB_IPV6_MULTICAST = 5
|
|
SUBT_TABLE_DUMPV2_RIB_GENERIC = 6
|
|
|
|
SUBT_BGP4MP_STATE_CHANGE = 0
|
|
SUBT_BGP4MP_MESSAGE = 1
|
|
SUBT_BGP4MP_MESSAGE_AS4 = 4
|
|
SUBT_BGP4MP_STATE_CHANGE_AS4 = 5
|
|
SUBT_BGP4MP_MESSAGE_LOCAL = 6
|
|
SUBT_BGP4MP_MESSAGE_AS4_LOCAL = 7
|
|
|
|
STATE_IDLE = 1
|
|
STATE_CONNECT = 2
|
|
STATE_ACTIVE = 3
|
|
STATE_OPENSENT = 4
|
|
STATE_OPENCONFIRM = 5
|
|
STATE_ESTABLISHED = 6
|
|
)
|
|
|
|
type Mrt interface {
|
|
Write(io.Writer)
|
|
Len() int
|
|
}
|
|
|
|
func WriteCommonHeader(buf io.Writer, timestamp time.Time, mrttype uint16, subtype uint16, length uint32) {
|
|
binary.Write(buf, binary.BigEndian, uint32(timestamp.Unix()))
|
|
binary.Write(buf, binary.BigEndian, mrttype)
|
|
binary.Write(buf, binary.BigEndian, subtype)
|
|
binary.Write(buf, binary.BigEndian, length)
|
|
}
|
|
|
|
type Peer struct {
|
|
Id net.IP
|
|
IP net.IP
|
|
ASN uint32
|
|
}
|
|
|
|
func (p *Peer) Write(buf io.Writer) {
|
|
newip := p.IP
|
|
firstbit := 1
|
|
if tmpip := newip.To4(); tmpip != nil {
|
|
newip = tmpip
|
|
firstbit = 0
|
|
}
|
|
|
|
longasn := 1
|
|
if p.ASN <= 0xffff {
|
|
longasn = 0
|
|
}
|
|
|
|
firstbyte := byte(longasn<<2 | firstbit)
|
|
binary.Write(buf, binary.BigEndian, firstbyte)
|
|
|
|
binary.Write(buf, binary.BigEndian, p.Id.To4())
|
|
|
|
binary.Write(buf, binary.BigEndian, newip)
|
|
if longasn == 1 {
|
|
binary.Write(buf, binary.BigEndian, p.ASN)
|
|
} else {
|
|
binary.Write(buf, binary.BigEndian, uint16(p.ASN))
|
|
}
|
|
}
|
|
|
|
func (p *Peer) Len() int {
|
|
iplen := 4
|
|
newip := p.IP
|
|
if tmpip := newip.To4(); tmpip == nil {
|
|
iplen = 16
|
|
}
|
|
longasn := 4
|
|
if p.ASN <= 0xffff {
|
|
longasn = 2
|
|
}
|
|
return 1 + 4 + iplen + longasn
|
|
}
|
|
|
|
type MrtTableDumpV2_PeerIndex struct {
|
|
Timestamp time.Time
|
|
CollectorId net.IP
|
|
ViewName string
|
|
Peers []*Peer
|
|
}
|
|
|
|
func NewMrtTableDumpV2_PeerIndex(collectorid net.IP, viewname string, ts time.Time) *MrtTableDumpV2_PeerIndex {
|
|
return &MrtTableDumpV2_PeerIndex{
|
|
Timestamp: ts,
|
|
CollectorId: collectorid,
|
|
ViewName: viewname,
|
|
Peers: make([]*Peer, 0),
|
|
}
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_PeerIndex) AddPeer(id net.IP, asn uint32, ip net.IP) uint16 {
|
|
peer := &Peer{
|
|
Id: id.To4(),
|
|
IP: ip,
|
|
ASN: asn,
|
|
}
|
|
mrt.Peers = append(mrt.Peers, peer)
|
|
return uint16(len(mrt.Peers) - 1)
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_PeerIndex) Write(buf io.Writer) {
|
|
WriteCommonHeader(buf, mrt.Timestamp, TYPE_TABLE_DUMPV2, SUBT_TABLE_DUMPV2_PEER_INDEX_TABLE, uint32(mrt.Len()))
|
|
binary.Write(buf, binary.BigEndian, mrt.CollectorId.To4())
|
|
binary.Write(buf, binary.BigEndian, uint16(len(mrt.ViewName)))
|
|
binary.Write(buf, binary.BigEndian, []byte(mrt.ViewName))
|
|
binary.Write(buf, binary.BigEndian, uint16(len(mrt.Peers)))
|
|
for i := range mrt.Peers {
|
|
mrt.Peers[i].Write(buf)
|
|
}
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_PeerIndex) Len() int {
|
|
totallen := 4 + 2 + len(mrt.ViewName) + 2
|
|
for i := range mrt.Peers {
|
|
totallen += mrt.Peers[i].Len()
|
|
}
|
|
return totallen
|
|
}
|
|
|
|
type RibEntry struct {
|
|
PeerIndex uint16
|
|
OrigTime time.Time
|
|
Attributes []messages.BGPAttributeIf
|
|
}
|
|
|
|
func (entry *RibEntry) Write(buf io.Writer) {
|
|
binary.Write(buf, binary.BigEndian, entry.PeerIndex)
|
|
binary.Write(buf, binary.BigEndian, uint32(entry.OrigTime.Unix()))
|
|
|
|
var size uint16
|
|
|
|
for i := range entry.Attributes {
|
|
switch attribute := entry.Attributes[i].(type) {
|
|
case *messages.BGPAttribute_MP_REACH:
|
|
size += attribute.LenMrt()
|
|
case *messages.BGPAttribute_MP_UNREACH:
|
|
default:
|
|
size += uint16(entry.Attributes[i].Len())
|
|
}
|
|
}
|
|
|
|
binary.Write(buf, binary.BigEndian, size)
|
|
|
|
for i := range entry.Attributes {
|
|
switch attribute := entry.Attributes[i].(type) {
|
|
case *messages.BGPAttribute_MP_REACH:
|
|
attribute.WriteMrt(buf)
|
|
case *messages.BGPAttribute_MP_UNREACH:
|
|
default:
|
|
entry.Attributes[i].Write(buf)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (entry *RibEntry) Len() uint32 {
|
|
size := uint32(2 + 4 + 2)
|
|
// To optimize
|
|
for i := range entry.Attributes {
|
|
switch attribute := entry.Attributes[i].(type) {
|
|
case *messages.BGPAttribute_MP_REACH:
|
|
size += uint32(attribute.LenMrt())
|
|
case *messages.BGPAttribute_MP_UNREACH:
|
|
default:
|
|
size += uint32(entry.Attributes[i].Len())
|
|
}
|
|
|
|
}
|
|
return size
|
|
}
|
|
|
|
type MrtTableDumpV2_Rib struct {
|
|
Timestamp time.Time
|
|
SequenceNumber uint32
|
|
Afi uint16
|
|
Safi byte
|
|
NLRI messages.NLRI
|
|
RibEntries []*RibEntry
|
|
|
|
WriteAsAfiSafi bool
|
|
}
|
|
|
|
func NewMrtTableDumpV2_RibGeneric(seqnum uint32, afi uint16, safi byte, nlri messages.NLRI, ts time.Time) *MrtTableDumpV2_Rib {
|
|
return &MrtTableDumpV2_Rib{
|
|
Timestamp: ts,
|
|
SequenceNumber: seqnum,
|
|
Afi: afi,
|
|
Safi: safi,
|
|
NLRI: nlri,
|
|
RibEntries: make([]*RibEntry, 0),
|
|
}
|
|
}
|
|
|
|
func NewMrtTableDumpV2_RibAfiSafi(seqnum uint32, afi uint16, safi byte, nlri messages.NLRI, ts time.Time) *MrtTableDumpV2_Rib {
|
|
mrt := NewMrtTableDumpV2_RibGeneric(seqnum, afi, safi, nlri, ts)
|
|
mrt.WriteAsAfiSafi = true
|
|
return mrt
|
|
}
|
|
|
|
func (entry *RibEntry) EntryToUpdate() *messages.BGPMessageUpdate {
|
|
update := &messages.BGPMessageUpdate{
|
|
PathAttributes: entry.Attributes,
|
|
}
|
|
return update
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_Rib) ConvertToUpdateIndex(index int) *messages.BGPMessageUpdate {
|
|
re := mrt.RibEntries
|
|
if index >= len(re) {
|
|
return nil
|
|
}
|
|
entry := re[index]
|
|
update := entry.EntryToUpdate()
|
|
|
|
if mrt.NLRI.GetAfi() == messages.AFI_IPV6 {
|
|
pa := update.PathAttributes
|
|
var hasreach bool
|
|
for i := range pa {
|
|
switch pai := pa[i].(type) {
|
|
case messages.BGPAttribute_MP_REACH:
|
|
// Check NLRI already in
|
|
var hasipinreach bool
|
|
hasreach = true
|
|
|
|
mpnlri := pai.NLRI
|
|
for j := range mpnlri {
|
|
if mrt.NLRI.Equals(mpnlri[j]) {
|
|
hasipinreach = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasipinreach {
|
|
pai.NLRI = append(pai.NLRI, mrt.NLRI)
|
|
}
|
|
|
|
}
|
|
}
|
|
if !hasreach {
|
|
attr := &messages.BGPAttribute_MP_REACH{
|
|
NLRI: []messages.NLRI{mrt.NLRI},
|
|
}
|
|
update.PathAttributes = append(update.PathAttributes, attr)
|
|
}
|
|
} else {
|
|
update.NLRI = []messages.NLRI{mrt.NLRI}
|
|
}
|
|
return update
|
|
}
|
|
func (mrt *MrtTableDumpV2_Rib) ConvertToUpdate() []*messages.BGPMessageUpdate {
|
|
updates := make([]*messages.BGPMessageUpdate, 0)
|
|
re := mrt.RibEntries
|
|
for i := range re {
|
|
update := mrt.ConvertToUpdateIndex(i)
|
|
if update != nil {
|
|
updates = append(updates, update)
|
|
}
|
|
}
|
|
return updates
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_Rib) AddEntry(peerindex uint16, origtime time.Time, attributes []messages.BGPAttributeIf) {
|
|
entry := &RibEntry{
|
|
PeerIndex: peerindex,
|
|
OrigTime: origtime,
|
|
Attributes: attributes,
|
|
}
|
|
mrt.RibEntries = append(mrt.RibEntries, entry)
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_Rib) GetSubtype() (bool, uint16) {
|
|
subt := uint16(SUBT_TABLE_DUMPV2_RIB_GENERIC)
|
|
force_generic := true
|
|
|
|
if mrt.WriteAsAfiSafi {
|
|
if mrt.Afi == messages.AFI_IPV4 && mrt.Safi == messages.SAFI_UNICAST {
|
|
force_generic = false
|
|
subt = SUBT_TABLE_DUMPV2_RIB_IPV4_UNICAST
|
|
} else if mrt.Afi == messages.AFI_IPV4 && mrt.Safi == messages.SAFI_MULTICAST {
|
|
force_generic = false
|
|
subt = SUBT_TABLE_DUMPV2_RIB_IPV4_MULTICAST
|
|
} else if mrt.Afi == messages.AFI_IPV6 && mrt.Safi == messages.SAFI_UNICAST {
|
|
force_generic = false
|
|
subt = SUBT_TABLE_DUMPV2_RIB_IPV6_UNICAST
|
|
} else if mrt.Afi == messages.AFI_IPV6 && mrt.Safi == messages.SAFI_MULTICAST {
|
|
force_generic = false
|
|
subt = SUBT_TABLE_DUMPV2_RIB_IPV6_MULTICAST
|
|
}
|
|
}
|
|
return force_generic, subt
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_Rib) Write(buf io.Writer) {
|
|
force_generic, subt := mrt.GetSubtype()
|
|
|
|
WriteCommonHeader(buf, mrt.Timestamp, TYPE_TABLE_DUMPV2, subt, uint32(mrt.Len()))
|
|
if force_generic {
|
|
binary.Write(buf, binary.BigEndian, mrt.Afi)
|
|
binary.Write(buf, binary.BigEndian, mrt.Safi)
|
|
}
|
|
|
|
binary.Write(buf, binary.BigEndian, mrt.SequenceNumber)
|
|
binary.Write(buf, binary.BigEndian, mrt.NLRI.Bytes(false))
|
|
binary.Write(buf, binary.BigEndian, uint16(len(mrt.RibEntries)))
|
|
for i := range mrt.RibEntries {
|
|
mrt.RibEntries[i].Write(buf)
|
|
}
|
|
}
|
|
|
|
func (mrt *MrtTableDumpV2_Rib) Len() int {
|
|
force_generic, _ := mrt.GetSubtype()
|
|
size := 4 + len(mrt.NLRI.Bytes(false)) + 2
|
|
if force_generic {
|
|
size += 2
|
|
}
|
|
for i := range mrt.RibEntries {
|
|
size += int(mrt.RibEntries[i].Len())
|
|
}
|
|
return size
|
|
}
|
|
|
|
type MrtBGP4MP_Msg_AS4 struct {
|
|
Timestamp time.Time
|
|
PeerAS uint32
|
|
LocalAS uint32
|
|
IfaceIndex uint16
|
|
PeerIP net.IP
|
|
LocalIP net.IP
|
|
Message messages.SerializableInterface
|
|
}
|
|
|
|
type MrtBGP4MP_StateChange_AS4 struct {
|
|
Timestamp time.Time
|
|
PeerAS uint32
|
|
LocalAS uint32
|
|
IfaceIndex uint16
|
|
PeerIP net.IP
|
|
LocalIP net.IP
|
|
OldState uint16
|
|
NewState uint16
|
|
}
|
|
|
|
func NewMrtBGP4MP_StateChange_AS4(peeras uint32, localas uint32, iface uint16, peerip net.IP, localip net.IP, oldstate uint16, newstate uint16) *MrtBGP4MP_StateChange_AS4 {
|
|
return &MrtBGP4MP_StateChange_AS4{
|
|
Timestamp: time.Now().UTC(),
|
|
PeerAS: peeras,
|
|
LocalAS: localas,
|
|
IfaceIndex: iface,
|
|
PeerIP: peerip,
|
|
LocalIP: localip,
|
|
OldState: oldstate,
|
|
NewState: newstate,
|
|
}
|
|
}
|
|
|
|
func (mrt *MrtBGP4MP_StateChange_AS4) IsIPv4() bool {
|
|
return mrt.PeerIP.To4() != nil
|
|
}
|
|
|
|
func (mrt *MrtBGP4MP_StateChange_AS4) Len() int {
|
|
ipsize := 4
|
|
if !mrt.IsIPv4() {
|
|
ipsize = 16
|
|
}
|
|
return 4 + 4 + 2 + 2 + 2*ipsize + 2 + 2
|
|
}
|
|
|
|
func (mrt *MrtBGP4MP_StateChange_AS4) Write(buf io.Writer) {
|
|
WriteCommonHeader(buf, mrt.Timestamp, TYPE_BGP4MP, SUBT_BGP4MP_STATE_CHANGE_AS4, uint32(mrt.Len()))
|
|
|
|
binary.Write(buf, binary.BigEndian, mrt.PeerAS)
|
|
binary.Write(buf, binary.BigEndian, mrt.LocalAS)
|
|
|
|
binary.Write(buf, binary.BigEndian, mrt.IfaceIndex)
|
|
if mrt.IsIPv4() {
|
|
binary.Write(buf, binary.BigEndian, uint16(messages.AFI_IPV4))
|
|
binary.Write(buf, binary.BigEndian, mrt.PeerIP.To4())
|
|
binary.Write(buf, binary.BigEndian, mrt.LocalIP.To4())
|
|
} else {
|
|
binary.Write(buf, binary.BigEndian, uint16(messages.AFI_IPV6))
|
|
binary.Write(buf, binary.BigEndian, mrt.PeerIP)
|
|
binary.Write(buf, binary.BigEndian, mrt.LocalIP)
|
|
}
|
|
|
|
binary.Write(buf, binary.BigEndian, mrt.OldState)
|
|
binary.Write(buf, binary.BigEndian, mrt.NewState)
|
|
}
|
|
|
|
func NewMrtBGP4MP_Msg_AS4(peeras uint32, localas uint32, iface uint16, peerip net.IP, localip net.IP, message messages.SerializableInterface) *MrtBGP4MP_Msg_AS4 {
|
|
return &MrtBGP4MP_Msg_AS4{
|
|
Timestamp: time.Now().UTC(),
|
|
PeerAS: peeras,
|
|
LocalAS: localas,
|
|
IfaceIndex: iface,
|
|
PeerIP: peerip,
|
|
LocalIP: localip,
|
|
Message: message,
|
|
}
|
|
}
|
|
|
|
func (mrt *MrtBGP4MP_Msg_AS4) IsIPv4() bool {
|
|
return mrt.PeerIP.To4() != nil
|
|
}
|
|
|
|
func (mrt *MrtBGP4MP_Msg_AS4) Len() int {
|
|
ipsize := 4
|
|
if !mrt.IsIPv4() {
|
|
ipsize = 16
|
|
}
|
|
return 4 + 4 + 2 + 2 + 2*ipsize + mrt.Message.Len()
|
|
}
|
|
|
|
func (mrt *MrtBGP4MP_Msg_AS4) Write(buf io.Writer) {
|
|
WriteCommonHeader(buf, mrt.Timestamp, TYPE_BGP4MP, SUBT_BGP4MP_MESSAGE_AS4, uint32(mrt.Len()))
|
|
|
|
binary.Write(buf, binary.BigEndian, mrt.PeerAS)
|
|
binary.Write(buf, binary.BigEndian, mrt.LocalAS)
|
|
|
|
binary.Write(buf, binary.BigEndian, mrt.IfaceIndex)
|
|
if mrt.IsIPv4() {
|
|
binary.Write(buf, binary.BigEndian, uint16(messages.AFI_IPV4))
|
|
binary.Write(buf, binary.BigEndian, mrt.PeerIP.To4())
|
|
binary.Write(buf, binary.BigEndian, mrt.LocalIP.To4())
|
|
} else {
|
|
binary.Write(buf, binary.BigEndian, uint16(messages.AFI_IPV6))
|
|
binary.Write(buf, binary.BigEndian, mrt.PeerIP)
|
|
binary.Write(buf, binary.BigEndian, mrt.LocalIP)
|
|
}
|
|
mrt.Message.Write(buf)
|
|
}
|
|
|
|
func DecodeBGP4MP(buf io.Reader, timestamp time.Time, subtype uint16, length uint32) (Mrt, error) {
|
|
switch subtype {
|
|
case SUBT_BGP4MP_MESSAGE_AS4:
|
|
var peeras uint32
|
|
var localas uint32
|
|
var ifaceindex uint16
|
|
var afi uint16
|
|
binary.Read(buf, binary.BigEndian, peeras)
|
|
binary.Read(buf, binary.BigEndian, localas)
|
|
binary.Read(buf, binary.BigEndian, ifaceindex)
|
|
binary.Read(buf, binary.BigEndian, afi)
|
|
var peerip []byte
|
|
var localip []byte
|
|
sizeip := 4
|
|
if afi == messages.AFI_IPV6 {
|
|
sizeip = 16
|
|
}
|
|
peerip = make([]byte, sizeip)
|
|
localip = make([]byte, sizeip)
|
|
binary.Read(buf, binary.BigEndian, peerip)
|
|
binary.Read(buf, binary.BigEndian, localip)
|
|
|
|
msgsize := length - uint32(4+4+2+2+2*(sizeip))
|
|
if msgsize < 0 {
|
|
return nil, errors.New("DecodeBGP4MP: cannot decode message with negative length")
|
|
}
|
|
msg := make([]byte, msgsize)
|
|
// Do progressive read or replace parsepacketheader with io.Reader
|
|
binary.Read(buf, binary.BigEndian, msg)
|
|
|
|
bgptype, bgplen, err1 := messages.ParsePacketHeader(msg)
|
|
if err1 != nil {
|
|
return nil, err1
|
|
}
|
|
pktd, err2 := messages.ParsePacket(bgptype, msg[19:19+bgplen])
|
|
|
|
mrt := &MrtBGP4MP_Msg_AS4{
|
|
Timestamp: timestamp,
|
|
PeerAS: peeras,
|
|
LocalAS: localas,
|
|
IfaceIndex: ifaceindex,
|
|
PeerIP: net.IP(peerip),
|
|
LocalIP: net.IP(localip),
|
|
Message: pktd,
|
|
}
|
|
|
|
return mrt, err2
|
|
case SUBT_BGP4MP_STATE_CHANGE_AS4:
|
|
var peeras uint32
|
|
var localas uint32
|
|
var ifaceindex uint16
|
|
var afi uint16
|
|
binary.Read(buf, binary.BigEndian, peeras)
|
|
binary.Read(buf, binary.BigEndian, localas)
|
|
binary.Read(buf, binary.BigEndian, ifaceindex)
|
|
binary.Read(buf, binary.BigEndian, afi)
|
|
var peerip []byte
|
|
var localip []byte
|
|
sizeip := 4
|
|
if afi == messages.AFI_IPV6 {
|
|
sizeip = 16
|
|
}
|
|
peerip = make([]byte, sizeip)
|
|
localip = make([]byte, sizeip)
|
|
binary.Read(buf, binary.BigEndian, peerip)
|
|
binary.Read(buf, binary.BigEndian, localip)
|
|
var oldstate uint16
|
|
var newstate uint16
|
|
binary.Read(buf, binary.BigEndian, oldstate)
|
|
binary.Read(buf, binary.BigEndian, newstate)
|
|
|
|
mrt := &MrtBGP4MP_StateChange_AS4{
|
|
Timestamp: timestamp,
|
|
PeerAS: peeras,
|
|
LocalAS: localas,
|
|
IfaceIndex: ifaceindex,
|
|
PeerIP: net.IP(peerip),
|
|
LocalIP: net.IP(localip),
|
|
OldState: oldstate,
|
|
NewState: newstate,
|
|
}
|
|
|
|
return mrt, nil
|
|
default:
|
|
return nil, errors.New(fmt.Sprintf("Decoding of subtype %v of BGP4MP not implemented", subtype))
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func DecodeNLRI(buf io.Reader, afi uint16, safi byte) (messages.NLRI, error) {
|
|
if afi != messages.AFI_IPV4 && afi != messages.AFI_IPV6 {
|
|
return nil, errors.New(fmt.Sprintf("Could not decode NLRI for Afi: %v", afi))
|
|
}
|
|
if safi != messages.SAFI_UNICAST && safi != messages.SAFI_MULTICAST {
|
|
return nil, errors.New(fmt.Sprintf("Could not decode NLRI for Safi: %v", safi))
|
|
}
|
|
|
|
var l byte
|
|
binary.Read(buf, binary.BigEndian, &l)
|
|
|
|
size := l / 8
|
|
if l%8 != 0 {
|
|
size++
|
|
}
|
|
b := make([]byte, size)
|
|
binary.Read(buf, binary.BigEndian, &b)
|
|
|
|
newb := append([]byte{l}, b...)
|
|
nlri, err := messages.ParseNLRI(newb, afi, safi, false)
|
|
|
|
if len(nlri) == 1 {
|
|
return nlri[0], err
|
|
} else {
|
|
return nil, errors.New(fmt.Sprintf("Could not decode NLRI %v (%v/%v) (number of results != 1): %v", newb, afi, safi, err))
|
|
}
|
|
}
|
|
|
|
func DecodeAttributes(buf io.Reader, attrlen uint16) ([]messages.BGPAttributeIf, error) {
|
|
b := make([]byte, attrlen)
|
|
binary.Read(buf, binary.BigEndian, b)
|
|
return messages.ParsePathAttribute(b, nil, false)
|
|
}
|
|
|
|
func DecodeRibEntries(buf io.Reader) (*RibEntry, error) {
|
|
var peerindex uint16
|
|
var origints uint32
|
|
var attrlen uint16
|
|
binary.Read(buf, binary.BigEndian, &peerindex)
|
|
binary.Read(buf, binary.BigEndian, &origints)
|
|
binary.Read(buf, binary.BigEndian, &attrlen)
|
|
attrs, err := DecodeAttributes(buf, attrlen)
|
|
origintsP := time.Unix(int64(origints), 0)
|
|
re := &RibEntry{
|
|
OrigTime: origintsP,
|
|
PeerIndex: peerindex,
|
|
Attributes: attrs,
|
|
}
|
|
return re, err
|
|
}
|
|
|
|
func DecodeBGP4TD2RIBSpec(buf io.Reader, subtype uint16, timestamp time.Time) (Mrt, error) {
|
|
var afi uint16
|
|
var safi byte
|
|
if subtype == SUBT_TABLE_DUMPV2_RIB_IPV4_UNICAST {
|
|
afi = messages.AFI_IPV4
|
|
safi = messages.SAFI_UNICAST
|
|
} else if subtype == SUBT_TABLE_DUMPV2_RIB_IPV6_UNICAST {
|
|
afi = messages.AFI_IPV6
|
|
safi = messages.SAFI_UNICAST
|
|
} else if subtype == SUBT_TABLE_DUMPV2_RIB_IPV4_MULTICAST {
|
|
afi = messages.AFI_IPV4
|
|
safi = messages.SAFI_MULTICAST
|
|
} else if subtype == SUBT_TABLE_DUMPV2_RIB_IPV6_MULTICAST {
|
|
afi = messages.AFI_IPV6
|
|
safi = messages.SAFI_MULTICAST
|
|
} else {
|
|
return nil, errors.New("Cannot decode as Rib Afi/Safi specific")
|
|
}
|
|
|
|
var seqnum uint32
|
|
var preflen uint8
|
|
var prefix []byte
|
|
binary.Read(buf, binary.BigEndian, &seqnum)
|
|
binary.Read(buf, binary.BigEndian, &preflen)
|
|
|
|
size := preflen / 8
|
|
if preflen%8 != 0 {
|
|
size++
|
|
}
|
|
prefix = make([]byte, size)
|
|
binary.Read(buf, binary.BigEndian, &prefix)
|
|
|
|
newb := append([]byte{preflen}, prefix...)
|
|
nlri, err := messages.ParseNLRI(newb, afi, safi, false)
|
|
|
|
mrt := &MrtTableDumpV2_Rib{
|
|
Timestamp: timestamp,
|
|
Afi: afi,
|
|
Safi: safi,
|
|
SequenceNumber: seqnum,
|
|
WriteAsAfiSafi: true,
|
|
}
|
|
|
|
if len(nlri) == 1 {
|
|
mrt.NLRI = nlri[0]
|
|
} else {
|
|
return mrt, errors.New(fmt.Sprintf("Could not decode NLRI %v (%v/%v) (number of results != 1): %v", newb, afi, safi, err))
|
|
}
|
|
|
|
if err != nil {
|
|
return mrt, err
|
|
}
|
|
|
|
var entrycount uint16
|
|
binary.Read(buf, binary.BigEndian, &entrycount)
|
|
entries := make([]*RibEntry, entrycount)
|
|
var errentry error
|
|
for i := 0; i < int(entrycount); i++ {
|
|
var entry *RibEntry
|
|
entry, errentry = DecodeRibEntries(buf)
|
|
entries[i] = entry
|
|
}
|
|
mrt.RibEntries = entries
|
|
return mrt, errentry
|
|
}
|
|
|
|
func DecodeBGP4TD2(buf io.Reader, timestamp time.Time, subtype uint16, length uint32) (Mrt, error) {
|
|
switch subtype {
|
|
case SUBT_TABLE_DUMPV2_PEER_INDEX_TABLE:
|
|
collid := make([]byte, 4)
|
|
var viewnamelen uint16
|
|
var peercount uint16
|
|
var viewname []byte
|
|
var peers []*Peer
|
|
|
|
binary.Read(buf, binary.BigEndian, &collid)
|
|
binary.Read(buf, binary.BigEndian, &viewnamelen)
|
|
|
|
viewname = make([]byte, viewnamelen)
|
|
|
|
binary.Read(buf, binary.BigEndian, &viewname)
|
|
binary.Read(buf, binary.BigEndian, &peercount)
|
|
|
|
peers = make([]*Peer, peercount)
|
|
|
|
for i := 0; i < int(peercount); i++ {
|
|
var peertype uint8
|
|
bgpid := make([]byte, 4)
|
|
var asn uint32
|
|
var peerip []byte
|
|
|
|
binary.Read(buf, binary.BigEndian, &peertype)
|
|
binary.Read(buf, binary.BigEndian, &bgpid)
|
|
|
|
sizeip := 4
|
|
sizeasn := 2
|
|
if peertype&0x2 != 0 {
|
|
sizeasn = 4
|
|
}
|
|
if peertype&0x1 != 0 {
|
|
sizeip = 16
|
|
}
|
|
tmpasn := make([]byte, sizeasn)
|
|
peerip = make([]byte, sizeip)
|
|
|
|
binary.Read(buf, binary.BigEndian, &peerip)
|
|
binary.Read(buf, binary.BigEndian, &tmpasn)
|
|
|
|
if sizeasn == 2 {
|
|
asn = uint32(binary.BigEndian.Uint16(tmpasn))
|
|
} else if sizeasn == 4 {
|
|
asn = binary.BigEndian.Uint32(tmpasn)
|
|
}
|
|
|
|
curpeer := &Peer{
|
|
Id: bgpid,
|
|
IP: peerip,
|
|
ASN: asn,
|
|
}
|
|
|
|
peers[i] = curpeer
|
|
}
|
|
|
|
mrt := &MrtTableDumpV2_PeerIndex{
|
|
Timestamp: timestamp,
|
|
CollectorId: collid,
|
|
ViewName: string(viewname),
|
|
Peers: peers,
|
|
}
|
|
return mrt, nil
|
|
|
|
case SUBT_TABLE_DUMPV2_RIB_GENERIC:
|
|
var seqnum uint32
|
|
var afi uint16
|
|
var safi byte
|
|
binary.Read(buf, binary.BigEndian, &seqnum)
|
|
binary.Read(buf, binary.BigEndian, &afi)
|
|
binary.Read(buf, binary.BigEndian, &safi)
|
|
|
|
nlri, err := DecodeNLRI(buf, afi, safi)
|
|
|
|
mrt := &MrtTableDumpV2_Rib{
|
|
Timestamp: timestamp,
|
|
Afi: afi,
|
|
Safi: safi,
|
|
SequenceNumber: seqnum,
|
|
NLRI: nlri,
|
|
}
|
|
if err != nil {
|
|
return mrt, err
|
|
}
|
|
|
|
var entrycount uint16
|
|
binary.Read(buf, binary.BigEndian, &entrycount)
|
|
entries := make([]*RibEntry, entrycount)
|
|
var errentry error
|
|
for i := 0; i < int(entrycount); i++ {
|
|
var entry *RibEntry
|
|
entry, errentry = DecodeRibEntries(buf)
|
|
entries[i] = entry
|
|
}
|
|
mrt.RibEntries = entries
|
|
return mrt, errentry
|
|
case SUBT_TABLE_DUMPV2_RIB_IPV4_UNICAST:
|
|
return DecodeBGP4TD2RIBSpec(buf, subtype, timestamp)
|
|
case SUBT_TABLE_DUMPV2_RIB_IPV6_UNICAST:
|
|
return DecodeBGP4TD2RIBSpec(buf, subtype, timestamp)
|
|
case SUBT_TABLE_DUMPV2_RIB_IPV4_MULTICAST:
|
|
return DecodeBGP4TD2RIBSpec(buf, subtype, timestamp)
|
|
case SUBT_TABLE_DUMPV2_RIB_IPV6_MULTICAST:
|
|
return DecodeBGP4TD2RIBSpec(buf, subtype, timestamp)
|
|
default:
|
|
return nil, errors.New(fmt.Sprintf("Decoding of subtype %v of BGP4TableDumpV2 not implemented", subtype))
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func DecodeSingle(buf io.Reader) (Mrt, error) {
|
|
var timestamp uint32
|
|
var mrttype uint16
|
|
var mrtsubtype uint16
|
|
var mrtlength uint32
|
|
|
|
binary.Read(buf, binary.BigEndian, ×tamp)
|
|
binary.Read(buf, binary.BigEndian, &mrttype)
|
|
binary.Read(buf, binary.BigEndian, &mrtsubtype)
|
|
binary.Read(buf, binary.BigEndian, &mrtlength)
|
|
|
|
timestampP := time.Unix(int64(timestamp), 0)
|
|
|
|
content := make([]byte, mrtlength)
|
|
binary.Read(buf, binary.BigEndian, &content)
|
|
tmpbuf := bytes.NewBuffer(content)
|
|
|
|
var mrt Mrt
|
|
var err error
|
|
switch mrttype {
|
|
case TYPE_BGP4MP:
|
|
mrt, err = DecodeBGP4MP(tmpbuf, timestampP, mrtsubtype, mrtlength)
|
|
case TYPE_TABLE_DUMPV2:
|
|
mrt, err = DecodeBGP4TD2(tmpbuf, timestampP, mrtsubtype, mrtlength)
|
|
default:
|
|
err = errors.New(fmt.Sprintf("Decoding of type %v not implemented", mrttype))
|
|
}
|
|
|
|
return mrt, err
|
|
}
|