util/linuxfw,go.{mod,sum}: don't log errors when deleting non-existant chains and rules (#11852)

This PR bumps iptables to a newer version that has a function to detect
'NotExists' errors and uses that function to determine whether errors
received on iptables rule and chain clean up are because the rule/chain
does not exist- if so don't log the error.

Updates corp#19336

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
Irbe Krumina
2024-04-23 21:08:18 +01:00
committed by GitHub
parent 3af0f526b8
commit add62af7c6
5 changed files with 24 additions and 40 deletions

View File

@@ -7,6 +7,7 @@ package linuxfw
import (
"bytes"
"errors"
"fmt"
"net/netip"
"os"
@@ -20,6 +21,14 @@ import (
"tailscale.com/util/multierr"
)
// isNotExistError needs to be overridden in tests that rely on distinguishing
// this error, because we don't have a good way how to create a new
// iptables.Error of that type.
var isNotExistError = func(err error) bool {
var e *iptables.Error
return errors.As(err, &e) && e.IsNotExist()
}
type iptablesInterface interface {
// Adding this interface for testing purposes so we can mock out
// the iptables library, in reality this is a wrapper to *iptables.IPTables.
@@ -156,10 +165,6 @@ func (i *iptablesRunner) HasIPV6NAT() bool {
return i.v6NATAvailable
}
func isErrChainNotExist(err error) bool {
return errCode(err) == 1
}
// getIPTByAddr returns the iptablesInterface with correct IP family
// that we will be using for the given address.
func (i *iptablesRunner) getIPTByAddr(addr netip.Addr) iptablesInterface {
@@ -261,7 +266,7 @@ func (i *iptablesRunner) AddChains() error {
// If the chain already exists, it is a no-op.
create := func(ipt iptablesInterface, table, chain string) error {
err := ipt.ClearChain(table, chain)
if isErrChainNotExist(err) {
if isNotExistError(err) {
// nonexistent chain. let's create it!
return ipt.NewChain(table, chain)
}
@@ -377,7 +382,7 @@ func (i *iptablesRunner) DNATNonTailscaleTraffic(tun string, dst netip.Addr) err
// originDst to the backend dsts. Traffic will be load balanced using round robin.
func (i *iptablesRunner) DNATWithLoadBalancer(origDst netip.Addr, dsts []netip.Addr) error {
table := i.getIPTByAddr(dsts[0])
if err := table.ClearChain("nat", "PREROUTING"); err != nil && !isErrChainNotExist(err) {
if err := table.ClearChain("nat", "PREROUTING"); err != nil && !isNotExistError(err) {
// If clearing the PREROUTING chain fails, fail the whole operation. This
// rule is currently only used in Kubernetes containers where a
// failed container gets restarted which should hopefully fix things.
@@ -454,7 +459,7 @@ func (i *iptablesRunner) DelChains() error {
func (i *iptablesRunner) DelBase() error {
del := func(ipt iptablesInterface, table, chain string) error {
if err := ipt.ClearChain(table, chain); err != nil {
if isErrChainNotExist(err) {
if isNotExistError(err) {
// nonexistent chain. That's fine, since it's
// the desired state anyway.
return nil
@@ -602,14 +607,8 @@ func IPTablesCleanUp(logf logger.Logf) {
func delTSHook(ipt iptablesInterface, table, chain string, logf logger.Logf) error {
tsChain := tsChain(chain)
args := []string{"-j", tsChain}
if err := ipt.Delete(table, chain, args...); err != nil {
// TODO(apenwarr): check for errCode(1) here.
// Unfortunately the error code from the iptables
// module resists unwrapping, unlike with other
// calls. So we have to assume if Delete fails,
// it's because there is no such rule.
logf("deleting %v in %s/%s: %v", args, table, chain, err)
return nil
if err := ipt.Delete(table, chain, args...); err != nil && !isNotExistError(err) {
return fmt.Errorf("deleting %v in %s/%s: %v", args, table, chain, err)
}
return nil
}
@@ -618,7 +617,7 @@ func delTSHook(ipt iptablesInterface, table, chain string, logf logger.Logf) err
// since the desired state is already achieved. otherwise, it returns an error.
func delChain(ipt iptablesInterface, table, chain string) error {
if err := ipt.ClearChain(table, chain); err != nil {
if isErrChainNotExist(err) {
if isNotExistError(err) {
// nonexistent chain. nothing to do.
return nil
}