Skip to content

Commit 722acae

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 b1c48c1 commit 722acae

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

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