Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions felix/fv/nat_outgoing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,74 @@ var _ = infrastructure.DatastoreDescribe("NATOutgoing rule rendering test", []ap
}
})
})

var _ = infrastructure.DatastoreDescribe("NATPortRange rendering test", []apiconfig.DatastoreType{apiconfig.EtcdV3, apiconfig.Kubernetes}, func(getInfra infrastructure.InfraFactory) {
var (
infra infrastructure.DatastoreInfra
tc infrastructure.TopologyContainers
client client.Interface
dumpedDiags bool
)

BeforeEach(func() {
var err error
infra = getInfra()

dumpedDiags = false
opts := infrastructure.DefaultTopologyOptions()
opts.IPIPMode = api.IPIPModeNever
opts.EnableIPv6 = true

opts.ExtraEnvVars = map[string]string{
"FELIX_NATPortRange": "32768:65535",
}
tc, client = infrastructure.StartSingleNodeTopology(opts, infra)

Comment on lines +130 to +134
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new FV test starts topology containers (StartSingleNodeTopology) and a datastore infra but never stops them. Please add an AfterEach (like the earlier NATOutgoing test in this file) to call tc.Stop() and infra.Stop() so the test suite doesn't leak containers/resources and become flaky.

Copilot uses AI. Check for mistakes.
ctx := context.Background()
ippool := api.NewIPPool()
ippool.Name = "nat-pool"
ippool.Spec.CIDR = "10.244.255.0/24"
ippool.Spec.NATOutgoing = true
ippool, err = client.IPPools().Create(ctx, ippool, options.SetOptions{})
Expect(err).NotTo(HaveOccurred())
})

// Utility function to dump diags if the test failed. Should be called in the inner-most
// AfterEach() to dump diags before the test is torn down. Only the first call for a given
// test has any effect.
dumpDiags := func() {
if !CurrentGinkgoTestDescription().Failed || dumpedDiags {
return
}
if NFTMode() {
logNFTDiags(tc.Felixes[0])
} else {
iptSave, err := tc.Felixes[0].ExecOutput("iptables-save", "-c")
if err == nil {
log.Info("iptables-save:\n" + iptSave)
}
}
dumpedDiags = true
infra.DumpErrorData()
}

AfterEach(func() {
dumpDiags()
tc.Stop()
infra.Stop()
})

It("should have expected rendering", func() {
if NFTMode() {
Eventually(func() string {
output, _ := tc.Felixes[0].ExecOutput("nft", "list", "chain", "ip", "calico", "nat-cali-nat-outgoing")
return output
}, 5*time.Second, 100*time.Millisecond).Should(ContainSubstring("32768-65535"))
Comment on lines +170 to +174
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NFT-mode assertion only checks for the port range substring ("32768-65535"). That would still pass even if the rendered rule is missing the required leading colon (the bug this PR is fixing). Please assert on the full expected fragment for nftables (e.g. include "to :32768-65535" / ":32768-65535").

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I could have included the colon in the test. However it's wrong to say that the test would pass before the bug fix: in fact what happened is that nft execution failed with an error, because of the missing colon, and hence those rules did not get programmed at all, so the test still failed even without the colon.

} else {
Eventually(func() string {
output, _ := tc.Felixes[0].ExecOutput("iptables-save", "-t", "nat")
return output
}, 5*time.Second, 100*time.Millisecond).Should(ContainSubstring("32768-65535"))
}
})
})
2 changes: 2 additions & 0 deletions felix/iptables/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ var _ = DescribeTable("Actions",
Entry("SNATAction", environment.Features{}, SNATAction{ToAddr: "10.0.0.1"}, "--jump SNAT --to-source 10.0.0.1"),
Entry("SNATAction fully random", environment.Features{SNATFullyRandom: true}, SNATAction{ToAddr: "10.0.0.1"}, "--jump SNAT --to-source 10.0.0.1 --random-fully"),
Entry("MasqAction", environment.Features{}, MasqAction{}, "--jump MASQUERADE"),
Entry("MasqAction", environment.Features{}, MasqAction{ToPorts: "32768-65535"}, "--jump MASQUERADE --to-ports 32768-65535"),
Entry("MasqAction", environment.Features{MASQFullyRandom: true}, MasqAction{}, "--jump MASQUERADE --random-fully"),
Entry("MasqAction", environment.Features{MASQFullyRandom: true}, MasqAction{ToPorts: "32768-65535"}, "--jump MASQUERADE --to-ports 32768-65535 --random-fully"),
Comment on lines 39 to +42
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new table entries reuse the same description string ("MasqAction") as existing entries. In Ginkgo Table output, that makes failures hard to attribute to a specific case; please give the new entries distinct names (e.g. indicate "with ports" and/or "fully random").

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Entry("ClearMarkAction", environment.Features{}, ClearMarkAction{Mark: 0x1000}, "--jump MARK --set-mark 0/0x1000"),
Entry("SetMarkAction", environment.Features{}, SetMarkAction{Mark: 0x1000}, "--jump MARK --set-mark 0x1000/0x1000"),
Entry("SetMaskedMarkAction", environment.Features{}, SetMaskedMarkAction{
Expand Down
2 changes: 1 addition & 1 deletion felix/nftables/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ func (g MasqAction) ToFragment(features *environment.Features) string {
}
if g.ToPorts != "" {
// e.g., masquerade to :1024-65535
return fmt.Sprintf("masquerade to %s"+fullyRand, g.ToPorts)
return fmt.Sprintf("masquerade to :%s"+fullyRand, g.ToPorts)
}
return "masquerade" + fullyRand
}
Expand Down
2 changes: 2 additions & 0 deletions felix/nftables/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ var _ = DescribeTable("Actions",
Entry("SNATAction", environment.Features{}, SNATAction{ToAddr: "10.0.0.1"}, "snat to 10.0.0.1"),
Entry("SNATAction fully random", environment.Features{SNATFullyRandom: true}, SNATAction{ToAddr: "10.0.0.1"}, "snat to 10.0.0.1 fully-random"),
Entry("MasqAction", environment.Features{}, MasqAction{}, "masquerade"),
Entry("MasqAction", environment.Features{}, MasqAction{ToPorts: "32768-65535"}, "masquerade to :32768-65535"),
Entry("MasqAction", environment.Features{MASQFullyRandom: true}, MasqAction{}, "masquerade fully-random"),
Entry("MasqAction", environment.Features{MASQFullyRandom: true}, MasqAction{ToPorts: "32768-65535"}, "masquerade to :32768-65535 fully-random"),
Comment on lines +40 to +42
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new table entries reuse the same description string ("MasqAction") as existing entries. In Ginkgo Table output, that makes failures hard to attribute to a specific case; please give the new entries distinct names (e.g. indicate "with ports" and/or "fully random").

Suggested change
Entry("MasqAction", environment.Features{}, MasqAction{ToPorts: "32768-65535"}, "masquerade to :32768-65535"),
Entry("MasqAction", environment.Features{MASQFullyRandom: true}, MasqAction{}, "masquerade fully-random"),
Entry("MasqAction", environment.Features{MASQFullyRandom: true}, MasqAction{ToPorts: "32768-65535"}, "masquerade to :32768-65535 fully-random"),
Entry("MasqAction with ports", environment.Features{}, MasqAction{ToPorts: "32768-65535"}, "masquerade to :32768-65535"),
Entry("MasqAction fully random", environment.Features{MASQFullyRandom: true}, MasqAction{}, "masquerade fully-random"),
Entry("MasqAction fully random with ports", environment.Features{MASQFullyRandom: true}, MasqAction{ToPorts: "32768-65535"}, "masquerade to :32768-65535 fully-random"),

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct but it seems conventional in this file.

Entry("ClearMarkAction", environment.Features{}, ClearMarkAction{Mark: 0x1000}, "meta mark set mark & 0xffffefff"),
Entry("SetMarkAction", environment.Features{}, SetMarkAction{Mark: 0x1000}, "meta mark set mark or 0x1000"),
Entry("SetMaskedMarkAction", environment.Features{}, SetMaskedMarkAction{Mark: 0x1000, Mask: 0xf000}, "meta mark set mark & 0xffff0fff ^ 0x1000"),
Expand Down