Skip to content

Commit

Permalink
packet/mrt: BGP with Geo-Location Extensions (RFC6397)
Browse files Browse the repository at this point in the history
This patch enables to decode/encode MRT format with BGP routing
information including the geographical location which described in
RFC6397.

Signed-off-by: IWASE Yusuke <[email protected]>
  • Loading branch information
iwaseyusuke committed Jun 8, 2017
1 parent 7a34c7c commit 6b710ba
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 1 deletion.
2 changes: 2 additions & 0 deletions gobgp/cmd/mrt.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ func injectMrt(filename string, count int, skip int, onlyBest bool) error {
peers = msg.Body.(*mrt.PeerIndexTable).Peers
continue
case mrt.RIB_IPV4_UNICAST, mrt.RIB_IPV6_UNICAST:
case mrt.GEO_PEER_TABLE:
fmt.Printf("WARNING: Skipping GEO_PEER_TABLE: %s", msg.Body.(*mrt.GeoPeerTable))
default:
exitWithError(fmt.Errorf("unsupported subType: %v", subType))
}
Expand Down
114 changes: 113 additions & 1 deletion packet/mrt/mrt.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const (
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
Expand Down Expand Up @@ -542,6 +543,115 @@ 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
Expand Down Expand Up @@ -812,6 +922,8 @@ func ParseMRTBody(h *MRTHeader, data []byte) (*MRTMessage, error) {
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
Expand All @@ -830,7 +942,7 @@ func ParseMRTBody(h *MRTHeader, data []byte) (*MRTMessage, error) {
return nil, fmt.Errorf("unsupported table dumpv2 subtype: %v\n", subType)
}

if subType != PEER_INDEX_TABLE {
if msg.Body == nil {
msg.Body = &Rib{
RouteFamily: rf,
isAddPath: isAddPath,
Expand Down
16 changes: 16 additions & 0 deletions packet/mrt/mrt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,22 @@ func TestMrtRibWithAddPath(t *testing.T) {
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()
Expand Down

0 comments on commit 6b710ba

Please sign in to comment.