Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2121bc3f01 | ||
|
|
bea26198e7 | ||
|
|
3df19f464e | ||
|
|
494b0ef858 | ||
|
|
f13cd94aa0 | ||
|
|
51ac6b34f1 | ||
|
|
31e29f93cc | ||
|
|
c410f7050c | ||
|
|
d89ab3f207 | ||
|
|
219c612399 | ||
|
|
a8ce3838bc | ||
|
|
35b5747b44 | ||
|
|
5cb6d27288 | ||
|
|
9105485a50 | ||
|
|
57aba1a5c4 | ||
|
|
7f3343169a | ||
|
|
22b811f938 | ||
|
|
618be14c7b |
1
Makefile
1
Makefile
@@ -29,4 +29,5 @@ lint_install:
|
||||
|
||||
test:
|
||||
go build -v .
|
||||
go test -bench=. ./internal/checksum_test
|
||||
#go test -v .
|
||||
|
||||
@@ -28,6 +28,6 @@ func BenchmarkGChecksum(b *testing.B) {
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
checksum.Checksum(packet[i%1000], 0)
|
||||
checksum.ChecksumDefault(packet[i%1000], 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,3 +38,8 @@ func Combine(a, b uint16) uint16 {
|
||||
v := uint32(a) + uint32(b)
|
||||
return uint16(v + v>>16)
|
||||
}
|
||||
|
||||
func ChecksumDefault(buf []byte, initial uint16) uint16 {
|
||||
s, _ := calculateChecksum(buf, false, initial)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -8,6 +8,5 @@ package checksum
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func Checksum(buf []byte, initial uint16) uint16 {
|
||||
s, _ := calculateChecksum(buf, false, initial)
|
||||
return s
|
||||
return ChecksumDefault(buf, initial)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build !amd64
|
||||
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -51,12 +51,11 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInterface := m.defaultInterface.Load()
|
||||
newInterface, err := m.interfaceFinder.ByIndex(link.Attrs().Index)
|
||||
if err != nil {
|
||||
return E.Cause(err, "find updated interface: ", link.Attrs().Name)
|
||||
}
|
||||
m.defaultInterface.Store(newInterface)
|
||||
oldInterface := m.defaultInterface.Swap(newInterface)
|
||||
if oldInterface != nil && oldInterface.Equals(*newInterface) && oldVPNEnabled == m.androidVPNEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -165,12 +165,11 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
if defaultInterface == nil {
|
||||
return ErrNoRoute
|
||||
}
|
||||
oldInterface := m.defaultInterface.Load()
|
||||
newInterface, err := m.interfaceFinder.ByIndex(defaultInterface.Index)
|
||||
if err != nil {
|
||||
return E.Cause(err, "find updated interface: ", defaultInterface.Name)
|
||||
}
|
||||
m.defaultInterface.Store(newInterface)
|
||||
oldInterface := m.defaultInterface.Swap(newInterface)
|
||||
if oldInterface != nil && oldInterface.Equals(*newInterface) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ type networkUpdateMonitor struct {
|
||||
var ErrNetlinkBanned = E.New(
|
||||
"netlink socket in Android is banned by Google, " +
|
||||
"use the root or system (ADB) user to run sing-box, " +
|
||||
"or switch to the sing-box Adnroid graphical interface client",
|
||||
"or switch to the sing-box Android graphical interface client",
|
||||
)
|
||||
|
||||
func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
|
||||
|
||||
@@ -25,12 +25,11 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInterface := m.defaultInterface.Load()
|
||||
newInterface, err := m.interfaceFinder.ByIndex(link.Attrs().Index)
|
||||
if err != nil {
|
||||
return E.Cause(err, "find updated interface: ", link.Attrs().Name)
|
||||
}
|
||||
m.defaultInterface.Store(newInterface)
|
||||
oldInterface := m.defaultInterface.Swap(newInterface)
|
||||
if oldInterface != nil && oldInterface.Equals(*newInterface) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -102,12 +102,11 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
return ErrNoRoute
|
||||
}
|
||||
|
||||
oldInterface := m.defaultInterface.Load()
|
||||
newInterface, err := m.interfaceFinder.ByIndex(index)
|
||||
if err != nil {
|
||||
return E.Cause(err, "find updated interface: ", alias)
|
||||
}
|
||||
m.defaultInterface.Store(newInterface)
|
||||
oldInterface := m.defaultInterface.Swap(newInterface)
|
||||
if oldInterface != nil && oldInterface.Equals(*newInterface) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
@@ -30,10 +27,7 @@ func (r *autoRedirect) setupIPTables() error {
|
||||
}
|
||||
|
||||
func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
|
||||
tableNameInput := r.tableName + "-input"
|
||||
tableNameForward := r.tableName + "-forward"
|
||||
tableNameOutput := r.tableName + "-output"
|
||||
tableNamePreRouteing := r.tableName + "-prerouting"
|
||||
redirectPort := r.redirectPort()
|
||||
// OUTPUT
|
||||
err := r.runShell(iptablesPath, "-t nat -N", tableNameOutput)
|
||||
@@ -50,184 +44,6 @@ func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS == "android" {
|
||||
return nil
|
||||
}
|
||||
// INPUT
|
||||
err = r.runShell(iptablesPath, "-N", tableNameInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameInput,
|
||||
"-i", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameInput,
|
||||
"-o", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-I INPUT -j", tableNameInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// FORWARD
|
||||
err = r.runShell(iptablesPath, "-N", tableNameForward)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameForward,
|
||||
"-i", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameForward,
|
||||
"-o", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-I FORWARD -j", tableNameForward)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// PREROUTING
|
||||
err = r.runShell(iptablesPath, "-t nat -N", tableNamePreRouteing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
routeAddress []netip.Prefix
|
||||
routeExcludeAddress []netip.Prefix
|
||||
)
|
||||
if iptablesPath == r.iptablesPath {
|
||||
routeAddress = r.tunOptions.Inet4RouteAddress
|
||||
routeExcludeAddress = r.tunOptions.Inet4RouteExcludeAddress
|
||||
} else {
|
||||
routeAddress = r.tunOptions.Inet6RouteAddress
|
||||
routeExcludeAddress = r.tunOptions.Inet6RouteExcludeAddress
|
||||
}
|
||||
if len(routeAddress) > 0 && (len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0) {
|
||||
return E.New("`*_route_address` is conflict with `include_interface` or `include_uid`")
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", r.tunOptions.Name, "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, address := range routeExcludeAddress {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-d", address.String(), "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, name := range r.tunOptions.ExcludeInterface {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", name, "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, uid := range r.tunOptions.ExcludeUID {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-m owner --uid-owner", uid, "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !r.tunOptions.EXP_DisableDNSHijack {
|
||||
dnsServer := common.Find(r.tunOptions.DNSServers, func(it netip.Addr) bool {
|
||||
return it.Is4() == (iptablesPath == r.iptablesPath)
|
||||
})
|
||||
if !dnsServer.IsValid() {
|
||||
if iptablesPath == r.iptablesPath {
|
||||
if HasNextAddress(r.tunOptions.Inet4Address[0], 1) {
|
||||
dnsServer = r.tunOptions.Inet4Address[0].Addr().Next()
|
||||
}
|
||||
} else {
|
||||
if HasNextAddress(r.tunOptions.Inet6Address[0], 1) {
|
||||
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
if dnsServer.IsValid() {
|
||||
if len(routeAddress) > 0 {
|
||||
for _, address := range routeAddress {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-d", address.String(), "-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
|
||||
for _, name := range r.tunOptions.IncludeInterface {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", name, "-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, uidRange := range r.tunOptions.IncludeUID {
|
||||
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-m owner --uid-owner", uid, "-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing, "-m addrtype --dst-type LOCAL -j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(routeAddress) > 0 {
|
||||
for _, address := range routeAddress {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-d", address.String(), "-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
|
||||
for _, name := range r.tunOptions.IncludeInterface {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", name, "-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, uidRange := range r.tunOptions.IncludeUID {
|
||||
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-m owner --uid-owner", uid, "-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-t nat -I PREROUTING -j", tableNamePreRouteing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -241,29 +57,11 @@ func (r *autoRedirect) cleanupIPTables() {
|
||||
}
|
||||
|
||||
func (r *autoRedirect) cleanupIPTablesForFamily(iptablesPath string) {
|
||||
tableNameInput := r.tableName + "-input"
|
||||
tableNameOutput := r.tableName + "-output"
|
||||
tableNameForward := r.tableName + "-forward"
|
||||
tableNamePreRouteing := r.tableName + "-prerouting"
|
||||
|
||||
_ = r.runShell(iptablesPath, "-t nat -D OUTPUT -j", tableNameOutput)
|
||||
_ = r.runShell(iptablesPath, "-t nat -F", tableNameOutput)
|
||||
_ = r.runShell(iptablesPath, "-t nat -X", tableNameOutput)
|
||||
if runtime.GOOS == "android" {
|
||||
return
|
||||
}
|
||||
|
||||
_ = r.runShell(iptablesPath, "-D INPUT -j", tableNameInput)
|
||||
_ = r.runShell(iptablesPath, "-F", tableNameInput)
|
||||
_ = r.runShell(iptablesPath, "-X", tableNameInput)
|
||||
|
||||
_ = r.runShell(iptablesPath, "-D FORWARD -j", tableNameForward)
|
||||
_ = r.runShell(iptablesPath, "-F", tableNameForward)
|
||||
_ = r.runShell(iptablesPath, "-X", tableNameForward)
|
||||
|
||||
_ = r.runShell(iptablesPath, "-t nat -D PREROUTING -j", tableNamePreRouteing)
|
||||
_ = r.runShell(iptablesPath, "-t nat -F", tableNamePreRouteing)
|
||||
_ = r.runShell(iptablesPath, "-t nat -X", tableNamePreRouteing)
|
||||
}
|
||||
|
||||
func (r *autoRedirect) runShell(commands ...any) error {
|
||||
|
||||
@@ -83,9 +83,8 @@ func (r *autoRedirect) Start() error {
|
||||
} else {
|
||||
if r.useNFTables {
|
||||
err = r.initializeNFTables()
|
||||
if err != nil && err != os.ErrInvalid {
|
||||
r.useNFTables = false
|
||||
r.logger.Debug("missing nftables support: ", err)
|
||||
if err != nil {
|
||||
return E.Cause(err, "missing nftables support")
|
||||
}
|
||||
}
|
||||
if len(r.tunOptions.Inet4Address) > 0 {
|
||||
|
||||
@@ -64,7 +64,7 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
r.nftablesCreateRedirect(nft, table, chainOutput)
|
||||
|
||||
chainOutputUDP := nft.AddChain(&nftables.Chain{
|
||||
Name: "output_udp",
|
||||
Name: "output_udp_icmp",
|
||||
Table: table,
|
||||
Hooknum: nftables.ChainHookOutput,
|
||||
Priority: nftables.ChainPriorityMangle,
|
||||
@@ -101,7 +101,9 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
}
|
||||
r.nftablesCreateUnreachable(nft, table, chainPreRouting)
|
||||
r.nftablesCreateRedirect(nft, table, chainPreRouting)
|
||||
r.nftablesCreateMark(nft, table, chainPreRouting)
|
||||
if r.tunOptions.AutoRedirectMarkMode {
|
||||
r.nftablesCreateMark(nft, table, chainPreRouting)
|
||||
}
|
||||
|
||||
if r.tunOptions.AutoRedirectMarkMode {
|
||||
chainPreRoutingUDP := nft.AddChain(&nftables.Chain{
|
||||
@@ -111,12 +113,6 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
Priority: nftables.ChainPriorityRef(*nftables.ChainPriorityNATDest + 2),
|
||||
Type: nftables.ChainTypeFilter,
|
||||
})
|
||||
if r.enableIPv4 {
|
||||
nftablesCreateExcludeDestinationIPSet(nft, table, chainPreRoutingUDP, 5, "inet4_local_address_set", nftables.TableFamilyIPv4, false)
|
||||
}
|
||||
if r.enableIPv6 {
|
||||
nftablesCreateExcludeDestinationIPSet(nft, table, chainPreRoutingUDP, 6, "inet6_local_address_set", nftables.TableFamilyIPv6, false)
|
||||
}
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
@@ -126,10 +122,28 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: []byte{unix.IPPROTO_UDP},
|
||||
},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyIIFNAME,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: nftablesIfname(r.tunOptions.Name),
|
||||
},
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
@@ -147,6 +161,40 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
&expr.Counter{},
|
||||
},
|
||||
})
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: binaryutil.NativeEndian.PutUint32(r.tunOptions.AutoRedirectInputMark),
|
||||
},
|
||||
&expr.Immediate{
|
||||
Register: 1,
|
||||
Data: binaryutil.NativeEndian.PutUint32(r.tunOptions.AutoRedirectOutputMark),
|
||||
},
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyMARK,
|
||||
Register: 1,
|
||||
SourceRegister: true,
|
||||
},
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyMARK,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
SourceRegister: true,
|
||||
},
|
||||
&expr.Counter{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
err = r.configureOpenWRTFirewall4(nft, false)
|
||||
|
||||
@@ -138,6 +138,27 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
|
||||
},
|
||||
},
|
||||
})
|
||||
if chain.Type == nftables.ChainTypeRoute {
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: binaryutil.NativeEndian.PutUint32(r.tunOptions.AutoRedirectOutputMark),
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if chain.Hooknum == nftables.ChainHookPrerouting {
|
||||
if len(r.tunOptions.IncludeInterface) > 0 {
|
||||
@@ -402,13 +423,13 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
|
||||
if !r.tunOptions.EXP_DisableDNSHijack && ((chain.Hooknum == nftables.ChainHookPrerouting && chain.Type == nftables.ChainTypeNAT) ||
|
||||
(r.tunOptions.AutoRedirectMarkMode && chain.Hooknum == nftables.ChainHookOutput && chain.Type == nftables.ChainTypeNAT)) {
|
||||
if r.enableIPv4 {
|
||||
err := r.nftablesCreateDNSHijackRulesForFamily(nft, table, chain, nftables.TableFamilyIPv4)
|
||||
err := r.nftablesCreateDNSHijackRulesForFamily(nft, table, chain, nftables.TableFamilyIPv4, 5, "inet4_local_address_set")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if r.enableIPv6 {
|
||||
err := r.nftablesCreateDNSHijackRulesForFamily(nft, table, chain, nftables.TableFamilyIPv6)
|
||||
err := r.nftablesCreateDNSHijackRulesForFamily(nft, table, chain, nftables.TableFamilyIPv6, 6, "inet6_local_address_set")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -418,6 +439,20 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
|
||||
if r.tunOptions.AutoRedirectMarkMode &&
|
||||
((chain.Hooknum == nftables.ChainHookOutput && chain.Type == nftables.ChainTypeRoute) ||
|
||||
(chain.Hooknum == nftables.ChainHookPrerouting && chain.Type == nftables.ChainTypeFilter)) {
|
||||
ipProto := &nftables.Set{
|
||||
Table: table,
|
||||
Anonymous: true,
|
||||
Constant: true,
|
||||
KeyType: nftables.TypeInetProto,
|
||||
}
|
||||
err := nft.AddSet(ipProto, []nftables.SetElement{
|
||||
{Key: []byte{unix.IPPROTO_UDP}},
|
||||
{Key: []byte{unix.IPPROTO_ICMP}},
|
||||
{Key: []byte{unix.IPPROTO_ICMPV6}},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
@@ -426,10 +461,11 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
|
||||
Key: expr.MetaKeyL4PROTO,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: []byte{unix.IPPROTO_UDP},
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: ipProto.ID,
|
||||
SetName: ipProto.Name,
|
||||
Invert: true,
|
||||
},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
@@ -553,7 +589,7 @@ func (r *autoRedirect) nftablesCreateRedirect(
|
||||
|
||||
func (r *autoRedirect) nftablesCreateDNSHijackRulesForFamily(
|
||||
nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain,
|
||||
family nftables.TableFamily,
|
||||
family nftables.TableFamily, setID uint32, setName string,
|
||||
) error {
|
||||
ipProto := &nftables.Set{
|
||||
Table: table,
|
||||
@@ -611,6 +647,33 @@ func (r *autoRedirect) nftablesCreateDNSHijackRulesForFamily(
|
||||
Data: nftablesIfname("lo"),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
if family == nftables.TableFamilyIPv4 {
|
||||
exprs = append(exprs,
|
||||
&expr.Payload{
|
||||
OperationType: expr.PayloadLoad,
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseNetworkHeader,
|
||||
Offset: 12,
|
||||
Len: 4,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
exprs = append(exprs,
|
||||
&expr.Payload{
|
||||
OperationType: expr.PayloadLoad,
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseNetworkHeader,
|
||||
Offset: 8,
|
||||
Len: 16,
|
||||
},
|
||||
)
|
||||
}
|
||||
exprs = append(exprs, &expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: setID,
|
||||
SetName: setName,
|
||||
})
|
||||
}
|
||||
exprs = append(exprs,
|
||||
&expr.Meta{
|
||||
@@ -634,6 +697,7 @@ func (r *autoRedirect) nftablesCreateDNSHijackRulesForFamily(
|
||||
Register: 1,
|
||||
Data: binaryutil.BigEndian.PutUint16(53),
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Immediate{
|
||||
Register: 1,
|
||||
Data: dnsServer.AsSlice(),
|
||||
@@ -643,7 +707,6 @@ func (r *autoRedirect) nftablesCreateDNSHijackRulesForFamily(
|
||||
Family: uint32(family),
|
||||
RegAddrMin: 1,
|
||||
},
|
||||
&expr.Counter{},
|
||||
)
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
@@ -679,9 +742,7 @@ func (r *autoRedirect) nftablesCreateUnreachable(
|
||||
Data: []byte{uint8(nfProto)},
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictDrop,
|
||||
},
|
||||
&expr.Reject{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
@@ -17,19 +19,25 @@ import (
|
||||
)
|
||||
|
||||
type gLazyConn struct {
|
||||
tcpConn *gonet.TCPConn
|
||||
parentCtx context.Context
|
||||
stack *stack.Stack
|
||||
request *tcp.ForwarderRequest
|
||||
localAddr net.Addr
|
||||
remoteAddr net.Addr
|
||||
handshakeDone bool
|
||||
handshakeErr error
|
||||
tcpConn *gonet.TCPConn
|
||||
parentCtx context.Context
|
||||
stack *stack.Stack
|
||||
request *tcp.ForwarderRequest
|
||||
localAddr net.Addr
|
||||
remoteAddr net.Addr
|
||||
handshakeAccess sync.Mutex
|
||||
handshakeDone bool
|
||||
handshakeErr error
|
||||
}
|
||||
|
||||
func (c *gLazyConn) HandshakeContext(ctx context.Context) error {
|
||||
if c.handshakeDone {
|
||||
return nil
|
||||
return c.handshakeErr
|
||||
}
|
||||
c.handshakeAccess.Lock()
|
||||
defer c.handshakeAccess.Unlock()
|
||||
if c.handshakeDone {
|
||||
return c.handshakeErr
|
||||
}
|
||||
defer func() {
|
||||
c.handshakeDone = true
|
||||
@@ -67,7 +75,12 @@ func (c *gLazyConn) HandshakeFailure(err error) error {
|
||||
if c.handshakeDone {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
c.request.Complete(err != ErrDrop)
|
||||
c.handshakeAccess.Lock()
|
||||
defer c.handshakeAccess.Unlock()
|
||||
if c.handshakeDone {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
c.request.Complete(!errors.Is(err, ErrDrop))
|
||||
c.handshakeDone = true
|
||||
c.handshakeErr = err
|
||||
return nil
|
||||
@@ -78,25 +91,17 @@ func (c *gLazyConn) HandshakeSuccess() error {
|
||||
}
|
||||
|
||||
func (c *gLazyConn) Read(b []byte) (n int, err error) {
|
||||
if !c.handshakeDone {
|
||||
err = c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if c.handshakeErr != nil {
|
||||
return 0, c.handshakeErr
|
||||
err = c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.tcpConn.Read(b)
|
||||
}
|
||||
|
||||
func (c *gLazyConn) Write(b []byte) (n int, err error) {
|
||||
if !c.handshakeDone {
|
||||
err = c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if c.handshakeErr != nil {
|
||||
return 0, c.handshakeErr
|
||||
err = c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.tcpConn.Write(b)
|
||||
}
|
||||
@@ -110,46 +115,41 @@ func (c *gLazyConn) RemoteAddr() net.Addr {
|
||||
}
|
||||
|
||||
func (c *gLazyConn) SetDeadline(t time.Time) error {
|
||||
if !c.handshakeDone {
|
||||
err := c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if c.handshakeErr != nil {
|
||||
return c.handshakeErr
|
||||
err := c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.tcpConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *gLazyConn) SetReadDeadline(t time.Time) error {
|
||||
if !c.handshakeDone {
|
||||
err := c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if c.handshakeErr != nil {
|
||||
return c.handshakeErr
|
||||
err := c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.tcpConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *gLazyConn) SetWriteDeadline(t time.Time) error {
|
||||
if !c.handshakeDone {
|
||||
err := c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if c.handshakeErr != nil {
|
||||
return c.handshakeErr
|
||||
err := c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.tcpConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (c *gLazyConn) Close() error {
|
||||
if !c.handshakeDone {
|
||||
c.request.Complete(true)
|
||||
c.handshakeErr = net.ErrClosed
|
||||
return nil
|
||||
c.handshakeAccess.Lock()
|
||||
if !c.handshakeDone {
|
||||
c.request.Complete(true)
|
||||
c.handshakeErr = net.ErrClosed
|
||||
c.handshakeDone = true
|
||||
return nil
|
||||
} else if c.handshakeErr != nil {
|
||||
return nil
|
||||
}
|
||||
c.handshakeAccess.Unlock()
|
||||
} else if c.handshakeErr != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -158,9 +158,16 @@ func (c *gLazyConn) Close() error {
|
||||
|
||||
func (c *gLazyConn) CloseRead() error {
|
||||
if !c.handshakeDone {
|
||||
c.request.Complete(true)
|
||||
c.handshakeErr = net.ErrClosed
|
||||
return nil
|
||||
c.handshakeAccess.Lock()
|
||||
if !c.handshakeDone {
|
||||
c.request.Complete(true)
|
||||
c.handshakeErr = net.ErrClosed
|
||||
c.handshakeDone = true
|
||||
return nil
|
||||
} else if c.handshakeErr != nil {
|
||||
return nil
|
||||
}
|
||||
c.handshakeAccess.Unlock()
|
||||
} else if c.handshakeErr != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -169,9 +176,16 @@ func (c *gLazyConn) CloseRead() error {
|
||||
|
||||
func (c *gLazyConn) CloseWrite() error {
|
||||
if !c.handshakeDone {
|
||||
c.request.Complete(true)
|
||||
c.handshakeErr = net.ErrClosed
|
||||
return nil
|
||||
c.handshakeAccess.Lock()
|
||||
if !c.handshakeDone {
|
||||
c.request.Complete(true)
|
||||
c.handshakeErr = net.ErrClosed
|
||||
c.handshakeDone = true
|
||||
return nil
|
||||
} else if c.handshakeErr != nil {
|
||||
return nil
|
||||
}
|
||||
c.handshakeAccess.Unlock()
|
||||
} else if c.handshakeErr != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -179,10 +193,14 @@ func (c *gLazyConn) CloseWrite() error {
|
||||
}
|
||||
|
||||
func (c *gLazyConn) ReaderReplaceable() bool {
|
||||
c.handshakeAccess.Lock()
|
||||
defer c.handshakeAccess.Unlock()
|
||||
return c.handshakeDone && c.handshakeErr == nil
|
||||
}
|
||||
|
||||
func (c *gLazyConn) WriterReplaceable() bool {
|
||||
c.handshakeAccess.Lock()
|
||||
defer c.handshakeAccess.Unlock()
|
||||
return c.handshakeDone && c.handshakeErr == nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||
@@ -37,7 +38,7 @@ func (f *TCPForwarder) Forward(r *tcp.ForwarderRequest) {
|
||||
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
|
||||
pErr := f.handler.PrepareConnection(N.NetworkTCP, source, destination)
|
||||
if pErr != nil {
|
||||
r.Complete(pErr != ErrDrop)
|
||||
r.Complete(!errors.Is(pErr, ErrDrop))
|
||||
return
|
||||
}
|
||||
conn := &gLazyConn{
|
||||
|
||||
@@ -4,6 +4,7 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"net/netip"
|
||||
"os"
|
||||
@@ -59,7 +60,7 @@ func rangeIterate(r stack.Range, fn func(*buffer.View))
|
||||
func (f *UDPForwarder) PreparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
|
||||
pErr := f.handler.PrepareConnection(N.NetworkUDP, source, destination)
|
||||
if pErr != nil {
|
||||
if pErr != ErrDrop {
|
||||
if !errors.Is(pErr, ErrDrop) {
|
||||
gWriteUnreachable(f.stack, userData.(*stack.PacketBuffer))
|
||||
}
|
||||
return false, nil, nil, nil
|
||||
|
||||
@@ -2,6 +2,7 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
@@ -107,10 +108,7 @@ func (s *System) Start() error {
|
||||
}
|
||||
|
||||
func (s *System) start() error {
|
||||
err := fixWindowsFirewall()
|
||||
if err != nil {
|
||||
return E.Cause(err, "fix windows firewall for system stack")
|
||||
}
|
||||
_ = fixWindowsFirewall()
|
||||
var listener net.ListenConfig
|
||||
if s.bindInterface {
|
||||
listener.Control = control.Append(listener.Control, func(network, address string, conn syscall.RawConn) error {
|
||||
@@ -122,6 +120,7 @@ func (s *System) start() error {
|
||||
})
|
||||
}
|
||||
var tcpListener net.Listener
|
||||
var err error
|
||||
if s.inet4Address.IsValid() {
|
||||
for i := 0; i < 3; i++ {
|
||||
tcpListener, err = listener.Listen(s.ctx, "tcp4", net.JoinHostPort(s.inet4ServerAddress.String(), "0"))
|
||||
@@ -356,7 +355,7 @@ func (s *System) processIPv4TCP(ipHdr header.IPv4, tcpHdr header.TCP) (bool, err
|
||||
} else {
|
||||
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
|
||||
if err != nil {
|
||||
if err == ErrDrop {
|
||||
if errors.Is(err, ErrDrop) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, s.resetIPv4TCP(ipHdr, tcpHdr)
|
||||
@@ -443,7 +442,7 @@ func (s *System) processIPv6TCP(ipHdr header.IPv6, tcpHdr header.TCP) (bool, err
|
||||
} else {
|
||||
natPort, err := s.tcpNat.Lookup(source, destination, s.handler)
|
||||
if err != nil {
|
||||
if err == ErrDrop {
|
||||
if errors.Is(err, ErrDrop) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, s.resetIPv6TCP(ipHdr, tcpHdr)
|
||||
@@ -538,7 +537,7 @@ func (s *System) processIPv6UDP(ipHdr header.IPv6, udpHdr header.UDP) error {
|
||||
func (s *System) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) {
|
||||
pErr := s.handler.PrepareConnection(N.NetworkUDP, source, destination)
|
||||
if pErr != nil {
|
||||
if pErr != ErrDrop {
|
||||
if !errors.Is(pErr, ErrDrop) {
|
||||
if source.IsIPv4() {
|
||||
ipHdr := userData.(header.IPv4)
|
||||
s.rejectIPv4WithICMP(ipHdr, header.ICMPv4PortUnreachable)
|
||||
|
||||
@@ -22,6 +22,7 @@ func fixWindowsFirewall() error {
|
||||
Protocol: winfw.NET_FW_IP_PROTOCOL_TCP,
|
||||
Direction: winfw.NET_FW_RULE_DIR_IN,
|
||||
Action: winfw.NET_FW_ACTION_ALLOW,
|
||||
Profiles: winfw.NET_FW_PROFILE2_ALL,
|
||||
}
|
||||
_, err = winfw.FirewallRuleAddAdvanced(rule)
|
||||
return err
|
||||
|
||||
@@ -268,45 +268,47 @@ func (t *NativeTun) UpdateRouteOptions(tunOptions Options) error {
|
||||
}
|
||||
|
||||
func (t *NativeTun) setRoutes() error {
|
||||
if t.options.AutoRoute && t.options.FileDescriptor == 0 {
|
||||
if t.options.FileDescriptor == 0 {
|
||||
routeRanges, err := t.options.BuildAutoRouteRanges(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gateway4, gateway6 := t.options.Inet4GatewayAddr(), t.options.Inet6GatewayAddr()
|
||||
for _, destination := range routeRanges {
|
||||
var gateway netip.Addr
|
||||
if destination.Addr().Is4() {
|
||||
gateway = gateway4
|
||||
} else {
|
||||
gateway = gateway6
|
||||
}
|
||||
var interfaceIndex int
|
||||
if t.options.InterfaceScope {
|
||||
iff, err := t.options.InterfaceFinder.ByName(t.options.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
interfaceIndex = iff.Index
|
||||
}
|
||||
err = execRoute(unix.RTM_ADD, t.options.InterfaceScope, interfaceIndex, destination, gateway)
|
||||
if err != nil {
|
||||
if errors.Is(err, unix.EEXIST) {
|
||||
err = execRoute(unix.RTM_DELETE, false, 0, destination, gateway)
|
||||
if err != nil {
|
||||
return E.Cause(err, "remove existing route: ", destination)
|
||||
}
|
||||
err = execRoute(unix.RTM_ADD, t.options.InterfaceScope, interfaceIndex, destination, gateway)
|
||||
if err != nil {
|
||||
return E.Cause(err, "re-add route: ", destination)
|
||||
}
|
||||
if len(routeRanges) > 0 {
|
||||
gateway4, gateway6 := t.options.Inet4GatewayAddr(), t.options.Inet6GatewayAddr()
|
||||
for _, destination := range routeRanges {
|
||||
var gateway netip.Addr
|
||||
if destination.Addr().Is4() {
|
||||
gateway = gateway4
|
||||
} else {
|
||||
return E.Cause(err, "add route: ", destination)
|
||||
gateway = gateway6
|
||||
}
|
||||
var interfaceIndex int
|
||||
if t.options.InterfaceScope {
|
||||
iff, err := t.options.InterfaceFinder.ByName(t.options.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
interfaceIndex = iff.Index
|
||||
}
|
||||
err = execRoute(unix.RTM_ADD, t.options.InterfaceScope, interfaceIndex, destination, gateway)
|
||||
if err != nil {
|
||||
if errors.Is(err, unix.EEXIST) {
|
||||
err = execRoute(unix.RTM_DELETE, false, 0, destination, gateway)
|
||||
if err != nil {
|
||||
return E.Cause(err, "remove existing route: ", destination)
|
||||
}
|
||||
err = execRoute(unix.RTM_ADD, t.options.InterfaceScope, interfaceIndex, destination, gateway)
|
||||
if err != nil {
|
||||
return E.Cause(err, "re-add route: ", destination)
|
||||
}
|
||||
} else {
|
||||
return E.Cause(err, "add route: ", destination)
|
||||
}
|
||||
}
|
||||
}
|
||||
flushDNSCache()
|
||||
t.routeSet = true
|
||||
}
|
||||
flushDNSCache()
|
||||
t.routeSet = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
20
tun_linux.go
20
tun_linux.go
@@ -202,7 +202,6 @@ func (t *NativeTun) enableGSO() error {
|
||||
err = setUDPOffload(t.tunFd)
|
||||
if err != nil {
|
||||
t.gro.disableUDPGRO()
|
||||
return E.Cause(err, "enable UDP offload")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -669,7 +668,7 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
}
|
||||
}
|
||||
if len(t.options.IncludeInterface) > 0 {
|
||||
matchPriority := priority + 2*len(t.options.IncludeInterface) + 1
|
||||
matchPriority := priority + 2
|
||||
for _, includeInterface := range t.options.IncludeInterface {
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
@@ -678,7 +677,6 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = matchPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
@@ -687,9 +685,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = matchPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
if p4 {
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
priority6++
|
||||
}
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
@@ -727,7 +730,6 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
@@ -736,9 +738,15 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
|
||||
if p4 {
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "android" && t.options.InterfaceMonitor.AndroidVPNEnabled() {
|
||||
|
||||
66
tun_rules.go
66
tun_rules.go
@@ -108,7 +108,7 @@ const autoRouteUseSubRanges = runtime.GOOS == "darwin"
|
||||
|
||||
func (o *Options) BuildAutoRouteRanges(underNetworkExtension bool) ([]netip.Prefix, error) {
|
||||
var routeRanges []netip.Prefix
|
||||
if o.AutoRoute && len(o.Inet4Address) > 0 {
|
||||
if len(o.Inet4Address) > 0 {
|
||||
var inet4Ranges []netip.Prefix
|
||||
if len(o.Inet4RouteAddress) > 0 {
|
||||
inet4Ranges = o.Inet4RouteAddress
|
||||
@@ -119,19 +119,27 @@ func (o *Options) BuildAutoRouteRanges(underNetworkExtension bool) ([]netip.Pref
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if autoRouteUseSubRanges && !underNetworkExtension {
|
||||
inet4Ranges = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 1}), 8),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 2}), 7),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 4}), 6),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 8}), 5),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 16}), 4),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 32}), 3),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 64}), 2),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 128}), 1),
|
||||
} else if o.AutoRoute {
|
||||
if autoRouteUseSubRanges && !underNetworkExtension {
|
||||
inet4Ranges = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 1}), 8),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 2}), 7),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 4}), 6),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 8}), 5),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 16}), 4),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 32}), 3),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 64}), 2),
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{0: 128}), 1),
|
||||
}
|
||||
} else {
|
||||
inet4Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv4Unspecified(), 0)}
|
||||
}
|
||||
} else if runtime.GOOS == "darwin" {
|
||||
for _, address := range o.Inet4Address {
|
||||
if address.Bits() < 32 {
|
||||
inet4Ranges = append(inet4Ranges, address.Masked())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inet4Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv4Unspecified(), 0)}
|
||||
}
|
||||
if len(o.Inet4RouteExcludeAddress) == 0 {
|
||||
routeRanges = append(routeRanges, inet4Ranges...)
|
||||
@@ -161,19 +169,27 @@ func (o *Options) BuildAutoRouteRanges(underNetworkExtension bool) ([]netip.Pref
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if autoRouteUseSubRanges && !underNetworkExtension {
|
||||
inet6Ranges = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 1}), 8),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 2}), 7),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 4}), 6),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 8}), 5),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 16}), 4),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 32}), 3),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 64}), 2),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 128}), 1),
|
||||
} else if o.AutoRoute {
|
||||
if autoRouteUseSubRanges && !underNetworkExtension {
|
||||
inet6Ranges = []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 1}), 8),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 2}), 7),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 4}), 6),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 8}), 5),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 16}), 4),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 32}), 3),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 64}), 2),
|
||||
netip.PrefixFrom(netip.AddrFrom16([16]byte{0: 128}), 1),
|
||||
}
|
||||
} else {
|
||||
inet6Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv6Unspecified(), 0)}
|
||||
}
|
||||
} else if runtime.GOOS == "darwin" {
|
||||
for _, address := range o.Inet6Address {
|
||||
if address.Bits() < 32 {
|
||||
inet6Ranges = append(inet6Ranges, address.Masked())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inet6Ranges = []netip.Prefix{netip.PrefixFrom(netip.IPv6Unspecified(), 0)}
|
||||
}
|
||||
if len(o.Inet6RouteExcludeAddress) == 0 {
|
||||
routeRanges = append(routeRanges, inet6Ranges...)
|
||||
|
||||
Reference in New Issue
Block a user