Просмотр исходного кода

Fixes: #17358 - Ensure correct comparison of overlapping IPRanges (#17391)

* Add new INET lookups for net_host_lt/gt/lte/gte comparisons irrespective of subnet inclusion

* Refactor Lookup subclasses to be more DRY

* Move comparison_sql to class attribute

* Add HostAsInet(Transform) to perform cast

* Remove unnecessary Lookup comparison classes

* Chain Host and Inet instead of making a new transform
bctiemann 1 год назад
Родитель
Сommit
ca210168df
3 измененных файлов с 38 добавлено и 7 удалено
  1. 2 0
      netbox/ipam/fields.py
  2. 7 7
      netbox/ipam/models/ip.py
  3. 29 0
      netbox/ipam/tests/test_models.py

+ 2 - 0
netbox/ipam/fields.py

@@ -105,6 +105,8 @@ IPAddressField.register_lookup(lookups.NetIn)
 IPAddressField.register_lookup(lookups.NetHostContained)
 IPAddressField.register_lookup(lookups.NetFamily)
 IPAddressField.register_lookup(lookups.NetMaskLength)
+IPAddressField.register_lookup(lookups.Host)
+IPAddressField.register_lookup(lookups.Inet)
 
 
 class ASNField(models.BigIntegerField):

+ 7 - 7
netbox/ipam/models/ip.py

@@ -580,15 +580,15 @@ class IPRange(ContactsMixin, PrimaryModel):
                 })
 
             # Check for overlapping ranges
-            overlapping_range = IPRange.objects.exclude(pk=self.pk).filter(vrf=self.vrf).filter(
-                Q(start_address__gte=self.start_address, start_address__lte=self.end_address) |  # Starts inside
-                Q(end_address__gte=self.start_address, end_address__lte=self.end_address) |  # Ends inside
-                Q(start_address__lte=self.start_address, end_address__gte=self.end_address)  # Starts & ends outside
-            ).first()
-            if overlapping_range:
+            overlapping_ranges = IPRange.objects.exclude(pk=self.pk).filter(vrf=self.vrf).filter(
+                Q(start_address__host__inet__gte=self.start_address.ip, start_address__host__inet__lte=self.end_address.ip) |  # Starts inside
+                Q(end_address__host__inet__gte=self.start_address.ip, end_address__host__inet__lte=self.end_address.ip) |  # Ends inside
+                Q(start_address__host__inet__lte=self.start_address.ip, end_address__host__inet__gte=self.end_address.ip)  # Starts & ends outside
+            )
+            if overlapping_ranges.exists():
                 raise ValidationError(
                     _("Defined addresses overlap with range {overlapping_range} in VRF {vrf}").format(
-                        overlapping_range=overlapping_range,
+                        overlapping_range=overlapping_ranges.first(),
                         vrf=self.vrf
                     ))
 

+ 29 - 0
netbox/ipam/tests/test_models.py

@@ -36,6 +36,35 @@ class TestAggregate(TestCase):
         self.assertEqual(aggregate.get_utilization(), 100)
 
 
+class TestIPRange(TestCase):
+
+    def test_overlapping_range(self):
+        iprange_192_168 = IPRange.objects.create(start_address=IPNetwork('192.168.0.1/22'), end_address=IPNetwork('192.168.0.49/22'))
+        iprange_192_168.clean()
+        iprange_3_1_99 = IPRange.objects.create(start_address=IPNetwork('1.2.3.1/24'), end_address=IPNetwork('1.2.3.99/24'))
+        iprange_3_1_99.clean()
+        iprange_3_100_199 = IPRange.objects.create(start_address=IPNetwork('1.2.3.100/24'), end_address=IPNetwork('1.2.3.199/24'))
+        iprange_3_100_199.clean()
+        iprange_3_200_255 = IPRange.objects.create(start_address=IPNetwork('1.2.3.200/24'), end_address=IPNetwork('1.2.3.255/24'))
+        iprange_3_200_255.clean()
+        iprange_4_1_99 = IPRange.objects.create(start_address=IPNetwork('1.2.4.1/24'), end_address=IPNetwork('1.2.4.99/24'))
+        iprange_4_1_99.clean()
+        iprange_4_200 = IPRange.objects.create(start_address=IPNetwork('1.2.4.200/24'), end_address=IPNetwork('1.2.4.255/24'))
+        iprange_4_200.clean()
+        # Overlapping range entirely within existing
+        with self.assertRaises(ValidationError):
+            iprange_3_123_124 = IPRange.objects.create(start_address=IPNetwork('1.2.3.123/26'), end_address=IPNetwork('1.2.3.124/26'))
+            iprange_3_123_124.clean()
+        # Overlapping range starting within existing
+        with self.assertRaises(ValidationError):
+            iprange_4_98_101 = IPRange.objects.create(start_address=IPNetwork('1.2.4.98/24'), end_address=IPNetwork('1.2.4.101/24'))
+            iprange_4_98_101.clean()
+        # Overlapping range ending within existing
+        with self.assertRaises(ValidationError):
+            iprange_4_198_201 = IPRange.objects.create(start_address=IPNetwork('1.2.4.198/24'), end_address=IPNetwork('1.2.4.201/24'))
+            iprange_4_198_201.clean()
+
+
 class TestPrefix(TestCase):
 
     def test_get_duplicates(self):