mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-04-02 19:37:40 +03:00
955 lines
36 KiB
Go
955 lines
36 KiB
Go
// Copyright 2020 The gVisor Authors.
|
|
//
|
|
// 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 header
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
|
|
"github.com/sagernet/gvisor/pkg/buffer"
|
|
"github.com/sagernet/sing-tun/internal/gtcpip"
|
|
"github.com/sagernet/sing/common"
|
|
)
|
|
|
|
// IPv6ExtensionHeaderIdentifier is an IPv6 extension header identifier.
|
|
type IPv6ExtensionHeaderIdentifier uint8
|
|
|
|
const (
|
|
// IPv6HopByHopOptionsExtHdrIdentifier is the header identifier of a Hop by
|
|
// Hop Options extension header, as per RFC 8200 section 4.3.
|
|
IPv6HopByHopOptionsExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 0
|
|
|
|
// IPv6RoutingExtHdrIdentifier is the header identifier of a Routing extension
|
|
// header, as per RFC 8200 section 4.4.
|
|
IPv6RoutingExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 43
|
|
|
|
// IPv6FragmentExtHdrIdentifier is the header identifier of a Fragment
|
|
// extension header, as per RFC 8200 section 4.5.
|
|
IPv6FragmentExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 44
|
|
|
|
// IPv6DestinationOptionsExtHdrIdentifier is the header identifier of a
|
|
// Destination Options extension header, as per RFC 8200 section 4.6.
|
|
IPv6DestinationOptionsExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 60
|
|
|
|
// IPv6NoNextHeaderIdentifier is the header identifier used to signify the end
|
|
// of an IPv6 payload, as per RFC 8200 section 4.7.
|
|
IPv6NoNextHeaderIdentifier IPv6ExtensionHeaderIdentifier = 59
|
|
|
|
// IPv6UnknownExtHdrIdentifier is reserved by IANA.
|
|
// https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml#extension-header
|
|
// "254 Use for experimentation and testing [RFC3692][RFC4727]"
|
|
IPv6UnknownExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 254
|
|
)
|
|
|
|
const (
|
|
// ipv6UnknownExtHdrOptionActionMask is the mask of the action to take when
|
|
// a node encounters an unrecognized option.
|
|
ipv6UnknownExtHdrOptionActionMask = 192
|
|
|
|
// ipv6UnknownExtHdrOptionActionShift is the least significant bits to discard
|
|
// from the action value for an unrecognized option identifier.
|
|
ipv6UnknownExtHdrOptionActionShift = 6
|
|
|
|
// ipv6RoutingExtHdrSegmentsLeftIdx is the index to the Segments Left field
|
|
// within an IPv6RoutingExtHdr.
|
|
ipv6RoutingExtHdrSegmentsLeftIdx = 1
|
|
|
|
// IPv6FragmentExtHdrLength is the length of an IPv6 extension header, in
|
|
// bytes.
|
|
IPv6FragmentExtHdrLength = 8
|
|
|
|
// ipv6FragmentExtHdrFragmentOffsetOffset is the offset to the start of the
|
|
// Fragment Offset field within an IPv6FragmentExtHdr.
|
|
ipv6FragmentExtHdrFragmentOffsetOffset = 0
|
|
|
|
// ipv6FragmentExtHdrFragmentOffsetShift is the bit offset of the Fragment
|
|
// Offset field within an IPv6FragmentExtHdr.
|
|
ipv6FragmentExtHdrFragmentOffsetShift = 3
|
|
|
|
// ipv6FragmentExtHdrFlagsIdx is the index to the flags field within an
|
|
// IPv6FragmentExtHdr.
|
|
ipv6FragmentExtHdrFlagsIdx = 1
|
|
|
|
// ipv6FragmentExtHdrMFlagMask is the mask of the More (M) flag within the
|
|
// flags field of an IPv6FragmentExtHdr.
|
|
ipv6FragmentExtHdrMFlagMask = 1
|
|
|
|
// ipv6FragmentExtHdrIdentificationOffset is the offset to the Identification
|
|
// field within an IPv6FragmentExtHdr.
|
|
ipv6FragmentExtHdrIdentificationOffset = 2
|
|
|
|
// ipv6ExtHdrLenBytesPerUnit is the unit size of an extension header's length
|
|
// field. That is, given a Length field of 2, the extension header expects
|
|
// 16 bytes following the first 8 bytes (see ipv6ExtHdrLenBytesExcluded for
|
|
// details about the first 8 bytes' exclusion from the Length field).
|
|
ipv6ExtHdrLenBytesPerUnit = 8
|
|
|
|
// ipv6ExtHdrLenBytesExcluded is the number of bytes excluded from an
|
|
// extension header's Length field following the Length field.
|
|
//
|
|
// The Length field excludes the first 8 bytes, but the Next Header and Length
|
|
// field take up the first 2 of the 8 bytes so we expect (at minimum) 6 bytes
|
|
// after the Length field.
|
|
//
|
|
// This ensures that every extension header is at least 8 bytes.
|
|
ipv6ExtHdrLenBytesExcluded = 6
|
|
|
|
// IPv6FragmentExtHdrFragmentOffsetBytesPerUnit is the unit size of a Fragment
|
|
// extension header's Fragment Offset field. That is, given a Fragment Offset
|
|
// of 2, the extension header is indicating that the fragment's payload
|
|
// starts at the 16th byte in the reassembled packet.
|
|
IPv6FragmentExtHdrFragmentOffsetBytesPerUnit = 8
|
|
)
|
|
|
|
// padIPv6OptionsLength returns the total length for IPv6 options of length l
|
|
// considering the 8-octet alignment as stated in RFC 8200 Section 4.2.
|
|
func padIPv6OptionsLength(length int) int {
|
|
return (length + ipv6ExtHdrLenBytesPerUnit - 1) & ^(ipv6ExtHdrLenBytesPerUnit - 1)
|
|
}
|
|
|
|
// padIPv6Option fills b with the appropriate padding options depending on its
|
|
// length.
|
|
func padIPv6Option(b []byte) {
|
|
switch len(b) {
|
|
case 0: // No padding needed.
|
|
case 1: // Pad with Pad1.
|
|
b[ipv6ExtHdrOptionTypeOffset] = uint8(ipv6Pad1ExtHdrOptionIdentifier)
|
|
default: // Pad with PadN.
|
|
s := b[ipv6ExtHdrOptionPayloadOffset:]
|
|
common.ClearArray(s)
|
|
b[ipv6ExtHdrOptionTypeOffset] = uint8(ipv6PadNExtHdrOptionIdentifier)
|
|
b[ipv6ExtHdrOptionLengthOffset] = uint8(len(s))
|
|
}
|
|
}
|
|
|
|
// ipv6OptionsAlignmentPadding returns the number of padding bytes needed to
|
|
// serialize an option at headerOffset with alignment requirements
|
|
// [align]n + alignOffset.
|
|
func ipv6OptionsAlignmentPadding(headerOffset int, align int, alignOffset int) int {
|
|
padLen := headerOffset - alignOffset
|
|
return ((padLen + align - 1) & ^(align - 1)) - padLen
|
|
}
|
|
|
|
// IPv6PayloadHeader is implemented by the various headers that can be found
|
|
// in an IPv6 payload.
|
|
//
|
|
// These headers include IPv6 extension headers or upper layer data.
|
|
type IPv6PayloadHeader interface {
|
|
isIPv6PayloadHeader()
|
|
|
|
// Release frees all resources held by the header.
|
|
Release()
|
|
}
|
|
|
|
// IPv6RawPayloadHeader the remainder of an IPv6 payload after an iterator
|
|
// encounters a Next Header field it does not recognize as an IPv6 extension
|
|
// header. The caller is responsible for releasing the underlying buffer after
|
|
// it's no longer needed.
|
|
type IPv6RawPayloadHeader struct {
|
|
Identifier IPv6ExtensionHeaderIdentifier
|
|
Buf buffer.Buffer
|
|
}
|
|
|
|
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
|
func (IPv6RawPayloadHeader) isIPv6PayloadHeader() {}
|
|
|
|
// Release implements IPv6PayloadHeader.Release.
|
|
func (i IPv6RawPayloadHeader) Release() {
|
|
i.Buf.Release()
|
|
}
|
|
|
|
// ipv6OptionsExtHdr is an IPv6 extension header that holds options.
|
|
type ipv6OptionsExtHdr struct {
|
|
buf *buffer.View
|
|
}
|
|
|
|
// Release implements IPv6PayloadHeader.Release.
|
|
func (i ipv6OptionsExtHdr) Release() {
|
|
if i.buf != nil {
|
|
i.buf.Release()
|
|
}
|
|
}
|
|
|
|
// Iter returns an iterator over the IPv6 extension header options held in b.
|
|
func (i ipv6OptionsExtHdr) Iter() IPv6OptionsExtHdrOptionsIterator {
|
|
it := IPv6OptionsExtHdrOptionsIterator{}
|
|
it.reader = i.buf
|
|
return it
|
|
}
|
|
|
|
// IPv6OptionsExtHdrOptionsIterator is an iterator over IPv6 extension header
|
|
// options.
|
|
//
|
|
// Note, between when an IPv6OptionsExtHdrOptionsIterator is obtained and last
|
|
// used, no changes to the underlying buffer may happen. Doing so may cause
|
|
// undefined and unexpected behaviour. It is fine to obtain an
|
|
// IPv6OptionsExtHdrOptionsIterator, iterate over the first few options then
|
|
// modify the backing payload so long as the IPv6OptionsExtHdrOptionsIterator
|
|
// obtained before modification is no longer used.
|
|
type IPv6OptionsExtHdrOptionsIterator struct {
|
|
reader *buffer.View
|
|
|
|
// optionOffset is the number of bytes from the first byte of the
|
|
// options field to the beginning of the current option.
|
|
optionOffset uint32
|
|
|
|
// nextOptionOffset is the offset of the next option.
|
|
nextOptionOffset uint32
|
|
}
|
|
|
|
// OptionOffset returns the number of bytes parsed while processing the
|
|
// option field of the current Extension Header.
|
|
func (i *IPv6OptionsExtHdrOptionsIterator) OptionOffset() uint32 {
|
|
return i.optionOffset
|
|
}
|
|
|
|
// IPv6OptionUnknownAction is the action that must be taken if the processing
|
|
// IPv6 node does not recognize the option, as outlined in RFC 8200 section 4.2.
|
|
type IPv6OptionUnknownAction int
|
|
|
|
const (
|
|
// IPv6OptionUnknownActionSkip indicates that the unrecognized option must
|
|
// be skipped and the node should continue processing the header.
|
|
IPv6OptionUnknownActionSkip IPv6OptionUnknownAction = 0
|
|
|
|
// IPv6OptionUnknownActionDiscard indicates that the packet must be silently
|
|
// discarded.
|
|
IPv6OptionUnknownActionDiscard IPv6OptionUnknownAction = 1
|
|
|
|
// IPv6OptionUnknownActionDiscardSendICMP indicates that the packet must be
|
|
// discarded and the node must send an ICMP Parameter Problem, Code 2, message
|
|
// to the packet's source, regardless of whether or not the packet's
|
|
// Destination was a multicast address.
|
|
IPv6OptionUnknownActionDiscardSendICMP IPv6OptionUnknownAction = 2
|
|
|
|
// IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest indicates that the
|
|
// packet must be discarded and the node must send an ICMP Parameter Problem,
|
|
// Code 2, message to the packet's source only if the packet's Destination was
|
|
// not a multicast address.
|
|
IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest IPv6OptionUnknownAction = 3
|
|
)
|
|
|
|
// IPv6ExtHdrOption is implemented by the various IPv6 extension header options.
|
|
type IPv6ExtHdrOption interface {
|
|
// UnknownAction returns the action to take in response to an unrecognized
|
|
// option.
|
|
UnknownAction() IPv6OptionUnknownAction
|
|
|
|
// isIPv6ExtHdrOption is used to "lock" this interface so it is not
|
|
// implemented by other packages.
|
|
isIPv6ExtHdrOption()
|
|
}
|
|
|
|
// IPv6ExtHdrOptionIdentifier is an IPv6 extension header option identifier.
|
|
type IPv6ExtHdrOptionIdentifier uint8
|
|
|
|
const (
|
|
// ipv6Pad1ExtHdrOptionIdentifier is the identifier for a padding option that
|
|
// provides 1 byte padding, as outlined in RFC 8200 section 4.2.
|
|
ipv6Pad1ExtHdrOptionIdentifier IPv6ExtHdrOptionIdentifier = 0
|
|
|
|
// ipv6PadNExtHdrOptionIdentifier is the identifier for a padding option that
|
|
// provides variable length byte padding, as outlined in RFC 8200 section 4.2.
|
|
ipv6PadNExtHdrOptionIdentifier IPv6ExtHdrOptionIdentifier = 1
|
|
|
|
// ipv6RouterAlertHopByHopOptionIdentifier is the identifier for the Router
|
|
// Alert Hop by Hop option as defined in RFC 2711 section 2.1.
|
|
ipv6RouterAlertHopByHopOptionIdentifier IPv6ExtHdrOptionIdentifier = 5
|
|
|
|
// ipv6ExtHdrOptionTypeOffset is the option type offset in an extension header
|
|
// option as defined in RFC 8200 section 4.2.
|
|
ipv6ExtHdrOptionTypeOffset = 0
|
|
|
|
// ipv6ExtHdrOptionLengthOffset is the option length offset in an extension
|
|
// header option as defined in RFC 8200 section 4.2.
|
|
ipv6ExtHdrOptionLengthOffset = 1
|
|
|
|
// ipv6ExtHdrOptionPayloadOffset is the option payload offset in an extension
|
|
// header option as defined in RFC 8200 section 4.2.
|
|
ipv6ExtHdrOptionPayloadOffset = 2
|
|
)
|
|
|
|
// ipv6UnknownActionFromIdentifier maps an extension header option's
|
|
// identifier's high bits to the action to take when the identifier is unknown.
|
|
func ipv6UnknownActionFromIdentifier(id IPv6ExtHdrOptionIdentifier) IPv6OptionUnknownAction {
|
|
return IPv6OptionUnknownAction((id & ipv6UnknownExtHdrOptionActionMask) >> ipv6UnknownExtHdrOptionActionShift)
|
|
}
|
|
|
|
// ErrMalformedIPv6ExtHdrOption indicates that an IPv6 extension header option
|
|
// is malformed.
|
|
var ErrMalformedIPv6ExtHdrOption = errors.New("malformed IPv6 extension header option")
|
|
|
|
// IPv6UnknownExtHdrOption holds the identifier and data for an IPv6 extension
|
|
// header option that is unknown by the parsing utilities.
|
|
type IPv6UnknownExtHdrOption struct {
|
|
Identifier IPv6ExtHdrOptionIdentifier
|
|
Data *buffer.View
|
|
}
|
|
|
|
// UnknownAction implements IPv6OptionUnknownAction.UnknownAction.
|
|
func (o *IPv6UnknownExtHdrOption) UnknownAction() IPv6OptionUnknownAction {
|
|
return ipv6UnknownActionFromIdentifier(o.Identifier)
|
|
}
|
|
|
|
// isIPv6ExtHdrOption implements IPv6ExtHdrOption.isIPv6ExtHdrOption.
|
|
func (*IPv6UnknownExtHdrOption) isIPv6ExtHdrOption() {}
|
|
|
|
// Next returns the next option in the options data.
|
|
//
|
|
// If the next item is not a known extension header option,
|
|
// IPv6UnknownExtHdrOption will be returned with the option identifier and data.
|
|
//
|
|
// The return is of the format (option, done, error). done will be true when
|
|
// Next is unable to return anything because the iterator has reached the end of
|
|
// the options data, or an error occurred.
|
|
func (i *IPv6OptionsExtHdrOptionsIterator) Next() (IPv6ExtHdrOption, bool, error) {
|
|
for {
|
|
i.optionOffset = i.nextOptionOffset
|
|
temp, err := i.reader.ReadByte()
|
|
if err != nil {
|
|
// If we can't read the first byte of a new option, then we know the
|
|
// options buffer has been exhausted and we are done iterating.
|
|
return nil, true, nil
|
|
}
|
|
id := IPv6ExtHdrOptionIdentifier(temp)
|
|
|
|
// If the option identifier indicates the option is a Pad1 option, then we
|
|
// know the option does not have Length and Data fields. End processing of
|
|
// the Pad1 option and continue processing the buffer as a new option.
|
|
if id == ipv6Pad1ExtHdrOptionIdentifier {
|
|
i.nextOptionOffset = i.optionOffset + 1
|
|
continue
|
|
}
|
|
|
|
length, err := i.reader.ReadByte()
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
// ReadByte should only ever return nil or io.EOF.
|
|
panic(fmt.Sprintf("unexpected error when reading the option's Length field for option with id = %d: %s", id, err))
|
|
}
|
|
|
|
// We use io.ErrUnexpectedEOF as exhausting the buffer is unexpected once
|
|
// we start parsing an option; we expect the reader to contain enough
|
|
// bytes for the whole option.
|
|
return nil, true, fmt.Errorf("error when reading the option's Length field for option with id = %d: %w", id, io.ErrUnexpectedEOF)
|
|
}
|
|
|
|
// Do we have enough bytes in the reader for the next option?
|
|
if n := i.reader.Size(); n < int(length) {
|
|
// Consume the remaining buffer.
|
|
i.reader.TrimFront(i.reader.Size())
|
|
|
|
// We return the same error as if we failed to read a non-padding option
|
|
// so consumers of this iterator don't need to differentiate between
|
|
// padding and non-padding options.
|
|
return nil, true, fmt.Errorf("read %d out of %d option data bytes for option with id = %d: %w", n, length, id, io.ErrUnexpectedEOF)
|
|
}
|
|
|
|
i.nextOptionOffset = i.optionOffset + uint32(length) + 1 /* option ID */ + 1 /* length byte */
|
|
|
|
switch id {
|
|
case ipv6PadNExtHdrOptionIdentifier:
|
|
// Special-case the variable length padding option to avoid a copy.
|
|
i.reader.TrimFront(int(length))
|
|
continue
|
|
case ipv6RouterAlertHopByHopOptionIdentifier:
|
|
var routerAlertValue [ipv6RouterAlertPayloadLength]byte
|
|
if n, err := io.ReadFull(i.reader, routerAlertValue[:]); err != nil {
|
|
switch err {
|
|
case io.EOF, io.ErrUnexpectedEOF:
|
|
return nil, true, fmt.Errorf("got invalid length (%d) for router alert option (want = %d): %w", length, ipv6RouterAlertPayloadLength, ErrMalformedIPv6ExtHdrOption)
|
|
default:
|
|
return nil, true, fmt.Errorf("read %d out of %d option data bytes for router alert option: %w", n, ipv6RouterAlertPayloadLength, err)
|
|
}
|
|
} else if n != int(length) {
|
|
return nil, true, fmt.Errorf("got invalid length (%d) for router alert option (want = %d): %w", length, ipv6RouterAlertPayloadLength, ErrMalformedIPv6ExtHdrOption)
|
|
}
|
|
return &IPv6RouterAlertOption{Value: IPv6RouterAlertValue(binary.BigEndian.Uint16(routerAlertValue[:]))}, false, nil
|
|
default:
|
|
bytes := buffer.NewView(int(length))
|
|
if n, err := io.CopyN(bytes, i.reader, int64(length)); err != nil {
|
|
if err == io.EOF {
|
|
err = io.ErrUnexpectedEOF
|
|
}
|
|
|
|
return nil, true, fmt.Errorf("read %d out of %d option data bytes for option with id = %d: %w", n, length, id, err)
|
|
}
|
|
return &IPv6UnknownExtHdrOption{Identifier: id, Data: bytes}, false, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// IPv6HopByHopOptionsExtHdr is a buffer holding the Hop By Hop Options
|
|
// extension header.
|
|
type IPv6HopByHopOptionsExtHdr struct {
|
|
ipv6OptionsExtHdr
|
|
}
|
|
|
|
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
|
func (IPv6HopByHopOptionsExtHdr) isIPv6PayloadHeader() {}
|
|
|
|
// IPv6DestinationOptionsExtHdr is a buffer holding the Destination Options
|
|
// extension header.
|
|
type IPv6DestinationOptionsExtHdr struct {
|
|
ipv6OptionsExtHdr
|
|
}
|
|
|
|
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
|
func (IPv6DestinationOptionsExtHdr) isIPv6PayloadHeader() {}
|
|
|
|
// IPv6RoutingExtHdr is a buffer holding the Routing extension header specific
|
|
// data as outlined in RFC 8200 section 4.4.
|
|
type IPv6RoutingExtHdr struct {
|
|
Buf *buffer.View
|
|
}
|
|
|
|
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
|
func (IPv6RoutingExtHdr) isIPv6PayloadHeader() {}
|
|
|
|
// Release implements IPv6PayloadHeader.Release.
|
|
func (b IPv6RoutingExtHdr) Release() {
|
|
b.Buf.Release()
|
|
}
|
|
|
|
// SegmentsLeft returns the Segments Left field.
|
|
func (b IPv6RoutingExtHdr) SegmentsLeft() uint8 {
|
|
return b.Buf.AsSlice()[ipv6RoutingExtHdrSegmentsLeftIdx]
|
|
}
|
|
|
|
// IPv6FragmentExtHdr is a buffer holding the Fragment extension header specific
|
|
// data as outlined in RFC 8200 section 4.5.
|
|
//
|
|
// Note, the buffer does not include the Next Header and Reserved fields.
|
|
type IPv6FragmentExtHdr [6]byte
|
|
|
|
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
|
func (IPv6FragmentExtHdr) isIPv6PayloadHeader() {}
|
|
|
|
// Release implements IPv6PayloadHeader.Release.
|
|
func (IPv6FragmentExtHdr) Release() {}
|
|
|
|
// FragmentOffset returns the Fragment Offset field.
|
|
//
|
|
// This value indicates where the buffer following the Fragment extension header
|
|
// starts in the target (reassembled) packet.
|
|
func (b IPv6FragmentExtHdr) FragmentOffset() uint16 {
|
|
return binary.BigEndian.Uint16(b[ipv6FragmentExtHdrFragmentOffsetOffset:]) >> ipv6FragmentExtHdrFragmentOffsetShift
|
|
}
|
|
|
|
// More returns the More (M) flag.
|
|
//
|
|
// This indicates whether any fragments are expected to succeed b.
|
|
func (b IPv6FragmentExtHdr) More() bool {
|
|
return b[ipv6FragmentExtHdrFlagsIdx]&ipv6FragmentExtHdrMFlagMask != 0
|
|
}
|
|
|
|
// ID returns the Identification field.
|
|
//
|
|
// This value is used to uniquely identify the packet, between a
|
|
// source and destination.
|
|
func (b IPv6FragmentExtHdr) ID() uint32 {
|
|
return binary.BigEndian.Uint32(b[ipv6FragmentExtHdrIdentificationOffset:])
|
|
}
|
|
|
|
// IsAtomic returns whether the fragment header indicates an atomic fragment. An
|
|
// atomic fragment is a fragment that contains all the data required to
|
|
// reassemble a full packet.
|
|
func (b IPv6FragmentExtHdr) IsAtomic() bool {
|
|
return !b.More() && b.FragmentOffset() == 0
|
|
}
|
|
|
|
// IPv6PayloadIterator is an iterator over the contents of an IPv6 payload.
|
|
//
|
|
// The IPv6 payload may contain IPv6 extension headers before any upper layer
|
|
// data.
|
|
//
|
|
// Note, between when an IPv6PayloadIterator is obtained and last used, no
|
|
// changes to the payload may happen. Doing so may cause undefined and
|
|
// unexpected behaviour. It is fine to obtain an IPv6PayloadIterator, iterate
|
|
// over the first few headers then modify the backing payload so long as the
|
|
// IPv6PayloadIterator obtained before modification is no longer used.
|
|
type IPv6PayloadIterator struct {
|
|
// The identifier of the next header to parse.
|
|
nextHdrIdentifier IPv6ExtensionHeaderIdentifier
|
|
|
|
payload buffer.Buffer
|
|
|
|
// Indicates to the iterator that it should return the remaining payload as a
|
|
// raw payload on the next call to Next.
|
|
forceRaw bool
|
|
|
|
// headerOffset is the offset of the beginning of the current extension
|
|
// header starting from the beginning of the fixed header.
|
|
headerOffset uint32
|
|
|
|
// parseOffset is the byte offset into the current extension header of the
|
|
// field we are currently examining. It can be added to the header offset
|
|
// if the absolute offset within the packet is required.
|
|
parseOffset uint32
|
|
|
|
// nextOffset is the offset of the next header.
|
|
nextOffset uint32
|
|
}
|
|
|
|
// HeaderOffset returns the offset to the start of the extension
|
|
// header most recently processed.
|
|
func (i IPv6PayloadIterator) HeaderOffset() uint32 {
|
|
return i.headerOffset
|
|
}
|
|
|
|
// ParseOffset returns the number of bytes successfully parsed.
|
|
func (i IPv6PayloadIterator) ParseOffset() uint32 {
|
|
return i.headerOffset + i.parseOffset
|
|
}
|
|
|
|
// MakeIPv6PayloadIterator returns an iterator over the IPv6 payload containing
|
|
// extension headers, or a raw payload if the payload cannot be parsed. The
|
|
// iterator takes ownership of the payload.
|
|
func MakeIPv6PayloadIterator(nextHdrIdentifier IPv6ExtensionHeaderIdentifier, payload buffer.Buffer) IPv6PayloadIterator {
|
|
return IPv6PayloadIterator{
|
|
nextHdrIdentifier: nextHdrIdentifier,
|
|
payload: payload,
|
|
nextOffset: IPv6FixedHeaderSize,
|
|
}
|
|
}
|
|
|
|
// Release frees the resources owned by the iterator.
|
|
func (i *IPv6PayloadIterator) Release() {
|
|
i.payload.Release()
|
|
}
|
|
|
|
// AsRawHeader returns the remaining payload of i as a raw header and
|
|
// optionally consumes the iterator.
|
|
//
|
|
// If consume is true, calls to Next after calling AsRawHeader on i will
|
|
// indicate that the iterator is done. The returned header takes ownership of
|
|
// its payload.
|
|
func (i *IPv6PayloadIterator) AsRawHeader(consume bool) IPv6RawPayloadHeader {
|
|
identifier := i.nextHdrIdentifier
|
|
|
|
var buf buffer.Buffer
|
|
if consume {
|
|
// Since we consume the iterator, we return the payload as is.
|
|
buf = i.payload
|
|
|
|
// Mark i as done, but keep track of where we were for error reporting.
|
|
*i = IPv6PayloadIterator{
|
|
nextHdrIdentifier: IPv6NoNextHeaderIdentifier,
|
|
headerOffset: i.headerOffset,
|
|
nextOffset: i.nextOffset,
|
|
}
|
|
} else {
|
|
buf = i.payload.Clone()
|
|
}
|
|
|
|
return IPv6RawPayloadHeader{Identifier: identifier, Buf: buf}
|
|
}
|
|
|
|
// Next returns the next item in the payload.
|
|
//
|
|
// If the next item is not a known IPv6 extension header, IPv6RawPayloadHeader
|
|
// will be returned with the remaining bytes and next header identifier.
|
|
//
|
|
// The return is of the format (header, done, error). done will be true when
|
|
// Next is unable to return anything because the iterator has reached the end of
|
|
// the payload, or an error occurred.
|
|
func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
|
i.headerOffset = i.nextOffset
|
|
i.parseOffset = 0
|
|
// We could be forced to return i as a raw header when the previous header was
|
|
// a fragment extension header as the data following the fragment extension
|
|
// header may not be complete.
|
|
if i.forceRaw {
|
|
return i.AsRawHeader(true /* consume */), false, nil
|
|
}
|
|
|
|
// Is the header we are parsing a known extension header?
|
|
switch i.nextHdrIdentifier {
|
|
case IPv6HopByHopOptionsExtHdrIdentifier:
|
|
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
|
if err != nil {
|
|
return nil, true, err
|
|
}
|
|
|
|
i.nextHdrIdentifier = nextHdrIdentifier
|
|
return IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
|
case IPv6RoutingExtHdrIdentifier:
|
|
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
|
if err != nil {
|
|
return nil, true, err
|
|
}
|
|
|
|
i.nextHdrIdentifier = nextHdrIdentifier
|
|
return IPv6RoutingExtHdr{view}, false, nil
|
|
case IPv6FragmentExtHdrIdentifier:
|
|
var data [6]byte
|
|
// We ignore the returned bytes because we know the fragment extension
|
|
// header specific data will fit in data.
|
|
nextHdrIdentifier, _, err := i.nextHeaderData(true /* fragmentHdr */, data[:])
|
|
if err != nil {
|
|
return nil, true, err
|
|
}
|
|
|
|
fragmentExtHdr := IPv6FragmentExtHdr(data)
|
|
|
|
// 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
|
|
}
|
|
|
|
i.nextHdrIdentifier = nextHdrIdentifier
|
|
return fragmentExtHdr, false, nil
|
|
case IPv6DestinationOptionsExtHdrIdentifier:
|
|
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
|
if err != nil {
|
|
return nil, true, err
|
|
}
|
|
|
|
i.nextHdrIdentifier = nextHdrIdentifier
|
|
return IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
|
case IPv6NoNextHeaderIdentifier:
|
|
// This indicates the end of the IPv6 payload.
|
|
return nil, true, nil
|
|
|
|
default:
|
|
// The header we are parsing is not a known extension header. Return the
|
|
// raw payload.
|
|
return i.AsRawHeader(true /* consume */), false, nil
|
|
}
|
|
}
|
|
|
|
// NextHeaderIdentifier returns the identifier of the header next returned by
|
|
// it.Next().
|
|
func (i *IPv6PayloadIterator) NextHeaderIdentifier() IPv6ExtensionHeaderIdentifier {
|
|
return i.nextHdrIdentifier
|
|
}
|
|
|
|
// nextHeaderData returns the extension header's Next Header field and raw data.
|
|
//
|
|
// fragmentHdr indicates that the extension header being parsed is the Fragment
|
|
// extension header so the Length field should be ignored as it is Reserved
|
|
// for the Fragment extension header.
|
|
//
|
|
// If bytes is not nil, extension header specific data will be read into bytes
|
|
// if it has enough capacity. If bytes is provided but does not have enough
|
|
// capacity for the data, nextHeaderData will panic.
|
|
func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IPv6ExtensionHeaderIdentifier, *buffer.View, error) {
|
|
// We ignore the number of bytes read because we know we will only ever read
|
|
// at max 1 bytes since rune has a length of 1. If we read 0 bytes, the Read
|
|
// would return io.EOF to indicate that io.Reader has reached the end of the
|
|
// payload.
|
|
rdr := i.payload.AsBufferReader()
|
|
nextHdrIdentifier, err := rdr.ReadByte()
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("error when reading the Next Header field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
|
}
|
|
i.parseOffset++
|
|
|
|
var length uint8
|
|
length, err = rdr.ReadByte()
|
|
if err != nil {
|
|
if fragmentHdr {
|
|
return 0, nil, fmt.Errorf("error when reading the Length field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
|
}
|
|
|
|
return 0, nil, fmt.Errorf("error when reading the Reserved field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
|
}
|
|
if fragmentHdr {
|
|
length = 0
|
|
}
|
|
|
|
// Make parseOffset point to the first byte of the Extension Header
|
|
// specific data.
|
|
i.parseOffset++
|
|
|
|
// length is in 8 byte chunks but doesn't include the first one.
|
|
// See RFC 8200 for each header type, sections 4.3-4.6 and the requirement
|
|
// in section 4.8 for new extension headers at the top of page 24.
|
|
// [ Hdr Ext Len ] ... Length of the Destination Options header in 8-octet
|
|
// units, not including the first 8 octets.
|
|
i.nextOffset += uint32((length + 1) * ipv6ExtHdrLenBytesPerUnit)
|
|
|
|
bytesLen := int(length)*ipv6ExtHdrLenBytesPerUnit + ipv6ExtHdrLenBytesExcluded
|
|
if fragmentHdr {
|
|
if n := len(bytes); n < bytesLen {
|
|
panic(fmt.Sprintf("bytes only has space for %d bytes but need space for %d bytes (length = %d) for extension header with id = %d", n, bytesLen, length, i.nextHdrIdentifier))
|
|
}
|
|
if n, err := io.ReadFull(&rdr, bytes); err != nil {
|
|
return 0, nil, fmt.Errorf("read %d out of %d extension header data bytes (length = %d) for header with id = %d: %w", n, bytesLen, length, i.nextHdrIdentifier, err)
|
|
}
|
|
return IPv6ExtensionHeaderIdentifier(nextHdrIdentifier), nil, nil
|
|
}
|
|
v := buffer.NewView(bytesLen)
|
|
if n, err := io.CopyN(v, &rdr, int64(bytesLen)); err != nil {
|
|
if err == io.EOF {
|
|
err = io.ErrUnexpectedEOF
|
|
}
|
|
v.Release()
|
|
return 0, nil, fmt.Errorf("read %d out of %d extension header data bytes (length = %d) for header with id = %d: %w", n, bytesLen, length, i.nextHdrIdentifier, err)
|
|
}
|
|
return IPv6ExtensionHeaderIdentifier(nextHdrIdentifier), v, nil
|
|
}
|
|
|
|
// IPv6SerializableExtHdr provides serialization for IPv6 extension
|
|
// headers.
|
|
type IPv6SerializableExtHdr interface {
|
|
// identifier returns the assigned IPv6 header identifier for this extension
|
|
// header.
|
|
identifier() IPv6ExtensionHeaderIdentifier
|
|
|
|
// length returns the total serialized length in bytes of this extension
|
|
// header, including the common next header and length fields.
|
|
length() int
|
|
|
|
// serializeInto serializes the receiver into the provided byte
|
|
// buffer and with the provided nextHeader value.
|
|
//
|
|
// Note, the caller MUST provide a byte buffer with size of at least
|
|
// length. Implementers of this function may assume that the byte buffer
|
|
// is of sufficient size. serializeInto MAY panic if the provided byte
|
|
// buffer is not of sufficient size.
|
|
//
|
|
// serializeInto returns the number of bytes that was used to serialize the
|
|
// receiver. Implementers must only use the number of bytes required to
|
|
// serialize the receiver. Callers MAY provide a larger buffer than required
|
|
// to serialize into.
|
|
serializeInto(nextHeader uint8, b []byte) int
|
|
}
|
|
|
|
var _ IPv6SerializableExtHdr = (*IPv6SerializableHopByHopExtHdr)(nil)
|
|
|
|
// IPv6SerializableHopByHopExtHdr implements serialization of the Hop by Hop
|
|
// options extension header.
|
|
type IPv6SerializableHopByHopExtHdr []IPv6SerializableHopByHopOption
|
|
|
|
const (
|
|
// ipv6HopByHopExtHdrNextHeaderOffset is the offset of the next header field
|
|
// in a hop by hop extension header as defined in RFC 8200 section 4.3.
|
|
ipv6HopByHopExtHdrNextHeaderOffset = 0
|
|
|
|
// ipv6HopByHopExtHdrLengthOffset is the offset of the length field in a hop
|
|
// by hop extension header as defined in RFC 8200 section 4.3.
|
|
ipv6HopByHopExtHdrLengthOffset = 1
|
|
|
|
// ipv6HopByHopExtHdrPayloadOffset is the offset of the options in a hop by
|
|
// hop extension header as defined in RFC 8200 section 4.3.
|
|
ipv6HopByHopExtHdrOptionsOffset = 2
|
|
|
|
// ipv6HopByHopExtHdrUnaccountedLenWords is the implicit number of 8-octet
|
|
// words in a hop by hop extension header's length field, as stated in RFC
|
|
// 8200 section 4.3:
|
|
// Length of the Hop-by-Hop Options header in 8-octet units,
|
|
// not including the first 8 octets.
|
|
ipv6HopByHopExtHdrUnaccountedLenWords = 1
|
|
)
|
|
|
|
// identifier implements IPv6SerializableExtHdr.
|
|
func (IPv6SerializableHopByHopExtHdr) identifier() IPv6ExtensionHeaderIdentifier {
|
|
return IPv6HopByHopOptionsExtHdrIdentifier
|
|
}
|
|
|
|
// length implements IPv6SerializableExtHdr.
|
|
func (h IPv6SerializableHopByHopExtHdr) length() int {
|
|
var total int
|
|
for _, opt := range h {
|
|
align, alignOffset := opt.alignment()
|
|
total += ipv6OptionsAlignmentPadding(total, align, alignOffset)
|
|
total += ipv6ExtHdrOptionPayloadOffset + int(opt.length())
|
|
}
|
|
// Account for next header and total length fields and add padding.
|
|
return padIPv6OptionsLength(ipv6HopByHopExtHdrOptionsOffset + total)
|
|
}
|
|
|
|
// serializeInto implements IPv6SerializableExtHdr.
|
|
func (h IPv6SerializableHopByHopExtHdr) serializeInto(nextHeader uint8, b []byte) int {
|
|
optBuffer := b[ipv6HopByHopExtHdrOptionsOffset:]
|
|
totalLength := ipv6HopByHopExtHdrOptionsOffset
|
|
for _, opt := range h {
|
|
// Calculate alignment requirements and pad buffer if necessary.
|
|
align, alignOffset := opt.alignment()
|
|
padLen := ipv6OptionsAlignmentPadding(totalLength, align, alignOffset)
|
|
if padLen != 0 {
|
|
padIPv6Option(optBuffer[:padLen])
|
|
totalLength += padLen
|
|
optBuffer = optBuffer[padLen:]
|
|
}
|
|
|
|
l := opt.serializeInto(optBuffer[ipv6ExtHdrOptionPayloadOffset:])
|
|
optBuffer[ipv6ExtHdrOptionTypeOffset] = uint8(opt.identifier())
|
|
optBuffer[ipv6ExtHdrOptionLengthOffset] = l
|
|
l += ipv6ExtHdrOptionPayloadOffset
|
|
totalLength += int(l)
|
|
optBuffer = optBuffer[l:]
|
|
}
|
|
padded := padIPv6OptionsLength(totalLength)
|
|
if padded != totalLength {
|
|
padIPv6Option(optBuffer[:padded-totalLength])
|
|
totalLength = padded
|
|
}
|
|
wordsLen := totalLength/ipv6ExtHdrLenBytesPerUnit - ipv6HopByHopExtHdrUnaccountedLenWords
|
|
if wordsLen > math.MaxUint8 {
|
|
panic(fmt.Sprintf("IPv6 hop by hop options too large: %d+1 64-bit words", wordsLen))
|
|
}
|
|
b[ipv6HopByHopExtHdrNextHeaderOffset] = nextHeader
|
|
b[ipv6HopByHopExtHdrLengthOffset] = uint8(wordsLen)
|
|
return totalLength
|
|
}
|
|
|
|
// IPv6SerializableHopByHopOption provides serialization for hop by hop options.
|
|
type IPv6SerializableHopByHopOption interface {
|
|
// identifier returns the option identifier of this Hop by Hop option.
|
|
identifier() IPv6ExtHdrOptionIdentifier
|
|
|
|
// length returns the *payload* size of the option (not considering the type
|
|
// and length fields).
|
|
length() uint8
|
|
|
|
// alignment returns the alignment requirements from this option.
|
|
//
|
|
// Alignment requirements take the form [align]n + offset as specified in
|
|
// RFC 8200 section 4.2. The alignment requirement is on the offset between
|
|
// the option type byte and the start of the hop by hop header.
|
|
//
|
|
// align must be a power of 2.
|
|
alignment() (align int, offset int)
|
|
|
|
// serializeInto serializes the receiver into the provided byte
|
|
// buffer.
|
|
//
|
|
// Note, the caller MUST provide a byte buffer with size of at least
|
|
// length. Implementers of this function may assume that the byte buffer
|
|
// is of sufficient size. serializeInto MAY panic if the provided byte
|
|
// buffer is not of sufficient size.
|
|
//
|
|
// serializeInto will return the number of bytes that was used to
|
|
// serialize the receiver. Implementers must only use the number of
|
|
// bytes required to serialize the receiver. Callers MAY provide a
|
|
// larger buffer than required to serialize into.
|
|
serializeInto([]byte) uint8
|
|
}
|
|
|
|
var _ IPv6SerializableHopByHopOption = (*IPv6RouterAlertOption)(nil)
|
|
|
|
// IPv6RouterAlertOption is the IPv6 Router alert Hop by Hop option defined in
|
|
// RFC 2711 section 2.1.
|
|
type IPv6RouterAlertOption struct {
|
|
Value IPv6RouterAlertValue
|
|
}
|
|
|
|
// IPv6RouterAlertValue is the payload of an IPv6 Router Alert option.
|
|
type IPv6RouterAlertValue uint16
|
|
|
|
const (
|
|
// IPv6RouterAlertMLD indicates a datagram containing a Multicast Listener
|
|
// Discovery message as defined in RFC 2711 section 2.1.
|
|
IPv6RouterAlertMLD IPv6RouterAlertValue = 0
|
|
// IPv6RouterAlertRSVP indicates a datagram containing an RSVP message as
|
|
// defined in RFC 2711 section 2.1.
|
|
IPv6RouterAlertRSVP IPv6RouterAlertValue = 1
|
|
// IPv6RouterAlertActiveNetworks indicates a datagram containing an Active
|
|
// Networks message as defined in RFC 2711 section 2.1.
|
|
IPv6RouterAlertActiveNetworks IPv6RouterAlertValue = 2
|
|
|
|
// ipv6RouterAlertPayloadLength is the length of the Router Alert payload
|
|
// as defined in RFC 2711.
|
|
ipv6RouterAlertPayloadLength = 2
|
|
|
|
// ipv6RouterAlertAlignmentRequirement is the alignment requirement for the
|
|
// Router Alert option defined as 2n+0 in RFC 2711.
|
|
ipv6RouterAlertAlignmentRequirement = 2
|
|
|
|
// ipv6RouterAlertAlignmentOffsetRequirement is the alignment offset
|
|
// requirement for the Router Alert option defined as 2n+0 in RFC 2711 section
|
|
// 2.1.
|
|
ipv6RouterAlertAlignmentOffsetRequirement = 0
|
|
)
|
|
|
|
// UnknownAction implements IPv6ExtHdrOption.
|
|
func (*IPv6RouterAlertOption) UnknownAction() IPv6OptionUnknownAction {
|
|
return ipv6UnknownActionFromIdentifier(ipv6RouterAlertHopByHopOptionIdentifier)
|
|
}
|
|
|
|
// isIPv6ExtHdrOption implements IPv6ExtHdrOption.
|
|
func (*IPv6RouterAlertOption) isIPv6ExtHdrOption() {}
|
|
|
|
// identifier implements IPv6SerializableHopByHopOption.
|
|
func (*IPv6RouterAlertOption) identifier() IPv6ExtHdrOptionIdentifier {
|
|
return ipv6RouterAlertHopByHopOptionIdentifier
|
|
}
|
|
|
|
// length implements IPv6SerializableHopByHopOption.
|
|
func (*IPv6RouterAlertOption) length() uint8 {
|
|
return ipv6RouterAlertPayloadLength
|
|
}
|
|
|
|
// alignment implements IPv6SerializableHopByHopOption.
|
|
func (*IPv6RouterAlertOption) alignment() (int, int) {
|
|
// From RFC 2711 section 2.1:
|
|
// Alignment requirement: 2n+0.
|
|
return ipv6RouterAlertAlignmentRequirement, ipv6RouterAlertAlignmentOffsetRequirement
|
|
}
|
|
|
|
// serializeInto implements IPv6SerializableHopByHopOption.
|
|
func (o *IPv6RouterAlertOption) serializeInto(b []byte) uint8 {
|
|
binary.BigEndian.PutUint16(b, uint16(o.Value))
|
|
return ipv6RouterAlertPayloadLength
|
|
}
|
|
|
|
// IPv6ExtHdrSerializer provides serialization of IPv6 extension headers.
|
|
type IPv6ExtHdrSerializer []IPv6SerializableExtHdr
|
|
|
|
// Serialize serializes the provided list of IPv6 extension headers into b.
|
|
//
|
|
// Note, b must be of sufficient size to hold all the headers in s. See
|
|
// IPv6ExtHdrSerializer.Length for details on the getting the total size of a
|
|
// serialized IPv6ExtHdrSerializer.
|
|
//
|
|
// Serialize may panic if b is not of sufficient size to hold all the options
|
|
// in s.
|
|
//
|
|
// Serialize takes the transportProtocol value to be used as the last extension
|
|
// header's Next Header value and returns the header identifier of the first
|
|
// serialized extension header and the total serialized length.
|
|
func (s IPv6ExtHdrSerializer) Serialize(transportProtocol tcpip.TransportProtocolNumber, b []byte) (uint8, int) {
|
|
nextHeader := uint8(transportProtocol)
|
|
if len(s) == 0 {
|
|
return nextHeader, 0
|
|
}
|
|
var totalLength int
|
|
for i, h := range s[:len(s)-1] {
|
|
length := h.serializeInto(uint8(s[i+1].identifier()), b)
|
|
b = b[length:]
|
|
totalLength += length
|
|
}
|
|
totalLength += s[len(s)-1].serializeInto(nextHeader, b)
|
|
return uint8(s[0].identifier()), totalLength
|
|
}
|
|
|
|
// Length returns the total number of bytes required to serialize the extension
|
|
// headers.
|
|
func (s IPv6ExtHdrSerializer) Length() int {
|
|
var totalLength int
|
|
for _, h := range s {
|
|
totalLength += h.length()
|
|
}
|
|
return totalLength
|
|
}
|