Skip to content

Commit 8eb4e8a

Browse files
committed
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.
1 parent 24b1227 commit 8eb4e8a

2 files changed

Lines changed: 146 additions & 0 deletions

File tree

redirect_nftables_rules.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package tun
44

55
import (
6+
"net"
67
"net/netip"
78
_ "unsafe"
89

@@ -375,6 +376,149 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
375376
})
376377
}
377378
}
379+
if len(r.tunOptions.IncludeMACAddress) > 0 {
380+
nft.AddRule(&nftables.Rule{
381+
Table: table,
382+
Chain: chain,
383+
Exprs: []expr.Any{
384+
&expr.Meta{Key: expr.MetaKeyIIFTYPE, Register: 1},
385+
&expr.Cmp{
386+
Op: expr.CmpOpNeq,
387+
Register: 1,
388+
Data: binaryutil.NativeEndian.PutUint16(unix.ARPHRD_ETHER),
389+
},
390+
&expr.Counter{},
391+
&expr.Verdict{
392+
Kind: expr.VerdictReturn,
393+
},
394+
},
395+
})
396+
if len(r.tunOptions.IncludeMACAddress) > 1 {
397+
includeMACSet := &nftables.Set{
398+
Table: table,
399+
Anonymous: true,
400+
Constant: true,
401+
KeyType: nftables.TypeEtherAddr,
402+
}
403+
err := nft.AddSet(includeMACSet, common.Map(r.tunOptions.IncludeMACAddress, func(it net.HardwareAddr) nftables.SetElement {
404+
return nftables.SetElement{
405+
Key: []byte(it),
406+
}
407+
}))
408+
if err != nil {
409+
return err
410+
}
411+
nft.AddRule(&nftables.Rule{
412+
Table: table,
413+
Chain: chain,
414+
Exprs: []expr.Any{
415+
&expr.Payload{
416+
OperationType: expr.PayloadLoad,
417+
DestRegister: 1,
418+
Base: expr.PayloadBaseLLHeader,
419+
Offset: 6,
420+
Len: 6,
421+
},
422+
&expr.Lookup{
423+
SourceRegister: 1,
424+
SetID: includeMACSet.ID,
425+
SetName: includeMACSet.Name,
426+
Invert: true,
427+
},
428+
&expr.Counter{},
429+
&expr.Verdict{
430+
Kind: expr.VerdictReturn,
431+
},
432+
},
433+
})
434+
} else {
435+
nft.AddRule(&nftables.Rule{
436+
Table: table,
437+
Chain: chain,
438+
Exprs: []expr.Any{
439+
&expr.Payload{
440+
OperationType: expr.PayloadLoad,
441+
DestRegister: 1,
442+
Base: expr.PayloadBaseLLHeader,
443+
Offset: 6,
444+
Len: 6,
445+
},
446+
&expr.Cmp{
447+
Op: expr.CmpOpNeq,
448+
Register: 1,
449+
Data: []byte(r.tunOptions.IncludeMACAddress[0]),
450+
},
451+
&expr.Counter{},
452+
&expr.Verdict{
453+
Kind: expr.VerdictReturn,
454+
},
455+
},
456+
})
457+
}
458+
}
459+
if len(r.tunOptions.ExcludeMACAddress) > 0 {
460+
if len(r.tunOptions.ExcludeMACAddress) > 1 {
461+
excludeMACSet := &nftables.Set{
462+
Table: table,
463+
Anonymous: true,
464+
Constant: true,
465+
KeyType: nftables.TypeEtherAddr,
466+
}
467+
err := nft.AddSet(excludeMACSet, common.Map(r.tunOptions.ExcludeMACAddress, func(it net.HardwareAddr) nftables.SetElement {
468+
return nftables.SetElement{
469+
Key: []byte(it),
470+
}
471+
}))
472+
if err != nil {
473+
return err
474+
}
475+
nft.AddRule(&nftables.Rule{
476+
Table: table,
477+
Chain: chain,
478+
Exprs: []expr.Any{
479+
&expr.Payload{
480+
OperationType: expr.PayloadLoad,
481+
DestRegister: 1,
482+
Base: expr.PayloadBaseLLHeader,
483+
Offset: 6,
484+
Len: 6,
485+
},
486+
&expr.Lookup{
487+
SourceRegister: 1,
488+
SetID: excludeMACSet.ID,
489+
SetName: excludeMACSet.Name,
490+
},
491+
&expr.Counter{},
492+
&expr.Verdict{
493+
Kind: expr.VerdictReturn,
494+
},
495+
},
496+
})
497+
} else {
498+
nft.AddRule(&nftables.Rule{
499+
Table: table,
500+
Chain: chain,
501+
Exprs: []expr.Any{
502+
&expr.Payload{
503+
OperationType: expr.PayloadLoad,
504+
DestRegister: 1,
505+
Base: expr.PayloadBaseLLHeader,
506+
Offset: 6,
507+
Len: 6,
508+
},
509+
&expr.Cmp{
510+
Op: expr.CmpOpEq,
511+
Register: 1,
512+
Data: []byte(r.tunOptions.ExcludeMACAddress[0]),
513+
},
514+
&expr.Counter{},
515+
&expr.Verdict{
516+
Kind: expr.VerdictReturn,
517+
},
518+
},
519+
})
520+
}
521+
}
378522
} else {
379523
if len(r.tunOptions.IncludeUID) > 0 {
380524
if len(r.tunOptions.IncludeUID) > 1 || r.tunOptions.IncludeUID[0].Start != r.tunOptions.IncludeUID[0].End {

tun.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ type Options struct {
102102
IncludeAndroidUser []int
103103
IncludePackage []string
104104
ExcludePackage []string
105+
IncludeMACAddress []net.HardwareAddr
106+
ExcludeMACAddress []net.HardwareAddr
105107
InterfaceFinder control.InterfaceFinder
106108
InterfaceMonitor DefaultInterfaceMonitor
107109
FileDescriptor int

0 commit comments

Comments
 (0)