util/linuxfw: fix delete snat rule (#15763)

* util/linuxfw: fix delete snat rule

This pr is fixing the bug that in nftables mode setting snat-subnet-routes=false doesn't
delete the masq rule in nat table.

Updates #15661

Signed-off-by: Kevin Liang <kevinliang@tailscale.com>

* change index arithmetic in test to chunk

Signed-off-by: Kevin Liang <kevinliang@tailscale.com>

* reuse rule creation function in rule delete

Signed-off-by: Kevin Liang <kevinliang@tailscale.com>

* add test for deleting the masq rule

Signed-off-by: Kevin Liang <kevinliang@tailscale.com>

---------

Signed-off-by: Kevin Liang <kevinliang@tailscale.com>
This commit is contained in:
KevinLiang10
2025-05-01 12:12:36 -04:00
committed by GitHub
parent fe0090909b
commit e05e620096
2 changed files with 98 additions and 64 deletions

View File

@@ -1710,55 +1710,43 @@ func (n *nftablesRunner) AddSNATRule() error {
return nil
}
func delMatchSubnetRouteMarkMasqRule(conn *nftables.Conn, table *nftables.Table, chain *nftables.Chain) error {
rule, err := createMatchSubnetRouteMarkRule(table, chain, Masq)
if err != nil {
return fmt.Errorf("create match subnet route mark rule: %w", err)
}
SNATRule, err := findRule(conn, rule)
if err != nil {
return fmt.Errorf("find SNAT rule v4: %w", err)
}
if SNATRule != nil {
_ = conn.DelRule(SNATRule)
}
if err := conn.Flush(); err != nil {
return fmt.Errorf("flush del SNAT rule: %w", err)
}
return nil
}
// DelSNATRule removes the netfilter rule to SNAT traffic destined for
// local subnets. An error is returned if the rule does not exist.
func (n *nftablesRunner) DelSNATRule() error {
conn := n.conn
hexTSFwmarkMask := getTailscaleFwmarkMask()
hexTSSubnetRouteMark := getTailscaleSubnetRouteMark()
exprs := []expr.Any{
&expr.Meta{Key: expr.MetaKeyMARK, Register: 1},
&expr.Bitwise{
SourceRegister: 1,
DestRegister: 1,
Len: 4,
Mask: hexTSFwmarkMask,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: hexTSSubnetRouteMark,
},
&expr.Counter{},
&expr.Masq{},
}
for _, table := range n.getTables() {
chain, err := getChainFromTable(conn, table.Nat, chainNamePostrouting)
if err != nil {
return fmt.Errorf("get postrouting chain v4: %w", err)
return fmt.Errorf("get postrouting chain: %w", err)
}
rule := &nftables.Rule{
Table: table.Nat,
Chain: chain,
Exprs: exprs,
}
SNATRule, err := findRule(conn, rule)
err = delMatchSubnetRouteMarkMasqRule(conn, table.Nat, chain)
if err != nil {
return fmt.Errorf("find SNAT rule v4: %w", err)
return err
}
if SNATRule != nil {
_ = conn.DelRule(SNATRule)
}
}
if err := conn.Flush(); err != nil {
return fmt.Errorf("flush del SNAT rule: %w", err)
}
return nil