diff --git a/redirect_nftables_rules.go b/redirect_nftables_rules.go index 9f950a38..27765f5b 100644 --- a/redirect_nftables_rules.go +++ b/redirect_nftables_rules.go @@ -3,6 +3,7 @@ package tun import ( + "net" "net/netip" _ "unsafe" @@ -375,6 +376,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 35cd0956..4d036490 100644 --- a/tun.go +++ b/tun.go @@ -99,9 +99,13 @@ type Options struct { ExcludeInterface []string IncludeUID []ranges.Range[uint32] ExcludeUID []ranges.Range[uint32] + ExcludeSrcPort []ranges.Range[uint16] + ExcludeDstPort []ranges.Range[uint16] IncludeAndroidUser []int IncludePackage []string ExcludePackage []string + IncludeMACAddress []net.HardwareAddr + ExcludeMACAddress []net.HardwareAddr InterfaceFinder control.InterfaceFinder InterfaceMonitor DefaultInterfaceMonitor FileDescriptor int diff --git a/tun_linux.go b/tun_linux.go index 20fdce23..b9fbb1f1 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -655,6 +655,42 @@ func (t *NativeTun) rules() []*netlink.Rule { } nopPriority := ruleStart + 10 + for _, excludePort := range t.options.ExcludeSrcPort { + if p4 { + it = netlink.NewRule() + it.Priority = priority + it.Sport = netlink.NewRulePortRange(excludePort.Start, excludePort.End) + it.Goto = nopPriority + it.Family = unix.AF_INET + rules = append(rules, it) + } + if p6 { + it = netlink.NewRule() + it.Priority = priority6 + it.Sport = netlink.NewRulePortRange(excludePort.Start, excludePort.End) + it.Goto = nopPriority + it.Family = unix.AF_INET6 + rules = append(rules, it) + } + } + for _, excludePort := range t.options.ExcludeDstPort { + if p4 { + it = netlink.NewRule() + it.Priority = priority + it.Dport = netlink.NewRulePortRange(excludePort.Start, excludePort.End) + it.Goto = nopPriority + it.Family = unix.AF_INET + rules = append(rules, it) + } + if p6 { + it = netlink.NewRule() + it.Priority = priority6 + it.Dport = netlink.NewRulePortRange(excludePort.Start, excludePort.End) + it.Goto = nopPriority + it.Family = unix.AF_INET6 + rules = append(rules, it) + } + } for _, excludeRange := range excludeRanges { if p4 { it = netlink.NewRule() @@ -673,7 +709,7 @@ func (t *NativeTun) rules() []*netlink.Rule { rules = append(rules, it) } } - if len(excludeRanges) > 0 { + if len(t.options.ExcludeSrcPort) > 0 || len(t.options.ExcludeDstPort) > 0 || len(excludeRanges) > 0 { if p4 { priority++ }