From 22b811f938fdf52b6fb4ef317568f21389a683e1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Mar 2025 12:36:26 +0800 Subject: [PATCH] Remove gvisor's buffer dependence of gtcpip --- .../gtcpip/header/ipv6_extension_headers.go | 448 ------------------ 1 file changed, 448 deletions(-) diff --git a/internal/gtcpip/header/ipv6_extension_headers.go b/internal/gtcpip/header/ipv6_extension_headers.go index 3ab135d..20064d8 100644 --- a/internal/gtcpip/header/ipv6_extension_headers.go +++ b/internal/gtcpip/header/ipv6_extension_headers.go @@ -18,10 +18,8 @@ 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" ) @@ -145,79 +143,6 @@ func ipv6OptionsAlignmentPadding(headerOffset int, align int, alignOffset int) i 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 @@ -294,143 +219,6 @@ func ipv6UnknownActionFromIdentifier(id IPv6ExtHdrOptionIdentifier) IPv6OptionUn // 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. // @@ -473,242 +261,6 @@ 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 {