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:
parent
71770e5662
commit
6db55a5bd8
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue