diff options
author | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2017-05-17 11:07:18 +0900 |
---|---|---|
committer | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2017-05-22 13:12:02 +0900 |
commit | 5bb073815657d605c77376a51e6219a593eb7af6 (patch) | |
tree | 08a6539364618a0cffc42c4930831937b21b1510 | |
parent | fa1378f58bb6306e0c327f2a69a798a46c9865a4 (diff) |
packet/bmp: Implement BMPStatisticsReport serializer
Currently, only parser for BMPStatisticsReport is implemented and does
not support the per-AFI/SAFI stats TLV types.
This patch implements BMPStatisticsReport serializer and re-implements
the stats TLV parser/serializer to support the missing TLV types.
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
-rw-r--r-- | packet/bmp/bmp.go | 207 | ||||
-rw-r--r-- | packet/bmp/bmp_test.go | 13 |
2 files changed, 181 insertions, 39 deletions
diff --git a/packet/bmp/bmp.go b/packet/bmp/bmp.go index d931643c..3cdccf46 100644 --- a/packet/bmp/bmp.go +++ b/packet/bmp/bmp.go @@ -195,15 +195,181 @@ const ( BMP_STAT_TYPE_DUPLICATE_UPDATE ) +type BMPStatsTLVInterface interface { + ParseValue([]byte) error + Serialize() ([]byte, error) +} + type BMPStatsTLV struct { Type uint16 Length uint16 - Value uint64 +} + +type BMPStatsTLV32 struct { + BMPStatsTLV + Value uint32 +} + +func NewBMPStatsTLV32(t uint16, v uint32) *BMPStatsTLV32 { + return &BMPStatsTLV32{ + BMPStatsTLV: BMPStatsTLV{ + Type: t, + Length: 4, + }, + Value: v, + } +} + +func (s *BMPStatsTLV32) ParseValue(data []byte) error { + if s.Length != 4 { + return fmt.Errorf("invalid lengh: %d bytes (%d bytes expected)", s.Length, 4) + } + s.Value = binary.BigEndian.Uint32(data[:8]) + return nil +} + +func (s *BMPStatsTLV32) Serialize() ([]byte, error) { + buf := make([]byte, 8) + binary.BigEndian.PutUint16(buf[0:2], s.Type) + binary.BigEndian.PutUint16(buf[2:4], 4) + binary.BigEndian.PutUint32(buf[4:8], s.Value) + return buf, nil +} + +type BMPStatsTLV64 struct { + BMPStatsTLV + Value uint64 +} + +func NewBMPStatsTLV64(t uint16, v uint64) *BMPStatsTLV64 { + return &BMPStatsTLV64{ + BMPStatsTLV: BMPStatsTLV{ + Type: t, + Length: 8, + }, + Value: v, + } +} + +func (s *BMPStatsTLV64) ParseValue(data []byte) error { + if s.Length != 8 { + return fmt.Errorf("invalid lengh: %d bytes (%d bytes expected)", s.Length, 8) + } + s.Value = binary.BigEndian.Uint64(data[:8]) + return nil +} + +func (s *BMPStatsTLV64) Serialize() ([]byte, error) { + buf := make([]byte, 12) + binary.BigEndian.PutUint16(buf[0:2], s.Type) + binary.BigEndian.PutUint16(buf[2:4], 8) + binary.BigEndian.PutUint64(buf[4:12], s.Value) + return buf, nil +} + +type BMPStatsTLVPerAfiSafi64 struct { + BMPStatsTLV + AFI uint16 + SAFI uint8 + Value uint64 +} + +func NewBMPStatsTLVPerAfiSafi64(t uint16, afi uint16, safi uint8, v uint64) *BMPStatsTLVPerAfiSafi64 { + return &BMPStatsTLVPerAfiSafi64{ + BMPStatsTLV: BMPStatsTLV{ + Type: t, + Length: 11, + }, + AFI: afi, + SAFI: safi, + Value: v, + } +} + +func (s *BMPStatsTLVPerAfiSafi64) ParseValue(data []byte) error { + if s.Length != 11 { + return fmt.Errorf("invalid lengh: %d bytes (%d bytes expected)", s.Length, 11) + } + s.AFI = binary.BigEndian.Uint16(data[0:2]) + s.SAFI = data[2] + s.Value = binary.BigEndian.Uint64(data[3:11]) + return nil +} + +func (s *BMPStatsTLVPerAfiSafi64) Serialize() ([]byte, error) { + buf := make([]byte, 15) + binary.BigEndian.PutUint16(buf[0:2], s.Type) + binary.BigEndian.PutUint16(buf[2:4], 11) + binary.BigEndian.PutUint16(buf[4:6], s.AFI) + buf[6] = s.SAFI + binary.BigEndian.PutUint64(buf[7:15], s.Value) + return buf, nil } type BMPStatisticsReport struct { Count uint32 - Stats []BMPStatsTLV + Stats []BMPStatsTLVInterface +} + +func NewBMPStatisticsReport(p BMPPeerHeader, stats []BMPStatsTLVInterface) *BMPMessage { + return &BMPMessage{ + Header: BMPHeader{ + Version: BMP_VERSION, + Type: BMP_MSG_STATISTICS_REPORT, + }, + PeerHeader: p, + Body: &BMPStatisticsReport{ + Count: uint32(len(stats)), + Stats: stats, + }, + } +} + +func (body *BMPStatisticsReport) ParseBody(msg *BMPMessage, data []byte) error { + body.Count = binary.BigEndian.Uint32(data[0:4]) + data = data[4:] + for len(data) >= 4 { + tl := BMPStatsTLV{ + Type: binary.BigEndian.Uint16(data[0:2]), + Length: binary.BigEndian.Uint16(data[2:4]), + } + data = data[4:] + if len(data) < int(tl.Length) { + return fmt.Errorf("value lengh is not enough: %d bytes (%d bytes expected)", len(data), tl.Length) + } + var s BMPStatsTLVInterface + var err error = nil + if tl.Type == BMP_STAT_TYPE_ADJ_RIB_IN || tl.Type == BMP_STAT_TYPE_LOC_RIB { + s = &BMPStatsTLV64{BMPStatsTLV: tl} + err = s.ParseValue(data) + } else if tl.Type == BMP_STAT_TYPE_PER_AFI_SAFI_ADJ_RIB_IN || tl.Type == BMP_STAT_TYPE_PER_AFI_SAFI_LOC_RIB { + s = &BMPStatsTLVPerAfiSafi64{BMPStatsTLV: tl} + err = s.ParseValue(data) + } else { + s = &BMPStatsTLV32{BMPStatsTLV: tl} + err = s.ParseValue(data) + } + if err != nil { + return err + } + body.Stats = append(body.Stats, s) + data = data[tl.Length:] + } + return nil +} + +func (body *BMPStatisticsReport) Serialize() ([]byte, error) { + buf := make([]byte, 4) + body.Count = uint32(len(body.Stats)) + binary.BigEndian.PutUint32(buf[0:4], body.Count) + for _, tlv := range body.Stats { + tlvBuf, err := tlv.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, tlvBuf...) + } + return buf, nil } const ( @@ -350,43 +516,6 @@ func (body *BMPPeerUpNotification) Serialize() ([]byte, error) { 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 -} - const ( BMP_INIT_TLV_TYPE_STRING = iota BMP_INIT_TLV_TYPE_SYS_DESCR diff --git a/packet/bmp/bmp_test.go b/packet/bmp/bmp_test.go index 3d7737e0..5e70710f 100644 --- a/packet/bmp/bmp_test.go +++ b/packet/bmp/bmp_test.go @@ -67,6 +67,19 @@ func Test_RouteMonitoring(t *testing.T) { verify(t, NewBMPRouteMonitoring(*p0, m)) } +func Test_StatisticsReport(t *testing.T) { + p0 := NewBMPPeerHeader(0, 0, 1000, "10.0.0.1", 70000, "10.0.0.2", 1) + s0 := NewBMPStatisticsReport( + *p0, + []BMPStatsTLVInterface{ + NewBMPStatsTLV32(BMP_STAT_TYPE_REJECTED, 100), + NewBMPStatsTLV64(BMP_STAT_TYPE_ADJ_RIB_IN, 200), + NewBMPStatsTLVPerAfiSafi64(BMP_STAT_TYPE_PER_AFI_SAFI_LOC_RIB, bgp.AFI_IP, bgp.SAFI_UNICAST, 300), + }, + ) + verify(t, s0) +} + func Test_BogusHeader(t *testing.T) { h, err := ParseBMPMessage(make([]byte, 10)) assert.Nil(t, h) |