Require that IPv6 headers be in the first fragment

Test:
- header_test.TestIPv6ExtHdrIter
- ipv6_test.TestReceiveIPv6Fragments

Updates #2197, #2333

PiperOrigin-RevId: 305330178
This commit is contained in:
Ghanan Gowripalan 2020-04-07 13:35:58 -07:00 committed by gVisor bot
parent 71770e5662
commit 6db55a5bd8
4 changed files with 111 additions and 19 deletions

View File

@ -395,17 +395,24 @@ func MakeIPv6PayloadIterator(nextHdrIdentifier IPv6ExtensionHeaderIdentifier, pa
}
// AsRawHeader returns the remaining payload of i as a raw header and
// completes the iterator.
// optionally consumes the iterator.
//
// Calls to Next after calling AsRawHeader on i will indicate that the
// iterator is done.
func (i *IPv6PayloadIterator) AsRawHeader() IPv6RawPayloadHeader {
buf := i.payload
// If consume is true, calls to Next after calling AsRawHeader on i will
// indicate that the iterator is done.
func (i *IPv6PayloadIterator) AsRawHeader(consume bool) IPv6RawPayloadHeader {
identifier := i.nextHdrIdentifier
// Mark i as done.
*i = IPv6PayloadIterator{
nextHdrIdentifier: IPv6NoNextHeaderIdentifier,
var buf buffer.VectorisedView
if consume {
// Since we consume the iterator, we return the payload as is.
buf = i.payload
// Mark i as done.
*i = IPv6PayloadIterator{
nextHdrIdentifier: IPv6NoNextHeaderIdentifier,
}
} else {
buf = i.payload.Clone(nil)
}
return IPv6RawPayloadHeader{Identifier: identifier, Buf: buf}
@ -424,7 +431,7 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
// a fragment extension header as the data following the fragment extension
// header may not be complete.
if i.forceRaw {
return i.AsRawHeader(), false, nil
return i.AsRawHeader(true /* consume */), false, nil
}
// Is the header we are parsing a known extension header?
@ -456,10 +463,12 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
fragmentExtHdr := IPv6FragmentExtHdr(data)
// If the packet is a fragmented packet, do not attempt to parse
// anything after the fragment extension header as the data following
// the extension header may not be complete.
if fragmentExtHdr.More() || fragmentExtHdr.FragmentOffset() != 0 {
// If the packet is not the first fragment, do not attempt to parse anything
// after the fragment extension header as the payload following the fragment
// extension header should not contain any headers; the first fragment must
// hold all the headers up to and including any upper layer headers, as per
// RFC 8200 section 4.5.
if fragmentExtHdr.FragmentOffset() != 0 {
i.forceRaw = true
}
@ -480,7 +489,7 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
default:
// The header we are parsing is not a known extension header. Return the
// raw payload.
return i.AsRawHeader(), false, nil
return i.AsRawHeader(true /* consume */), false, nil
}
}

View File

@ -673,19 +673,26 @@ func TestIPv6ExtHdrIter(t *testing.T) {
payload buffer.VectorisedView
expected []IPv6PayloadHeader
}{
// With a non-atomic fragment, the payload after the fragment will not be
// parsed because the payload may not be complete.
// With a non-atomic fragment that is not the first fragment, the payload
// after the fragment will not be parsed because the payload is expected to
// only hold upper layer data.
{
name: "hopbyhop - fragment - routing - upper",
name: "hopbyhop - fragment (not first) - routing - upper",
firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
payload: makeVectorisedViewFromByteBuffers([]byte{
// Hop By Hop extension header.
uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
// Fragment extension header.
//
// More = 1, Fragment Offset = 2117, ID = 2147746305
uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1,
// Routing extension header.
//
// Even though we have a routing ext header here, it should be
// be interpretted as raw bytes as only the first fragment is expected
// to hold headers.
255, 0, 1, 2, 3, 4, 5, 6,
// Upper layer data.
@ -700,6 +707,34 @@ func TestIPv6ExtHdrIter(t *testing.T) {
},
},
},
{
name: "hopbyhop - fragment (first) - routing - upper",
firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
payload: makeVectorisedViewFromByteBuffers([]byte{
// Hop By Hop extension header.
uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
// Fragment extension header.
//
// More = 1, Fragment Offset = 0, ID = 2147746305
uint8(IPv6RoutingExtHdrIdentifier), 0, 0, 1, 128, 4, 2, 1,
// Routing extension header.
255, 0, 1, 2, 3, 4, 5, 6,
// Upper layer data.
1, 2, 3, 4,
}),
expected: []IPv6PayloadHeader{
IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}},
IPv6FragmentExtHdr([6]byte{0, 1, 128, 4, 2, 1}),
IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
IPv6RawPayloadHeader{
Identifier: 255,
Buf: upperLayerData.ToVectorisedView(),
},
},
},
{
name: "fragment - routing - upper (across views)",
firstNextHdr: IPv6FragmentExtHdrIdentifier,

View File

@ -270,7 +270,55 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt stack.PacketBuffer) {
continue
}
rawPayload := it.AsRawHeader()
// Don't consume the iterator if we have the first fragment because we
// will use it to validate that the first fragment holds the upper layer
// header.
rawPayload := it.AsRawHeader(fragmentOffset != 0 /* consume */)
if fragmentOffset == 0 {
// Check that the iterator ends with a raw payload as the first fragment
// should include all headers up to and including any upper layer
// headers, as per RFC 8200 section 4.5; only upper layer data
// (non-headers) should follow the fragment extension header.
var lastHdr header.IPv6PayloadHeader
for {
it, done, err := it.Next()
if err != nil {
r.Stats().IP.MalformedPacketsReceived.Increment()
r.Stats().IP.MalformedPacketsReceived.Increment()
return
}
if done {
break
}
lastHdr = it
}
// If the last header is a raw header, then the last portion of the IPv6
// payload is not a known IPv6 extension header. Note, this does not
// mean that the last portion is an upper layer header or not an
// extension header because:
// 1) we do not yet support all extension headers
// 2) we do not validate the upper layer header before reassembling.
//
// This check makes sure that a known IPv6 extension header is not
// present after the Fragment extension header in a non-initial
// fragment.
//
// TODO(#2196): Support IPv6 Authentication and Encapsulated
// Security Payload extension headers.
// TODO(#2333): Validate that the upper layer header is valid.
switch lastHdr.(type) {
case header.IPv6RawPayloadHeader:
default:
r.Stats().IP.MalformedPacketsReceived.Increment()
r.Stats().IP.MalformedFragmentsReceived.Increment()
return
}
}
fragmentPayloadLen := rawPayload.Buf.Size()
if fragmentPayloadLen == 0 {
// Drop the packet as it's marked as a fragment but has no payload.

View File

@ -1014,7 +1014,7 @@ func TestReceiveIPv6Fragments(t *testing.T) {
),
},
},
expectedPayloads: [][]byte{udpPayload1},
expectedPayloads: nil,
},
{
name: "Two fragments with routing header with non-zero segments left across fragments",