diff --git a/pkg/packet/rtr/rtr.go b/pkg/packet/rtr/rtr.go index 59755b781..b1a768277 100644 --- a/pkg/packet/rtr/rtr.go +++ b/pkg/packet/rtr/rtr.go @@ -41,15 +41,17 @@ const ( ) const ( - RTR_SERIAL_NOTIFY_LEN = 12 - RTR_SERIAL_QUERY_LEN = 12 - RTR_RESET_QUERY_LEN = 8 - RTR_CACHE_RESPONSE_LEN = 8 - RTR_IPV4_PREFIX_LEN = 20 - RTR_IPV6_PREFIX_LEN = 32 - RTR_END_OF_DATA_LEN = 12 - RTR_CACHE_RESET_LEN = 8 - RTR_MIN_LEN = 8 + RTR_SERIAL_NOTIFY_LEN = 12 + RTR_SERIAL_QUERY_LEN = 12 + RTR_RESET_QUERY_LEN = 8 + RTR_CACHE_RESPONSE_LEN = 8 + RTR_IPV4_PREFIX_LEN = 20 + RTR_IPV6_PREFIX_LEN = 32 + RTR_END_OF_DATA_LEN = 12 + RTR_CACHE_RESET_LEN = 8 + RTR_MIN_LEN = 8 + // RTR_MAX_LEN caps the on-wire Length; every defined PDU is far smaller. + RTR_MAX_LEN = 65535 RTR_ERROR_REPORT_ERR_PDU_LEN = 4 RTR_ERROR_REPORT_ERR_TEXT_LEN = 4 ) diff --git a/pkg/server/rpki.go b/pkg/server/rpki.go index f137e4430..f2edb7afa 100644 --- a/pkg/server/rpki.go +++ b/pkg/server/rpki.go @@ -444,25 +444,38 @@ func (c *roaClient) established() (err error) { } for { - header := make([]byte, rtr.RTR_MIN_LEN) - if _, err = io.ReadFull(c.conn, header); err != nil { - return err - } - totalLen := binary.BigEndian.Uint32(header[4:8]) - if totalLen < rtr.RTR_MIN_LEN { - return fmt.Errorf("too short header length %v", totalLen) - } - - body := make([]byte, totalLen-rtr.RTR_MIN_LEN) - if _, err = io.ReadFull(c.conn, body); err != nil { + var data []byte + data, err = readRTRMessage(c.conn) + if err != nil { return err } c.eventCh <- &roaEvent{ EventType: roaRTR, Src: c.host, - Data: append(header, body...), + Data: data, timestamp: time.Now(), } } } + +func readRTRMessage(r io.Reader) ([]byte, error) { + header := make([]byte, rtr.RTR_MIN_LEN) + if _, err := io.ReadFull(r, header); err != nil { + return nil, err + } + totalLen := binary.BigEndian.Uint32(header[4:8]) + if totalLen < rtr.RTR_MIN_LEN { + return nil, fmt.Errorf("too short header length %v", totalLen) + } + if totalLen > rtr.RTR_MAX_LEN { + return nil, fmt.Errorf("too large header length %v", totalLen) + } + + body := make([]byte, totalLen-rtr.RTR_MIN_LEN) + if _, err := io.ReadFull(r, body); err != nil { + return nil, err + } + + return append(header, body...), nil +} diff --git a/pkg/server/rpki_test.go b/pkg/server/rpki_test.go new file mode 100644 index 000000000..8371b8698 --- /dev/null +++ b/pkg/server/rpki_test.go @@ -0,0 +1,50 @@ +// Copyright (C) 2015-2021 Nippon Telegraph and Telephone Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "bytes" + "encoding/binary" + "testing" + + "github.com/osrg/gobgp/v4/pkg/packet/rtr" +) + +func Test_readRTRMessageRejectsOversizedLength(t *testing.T) { + header := make([]byte, rtr.RTR_MIN_LEN) + header[0] = 1 // Protocol Version + header[1] = rtr.RTR_CACHE_RESET + binary.BigEndian.PutUint32(header[4:8], 0xffffffff) + + if _, err := readRTRMessage(bytes.NewReader(header)); err == nil { + t.Fatal("expected an error for an oversized RTR Length, got nil") + } +} + +func Test_readRTRMessageAcceptsValidLength(t *testing.T) { + header := make([]byte, rtr.RTR_MIN_LEN) + header[0] = 1 // Protocol Version + header[1] = rtr.RTR_CACHE_RESET + binary.BigEndian.PutUint32(header[4:8], rtr.RTR_MIN_LEN) + + data, err := readRTRMessage(bytes.NewReader(header)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(data) != rtr.RTR_MIN_LEN { + t.Fatalf("expected %d bytes, got %d", rtr.RTR_MIN_LEN, len(data)) + } +}