Skip to content

Commit d87ef88

Browse files
committed
fix(router): nft tables limit number of peers source
1 parent 2097306 commit d87ef88

File tree

2 files changed

+88
-16
lines changed

2 files changed

+88
-16
lines changed

client/firewall/nftables/manager_linux_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,56 @@ func TestNftablesManagerCompatibilityWithIptables(t *testing.T) {
386386
verifyIptablesOutput(t, stdout, stderr)
387387
}
388388

389+
func TestNftablesManagerCompatibilityWithIptablesFor10kPrefixes(t *testing.T) {
390+
if check() != NFTABLES {
391+
t.Skip("nftables not supported on this system")
392+
}
393+
394+
if _, err := exec.LookPath("iptables-save"); err != nil {
395+
t.Skipf("iptables-save not available on this system: %v", err)
396+
}
397+
398+
// First ensure iptables-nft tables exist by running iptables-save
399+
stdout, stderr := runIptablesSave(t)
400+
verifyIptablesOutput(t, stdout, stderr)
401+
402+
manager, err := Create(ifaceMock, iface.DefaultMTU)
403+
require.NoError(t, err, "failed to create manager")
404+
require.NoError(t, manager.Init(nil))
405+
406+
t.Cleanup(func() {
407+
err := manager.Close(nil)
408+
require.NoError(t, err, "failed to reset manager state")
409+
410+
// Verify iptables output after reset
411+
stdout, stderr := runIptablesSave(t)
412+
verifyIptablesOutput(t, stdout, stderr)
413+
})
414+
415+
const octet2Count = 25
416+
const octet3Count = 255
417+
prefixes := make([]netip.Prefix, 0, (octet2Count-1)*(octet3Count-1))
418+
for i := 1; i < octet2Count; i++ {
419+
for j := 1; j < octet3Count; j++ {
420+
addr := netip.AddrFrom4([4]byte{192, byte(j), byte(i), 0})
421+
prefixes = append(prefixes, netip.PrefixFrom(addr, 24))
422+
}
423+
}
424+
_, err = manager.AddRouteFiltering(
425+
nil,
426+
prefixes,
427+
fw.Network{Prefix: netip.MustParsePrefix("10.2.0.0/24")},
428+
fw.ProtocolTCP,
429+
nil,
430+
&fw.Port{Values: []uint16{443}},
431+
fw.ActionAccept,
432+
)
433+
require.NoError(t, err, "failed to add route filtering rule")
434+
435+
stdout, stderr = runIptablesSave(t)
436+
verifyIptablesOutput(t, stdout, stderr)
437+
}
438+
389439
func compareExprsIgnoringCounters(t *testing.T, got, want []expr.Any) {
390440
t.Helper()
391441
require.Equal(t, len(got), len(want), "expression count mismatch")

client/firewall/nftables/router_linux.go

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -346,25 +346,11 @@ func (r *router) AddRouteFiltering(
346346
}
347347

348348
chain := r.chains[chainNameRoutingFw]
349-
var exprs []expr.Any
350-
351-
var source firewall.Network
352-
switch {
353-
case len(sources) == 1 && sources[0].Bits() == 0:
354-
// If it's 0.0.0.0/0, we don't need to add any source matching
355-
case len(sources) == 1:
356-
// If there's only one source, we can use it directly
357-
source.Prefix = sources[0]
358-
default:
359-
// If there are multiple sources, use a set
360-
source.Set = firewall.NewPrefixSet(sources)
361-
}
362349

363-
sourceExp, err := r.applyNetwork(source, sources, true)
350+
exprs, err := r.applySources(sources)
364351
if err != nil {
365-
return nil, fmt.Errorf("apply source: %w", err)
352+
return nil, err
366353
}
367-
exprs = append(exprs, sourceExp...)
368354

369355
destExp, err := r.applyNetwork(destination, nil, false)
370356
if err != nil {
@@ -425,6 +411,42 @@ func (r *router) AddRouteFiltering(
425411
return ruleKey, nil
426412
}
427413

414+
func (r *router) applySources(sources []netip.Prefix) ([]expr.Any, error) {
415+
var exprs []expr.Any
416+
417+
var source firewall.Network
418+
if len(sources) == 1 {
419+
if sources[0].Bits() == 0 {
420+
// If it's 0.0.0.0/0, we don't need to add any source matching
421+
} else { // If there's only one source, we can use it directly
422+
source.Prefix = sources[0]
423+
}
424+
sourceExp, err := r.applyNetwork(source, sources, true)
425+
if err != nil {
426+
return nil, fmt.Errorf("apply source: %w", err)
427+
}
428+
exprs = append(exprs, sourceExp...)
429+
} else { // If there are multiple sources, use a set
430+
var subEnd int
431+
maxSize := 200
432+
for subStart := 0; subStart < len(sources); subStart += maxSize {
433+
subEnd += maxSize
434+
if subEnd > len(sources) {
435+
subEnd = len(sources)
436+
}
437+
subSources := sources[subStart:subEnd]
438+
source.Set = firewall.NewPrefixSet(subSources)
439+
440+
sourceExp, err := r.applyNetwork(source, subSources, true)
441+
if err != nil {
442+
return nil, fmt.Errorf("apply source: %w", err)
443+
}
444+
exprs = append(exprs, sourceExp...)
445+
}
446+
}
447+
return exprs, nil
448+
}
449+
428450
func (r *router) getIpSet(set firewall.Set, prefixes []netip.Prefix, isSource bool) ([]expr.Any, error) {
429451
ref, err := r.ipsetCounter.Increment(set.HashedName(), setInput{
430452
set: set,

0 commit comments

Comments
 (0)