Add MAC address include/exclude filtering for nftables auto-redirect

Support filtering traffic by source MAC address in the prerouting chain,
using ether addr payload matching with set lookups for multiple addresses.
This commit is contained in:
世界
2026-03-03 20:36:23 +08:00
parent e5d2fab035
commit 7de582cd66
2 changed files with 146 additions and 0 deletions
+144
View File
@@ -3,6 +3,7 @@
package tun
import (
"net"
"net/netip"
_ "unsafe"
@@ -377,6 +378,149 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
})
}
}
if len(r.tunOptions.IncludeMACAddress) > 0 {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chain,
Exprs: []expr.Any{
&expr.Meta{Key: expr.MetaKeyIIFTYPE, Register: 1},
&expr.Cmp{
Op: expr.CmpOpNeq,
Register: 1,
Data: binaryutil.NativeEndian.PutUint16(unix.ARPHRD_ETHER),
},
&expr.Counter{},
&expr.Verdict{
Kind: expr.VerdictReturn,
},
},
})
if len(r.tunOptions.IncludeMACAddress) > 1 {
includeMACSet := &nftables.Set{
Table: table,
Anonymous: true,
Constant: true,
KeyType: nftables.TypeEtherAddr,
}
err := nft.AddSet(includeMACSet, common.Map(r.tunOptions.IncludeMACAddress, func(it net.HardwareAddr) nftables.SetElement {
return nftables.SetElement{
Key: []byte(it),
}
}))
if err != nil {
return err
}
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chain,
Exprs: []expr.Any{
&expr.Payload{
OperationType: expr.PayloadLoad,
DestRegister: 1,
Base: expr.PayloadBaseLLHeader,
Offset: 6,
Len: 6,
},
&expr.Lookup{
SourceRegister: 1,
SetID: includeMACSet.ID,
SetName: includeMACSet.Name,
Invert: true,
},
&expr.Counter{},
&expr.Verdict{
Kind: expr.VerdictReturn,
},
},
})
} else {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chain,
Exprs: []expr.Any{
&expr.Payload{
OperationType: expr.PayloadLoad,
DestRegister: 1,
Base: expr.PayloadBaseLLHeader,
Offset: 6,
Len: 6,
},
&expr.Cmp{
Op: expr.CmpOpNeq,
Register: 1,
Data: []byte(r.tunOptions.IncludeMACAddress[0]),
},
&expr.Counter{},
&expr.Verdict{
Kind: expr.VerdictReturn,
},
},
})
}
}
if len(r.tunOptions.ExcludeMACAddress) > 0 {
if len(r.tunOptions.ExcludeMACAddress) > 1 {
excludeMACSet := &nftables.Set{
Table: table,
Anonymous: true,
Constant: true,
KeyType: nftables.TypeEtherAddr,
}
err := nft.AddSet(excludeMACSet, common.Map(r.tunOptions.ExcludeMACAddress, func(it net.HardwareAddr) nftables.SetElement {
return nftables.SetElement{
Key: []byte(it),
}
}))
if err != nil {
return err
}
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chain,
Exprs: []expr.Any{
&expr.Payload{
OperationType: expr.PayloadLoad,
DestRegister: 1,
Base: expr.PayloadBaseLLHeader,
Offset: 6,
Len: 6,
},
&expr.Lookup{
SourceRegister: 1,
SetID: excludeMACSet.ID,
SetName: excludeMACSet.Name,
},
&expr.Counter{},
&expr.Verdict{
Kind: expr.VerdictReturn,
},
},
})
} else {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chain,
Exprs: []expr.Any{
&expr.Payload{
OperationType: expr.PayloadLoad,
DestRegister: 1,
Base: expr.PayloadBaseLLHeader,
Offset: 6,
Len: 6,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte(r.tunOptions.ExcludeMACAddress[0]),
},
&expr.Counter{},
&expr.Verdict{
Kind: expr.VerdictReturn,
},
},
})
}
}
} else {
if len(r.tunOptions.IncludeUID) > 0 {
if len(r.tunOptions.IncludeUID) > 1 || r.tunOptions.IncludeUID[0].Start != r.tunOptions.IncludeUID[0].End {
+2
View File
@@ -102,6 +102,8 @@ type Options struct {
IncludeAndroidUser []int
IncludePackage []string
ExcludePackage []string
IncludeMACAddress []net.HardwareAddr
ExcludeMACAddress []net.HardwareAddr
InterfaceFinder control.InterfaceFinder
InterfaceMonitor DefaultInterfaceMonitor
FileDescriptor int