Skip to content

Commit bf29881

Browse files
authoredMar 22, 2024··
Merge pull request #109 from apernet/wip-io-rst
feat: io tcp reset support (forward only)
2 parents 6ad7714 + ef14162 commit bf29881

File tree

5 files changed

+109
-69
lines changed

5 files changed

+109
-69
lines changed
 

‎README.ja.md

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ io:
7878
rcvBuf: 4194304
7979
sndBuf: 4194304
8080
local: true # FORWARD チェーンで OpenGFW を実行したい場合は false に設定する
81+
rst: false # ブロックされたTCP接続に対してRSTを送信する場合はtrueに設定してください。local=falseのみです
8182

8283
workers:
8384
count: 4

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ io:
8282
rcvBuf: 4194304
8383
sndBuf: 4194304
8484
local: true # set to false if you want to run OpenGFW on FORWARD chain
85+
rst: false # set to true if you want to send RST for blocked TCP connections, local=false only
8586

8687
workers:
8788
count: 4

‎README.zh.md

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ io:
7878
rcvBuf: 4194304
7979
sndBuf: 4194304
8080
local: true # 如果需要在 FORWARD 链上运行 OpenGFW,请设置为 false
81+
rst: false # 是否对要阻断的 TCP 连接发送 RST。仅在 local=false 时有效
8182

8283
workers:
8384
count: 4

‎cmd/root.go

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ type cliConfigIO struct {
171171
ReadBuffer int `mapstructure:"rcvBuf"`
172172
WriteBuffer int `mapstructure:"sndBuf"`
173173
Local bool `mapstructure:"local"`
174+
RST bool `mapstructure:"rst"`
174175
}
175176

176177
type cliConfigWorkers struct {
@@ -197,6 +198,7 @@ func (c *cliConfig) fillIO(config *engine.Config) error {
197198
ReadBuffer: c.IO.ReadBuffer,
198199
WriteBuffer: c.IO.WriteBuffer,
199200
Local: c.IO.Local,
201+
RST: c.IO.RST,
200202
})
201203
if err != nil {
202204
return configError{Field: "io", Err: err}

‎io/nfqueue.go

+104-69
Original file line numberDiff line numberDiff line change
@@ -27,59 +27,60 @@ const (
2727
nftTable = "opengfw"
2828
)
2929

30-
var nftRulesForward = fmt.Sprintf(`
31-
define ACCEPT_CTMARK=%d
32-
define DROP_CTMARK=%d
33-
define QUEUE_NUM=%d
34-
35-
table %s %s {
36-
chain FORWARD {
37-
type filter hook forward priority filter; policy accept;
38-
39-
ct mark $ACCEPT_CTMARK counter accept
40-
ct mark $DROP_CTMARK counter drop
41-
counter queue num $QUEUE_NUM bypass
42-
}
43-
}
44-
`, nfqueueConnMarkAccept, nfqueueConnMarkDrop, nfqueueNum, nftFamily, nftTable)
45-
46-
var nftRulesLocal = fmt.Sprintf(`
47-
define ACCEPT_CTMARK=%d
48-
define DROP_CTMARK=%d
49-
define QUEUE_NUM=%d
50-
51-
table %s %s {
52-
chain INPUT {
53-
type filter hook input priority filter; policy accept;
54-
55-
ct mark $ACCEPT_CTMARK counter accept
56-
ct mark $DROP_CTMARK counter drop
57-
counter queue num $QUEUE_NUM bypass
58-
}
59-
chain OUTPUT {
60-
type filter hook output priority filter; policy accept;
61-
62-
ct mark $ACCEPT_CTMARK counter accept
63-
ct mark $DROP_CTMARK counter drop
64-
counter queue num $QUEUE_NUM bypass
65-
}
66-
}
67-
`, nfqueueConnMarkAccept, nfqueueConnMarkDrop, nfqueueNum, nftFamily, nftTable)
68-
69-
var iptRulesForward = []iptRule{
70-
{"filter", "FORWARD", []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkAccept), "-j", "ACCEPT"}},
71-
{"filter", "FORWARD", []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkDrop), "-j", "DROP"}},
72-
{"filter", "FORWARD", []string{"-j", "NFQUEUE", "--queue-num", strconv.Itoa(nfqueueNum), "--queue-bypass"}},
30+
func generateNftRules(local, rst bool) (*nftTableSpec, error) {
31+
if local && rst {
32+
return nil, errors.New("tcp rst is not supported in local mode")
33+
}
34+
table := &nftTableSpec{
35+
Family: nftFamily,
36+
Table: nftTable,
37+
}
38+
table.Defines = append(table.Defines, fmt.Sprintf("define ACCEPT_CTMARK=%d", nfqueueConnMarkAccept))
39+
table.Defines = append(table.Defines, fmt.Sprintf("define DROP_CTMARK=%d", nfqueueConnMarkDrop))
40+
table.Defines = append(table.Defines, fmt.Sprintf("define QUEUE_NUM=%d", nfqueueNum))
41+
if local {
42+
table.Chains = []nftChainSpec{
43+
{Chain: "INPUT", Header: "type filter hook input priority filter; policy accept;"},
44+
{Chain: "OUTPUT", Header: "type filter hook output priority filter; policy accept;"},
45+
}
46+
} else {
47+
table.Chains = []nftChainSpec{
48+
{Chain: "FORWARD", Header: "type filter hook forward priority filter; policy accept;"},
49+
}
50+
}
51+
for i := range table.Chains {
52+
c := &table.Chains[i]
53+
c.Rules = append(c.Rules, "ct mark $ACCEPT_CTMARK counter accept")
54+
if rst {
55+
c.Rules = append(c.Rules, "ip protocol tcp ct mark $DROP_CTMARK counter reject with tcp reset")
56+
}
57+
c.Rules = append(c.Rules, "ct mark $DROP_CTMARK counter drop")
58+
c.Rules = append(c.Rules, "counter queue num $QUEUE_NUM bypass")
59+
}
60+
return table, nil
7361
}
7462

75-
var iptRulesLocal = []iptRule{
76-
{"filter", "INPUT", []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkAccept), "-j", "ACCEPT"}},
77-
{"filter", "INPUT", []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkDrop), "-j", "DROP"}},
78-
{"filter", "INPUT", []string{"-j", "NFQUEUE", "--queue-num", strconv.Itoa(nfqueueNum), "--queue-bypass"}},
63+
func generateIptRules(local, rst bool) ([]iptRule, error) {
64+
if local && rst {
65+
return nil, errors.New("tcp rst is not supported in local mode")
66+
}
67+
var chains []string
68+
if local {
69+
chains = []string{"INPUT", "OUTPUT"}
70+
} else {
71+
chains = []string{"FORWARD"}
72+
}
73+
rules := make([]iptRule, 0, 4*len(chains))
74+
for _, chain := range chains {
75+
rules = append(rules, iptRule{"filter", chain, []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkAccept), "-j", "ACCEPT"}})
76+
if rst {
77+
rules = append(rules, iptRule{"filter", chain, []string{"-p", "tcp", "-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkDrop), "-j", "REJECT", "--reject-with", "tcp-reset"}})
78+
}
79+
rules = append(rules, iptRule{"filter", chain, []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkDrop), "-j", "DROP"}})
80+
rules = append(rules, iptRule{"filter", chain, []string{"-j", "NFQUEUE", "--queue-num", strconv.Itoa(nfqueueNum), "--queue-bypass"}})
81+
}
7982

80-
{"filter", "OUTPUT", []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkAccept), "-j", "ACCEPT"}},
81-
{"filter", "OUTPUT", []string{"-m", "connmark", "--mark", strconv.Itoa(nfqueueConnMarkDrop), "-j", "DROP"}},
82-
{"filter", "OUTPUT", []string{"-j", "NFQUEUE", "--queue-num", strconv.Itoa(nfqueueNum), "--queue-bypass"}},
83+
return rules, nil
8384
}
8485

8586
var _ PacketIO = (*nfqueuePacketIO)(nil)
@@ -89,6 +90,7 @@ var errNotNFQueuePacket = errors.New("not an NFQueue packet")
8990
type nfqueuePacketIO struct {
9091
n *nfqueue.Nfqueue
9192
local bool
93+
rst bool
9294
rSet bool // whether the nftables/iptables rules have been set
9395

9496
// iptables not nil = use iptables instead of nftables
@@ -101,6 +103,7 @@ type NFQueuePacketIOConfig struct {
101103
ReadBuffer int
102104
WriteBuffer int
103105
Local bool
106+
RST bool
104107
}
105108

106109
func NewNFQueuePacketIO(config NFQueuePacketIOConfig) (PacketIO, error) {
@@ -147,6 +150,7 @@ func NewNFQueuePacketIO(config NFQueuePacketIOConfig) (PacketIO, error) {
147150
return &nfqueuePacketIO{
148151
n: n,
149152
local: config.Local,
153+
rst: config.RST,
150154
ipt4: ipt4,
151155
ipt6: ipt6,
152156
}, nil
@@ -182,9 +186,9 @@ func (n *nfqueuePacketIO) Register(ctx context.Context, cb PacketCallback) error
182186
}
183187
if !n.rSet {
184188
if n.ipt4 != nil {
185-
err = n.setupIpt(n.local, false)
189+
err = n.setupIpt(n.local, n.rst, false)
186190
} else {
187-
err = n.setupNft(n.local, false)
191+
err = n.setupNft(n.local, n.rst, false)
188192
}
189193
if err != nil {
190194
return err
@@ -238,44 +242,39 @@ func (n *nfqueuePacketIO) SetVerdict(p Packet, v Verdict, newPacket []byte) erro
238242
func (n *nfqueuePacketIO) Close() error {
239243
if n.rSet {
240244
if n.ipt4 != nil {
241-
_ = n.setupIpt(n.local, true)
245+
_ = n.setupIpt(n.local, n.rst, true)
242246
} else {
243-
_ = n.setupNft(n.local, true)
247+
_ = n.setupNft(n.local, n.rst, true)
244248
}
245249
n.rSet = false
246250
}
247251
return n.n.Close()
248252
}
249253

250-
func (n *nfqueuePacketIO) setupNft(local, remove bool) error {
251-
var rules string
252-
if local {
253-
rules = nftRulesLocal
254-
} else {
255-
rules = nftRulesForward
254+
func (n *nfqueuePacketIO) setupNft(local, rst, remove bool) error {
255+
rules, err := generateNftRules(local, rst)
256+
if err != nil {
257+
return err
256258
}
257-
var err error
259+
rulesText := rules.String()
258260
if remove {
259261
err = nftDelete(nftFamily, nftTable)
260262
} else {
261263
// Delete first to make sure no leftover rules
262264
_ = nftDelete(nftFamily, nftTable)
263-
err = nftAdd(rules)
265+
err = nftAdd(rulesText)
264266
}
265267
if err != nil {
266268
return err
267269
}
268270
return nil
269271
}
270272

271-
func (n *nfqueuePacketIO) setupIpt(local, remove bool) error {
272-
var rules []iptRule
273-
if local {
274-
rules = iptRulesLocal
275-
} else {
276-
rules = iptRulesForward
273+
func (n *nfqueuePacketIO) setupIpt(local, rst, remove bool) error {
274+
rules, err := generateIptRules(local, rst)
275+
if err != nil {
276+
return err
277277
}
278-
var err error
279278
if remove {
280279
err = iptsBatchDeleteIfExists([]*iptables.IPTables{n.ipt4, n.ipt6}, rules)
281280
} else {
@@ -330,6 +329,42 @@ func nftDelete(family, table string) error {
330329
return cmd.Run()
331330
}
332331

332+
type nftTableSpec struct {
333+
Defines []string
334+
Family, Table string
335+
Chains []nftChainSpec
336+
}
337+
338+
func (t *nftTableSpec) String() string {
339+
chains := make([]string, 0, len(t.Chains))
340+
for _, c := range t.Chains {
341+
chains = append(chains, c.String())
342+
}
343+
344+
return fmt.Sprintf(`
345+
%s
346+
347+
table %s %s {
348+
%s
349+
}
350+
`, strings.Join(t.Defines, "\n"), t.Family, t.Table, strings.Join(chains, ""))
351+
}
352+
353+
type nftChainSpec struct {
354+
Chain string
355+
Header string
356+
Rules []string
357+
}
358+
359+
func (c *nftChainSpec) String() string {
360+
return fmt.Sprintf(`
361+
chain %s {
362+
%s
363+
%s
364+
}
365+
`, c.Chain, c.Header, strings.Join(c.Rules, "\n\x20\x20\x20\x20"))
366+
}
367+
333368
type iptRule struct {
334369
Table, Chain string
335370
RuleSpec []string

0 commit comments

Comments
 (0)
Please sign in to comment.