diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go index 1aa9e92a1cb8d8f97088145a666ac3553d10e8ac..65665f29182e3b81e337d2f1bc8e25d2afbc8b50 100644 --- a/protocols/bgp/packet/mp_reach_nlri.go +++ b/protocols/bgp/packet/mp_reach_nlri.go @@ -38,8 +38,13 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint16 { func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error) { n := MultiProtocolReachNLRI{} nextHopLength := uint8(0) - variable := make([]byte, len(b)-4) + variableLength := len(b) - 4 // 4 <- AFI + SAFI + NextHopLength + if variableLength <= 0 { + return n, fmt.Errorf("Invalid length of MP_REACH_NLRI: expected more than 4 bytes but got %d", len(b)) + } + + variable := make([]byte, variableLength) fields := []interface{}{ &n.AFI, &n.SAFI, @@ -51,19 +56,25 @@ func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error) return MultiProtocolReachNLRI{}, err } + budget := variableLength + if budget < int(nextHopLength) { + return MultiProtocolReachNLRI{}, + fmt.Errorf("Failed to decode next hop IP: expected %d bytes for NLRI, only %d remaining", nextHopLength, budget) + } + n.NextHop, err = bnet.IPFromBytes(variable[:nextHopLength]) if err != nil { return MultiProtocolReachNLRI{}, fmt.Errorf("Failed to decode next hop IP: %v", err) } - - variable = variable[1+nextHopLength:] + budget -= int(nextHopLength) n.Prefixes = make([]bnet.Prefix, 0) - - if len(variable) == 0 { + if budget == 0 { return n, nil } + variable = variable[1+nextHopLength:] // 1 <- RESERVED field + idx := uint16(0) for idx < uint16(len(variable)) { pfxLen := variable[idx] diff --git a/protocols/bgp/packet/mp_unreach_nlri.go b/protocols/bgp/packet/mp_unreach_nlri.go index 3a78b00f7701a76a663005c404e4757eb934404c..63cccd580dfe79e474c1be33f96485eebec1d43b 100644 --- a/protocols/bgp/packet/mp_unreach_nlri.go +++ b/protocols/bgp/packet/mp_unreach_nlri.go @@ -30,36 +30,41 @@ func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint16 { func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, error) { n := MultiProtocolUnreachNLRI{} - prefix := make([]byte, len(b)-3) + prefixesLength := len(b) - 3 // 3 <- AFI + SAFI + if prefixesLength < 0 { + return n, fmt.Errorf("Invalid length of MP_UNREACH_NLRI: expected more than 3 bytes but got %d", len(b)) + } + + prefixes := make([]byte, prefixesLength) fields := []interface{}{ &n.AFI, &n.SAFI, - &prefix, + &prefixes, } err := decode(bytes.NewBuffer(b), fields) if err != nil { return MultiProtocolUnreachNLRI{}, err } - if len(prefix) == 0 { + if len(prefixes) == 0 { return n, nil } idx := uint16(0) - for idx < uint16(len(prefix)) { - pfxLen := prefix[idx] + for idx < uint16(len(prefixes)) { + pfxLen := prefixes[idx] numBytes := uint16(BytesInAddr(pfxLen)) idx++ - r := uint16(len(prefix)) - idx + r := uint16(len(prefixes)) - idx if r < numBytes { return MultiProtocolUnreachNLRI{}, fmt.Errorf("expected %d bytes for NLRI, only %d remaining", numBytes, r) } start := idx end := idx + numBytes - pfx, err := deserializePrefix(prefix[start:end], pfxLen, n.AFI) + pfx, err := deserializePrefix(prefixes[start:end], pfxLen, n.AFI) if err != nil { return MultiProtocolUnreachNLRI{}, err } diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index dd4b31e8005812f7a8f90c36b139d8ac244e320e..606ee24c44b760292f155b3bc794e1424a92c803 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -934,6 +934,40 @@ func TestDecodeMultiProtocolReachNLRI(t *testing.T) { }, }, }, + { + name: "MP_REACH_NLRI with invalid length", + input: []byte{ + 0x00, 0x02, // AFI + }, + wantFail: true, + }, + { + name: "MP_REACH_NLRI with invalid length 2", + input: []byte{ + 0x00, 0x02, // AFI + 0x01, // SAFI + 0x10, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x00, // incomplete NextHop + }, + wantFail: true, + }, + { + name: "MP_REACH_NLRI without prefixes", + 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 + }, + expected: &PathAttribute{ + Length: 21, + Value: MultiProtocolReachNLRI{ + AFI: IPv6AFI, + SAFI: UnicastSAFI, + NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2), + Prefixes: []bnet.Prefix{}, + }, + }, + }, } t.Parallel() @@ -998,6 +1032,13 @@ func TestDecodeMultiProtocolUnreachNLRI(t *testing.T) { }, }, }, + { + name: "MP_UNREACH_NLRI with invalid length", + input: []byte{ + 0x00, 0x02, // AFI + }, + wantFail: true, + }, } t.Parallel()