diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go new file mode 100644 index 0000000000000000000000000000000000000000..82205b5fa8acc0fcb510764f6052986a7b8d0281 --- /dev/null +++ b/protocols/bgp/packet/mp_reach_nlri.go @@ -0,0 +1,48 @@ +package packet + +import ( + "bytes" + "math" + + "github.com/taktv6/tflow2/convert" + + bnet "github.com/bio-routing/bio-rd/net" +) + +// MultiProtocolReachNLRI represents Network Layer Reachability Information for one prefix of an IP address family (rfc4760) +type MultiProtocolReachNLRI struct { + AFI uint16 + SAFI uint8 + NextHop bnet.IP + Prefix bnet.Prefix +} + +func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 { + nextHop := n.NextHop.Bytes() + + tempBuf := bytes.NewBuffer(nil) + tempBuf.Write(convert.Uint16Byte(n.AFI)) + tempBuf.WriteByte(n.SAFI) + tempBuf.WriteByte(uint8(len(nextHop))) // NextHop length + tempBuf.Write(nextHop) + tempBuf.WriteByte(0) // RESERVED + tempBuf.Write(n.serializePrefix()) + + buf.Write(tempBuf.Bytes()) + + return uint8(tempBuf.Len()) +} + +func (n *MultiProtocolReachNLRI) serializePrefix() []byte { + if n.Prefix.Pfxlen() == 0 { + return []byte{} + } + + numBytes := uint8(math.Ceil(float64(n.Prefix.Pfxlen()) / float64(8))) + + b := make([]byte, numBytes+1) + b[0] = n.Prefix.Pfxlen() + copy(b[1:numBytes+1], n.Prefix.Addr().Bytes()[0:numBytes]) + + return b +} diff --git a/protocols/bgp/packet/mp_reach_nlri_test.go b/protocols/bgp/packet/mp_reach_nlri_test.go new file mode 100644 index 0000000000000000000000000000000000000000..935c6661f3ea84a8cb9ad07c3eeac2284c9bd314 --- /dev/null +++ b/protocols/bgp/packet/mp_reach_nlri_test.go @@ -0,0 +1,42 @@ +package packet + +import ( + "bytes" + "testing" + + bnet "github.com/bio-routing/bio-rd/net" + "github.com/stretchr/testify/assert" +) + +func TestSerializeMultiProtocolNLRI(t *testing.T) { + tests := []struct { + name string + nlri MultiProtocolReachNLRI + expected []byte + }{ + { + name: "Simple IPv6 prefix", + nlri: MultiProtocolReachNLRI{ + AFI: IPv6AFI, + SAFI: UnicastSAFI, + NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2), + Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48), + }, + expected: []byte{ + 0x00, 0x02, // AFI + 0x01, // SAFI + 0x10, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // NextHop + 0x00, // RESERVED + 0x30, 0x26, 0x00, 0x00, 0x06, 0xff, 0x05, // Prefix + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + buf := &bytes.Buffer{} + test.nlri.serialize(buf) + assert.Equal(t, test.expected, buf.Bytes()) + }) + } +}