mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-04-03 20:07:40 +03:00
auto-redirect: Add route address set support for nftables
This commit is contained in:
parent
85fe25a592
commit
85f5f2dd58
14 changed files with 1255 additions and 426 deletions
181
redirect_nftables_exprs.go
Normal file
181
redirect_nftables_exprs.go
Normal file
|
@ -0,0 +1,181 @@
|
|||
//go:build linux
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/nftables"
|
||||
"github.com/sagernet/nftables/expr"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
func nftablesIfname(n string) []byte {
|
||||
b := make([]byte, 16)
|
||||
copy(b, n+"\x00")
|
||||
return b
|
||||
}
|
||||
|
||||
func nftablesCreateExcludeDestinationIPSet(
|
||||
nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain,
|
||||
id uint32, name string, family nftables.TableFamily, invert bool,
|
||||
) {
|
||||
exprs := []expr.Any{
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyNFPROTO,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: []byte{byte(family)},
|
||||
},
|
||||
}
|
||||
if family == nftables.TableFamilyIPv4 {
|
||||
exprs = append(exprs,
|
||||
&expr.Payload{
|
||||
OperationType: expr.PayloadLoad,
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseNetworkHeader,
|
||||
Offset: 16,
|
||||
Len: 4,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
exprs = append(exprs,
|
||||
&expr.Payload{
|
||||
OperationType: expr.PayloadLoad,
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseNetworkHeader,
|
||||
Offset: 24,
|
||||
Len: 16,
|
||||
},
|
||||
)
|
||||
}
|
||||
exprs = append(exprs,
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetID: id,
|
||||
SetName: name,
|
||||
Invert: invert,
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
})
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: exprs,
|
||||
})
|
||||
}
|
||||
|
||||
func nftablesCreateIPSet(
|
||||
nft *nftables.Conn, table *nftables.Table,
|
||||
id uint32, name string, family nftables.TableFamily,
|
||||
setList []*netipx.IPSet, prefixList []netip.Prefix, appendDefault bool, update bool,
|
||||
) (*nftables.Set, error) {
|
||||
if len(prefixList) > 0 {
|
||||
var builder netipx.IPSetBuilder
|
||||
if appendDefault && len(setList) == 0 {
|
||||
if family == nftables.TableFamilyIPv4 {
|
||||
prefixList = append(prefixList, netip.PrefixFrom(netip.IPv4Unspecified(), 0))
|
||||
} else {
|
||||
prefixList = append(prefixList, netip.PrefixFrom(netip.IPv6Unspecified(), 0))
|
||||
}
|
||||
}
|
||||
for _, prefix := range prefixList {
|
||||
builder.AddPrefix(prefix)
|
||||
}
|
||||
|
||||
ipSet, err := builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setList = append(setList, ipSet)
|
||||
}
|
||||
ipSets := make([]*myIPSet, 0, len(setList))
|
||||
var rangeLen int
|
||||
for _, set := range setList {
|
||||
mySet := (*myIPSet)(unsafe.Pointer(set))
|
||||
ipSets = append(ipSets, mySet)
|
||||
rangeLen += len(mySet.rr)
|
||||
}
|
||||
setElements := make([]nftables.SetElement, 0, len(prefixList)+rangeLen)
|
||||
for _, mySet := range ipSets {
|
||||
for _, rr := range mySet.rr {
|
||||
if (family == nftables.TableFamilyIPv4) != rr.from.Is4() {
|
||||
continue
|
||||
}
|
||||
endAddr := rr.to.Next()
|
||||
if !endAddr.IsValid() {
|
||||
endAddr = rr.from
|
||||
}
|
||||
setElements = append(setElements, nftables.SetElement{
|
||||
Key: rr.from.AsSlice(),
|
||||
})
|
||||
setElements = append(setElements, nftables.SetElement{
|
||||
Key: endAddr.AsSlice(),
|
||||
IntervalEnd: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
var keyType nftables.SetDatatype
|
||||
if family == nftables.TableFamilyIPv4 {
|
||||
keyType = nftables.TypeIPAddr
|
||||
} else {
|
||||
keyType = nftables.TypeIP6Addr
|
||||
}
|
||||
mySet := &nftables.Set{
|
||||
Table: table,
|
||||
ID: id,
|
||||
Name: name,
|
||||
Interval: true,
|
||||
KeyType: keyType,
|
||||
}
|
||||
if id == 0 {
|
||||
mySet.Anonymous = true
|
||||
mySet.Constant = true
|
||||
}
|
||||
if id == 0 {
|
||||
err := nft.AddSet(mySet, setElements)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mySet, nil
|
||||
} else if update {
|
||||
nft.FlushSet(mySet)
|
||||
} else {
|
||||
err := nft.AddSet(mySet, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for len(setElements) > 0 {
|
||||
toAdd := setElements
|
||||
if len(toAdd) > 1000 {
|
||||
toAdd = toAdd[:1000]
|
||||
}
|
||||
setElements = setElements[len(toAdd):]
|
||||
err := nft.SetAddElements(mySet, toAdd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = nft.Flush()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return mySet, nil
|
||||
}
|
||||
|
||||
type myIPSet struct {
|
||||
rr []myIPRange
|
||||
}
|
||||
|
||||
type myIPRange struct {
|
||||
from netip.Addr
|
||||
to netip.Addr
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue