Skip to content

Commit 11f18fa

Browse files
committed
Add loopback address support
1 parent 7323ddc commit 11f18fa

File tree

7 files changed

+518
-177
lines changed

7 files changed

+518
-177
lines changed

redirect_nftables.go

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ func (r *autoRedirect) setupNFTables() error {
4646
return err
4747
}
4848

49+
err = r.nftablesCreateLoopbackAddressSets(nft, table)
50+
if err != nil {
51+
return err
52+
}
53+
4954
skipOutput := len(r.tunOptions.IncludeInterface) > 0 && !common.Contains(r.tunOptions.IncludeInterface, "lo") || common.Contains(r.tunOptions.ExcludeInterface, "lo")
5055
if !skipOutput {
5156
chainOutput := nft.AddChain(&nftables.Chain{
@@ -61,8 +66,23 @@ func (r *autoRedirect) setupNFTables() error {
6166
return err
6267
}
6368
r.nftablesCreateUnreachable(nft, table, chainOutput)
64-
r.nftablesCreateRedirect(nft, table, chainOutput)
65-
69+
err = r.nftablesCreateRedirect(nft, table, chainOutput)
70+
if err != nil {
71+
return err
72+
}
73+
if len(r.tunOptions.Inet4LoopbackAddress) > 0 || len(r.tunOptions.Inet6LoopbackAddress) > 0 {
74+
chainOutputRoute := nft.AddChain(&nftables.Chain{
75+
Name: "output_route",
76+
Table: table,
77+
Hooknum: nftables.ChainHookOutput,
78+
Priority: nftables.ChainPriorityMangle,
79+
Type: nftables.ChainTypeRoute,
80+
})
81+
err = r.nftablesCreateLoopbackReroute(nft, table, chainOutputRoute)
82+
if err != nil {
83+
return err
84+
}
85+
}
6686
chainOutputUDP := nft.AddChain(&nftables.Chain{
6787
Name: "output_udp_icmp",
6888
Table: table,
@@ -77,14 +97,17 @@ func (r *autoRedirect) setupNFTables() error {
7797
r.nftablesCreateUnreachable(nft, table, chainOutputUDP)
7898
r.nftablesCreateMark(nft, table, chainOutputUDP)
7999
} else {
80-
r.nftablesCreateRedirect(nft, table, chainOutput, &expr.Meta{
100+
err = r.nftablesCreateRedirect(nft, table, chainOutput, &expr.Meta{
81101
Key: expr.MetaKeyOIFNAME,
82102
Register: 1,
83103
}, &expr.Cmp{
84104
Op: expr.CmpOpEq,
85105
Register: 1,
86106
Data: nftablesIfname(r.tunOptions.Name),
87107
})
108+
if err != nil {
109+
return err
110+
}
88111
}
89112
}
90113

@@ -100,12 +123,25 @@ func (r *autoRedirect) setupNFTables() error {
100123
return err
101124
}
102125
r.nftablesCreateUnreachable(nft, table, chainPreRouting)
103-
r.nftablesCreateRedirect(nft, table, chainPreRouting)
104-
if r.tunOptions.AutoRedirectMarkMode {
105-
r.nftablesCreateMark(nft, table, chainPreRouting)
126+
err = r.nftablesCreateRedirect(nft, table, chainPreRouting)
127+
if err != nil {
128+
return err
106129
}
107-
108130
if r.tunOptions.AutoRedirectMarkMode {
131+
r.nftablesCreateMark(nft, table, chainPreRouting)
132+
if len(r.tunOptions.Inet4LoopbackAddress) > 0 || len(r.tunOptions.Inet6LoopbackAddress) > 0 {
133+
chainPreRoutingFilter := nft.AddChain(&nftables.Chain{
134+
Name: "prerouting_filter",
135+
Table: table,
136+
Hooknum: nftables.ChainHookPrerouting,
137+
Priority: nftables.ChainPriorityRef(*nftables.ChainPriorityNATDest + 1),
138+
Type: nftables.ChainTypeFilter,
139+
})
140+
err = r.nftablesCreateLoopbackReroute(nft, table, chainPreRoutingFilter)
141+
if err != nil {
142+
return err
143+
}
144+
}
109145
chainPreRoutingUDP := nft.AddChain(&nftables.Chain{
110146
Name: "prerouting_udp",
111147
Table: table,

redirect_nftables_exprs.go

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/sagernet/nftables"
99
"github.com/sagernet/nftables/expr"
10+
"github.com/sagernet/sing/common"
1011

1112
"go4.org/netipx"
1213
)
@@ -21,6 +22,20 @@ func nftablesCreateExcludeDestinationIPSet(
2122
nft *nftables.Conn, table *nftables.Table, chain *nftables.Chain,
2223
id uint32, name string, family nftables.TableFamily, invert bool,
2324
) {
25+
nft.AddRule(&nftables.Rule{
26+
Table: table,
27+
Chain: chain,
28+
Exprs: append(
29+
nftablesCreateDestinationIPSetExprs(id, name, family, invert),
30+
&expr.Counter{},
31+
&expr.Verdict{
32+
Kind: expr.VerdictReturn,
33+
},
34+
),
35+
})
36+
}
37+
38+
func nftablesCreateDestinationIPSetExprs(id uint32, name string, family nftables.TableFamily, invert bool) []expr.Any {
2439
exprs := []expr.Any{
2540
&expr.Meta{
2641
Key: expr.MetaKeyNFPROTO,
@@ -53,22 +68,63 @@ func nftablesCreateExcludeDestinationIPSet(
5368
},
5469
)
5570
}
56-
exprs = append(exprs,
57-
&expr.Lookup{
58-
SourceRegister: 1,
59-
SetID: id,
60-
SetName: name,
61-
Invert: invert,
62-
},
63-
&expr.Counter{},
64-
&expr.Verdict{
65-
Kind: expr.VerdictReturn,
66-
})
67-
nft.AddRule(&nftables.Rule{
68-
Table: table,
69-
Chain: chain,
70-
Exprs: exprs,
71+
exprs = append(exprs, &expr.Lookup{
72+
SourceRegister: 1,
73+
SetID: id,
74+
SetName: name,
75+
Invert: invert,
7176
})
77+
return exprs
78+
}
79+
80+
func nftablesCreateIPConst(
81+
nft *nftables.Conn, table *nftables.Table, id uint32, name string, family nftables.TableFamily, addressList []netip.Addr,
82+
) (*nftables.Set, error) {
83+
var keyType nftables.SetDatatype
84+
if family == nftables.TableFamilyIPv4 {
85+
keyType = nftables.TypeIPAddr
86+
} else {
87+
keyType = nftables.TypeIP6Addr
88+
}
89+
mySet := &nftables.Set{
90+
Table: table,
91+
ID: id,
92+
Name: name,
93+
KeyType: keyType,
94+
Constant: true,
95+
}
96+
if id == 0 {
97+
mySet.Anonymous = true
98+
}
99+
setElements := common.Map(addressList, func(addr netip.Addr) nftables.SetElement { return nftables.SetElement{Key: addr.AsSlice()} })
100+
if id == 0 {
101+
err := nft.AddSet(mySet, setElements)
102+
if err != nil {
103+
return nil, err
104+
}
105+
return mySet, nil
106+
} else {
107+
err := nft.AddSet(mySet, nil)
108+
if err != nil {
109+
return nil, err
110+
}
111+
}
112+
for len(setElements) > 0 {
113+
toAdd := setElements
114+
if len(toAdd) > 1000 {
115+
toAdd = toAdd[:1000]
116+
}
117+
setElements = setElements[len(toAdd):]
118+
err := nft.SetAddElements(mySet, toAdd)
119+
if err != nil {
120+
return nil, err
121+
}
122+
err = nft.Flush()
123+
if err != nil {
124+
return nil, err
125+
}
126+
}
127+
return mySet, nil
72128
}
73129

74130
func nftablesCreateIPSet(

0 commit comments

Comments
 (0)