Fix missing UDP and ICMP bypass for auto redirect
This commit is contained in:
parent
80ea96e5b4
commit
99fb96a4c8
2 changed files with 231 additions and 85 deletions
212
nfqueue_linux.go
212
nfqueue_linux.go
|
|
@ -5,6 +5,7 @@ package tun
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/netip"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||||
|
|
@ -100,51 +101,147 @@ func (h *nfqueueHandler) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIPv6TransportHeader(payload []byte) (transportProto uint8, transportOffset int, ok bool) {
|
const ipv6AuthenticationHeaderIdentifier header.IPv6ExtensionHeaderIdentifier = 51
|
||||||
if len(payload) < header.IPv6MinimumSize {
|
|
||||||
return 0, 0, false
|
type preMatchPacket struct {
|
||||||
|
protocol uint8
|
||||||
|
network string
|
||||||
|
source M.Socksaddr
|
||||||
|
destination M.Socksaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePreMatchPacket(packet []byte) (preMatchPacket, bool) {
|
||||||
|
if len(packet) < 1 {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
protocol uint8
|
||||||
|
transportOffset int
|
||||||
|
source netip.Addr
|
||||||
|
destination netip.Addr
|
||||||
|
)
|
||||||
|
switch header.IPVersion(packet) {
|
||||||
|
case header.IPv4Version:
|
||||||
|
if len(packet) < header.IPv4MinimumSize {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
ipHdr := header.IPv4(packet)
|
||||||
|
transportOffset = int(ipHdr.HeaderLength())
|
||||||
|
if transportOffset < header.IPv4MinimumSize || transportOffset > len(packet) || int(ipHdr.TotalLength()) < transportOffset || ipHdr.FragmentOffset() != 0 {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
protocol = uint8(ipHdr.TransportProtocol())
|
||||||
|
source = ipHdr.SourceAddr()
|
||||||
|
destination = ipHdr.DestinationAddr()
|
||||||
|
case header.IPv6Version:
|
||||||
|
if len(packet) < header.IPv6MinimumSize {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
ipHdr := header.IPv6(packet)
|
||||||
|
var ok bool
|
||||||
|
protocol, transportOffset, ok = parsePreMatchIPv6Transport(packet)
|
||||||
|
if !ok {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
source = ipHdr.SourceAddr()
|
||||||
|
destination = ipHdr.DestinationAddr()
|
||||||
|
default:
|
||||||
|
return preMatchPacket{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv6 := header.IPv6(payload)
|
transport := packet[transportOffset:]
|
||||||
nextHeader := ipv6.NextHeader()
|
parsed := preMatchPacket{protocol: protocol}
|
||||||
|
switch protocol {
|
||||||
|
case uint8(header.TCPProtocolNumber):
|
||||||
|
if len(transport) < header.TCPMinimumSize {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
tcpHdr := header.TCP(transport)
|
||||||
|
flags := tcpHdr.Flags()
|
||||||
|
if !flags.Contains(header.TCPFlagSyn) || flags.Contains(header.TCPFlagAck) {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
parsed.network = N.NetworkTCP
|
||||||
|
parsed.source = M.SocksaddrFrom(source, tcpHdr.SourcePort())
|
||||||
|
parsed.destination = M.SocksaddrFrom(destination, tcpHdr.DestinationPort())
|
||||||
|
case uint8(header.UDPProtocolNumber):
|
||||||
|
if len(transport) < header.UDPMinimumSize {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
udpHdr := header.UDP(transport)
|
||||||
|
if int(udpHdr.Length()) < header.UDPMinimumSize {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
parsed.network = N.NetworkUDP
|
||||||
|
parsed.source = M.SocksaddrFrom(source, udpHdr.SourcePort())
|
||||||
|
parsed.destination = M.SocksaddrFrom(destination, udpHdr.DestinationPort())
|
||||||
|
case uint8(header.ICMPv4ProtocolNumber):
|
||||||
|
if !source.Is4() || len(transport) < header.ICMPv4MinimumSize {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
icmpHdr := header.ICMPv4(transport)
|
||||||
|
if icmpHdr.Type() != header.ICMPv4Echo || icmpHdr.Code() != 0 {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
parsed.network = N.NetworkICMP
|
||||||
|
parsed.source = M.SocksaddrFrom(source, 0)
|
||||||
|
parsed.destination = M.SocksaddrFrom(destination, 0)
|
||||||
|
case uint8(header.ICMPv6ProtocolNumber):
|
||||||
|
if !source.Is6() || len(transport) < header.ICMPv6MinimumSize {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
icmpHdr := header.ICMPv6(transport)
|
||||||
|
if icmpHdr.Type() != header.ICMPv6EchoRequest || icmpHdr.Code() != 0 {
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
parsed.network = N.NetworkICMP
|
||||||
|
parsed.source = M.SocksaddrFrom(source, 0)
|
||||||
|
parsed.destination = M.SocksaddrFrom(destination, 0)
|
||||||
|
default:
|
||||||
|
return preMatchPacket{}, false
|
||||||
|
}
|
||||||
|
return parsed, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePreMatchIPv6Transport(packet []byte) (transportProto uint8, transportOffset int, ok bool) {
|
||||||
|
nextHeader := header.IPv6(packet).NextHeader()
|
||||||
offset := header.IPv6MinimumSize
|
offset := header.IPv6MinimumSize
|
||||||
|
|
||||||
for {
|
for {
|
||||||
switch nextHeader {
|
switch header.IPv6ExtensionHeaderIdentifier(nextHeader) {
|
||||||
case unix.IPPROTO_HOPOPTS,
|
case header.IPv6HopByHopOptionsExtHdrIdentifier,
|
||||||
unix.IPPROTO_ROUTING,
|
header.IPv6RoutingExtHdrIdentifier,
|
||||||
unix.IPPROTO_DSTOPTS:
|
header.IPv6DestinationOptionsExtHdrIdentifier:
|
||||||
if len(payload) < offset+2 {
|
if len(packet) < offset+2 {
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
nextHeader = payload[offset]
|
nextHeader = packet[offset]
|
||||||
extLen := int(payload[offset+1]+1) * 8
|
extensionLength := (int(packet[offset+1]) + 1) * 8
|
||||||
if len(payload) < offset+extLen {
|
if len(packet) < offset+extensionLength {
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
offset += extLen
|
offset += extensionLength
|
||||||
|
case header.IPv6FragmentExtHdrIdentifier:
|
||||||
case unix.IPPROTO_FRAGMENT:
|
if len(packet) < offset+header.IPv6FragmentHeaderSize {
|
||||||
if len(payload) < offset+8 {
|
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
nextHeader = payload[offset]
|
fragmentHdr := header.IPv6Fragment(packet[offset:])
|
||||||
offset += 8
|
if fragmentHdr.FragmentOffset() != 0 {
|
||||||
|
|
||||||
case unix.IPPROTO_AH:
|
|
||||||
if len(payload) < offset+2 {
|
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
nextHeader = payload[offset]
|
nextHeader = fragmentHdr.NextHeader()
|
||||||
extLen := int(payload[offset+1]+2) * 4
|
offset += header.IPv6FragmentHeaderSize
|
||||||
if len(payload) < offset+extLen {
|
case ipv6AuthenticationHeaderIdentifier:
|
||||||
|
if len(packet) < offset+2 {
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
offset += extLen
|
nextHeader = packet[offset]
|
||||||
|
extensionLength := (int(packet[offset+1]) + 2) * 4
|
||||||
case unix.IPPROTO_NONE:
|
if len(packet) < offset+extensionLength {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
offset += extensionLength
|
||||||
|
case header.IPv6NoNextHeaderIdentifier:
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nextHeader, offset, true
|
return nextHeader, offset, true
|
||||||
}
|
}
|
||||||
|
|
@ -162,56 +259,13 @@ func (h *nfqueueHandler) handlePacket(attr nfqueue.Attribute) int {
|
||||||
packetID := *attr.PacketID
|
packetID := *attr.PacketID
|
||||||
payload := *attr.Payload
|
payload := *attr.Payload
|
||||||
|
|
||||||
if len(payload) < header.IPv4MinimumSize {
|
packet, loaded := parsePreMatchPacket(payload)
|
||||||
|
if !loaded {
|
||||||
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var srcAddr, dstAddr M.Socksaddr
|
_, pErr := h.handler.PrepareConnection(packet.network, packet.source, packet.destination, nil, 0)
|
||||||
var tcpOffset int
|
|
||||||
|
|
||||||
version := payload[0] >> 4
|
|
||||||
switch version {
|
|
||||||
case 4:
|
|
||||||
ipv4 := header.IPv4(payload)
|
|
||||||
if !ipv4.IsValid(len(payload)) || ipv4.Protocol() != uint8(unix.IPPROTO_TCP) {
|
|
||||||
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
srcAddr = M.SocksaddrFrom(ipv4.SourceAddr(), 0)
|
|
||||||
dstAddr = M.SocksaddrFrom(ipv4.DestinationAddr(), 0)
|
|
||||||
tcpOffset = int(ipv4.HeaderLength())
|
|
||||||
case 6:
|
|
||||||
transportProto, transportOffset, ok := parseIPv6TransportHeader(payload)
|
|
||||||
if !ok || transportProto != unix.IPPROTO_TCP {
|
|
||||||
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
ipv6 := header.IPv6(payload)
|
|
||||||
srcAddr = M.SocksaddrFrom(ipv6.SourceAddr(), 0)
|
|
||||||
dstAddr = M.SocksaddrFrom(ipv6.DestinationAddr(), 0)
|
|
||||||
tcpOffset = transportOffset
|
|
||||||
default:
|
|
||||||
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(payload) < tcpOffset+header.TCPMinimumSize {
|
|
||||||
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
tcp := header.TCP(payload[tcpOffset:])
|
|
||||||
srcAddr = M.SocksaddrFrom(srcAddr.Addr, tcp.SourcePort())
|
|
||||||
dstAddr = M.SocksaddrFrom(dstAddr.Addr, tcp.DestinationPort())
|
|
||||||
|
|
||||||
flags := tcp.Flags()
|
|
||||||
if !flags.Contains(header.TCPFlagSyn) || flags.Contains(header.TCPFlagAck) {
|
|
||||||
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
_, pErr := h.handler.PrepareConnection(N.NetworkTCP, srcAddr, dstAddr, nil, 0)
|
|
||||||
|
|
||||||
// Use NfRepeat for bypass/reset so the packet re-enters the chain
|
// Use NfRepeat for bypass/reset so the packet re-enters the chain
|
||||||
// from the beginning, allowing mark-checking rules to save the mark
|
// from the beginning, allowing mark-checking rules to save the mark
|
||||||
|
|
@ -221,7 +275,11 @@ func (h *nfqueueHandler) handlePacket(attr nfqueue.Attribute) int {
|
||||||
case errors.Is(pErr, ErrBypass):
|
case errors.Is(pErr, ErrBypass):
|
||||||
h.setVerdict(packetID, nfqueue.NfRepeat, h.outputMark)
|
h.setVerdict(packetID, nfqueue.NfRepeat, h.outputMark)
|
||||||
case errors.Is(pErr, ErrReset):
|
case errors.Is(pErr, ErrReset):
|
||||||
h.setVerdict(packetID, nfqueue.NfRepeat, h.resetMark)
|
if packet.protocol == uint8(unix.IPPROTO_TCP) {
|
||||||
|
h.setVerdict(packetID, nfqueue.NfRepeat, h.resetMark)
|
||||||
|
} else {
|
||||||
|
h.setVerdict(packetID, nfqueue.NfAccept, 0)
|
||||||
|
}
|
||||||
case errors.Is(pErr, ErrDrop):
|
case errors.Is(pErr, ErrDrop):
|
||||||
h.setVerdict(packetID, nfqueue.NfDrop, 0)
|
h.setVerdict(packetID, nfqueue.NfDrop, 0)
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/sagernet/nftables"
|
"github.com/sagernet/nftables"
|
||||||
"github.com/sagernet/nftables/binaryutil"
|
"github.com/sagernet/nftables/binaryutil"
|
||||||
"github.com/sagernet/nftables/expr"
|
"github.com/sagernet/nftables/expr"
|
||||||
|
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
@ -393,7 +394,10 @@ func (r *autoRedirect) nftablesCreatePreMatchChains(nft *nftables.Conn, table *n
|
||||||
Priority: nftables.ChainPriorityRef(*nftables.ChainPriorityNATDest - 1),
|
Priority: nftables.ChainPriorityRef(*nftables.ChainPriorityNATDest - 1),
|
||||||
Type: nftables.ChainTypeFilter,
|
Type: nftables.ChainTypeFilter,
|
||||||
})
|
})
|
||||||
r.nftablesAddPreMatchRules(nft, table, chainPreroutingPreMatch, true)
|
err := r.nftablesAddPreMatchRules(nft, table, chainPreroutingPreMatch, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if !r.shouldSkipOutputChain() {
|
if !r.shouldSkipOutputChain() {
|
||||||
chainOutputPreMatch := nft.AddChain(&nftables.Chain{
|
chainOutputPreMatch := nft.AddChain(&nftables.Chain{
|
||||||
|
|
@ -403,13 +407,16 @@ func (r *autoRedirect) nftablesCreatePreMatchChains(nft *nftables.Conn, table *n
|
||||||
Priority: nftables.ChainPriorityRef(*nftables.ChainPriorityMangle - 1),
|
Priority: nftables.ChainPriorityRef(*nftables.ChainPriorityMangle - 1),
|
||||||
Type: nftables.ChainTypeFilter,
|
Type: nftables.ChainTypeFilter,
|
||||||
})
|
})
|
||||||
r.nftablesAddPreMatchRules(nft, table, chainOutputPreMatch, false)
|
err = r.nftablesAddPreMatchRules(nft, table, chainOutputPreMatch, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *autoRedirect) nftablesAddPreMatchRules(nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain, isPrerouting bool) {
|
func (r *autoRedirect) nftablesAddPreMatchRules(nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain, isPrerouting bool) error {
|
||||||
ifnameKey := expr.MetaKeyOIFNAME
|
ifnameKey := expr.MetaKeyOIFNAME
|
||||||
if isPrerouting {
|
if isPrerouting {
|
||||||
ifnameKey = expr.MetaKeyIIFNAME
|
ifnameKey = expr.MetaKeyIIFNAME
|
||||||
|
|
@ -424,12 +431,35 @@ func (r *autoRedirect) nftablesAddPreMatchRules(nft *nftables.Conn, table *nftab
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
preMatchProtocols := &nftables.Set{
|
||||||
|
Table: table,
|
||||||
|
Anonymous: true,
|
||||||
|
Constant: true,
|
||||||
|
KeyType: nftables.TypeInetProto,
|
||||||
|
}
|
||||||
|
preMatchProtocolElements := []nftables.SetElement{{Key: []byte{unix.IPPROTO_TCP}}}
|
||||||
|
if r.tunOptions.AutoRedirectMarkMode {
|
||||||
|
preMatchProtocolElements = append(preMatchProtocolElements,
|
||||||
|
nftables.SetElement{Key: []byte{unix.IPPROTO_UDP}},
|
||||||
|
nftables.SetElement{Key: []byte{unix.IPPROTO_ICMP}},
|
||||||
|
nftables.SetElement{Key: []byte{unix.IPPROTO_ICMPV6}},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
err := nft.AddSet(preMatchProtocols, preMatchProtocolElements)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "add pre-match protocol set")
|
||||||
|
}
|
||||||
nft.AddRule(&nftables.Rule{
|
nft.AddRule(&nftables.Rule{
|
||||||
Table: table,
|
Table: table,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
Exprs: []expr.Any{
|
Exprs: []expr.Any{
|
||||||
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
||||||
&expr.Cmp{Op: expr.CmpOpNeq, Register: 1, Data: []byte{unix.IPPROTO_TCP}},
|
&expr.Lookup{
|
||||||
|
SourceRegister: 1,
|
||||||
|
SetID: preMatchProtocols.ID,
|
||||||
|
SetName: preMatchProtocols.Name,
|
||||||
|
Invert: true,
|
||||||
|
},
|
||||||
&expr.Verdict{Kind: expr.VerdictReturn},
|
&expr.Verdict{Kind: expr.VerdictReturn},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -459,6 +489,8 @@ func (r *autoRedirect) nftablesAddPreMatchRules(nft *nftables.Conn, table *nftab
|
||||||
Table: table,
|
Table: table,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
Exprs: []expr.Any{
|
Exprs: []expr.Any{
|
||||||
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
||||||
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_TCP}},
|
||||||
&expr.Meta{Key: expr.MetaKeyMARK, Register: 1},
|
&expr.Meta{Key: expr.MetaKeyMARK, Register: 1},
|
||||||
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: binaryutil.NativeEndian.PutUint32(r.effectiveResetMark())},
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: binaryutil.NativeEndian.PutUint32(r.effectiveResetMark())},
|
||||||
&expr.Counter{},
|
&expr.Counter{},
|
||||||
|
|
@ -477,11 +509,32 @@ func (r *autoRedirect) nftablesAddPreMatchRules(nft *nftables.Conn, table *nftab
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if r.tunOptions.AutoRedirectMarkMode {
|
||||||
|
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.AutoRedirectInputMark)},
|
||||||
|
&expr.Verdict{Kind: expr.VerdictReturn},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
queueExpression := func() *expr.Queue {
|
||||||
|
return &expr.Queue{
|
||||||
|
Num: r.effectiveNFQueue(),
|
||||||
|
Flag: expr.QueueFlagBypass,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TCP SYN: send to NFQUEUE for pre-match evaluation.
|
// TCP SYN: send to NFQUEUE for pre-match evaluation.
|
||||||
nft.AddRule(&nftables.Rule{
|
nft.AddRule(&nftables.Rule{
|
||||||
Table: table,
|
Table: table,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
Exprs: []expr.Any{
|
Exprs: []expr.Any{
|
||||||
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
||||||
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_TCP}},
|
||||||
&expr.Payload{
|
&expr.Payload{
|
||||||
OperationType: expr.PayloadLoad,
|
OperationType: expr.PayloadLoad,
|
||||||
DestRegister: 1,
|
DestRegister: 1,
|
||||||
|
|
@ -498,10 +551,45 @@ func (r *autoRedirect) nftablesAddPreMatchRules(nft *nftables.Conn, table *nftab
|
||||||
},
|
},
|
||||||
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{0x02}},
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{0x02}},
|
||||||
&expr.Counter{},
|
&expr.Counter{},
|
||||||
&expr.Queue{
|
queueExpression(),
|
||||||
Num: r.effectiveNFQueue(),
|
|
||||||
Flag: expr.QueueFlagBypass,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if r.tunOptions.AutoRedirectMarkMode {
|
||||||
|
nft.AddRule(&nftables.Rule{
|
||||||
|
Table: table,
|
||||||
|
Chain: chain,
|
||||||
|
Exprs: []expr.Any{
|
||||||
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
||||||
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_UDP}},
|
||||||
|
&expr.Counter{},
|
||||||
|
queueExpression(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
nft.AddRule(&nftables.Rule{
|
||||||
|
Table: table,
|
||||||
|
Chain: chain,
|
||||||
|
Exprs: []expr.Any{
|
||||||
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
||||||
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_ICMP}},
|
||||||
|
&expr.Payload{OperationType: expr.PayloadLoad, DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 0, Len: 2},
|
||||||
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(header.ICMPv4Echo), 0}},
|
||||||
|
&expr.Counter{},
|
||||||
|
queueExpression(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
nft.AddRule(&nftables.Rule{
|
||||||
|
Table: table,
|
||||||
|
Chain: chain,
|
||||||
|
Exprs: []expr.Any{
|
||||||
|
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
||||||
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_ICMPV6}},
|
||||||
|
&expr.Payload{OperationType: expr.PayloadLoad, DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 0, Len: 2},
|
||||||
|
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(header.ICMPv6EchoRequest), 0}},
|
||||||
|
&expr.Counter{},
|
||||||
|
queueExpression(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue