Improve nftables rules for openwrt

This commit is contained in:
世界 2025-06-30 18:00:36 +08:00
parent 5e343c4b66
commit 8763c24e49
No known key found for this signature in database
GPG key ID: CD109927C34A63C4

View file

@ -3,101 +3,50 @@
package tun package tun
import ( import (
"github.com/sagernet/nftables" "os"
"github.com/sagernet/nftables/expr" "os/exec"
"golang.org/x/exp/slices" "github.com/sagernet/nftables"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/shell"
) )
func (r *autoRedirect) configureOpenWRTFirewall4(nft *nftables.Conn, cleanup bool) error { func (r *autoRedirect) configureOpenWRTFirewall4(nft *nftables.Conn, cleanup bool) error {
tableFW4, err := nft.ListTableOfFamily("fw4", nftables.TableFamilyINet) _, err := nft.ListTableOfFamily("fw4", nftables.TableFamilyINet)
if err != nil { if err != nil {
return nil return nil
} }
if !cleanup { fw4Path, err := exec.LookPath("fw4")
ruleIif := &nftables.Rule{ if err != nil {
Table: tableFW4,
Exprs: []expr.Any{
&expr.Meta{
Key: expr.MetaKeyIIFNAME,
Register: 1,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: nftablesIfname(r.tunOptions.Name),
},
&expr.Counter{},
&expr.Verdict{
Kind: expr.VerdictAccept,
},
},
}
ruleOif := &nftables.Rule{
Table: tableFW4,
Exprs: []expr.Any{
&expr.Meta{
Key: expr.MetaKeyOIFNAME,
Register: 1,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: nftablesIfname(r.tunOptions.Name),
},
&expr.Counter{},
&expr.Verdict{
Kind: expr.VerdictAccept,
},
},
}
chainForward := &nftables.Chain{
Name: "forward",
}
ruleIif.Chain = chainForward
ruleOif.Chain = chainForward
nft.InsertRule(ruleOif)
nft.InsertRule(ruleIif)
chainInput := &nftables.Chain{
Name: "input",
}
ruleIif.Chain = chainInput
ruleOif.Chain = chainInput
nft.InsertRule(ruleOif)
nft.InsertRule(ruleIif)
return nil return nil
} }
for _, chainName := range []string{"input", "forward"} { rulePath := "/etc/nftables.d/0-" + r.tableName + "-auto-redirect.nft"
var rules []*nftables.Rule if !cleanup {
rules, err = nft.GetRules(tableFW4, &nftables.Chain{ err = os.WriteFile(rulePath, []byte(`chain input {
Name: chainName, type filter hook input priority filter; policy accept;
}) iifname "`+r.tunOptions.Name+`" counter accept comment "!`+r.tableName+`: Accept traffic from tun"
oifname "`+r.tunOptions.Name+`" counter accept comment "!`+r.tableName+`: Accept traffic from tun"
}
chain forward {
type filter hook forward priority filter; policy accept;
iifname "`+r.tunOptions.Name+`" counter accept comment "!`+r.tableName+`: Accept traffic from tun"
oifname "`+r.tunOptions.Name+`" counter accept comment "!`+r.tableName+`: Accept traffic from tun"
}
`), 0o644)
if err != nil { if err != nil {
return err return E.Cause(err, "write fw4 rules")
} }
for _, rule := range rules { } else if _, err = os.Stat(rulePath); os.IsNotExist(err) {
if len(rule.Exprs) != 4 { return nil
continue } else {
} err = os.Remove(rulePath)
exprMeta, isMeta := rule.Exprs[0].(*expr.Meta) if err != nil {
if !isMeta { return E.Cause(err, "clean fw4 rules")
continue
}
if exprMeta.Key != expr.MetaKeyIIFNAME && exprMeta.Key != expr.MetaKeyOIFNAME {
continue
}
exprCmp, isCmp := rule.Exprs[1].(*expr.Cmp)
if !isCmp {
continue
}
if !slices.Equal(exprCmp.Data, nftablesIfname(r.tunOptions.Name)) {
continue
}
err = nft.DelRule(rule)
if err != nil {
return err
}
} }
} }
_, err = shell.Exec(fw4Path, "reload").Read()
if err != nil {
return E.Cause(err, "reload fw4 rules")
}
return nil return nil
} }