Skip to content
Snippets Groups Projects
Commit 640b0893 authored by Daniel Czerwonk's avatar Daniel Czerwonk
Browse files

added MP_UNREACH and MP_REACH decoding

parent 5ecbc83c
No related branches found
No related tags found
No related merge requests found
...@@ -41,6 +41,27 @@ func IPv6FromBlocks(b1, b2, b3, b4, b5, b6, b7, b8 uint16) IP { ...@@ -41,6 +41,27 @@ func IPv6FromBlocks(b1, b2, b3, b4, b5, b6, b7, b8 uint16) IP {
uint64(uint64(b5)<<48+uint64(b6)<<32+uint64(b7)<<16+uint64(b8))) uint64(uint64(b5)<<48+uint64(b6)<<32+uint64(b7)<<16+uint64(b8)))
} }
// IPFromBytes returns an IP address for a byte slice
func IPFromBytes(b []byte) (IP, error) {
if len(b) == 4 {
return IPv4FromOctets(b[0], b[1], b[2], b[3]), nil
}
if len(b) == 16 {
return IPv6FromBlocks(
uint16(b[0])<<8+uint16(b[1]),
uint16(b[2])<<8+uint16(b[3]),
uint16(b[4])<<8+uint16(b[5]),
uint16(b[6])<<8+uint16(b[7]),
uint16(b[8])<<8+uint16(b[9]),
uint16(b[10])<<8+uint16(b[11]),
uint16(b[12])<<8+uint16(b[13]),
uint16(b[14])<<8+uint16(b[15])), nil
}
return IP{}, fmt.Errorf("byte slice has an invalid legth. Expected either 4 (IPv4) or 16 (IPv6) bytes but got: %d", len(b))
}
// Equal returns true if ip is equal to other // Equal returns true if ip is equal to other
func (ip IP) Equal(other IP) bool { func (ip IP) Equal(other IP) bool {
return ip == other return ip == other
......
...@@ -228,6 +228,50 @@ func TestIPv6FromBlocks(t *testing.T) { ...@@ -228,6 +228,50 @@ func TestIPv6FromBlocks(t *testing.T) {
} }
} }
func TestIPFromBytes(t *testing.T) {
tests := []struct {
name string
bytes []byte
expected IP
wantFail bool
}{
{
name: "IPV4: 172.217.16.195",
bytes: []byte{172, 217, 16, 195},
expected: IP{
higher: 0,
lower: 2899906755,
ipVersion: 4,
},
},
{
name: "IPV6: IPv6 2001:678:1E0:1234:5678:DEAD:BEEF:CAFE",
bytes: []byte{0x20, 0x01, 0x06, 0x78, 0x01, 0xE0, 0x12, 0x34, 0x56, 0x78, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE},
expected: IP{
higher: 2306131596687708724,
lower: 6230974922281175806,
ipVersion: 6,
},
},
{
name: "invalid length",
bytes: []byte{172, 217, 123, 231, 95},
wantFail: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ip, err := IPFromBytes(test.bytes)
if err == nil && test.wantFail {
t.Fatalf("Expected test to fail, but did not")
}
assert.Equal(t, test.expected, ip)
})
}
}
func TestToNetIP(t *testing.T) { func TestToNetIP(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
......
...@@ -85,17 +85,26 @@ const ( ...@@ -85,17 +85,26 @@ const (
ConnectionCollisionResolution = 7 ConnectionCollisionResolution = 7
OutOfResoutces = 8 OutOfResoutces = 8
IPv4AFI = 1 IPv4AFI = 1
IPv6AFI = 2 IPv6AFI = 2
UnicastSAFI = 1 UnicastSAFI = 1
CapabilitiesParamType = 2 CapabilitiesParamType = 2
MultiProtocolCapabilityCode = 1 MultiProtocolCapabilityCode = 1
AddPathCapabilityCode = 69 MultiProtocolReachNLRICode = 14
ASN4CapabilityCode = 65 MultiProtocolUnreachNLRICode = 15
AddPathReceive = 1 AddPathCapabilityCode = 69
AddPathSend = 2 ASN4CapabilityCode = 65
AddPathSendReceive = 3 AddPathReceive = 1
ASTransASN = 23456 AddPathSend = 2
AddPathSendReceive = 3
ASTransASN = 23456
)
var (
afiAddrLenBytes = map[uint16]uint8{
1: 4,
2: 16,
}
) )
type BGPError struct { type BGPError struct {
......
package packet package packet
import ( import (
"fmt"
"math" "math"
bnet "github.com/bio-routing/bio-rd/net" bnet "github.com/bio-routing/bio-rd/net"
...@@ -11,7 +12,7 @@ func serializePrefix(pfx bnet.Prefix) []byte { ...@@ -11,7 +12,7 @@ func serializePrefix(pfx bnet.Prefix) []byte {
return []byte{} return []byte{}
} }
numBytes := uint8(math.Ceil(float64(pfx.Pfxlen()) / float64(8))) numBytes := uint8(math.Ceil(float64(pfx.Pfxlen()) / 8))
b := make([]byte, numBytes+1) b := make([]byte, numBytes+1)
b[0] = pfx.Pfxlen() b[0] = pfx.Pfxlen()
...@@ -19,3 +20,21 @@ func serializePrefix(pfx bnet.Prefix) []byte { ...@@ -19,3 +20,21 @@ func serializePrefix(pfx bnet.Prefix) []byte {
return b return b
} }
func deserializePrefix(b []byte, pfxLen uint8, afi uint16) (bnet.Prefix, error) {
numBytes := int(math.Ceil(float64(pfxLen) / 8))
if numBytes != len(b) {
return bnet.Prefix{}, fmt.Errorf("could not parse prefix of legth %d. Expected %d bytes, got %d", pfxLen, numBytes, len(b))
}
ipBytes := make([]byte, afiAddrLenBytes[afi])
copy(ipBytes, b)
ip, err := bnet.IPFromBytes(ipBytes)
if err != nil {
return bnet.Prefix{}, err
}
return bnet.NewPfx(ip, pfxLen), nil
}
...@@ -2,6 +2,7 @@ package packet ...@@ -2,6 +2,7 @@ package packet
import ( import (
"bytes" "bytes"
"fmt"
"github.com/taktv6/tflow2/convert" "github.com/taktv6/tflow2/convert"
...@@ -31,3 +32,32 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 { ...@@ -31,3 +32,32 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 {
return uint8(tempBuf.Len()) return uint8(tempBuf.Len())
} }
func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error) {
n := MultiProtocolReachNLRI{}
nextHopLength := uint8(0)
variable := make([]byte, len(b)-4)
fields := []interface{}{
&n.AFI,
&n.SAFI,
&nextHopLength,
&variable,
}
err := decode(bytes.NewBuffer(b), fields)
if err != nil {
return MultiProtocolReachNLRI{}, err
}
n.NextHop, err = bnet.IPFromBytes(variable[:nextHopLength])
if err != nil {
return MultiProtocolReachNLRI{}, fmt.Errorf("Failed to decode next hop IP: %v", err)
}
n.Prefix, err = deserializePrefix(variable[nextHopLength+2:], variable[nextHopLength+1], n.AFI)
if err != nil {
return MultiProtocolReachNLRI{}, err
}
return n, nil
}
...@@ -24,3 +24,25 @@ func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint8 { ...@@ -24,3 +24,25 @@ func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint8 {
return uint8(tempBuf.Len()) return uint8(tempBuf.Len())
} }
func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, error) {
n := MultiProtocolUnreachNLRI{}
prefix := make([]byte, len(b)-3)
fields := []interface{}{
&n.AFI,
&n.SAFI,
&prefix,
}
err := decode(bytes.NewBuffer(b), fields)
if err != nil {
return MultiProtocolUnreachNLRI{}, err
}
n.Prefix, err = deserializePrefix(prefix[1:], prefix[0], n.AFI)
if err != nil {
return MultiProtocolUnreachNLRI{}, err
}
return n, nil
}
...@@ -99,6 +99,14 @@ func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, c ...@@ -99,6 +99,14 @@ func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, c
if err := pa.decodeCommunities(buf); err != nil { if err := pa.decodeCommunities(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode Community: %v", err) return nil, consumed, fmt.Errorf("Failed to decode Community: %v", err)
} }
case MultiProtocolReachNLRICode:
if err := pa.decodeMultiProtocolReachNLRI(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to multi protocol reachable NLRI: %v", err)
}
case MultiProtocolUnreachNLRICode:
if err := pa.decodeMultiProtocolUnreachNLRI(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to multi protocol unreachable NLRI: %v", err)
}
case AS4AggregatorAttr: case AS4AggregatorAttr:
if err := pa.decodeAS4Aggregator(buf); err != nil { if err := pa.decodeAS4Aggregator(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Aggregator: %v", err) return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Aggregator: %v", err)
...@@ -116,18 +124,53 @@ func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, c ...@@ -116,18 +124,53 @@ func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, c
return pa, consumed + pa.Length, nil return pa, consumed + pa.Length, nil
} }
func (pa *PathAttribute) decodeMultiProtocolReachNLRI(buf *bytes.Buffer) error {
b := make([]byte, pa.Length)
n, err := buf.Read(b)
if err != nil {
return fmt.Errorf("Unable to read %d bytes from buffer: %v", pa.Length, err)
}
if n != int(pa.Length) {
return fmt.Errorf("Unable to read %d bytes from buffer, only got %d bytes", pa.Length, n)
}
nlri, err := deserializeMultiProtocolReachNLRI(b)
if err != nil {
return fmt.Errorf("Unable to decode MP_REACH_NLRI: %v", err)
}
pa.Value = nlri
return nil
}
func (pa *PathAttribute) decodeMultiProtocolUnreachNLRI(buf *bytes.Buffer) error {
b := make([]byte, pa.Length)
n, err := buf.Read(b)
if err != nil {
return fmt.Errorf("Unable to read %d bytes from buffer: %v", pa.Length, err)
}
if n != int(pa.Length) {
return fmt.Errorf("Unable to read %d bytes from buffer, only got %d bytes", pa.Length, n)
}
nlri, err := deserializeMultiProtocolUnreachNLRI(b)
if err != nil {
return fmt.Errorf("Unable to decode MP_UNREACH_NLRI: %v", err)
}
pa.Value = nlri
return nil
}
func (pa *PathAttribute) decodeUnknown(buf *bytes.Buffer) error { func (pa *PathAttribute) decodeUnknown(buf *bytes.Buffer) error {
u := make([]byte, pa.Length) u := make([]byte, pa.Length)
p := uint16(0)
err := decode(buf, []interface{}{&u}) err := decode(buf, []interface{}{&u})
if err != nil { if err != nil {
return fmt.Errorf("Unable to decode: %v", err) return fmt.Errorf("Unable to decode: %v", err)
} }
pa.Value = u pa.Value = u
p += pa.Length
return nil return nil
} }
......
...@@ -746,6 +746,133 @@ func TestDecodeCommunity(t *testing.T) { ...@@ -746,6 +746,133 @@ func TestDecodeCommunity(t *testing.T) {
} }
} }
func TestDecodeMultiProtocolReachNLRI(t *testing.T) {
tests := []struct {
name string
input []byte
wantFail bool
explicitLength uint16
expected *PathAttribute
}{
{
name: "incomplete",
input: []byte{0, 0, 0, 0},
wantFail: true,
explicitLength: 32,
},
{
name: "valid MP_REACH_NLRI",
input: []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
},
expected: &PathAttribute{
Length: 28,
Value: 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),
},
},
},
}
t.Parallel()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
l := uint16(len(test.input))
if test.explicitLength != 0 {
l = test.explicitLength
}
pa := &PathAttribute{
Length: l,
}
err := pa.decodeMultiProtocolReachNLRI(bytes.NewBuffer(test.input))
if test.wantFail {
if err != nil {
return
}
t.Fatalf("Expected error did not happen for test %q", test.name)
}
if err != nil {
t.Fatalf("Unexpected failure for test %q: %v", test.name, err)
}
assert.Equal(t, test.expected, pa)
})
}
}
func TestDecodeMultiProtocolUnreachNLRI(t *testing.T) {
tests := []struct {
name string
input []byte
wantFail bool
explicitLength uint16
expected *PathAttribute
}{
{
name: "incomplete",
input: []byte{0, 0, 0, 0},
wantFail: true,
explicitLength: 32,
},
{
name: "valid MP_UNREACH_NLRI",
input: []byte{
0x00, 0x02, // AFI
0x01, // SAFI
0x2c, 0x26, 0x20, 0x01, 0x10, 0x90, 0x00, // Prefix
},
expected: &PathAttribute{
Length: 10,
Value: MultiProtocolUnreachNLRI{
AFI: IPv6AFI,
SAFI: UnicastSAFI,
Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44),
},
},
},
}
t.Parallel()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
l := uint16(len(test.input))
if test.explicitLength != 0 {
l = test.explicitLength
}
pa := &PathAttribute{
Length: l,
}
err := pa.decodeMultiProtocolUnreachNLRI(bytes.NewBuffer(test.input))
if test.wantFail {
if err != nil {
return
}
t.Fatalf("Expected error did not happen for test %q", test.name)
}
if err != nil {
t.Fatalf("Unexpected failure for test %q: %v", test.name, err)
}
assert.Equal(t, test.expected, pa)
})
}
}
func TestSetLength(t *testing.T) { func TestSetLength(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment