Skip to content

Commit b5613a2

Browse files
committed
Do not allocate subnet router anycast in certain IPv6 prefixes
1 parent 517d015 commit b5613a2

File tree

2 files changed

+24
-8
lines changed

2 files changed

+24
-8
lines changed

netbox/ipam/models/ip.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -507,16 +507,20 @@ def get_available_ips(self):
507507
child_ranges.add(iprange.range)
508508
available_ips = prefix - child_ips - child_ranges
509509

510-
# IPv6, pool, or IPv4 /31-/32 sets are fully usable
511-
if self.family == 6 or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31):
510+
# IPv6 /127's, pool, or IPv4 /31-/32 sets are fully usable
511+
if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31):
512512
return available_ips
513513

514-
# For "normal" IPv4 prefixes, omit first and last addresses
515-
available_ips -= netaddr.IPSet([
516-
netaddr.IPAddress(self.prefix.first),
517-
netaddr.IPAddress(self.prefix.last),
518-
])
519-
514+
if self.family == 4:
515+
# For "normal" IPv4 prefixes, omit first and last addresses
516+
available_ips -= netaddr.IPSet([
517+
netaddr.IPAddress(self.prefix.first),
518+
netaddr.IPAddress(self.prefix.last),
519+
])
520+
else:
521+
# For IPv6 prefixes, omit the Subnet-Router anycast address
522+
# per RFC 4291
523+
available_ips -= netaddr.IPSet([netaddr.IPAddress(self.prefix.first)])
520524
return available_ips
521525

522526
def get_first_available_ip(self):

netbox/ipam/tests/test_models.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,18 @@ def test_get_first_available_ip(self):
185185
IPAddress.objects.create(address=IPNetwork('10.0.0.4/24'))
186186
self.assertEqual(parent_prefix.get_first_available_ip(), '10.0.0.5/24')
187187

188+
def test_get_first_available_ip_ipv6(self):
189+
parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8:500::/64'))
190+
self.assertEqual(parent_prefix.get_first_available_ip(), '2001:db8:500::1/64')
191+
192+
def test_get_first_available_ip_ipv6_rfc3627(self):
193+
parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8:500:4::/126'))
194+
self.assertEqual(parent_prefix.get_first_available_ip(), '2001:db8:500:4::1/126')
195+
196+
def test_get_first_available_ip_ipv6_rfc6164(self):
197+
parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8:500:5::/127'))
198+
self.assertEqual(parent_prefix.get_first_available_ip(), '2001:db8:500:5::/127')
199+
188200
def test_get_utilization_container(self):
189201
prefixes = (
190202
Prefix(prefix=IPNetwork('10.0.0.0/24'), status=PrefixStatusChoices.STATUS_CONTAINER),

0 commit comments

Comments
 (0)