Skip to content

Commit c5c9bfd

Browse files
Merge pull request #1251 from metal3-io-bot/cherry-pick-1250-to-release-1.11
🐛 Fix IPv6 preAllocation canonicalization in IPAM
2 parents 74844b0 + bd65d07 commit c5c9bfd

File tree

2 files changed

+86
-8
lines changed

2 files changed

+86
-8
lines changed

ipam/ippool_manager.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package ipam
1818

1919
import (
2020
"context"
21+
"net"
2122
"reflect"
2223
"strings"
2324

@@ -86,6 +87,27 @@ func (m *IPPoolManager) UnsetFinalizer() {
8687
)
8788
}
8889

90+
// ipEqual compares two IP addresses by their parsed values to handle non-canonical
91+
// IPv6 formats (e.g., ::0005 vs ::5). Returns false if either address fails to parse,
92+
// logging a warning to aid in diagnosing configuration issues.
93+
func (m *IPPoolManager) ipEqual(a, b ipamv1.IPAddressStr) bool {
94+
ipA := net.ParseIP(string(a))
95+
ipB := net.ParseIP(string(b))
96+
if ipA == nil {
97+
if a != "" {
98+
m.Log.Info("Failed to parse IP address", "address", a)
99+
}
100+
return false
101+
}
102+
if ipB == nil {
103+
if b != "" {
104+
m.Log.Info("Failed to parse IP address", "address", b)
105+
}
106+
return false
107+
}
108+
return ipA.Equal(ipB)
109+
}
110+
89111
func (m *IPPoolManager) SetClusterOwnerRef(cluster *clusterv1beta1.Cluster) error {
90112
if cluster == nil {
91113
return errors.New("Missing cluster")
@@ -432,7 +454,7 @@ func (m *IPPoolManager) allocateAddress(addressClaim *ipamv1.IPClaim,
432454
isRequestedIPAllocated := false
433455

434456
// Conflict-case, claim is preAllocated but has requested different IP
435-
if requestedIP != "" && ipPreAllocated && requestedIP != preAllocatedAddress {
457+
if requestedIP != "" && ipPreAllocated && !m.ipEqual(requestedIP, preAllocatedAddress) {
436458
addressClaim.Status.ErrorMessage = ptr.To("PreAllocation and requested ip address are conflicting")
437459
return "", 0, nil, []ipamv1.IPAddressStr{}, errors.New("PreAllocation and requested ip address are conflicting")
438460
}
@@ -449,15 +471,15 @@ func (m *IPPoolManager) allocateAddress(addressClaim *ipamv1.IPClaim,
449471
}
450472
index++
451473
// Check if requestedIP is present and matches the current address
452-
if requestedIP != "" && allocatedAddress != requestedIP {
474+
if requestedIP != "" && !m.ipEqual(allocatedAddress, requestedIP) {
453475
continue
454476
}
455-
if requestedIP != "" && allocatedAddress == requestedIP {
477+
if requestedIP != "" && m.ipEqual(allocatedAddress, requestedIP) {
456478
isRequestedIPAllocated = true
457479
}
458480
// We have a pre-allocated ip, we just need to ensure that it matches the current address
459481
// if it does not, continue and try the next address
460-
if ipPreAllocated && allocatedAddress != preAllocatedAddress {
482+
if ipPreAllocated && !m.ipEqual(allocatedAddress, preAllocatedAddress) {
461483
continue
462484
}
463485
// Here the two addresses match, so we continue with that one
@@ -523,7 +545,7 @@ func (m *IPPoolManager) capiAllocateAddress(addressClaim *capipamv1beta1.IPAddre
523545
isRequestedIPAllocated := false
524546

525547
// Conflict-case, claim is preAllocated but has requested different IP
526-
if requestedIP != "" && ipPreAllocated && requestedIP != preAllocatedAddress {
548+
if requestedIP != "" && ipPreAllocated && !m.ipEqual(requestedIP, preAllocatedAddress) {
527549
conditions := clusterv1beta1.Conditions{}
528550
conditions = append(conditions, clusterv1beta1.Condition{
529551
Type: "ErrorMessage",
@@ -549,15 +571,15 @@ func (m *IPPoolManager) capiAllocateAddress(addressClaim *capipamv1beta1.IPAddre
549571
}
550572
index++
551573
// Check if requestedIP is present and matches the current address
552-
if requestedIP != "" && allocatedAddress != requestedIP {
574+
if requestedIP != "" && !m.ipEqual(allocatedAddress, requestedIP) {
553575
continue
554576
}
555-
if requestedIP != "" && allocatedAddress == requestedIP {
577+
if requestedIP != "" && m.ipEqual(allocatedAddress, requestedIP) {
556578
isRequestedIPAllocated = true
557579
}
558580
// We have a pre-allocated ip, we just need to ensure that it matches the current address
559581
// if it does not, continue and try the next address
560-
if ipPreAllocated && allocatedAddress != preAllocatedAddress {
582+
if ipPreAllocated && !m.ipEqual(allocatedAddress, preAllocatedAddress) {
561583
continue
562584
}
563585
// Here the two addresses match, so we continue with that one

ipam/ippool_manager_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,37 @@ var _ = Describe("IPPool manager", func() {
20112011
},
20122012
expectedPrefix: 24,
20132013
}),
2014+
Entry("One pool, IPv6 pre-allocated (non-canonical)", testCaseAllocateAddress{
2015+
ipPool: &ipamv1.IPPool{
2016+
Spec: ipamv1.IPPoolSpec{
2017+
Pools: []ipamv1.Pool{
2018+
{
2019+
Start: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::1")),
2020+
End: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::a")),
2021+
},
2022+
},
2023+
PreAllocations: map[string]ipamv1.IPAddressStr{
2024+
"TestRef": ipamv1.IPAddressStr("2001:db8::0005"),
2025+
},
2026+
Prefix: 64,
2027+
Gateway: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::ffff")),
2028+
DNSServers: []ipamv1.IPAddressStr{
2029+
ipamv1.IPAddressStr("2001:4860:4860::8888"),
2030+
},
2031+
},
2032+
},
2033+
ipClaim: &ipamv1.IPClaim{
2034+
ObjectMeta: metav1.ObjectMeta{
2035+
Name: "TestRef",
2036+
},
2037+
},
2038+
expectedAddress: ipamv1.IPAddressStr("2001:db8::5"),
2039+
expectedGateway: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::ffff")),
2040+
expectedDNSServers: []ipamv1.IPAddressStr{
2041+
ipamv1.IPAddressStr("2001:4860:4860::8888"),
2042+
},
2043+
expectedPrefix: 64,
2044+
}),
20142045
Entry("One pool, pre-allocated, with overrides", testCaseAllocateAddress{
20152046
ipPool: &ipamv1.IPPool{
20162047
Spec: ipamv1.IPPoolSpec{
@@ -2459,6 +2490,31 @@ var _ = Describe("IPPool manager", func() {
24592490
expectedGateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
24602491
expectedPrefix: 24,
24612492
}),
2493+
Entry("One pool, IPv6 pre-allocated (non-canonical)", testCapiCaseAllocateAddress{
2494+
ipPool: &ipamv1.IPPool{
2495+
Spec: ipamv1.IPPoolSpec{
2496+
Pools: []ipamv1.Pool{
2497+
{
2498+
Start: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::1")),
2499+
End: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::a")),
2500+
},
2501+
},
2502+
PreAllocations: map[string]ipamv1.IPAddressStr{
2503+
"TestRef": ipamv1.IPAddressStr("2001:db8::0005"),
2504+
},
2505+
Prefix: 64,
2506+
Gateway: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::ffff")),
2507+
},
2508+
},
2509+
ipAddressClaim: &capipamv1beta1.IPAddressClaim{
2510+
ObjectMeta: metav1.ObjectMeta{
2511+
Name: "TestRef",
2512+
},
2513+
},
2514+
expectedAddress: ipamv1.IPAddressStr("2001:db8::5"),
2515+
expectedGateway: (*ipamv1.IPAddressStr)(ptr.To("2001:db8::ffff")),
2516+
expectedPrefix: 64,
2517+
}),
24622518
Entry("One pool, pre-allocated, with overrides", testCapiCaseAllocateAddress{
24632519
ipPool: &ipamv1.IPPool{
24642520
Spec: ipamv1.IPPoolSpec{

0 commit comments

Comments
 (0)