mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 11:39:57 +00:00
264 lines
7.4 KiB
Go
264 lines
7.4 KiB
Go
//go:build !android
|
|
|
|
package iptables
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
nberrors "github.com/netbirdio/netbird/client/errors"
|
|
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
|
nbnet "github.com/netbirdio/netbird/client/net"
|
|
)
|
|
|
|
func (r *family) AddNatRule(pair firewall.RouterPair) error {
|
|
if r.legacyManagement {
|
|
log.Warnf("This peer is connected to a NetBird Management service with an older version. Allowing all traffic for %s", pair.Destination)
|
|
if err := r.addLegacyRouteRule(pair); err != nil {
|
|
return fmt.Errorf("add legacy routing rule: %w", err)
|
|
}
|
|
}
|
|
|
|
if pair.Masquerade {
|
|
if err := r.addNatRule(pair); err != nil {
|
|
return fmt.Errorf("add nat rule: %w", err)
|
|
}
|
|
|
|
if err := r.addNatRule(firewall.GetInversePair(pair)); err != nil {
|
|
return fmt.Errorf("add inverse nat rule: %w", err)
|
|
}
|
|
}
|
|
|
|
r.updateState()
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveNatRule removes an iptables rule pair from forwarding and nat chains
|
|
func (r *family) RemoveNatRule(pair firewall.RouterPair) error {
|
|
if pair.Masquerade {
|
|
if err := r.removeNatRule(pair); err != nil {
|
|
return fmt.Errorf("remove nat rule: %w", err)
|
|
}
|
|
|
|
if err := r.removeNatRule(firewall.GetInversePair(pair)); err != nil {
|
|
return fmt.Errorf("remove inverse nat rule: %w", err)
|
|
}
|
|
}
|
|
|
|
if err := r.removeLegacyRouteRule(pair); err != nil {
|
|
return fmt.Errorf("remove legacy routing rule: %w", err)
|
|
}
|
|
|
|
r.updateState()
|
|
|
|
return nil
|
|
}
|
|
|
|
// addLegacyRouteRule adds a legacy routing rule for mgmt servers pre route acls
|
|
func (r *family) addLegacyRouteRule(pair firewall.RouterPair) error {
|
|
ruleID := pair.GenKey(firewall.ForwardingFormat)
|
|
|
|
if err := r.removeLegacyRouteRule(pair); err != nil {
|
|
return err
|
|
}
|
|
|
|
rule := []string{"-s", pair.Source.String(), "-d", pair.Destination.String(), "-j", "ACCEPT"}
|
|
if err := r.iptablesClient.Append(tableFilter, chainRTFwdIn, rule...); err != nil {
|
|
return fmt.Errorf("add legacy forwarding rule %s -> %s: %w", pair.Source, pair.Destination, err)
|
|
}
|
|
|
|
r.rules[ruleID] = rule
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *family) removeLegacyRouteRule(pair firewall.RouterPair) error {
|
|
ruleID := pair.GenKey(firewall.ForwardingFormat)
|
|
|
|
if rule, exists := r.rules[ruleID]; exists {
|
|
if err := r.iptablesClient.DeleteIfExists(tableFilter, chainRTFwdIn, rule...); err != nil {
|
|
return fmt.Errorf("remove legacy forwarding rule %s -> %s: %w", pair.Source, pair.Destination, err)
|
|
}
|
|
delete(r.rules, ruleID)
|
|
|
|
if err := r.decrementSetCounter(rule); err != nil {
|
|
return fmt.Errorf("decrement ipset counter: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetLegacyManagement returns the current legacy management mode
|
|
func (r *family) GetLegacyManagement() bool {
|
|
return r.legacyManagement
|
|
}
|
|
|
|
// SetLegacyManagement sets the route manager to use legacy management mode
|
|
func (r *family) SetLegacyManagement(isLegacy bool) {
|
|
r.legacyManagement = isLegacy
|
|
}
|
|
|
|
// RemoveAllLegacyRouteRules removes all legacy routing rules for mgmt servers pre route acls
|
|
func (r *family) RemoveAllLegacyRouteRules() error {
|
|
var merr *multierror.Error
|
|
for k, rule := range r.rules {
|
|
if !strings.HasPrefix(string(k), firewall.ForwardingFormatPrefix) {
|
|
continue
|
|
}
|
|
if err := r.iptablesClient.DeleteIfExists(tableFilter, chainRTFwdIn, rule...); err != nil {
|
|
merr = multierror.Append(merr, fmt.Errorf("remove legacy forwarding rule: %w", err))
|
|
} else {
|
|
delete(r.rules, k)
|
|
}
|
|
}
|
|
|
|
r.updateState()
|
|
|
|
return nberrors.FormatErrorOrNil(merr)
|
|
}
|
|
|
|
func (r *family) addPostroutingRules() error {
|
|
// First rule for outbound masquerade
|
|
rule1 := []string{
|
|
"-m", "mark", "--mark", fmt.Sprintf("%#x", nbnet.PreroutingFwmarkMasquerade),
|
|
"!", "-o", "lo",
|
|
"-j", "MASQUERADE",
|
|
}
|
|
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule1...); err != nil {
|
|
return fmt.Errorf("add outbound masquerade rule: %w", err)
|
|
}
|
|
r.rules["static-nat-outbound"] = rule1
|
|
|
|
// Second rule for return traffic masquerade
|
|
rule2 := []string{
|
|
"-m", "mark", "--mark", fmt.Sprintf("%#x", nbnet.PreroutingFwmarkMasqueradeReturn),
|
|
"-o", r.wgIface.Name(),
|
|
"-j", "MASQUERADE",
|
|
}
|
|
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule2...); err != nil {
|
|
return fmt.Errorf("add return masquerade rule: %w", err)
|
|
}
|
|
r.rules["static-nat-return"] = rule2
|
|
|
|
return nil
|
|
}
|
|
|
|
// addMSSClampingRules adds MSS clamping rules to prevent fragmentation for forwarded traffic.
|
|
func (r *family) addMSSClampingRules() error {
|
|
overhead := uint16(ipv4TCPHeaderSize)
|
|
if r.v6 {
|
|
overhead = ipv6TCPHeaderSize
|
|
}
|
|
mss := r.mtu - overhead
|
|
|
|
// Add jump rule from FORWARD chain in mangle table to our custom chain
|
|
jumpRule := jumpRuleSpec(chainRTMSSClamp)
|
|
if err := r.iptablesClient.Insert(tableMangle, chainForward, 1, jumpRule...); err != nil {
|
|
return fmt.Errorf("add jump to MSS clamp chain: %w", err)
|
|
}
|
|
r.rules[jumpMSSClamp] = jumpRule
|
|
|
|
ruleOut := []string{
|
|
"-o", r.wgIface.Name(),
|
|
"-p", "tcp",
|
|
"--tcp-flags", "SYN,RST", "SYN",
|
|
"-j", "TCPMSS",
|
|
"--set-mss", fmt.Sprintf("%d", mss),
|
|
}
|
|
if err := r.iptablesClient.Append(tableMangle, chainRTMSSClamp, ruleOut...); err != nil {
|
|
return fmt.Errorf("add outbound MSS clamp rule: %w", err)
|
|
}
|
|
r.rules["mss-clamp-out"] = ruleOut
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *family) insertEstablishedRule(chain string) error {
|
|
establishedRule := getConntrackEstablished()
|
|
|
|
err := r.iptablesClient.Insert(tableFilter, chain, 1, establishedRule...)
|
|
if err != nil {
|
|
return fmt.Errorf("insert established rule: %w", err)
|
|
}
|
|
|
|
ruleID := firewall.RuleID("established-" + chain)
|
|
r.rules[ruleID] = establishedRule
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *family) addNatRule(pair firewall.RouterPair) error {
|
|
ruleID := pair.GenKey(firewall.NatFormat)
|
|
|
|
if rule, exists := r.rules[ruleID]; exists {
|
|
if err := r.iptablesClient.DeleteIfExists(tableMangle, chainRTPre, rule...); err != nil {
|
|
return fmt.Errorf("remove existing marking rule for %s: %w", pair.Destination, err)
|
|
}
|
|
delete(r.rules, ruleID)
|
|
}
|
|
|
|
markValue := nbnet.PreroutingFwmarkMasquerade
|
|
if pair.Inverse {
|
|
markValue = nbnet.PreroutingFwmarkMasqueradeReturn
|
|
}
|
|
|
|
rule := []string{"-i", r.wgIface.Name()}
|
|
if pair.Inverse {
|
|
rule = []string{"!", "-i", r.wgIface.Name()}
|
|
}
|
|
|
|
rule = append(rule,
|
|
"-m", "conntrack",
|
|
"--ctstate", "NEW",
|
|
)
|
|
sourceExp, err := r.applyNetwork("-s", pair.Source, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("apply network -s: %w", err)
|
|
}
|
|
destExp, err := r.applyNetwork("-d", pair.Destination, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("apply network -d: %w", err)
|
|
}
|
|
|
|
rule = append(rule, sourceExp...)
|
|
rule = append(rule, destExp...)
|
|
rule = append(rule,
|
|
"-j", "MARK", "--set-mark", fmt.Sprintf("%#x", markValue),
|
|
)
|
|
|
|
// Ensure nat rules come first, so the mark can be overwritten.
|
|
// Currently overwritten by the dst-type LOCAL rules for redirected traffic.
|
|
if err := r.iptablesClient.Insert(tableMangle, chainRTPre, 1, rule...); err != nil {
|
|
r.dropSourceMatch(rule)
|
|
return fmt.Errorf("add marking rule for %s: %w", pair.Destination, err)
|
|
}
|
|
|
|
r.rules[ruleID] = rule
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *family) removeNatRule(pair firewall.RouterPair) error {
|
|
ruleID := pair.GenKey(firewall.NatFormat)
|
|
|
|
if rule, exists := r.rules[ruleID]; exists {
|
|
if err := r.iptablesClient.DeleteIfExists(tableMangle, chainRTPre, rule...); err != nil {
|
|
return fmt.Errorf("remove marking rule for %s: %w", pair.Destination, err)
|
|
}
|
|
delete(r.rules, ruleID)
|
|
|
|
if err := r.decrementSetCounter(rule); err != nil {
|
|
return fmt.Errorf("decrement ipset counter: %w", err)
|
|
}
|
|
} else {
|
|
log.Debugf("marking rule %s not found", ruleID)
|
|
}
|
|
|
|
return nil
|
|
}
|