summaryrefslogtreecommitdiffhomepage
path: root/util/linuxfw/iptables_runner.go
diff options
context:
space:
mode:
Diffstat (limited to 'util/linuxfw/iptables_runner.go')
-rw-r--r--util/linuxfw/iptables_runner.go24
1 files changed, 24 insertions, 0 deletions
diff --git a/util/linuxfw/iptables_runner.go b/util/linuxfw/iptables_runner.go
index 83c069af4..f688e82da 100644
--- a/util/linuxfw/iptables_runner.go
+++ b/util/linuxfw/iptables_runner.go
@@ -373,6 +373,30 @@ func (i *iptablesRunner) DNATNonTailscaleTraffic(tun string, dst netip.Addr) err
return table.Insert("nat", "PREROUTING", 1, "!", "-i", tun, "-j", "DNAT", "--to-destination", dst.String())
}
+// DNATWithLoadBalancer adds DNAT rules to load balance all incoming traffic NOT
+// destined to tailscale0 interface to provided destinations using round robin.
+// NB: this function clears the nat PREROUTING chain on start, so it is only
+// safe to use on systems where Tailscale is the only process that uses this
+// chain (i.e containers).
+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 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.
+ return fmt.Errorf("error clearing nat PREROUTING chain: %w", err)
+ }
+ // If dsts contain more than one address, for n := n in range(len(dsts)..2) route packets for every nth connection to dsts[n].
+ for i := len(dsts); i >= 2; i-- {
+ dst := dsts[i-1] // the order in which rules for addrs are installed does not matter
+ if err := table.Append("nat", "PREROUTING", "--destination", origDst.String(), "-m", "statistic", "--mode", "nth", "--every", fmt.Sprint(i), "--packet", "0", "-j", "DNAT", "--to-destination", dst.String()); err != nil {
+ return fmt.Errorf("error adding DNAT rule for %s: %w", dst.String(), err)
+ }
+ }
+ // If the packet falls through to this rule, we route to the first destination in the list unconditionally.
+ return table.Append("nat", "PREROUTING", "--destination", origDst.String(), "-j", "DNAT", "--to-destination", dsts[0].String())
+}
+
func (i *iptablesRunner) ClampMSSToPMTU(tun string, addr netip.Addr) error {
table := i.getIPTByAddr(addr)
return table.Append("mangle", "FORWARD", "-o", tun, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu")