mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-24 16:41:30 +02:00
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
package iptables
|
|
|
|
import (
|
|
"sync"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ipsetSupport tracks whether ipset-backed firewall rules can be installed.
|
|
//
|
|
// It starts optimistic and latches to unsupported the first time the kernel
|
|
// proves otherwise: either the hash:net set type is missing (ip_set_hash_net) or
|
|
// iptables cannot match against a set (xt_set). Callers then emit per-IP and
|
|
// per-prefix rules instead. Without the fallback, a rule referencing an unusable
|
|
// set is never installed and the catch-all DROP silently blocks traffic the
|
|
// policy permits.
|
|
//
|
|
// One instance is shared by the ACL managers and routers of both address
|
|
// families, because ipset availability is a property of the kernel rather than
|
|
// of any single table.
|
|
type ipsetSupport struct {
|
|
mu sync.RWMutex
|
|
unsupported bool
|
|
}
|
|
|
|
func newIPSetSupport() *ipsetSupport {
|
|
return &ipsetSupport{}
|
|
}
|
|
|
|
func (s *ipsetSupport) supported() bool {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
return !s.unsupported
|
|
}
|
|
|
|
// markUnsupported records that ipset cannot be used, logging the reason once.
|
|
func (s *ipsetSupport) markUnsupported(cause error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if s.unsupported {
|
|
return
|
|
}
|
|
s.unsupported = true
|
|
|
|
log.Warnf("ipset is unavailable (%v); falling back to per-IP firewall rules. "+
|
|
"Ensure the kernel provides ip_set_hash_net and xt_set; without them rule "+
|
|
"sets are larger and slower to converge on networks with many peers", cause)
|
|
}
|
|
|
|
// ipsetUnusableError marks a failure attributable to ipset, so the caller can
|
|
// retry the same rule in its per-IP form before latching the capability off.
|
|
type ipsetUnusableError struct {
|
|
cause error
|
|
}
|
|
|
|
func (e *ipsetUnusableError) Error() string { return e.cause.Error() }
|
|
|
|
func (e *ipsetUnusableError) Unwrap() error { return e.cause }
|
|
|
|
func ipsetUnusable(cause error) error {
|
|
return &ipsetUnusableError{cause: cause}
|
|
}
|
|
|
|
// maybeIPSetUnusable marks an iptables failure as ipset-attributable only when the
|
|
// rule actually carried a set match, since the same call can fail for unrelated
|
|
// reasons on a rule that matches addresses directly.
|
|
func maybeIPSetUnusable(ipsetName string, err error) error {
|
|
if ipsetName == "" {
|
|
return err
|
|
}
|
|
|
|
return ipsetUnusable(err)
|
|
}
|