From f8e7794cc255baac35e6c8739b00930bd09704e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 3 Mar 2026 20:36:23 +0800 Subject: [PATCH] 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. --- redirect_nftables_rules.go | 144 +++++++++++++++++++++++++++++++++++++ tun.go | 2 + 2 files changed, 146 insertions(+) diff --git a/redirect_nftables_rules.go b/redirect_nftables_rules.go index d5b10f2..b53675d 100644 --- a/redirect_nftables_rules.go +++ b/redirect_nftables_rules.go @@ -3,6 +3,7 @@ package tun import ( + "net" "net/netip" _ "unsafe" @@ -376,6 +377,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 { diff --git a/tun.go b/tun.go index 07b448c..885d20c 100644 --- a/tun.go +++ b/tun.go @@ -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