]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
reject leading '0's in IPv4 addr to avoid ambiguity
authorBo Chi <shcbo@cn.ibm.com>
Thu, 10 Dec 2015 13:42:26 +0000 (08:42 -0500)
committerBo Chi <shcbo@cn.ibm.com>
Thu, 17 Dec 2015 12:45:34 +0000 (07:45 -0500)
If a IPv4 address has a leading '0', it will be interpreted as an
octal number, e.g. 10.0.0.011 will be interpreted as 10.0.0.9.
Users who are not familiar with or not expecting octal interpretation
will not get the address they intended. Since there is no standard
around this, we reject leading '0's to avoid ambiguity.

APIImpact
Change-Id: I3163ba13468c47d385585221d2167fbe31d24010
Closes-Bug: #1524220

neutron/api/v2/attributes.py
neutron/tests/unit/api/v2/test_attributes.py

index e7b5ce592a9f46cece72da34222ea511d2cb879d..af4a69ba4a33ed5ad491c1be6e78b38c8d601278 100644 (file)
@@ -206,7 +206,10 @@ def _validate_mac_address_or_none(data, valid_values=None):
 def _validate_ip_address(data, valid_values=None):
     msg = None
     try:
-        netaddr.IPAddress(_validate_no_whitespace(data))
+        # netaddr.core.ZEROFILL is only applicable to IPv4.
+        # it will remove leading zeros from IPv4 address octets.
+        ip = netaddr.IPAddress(_validate_no_whitespace(data),
+                               flags=netaddr.core.ZEROFILL)
         # The followings are quick checks for IPv6 (has ':') and
         # IPv4.  (has 3 periods like 'xx.xx.xx.xx')
         # NOTE(yamamoto): netaddr uses libraries provided by the underlying
@@ -222,6 +225,13 @@ def _validate_ip_address(data, valid_values=None):
         #   >>>
         if ':' not in data and data.count('.') != 3:
             msg = _("'%s' is not a valid IP address") % data
+        # A leading '0' in IPv4 address may be interpreted as an octal number,
+        # e.g. 011 octal is 9 decimal. Since there is no standard saying
+        # whether IP address with leading '0's should be interpreted as octal
+        # or decimal, hence we reject leading '0's to avoid ambiguity.
+        if ip.version == 4 and str(ip) != data:
+            msg = _("'%(data)s' is not an accepted IP address, "
+                    "'%(ip)s' is recommended") % {"data": data, "ip": ip}
     except Exception:
         msg = _("'%s' is not a valid IP address") % data
     if msg:
index 4401566f61d85152b2070b0ba3626f02fb0eae9d..57dda2523a07842cef447fb83ef0f7dccc7489fd 100644 (file)
@@ -17,6 +17,7 @@ import string
 import testtools
 
 import mock
+import netaddr
 
 from neutron._i18n import _
 from neutron.api.v2 import attributes
@@ -270,6 +271,28 @@ class TestAttributes(base.BaseTestCase):
             msg = attributes._validate_ip_address(ip_addr)
             self.assertEqual("'%s' is not a valid IP address" % ip_addr, msg)
 
+    def test_validate_ip_address_with_leading_zero(self):
+        ip_addr = '1.1.1.01'
+        expected_msg = ("'%(data)s' is not an accepted IP address, "
+                        "'%(ip)s' is recommended")
+        msg = attributes._validate_ip_address(ip_addr)
+        self.assertEqual(expected_msg % {"data": ip_addr, "ip": '1.1.1.1'},
+                         msg)
+
+        ip_addr = '1.1.1.011'
+        msg = attributes._validate_ip_address(ip_addr)
+        self.assertEqual(expected_msg % {"data": ip_addr, "ip": '1.1.1.11'},
+                         msg)
+
+        ip_addr = '1.1.1.09'
+        msg = attributes._validate_ip_address(ip_addr)
+        self.assertEqual(expected_msg % {"data": ip_addr, "ip": '1.1.1.9'},
+                         msg)
+
+        ip_addr = "fe80:0:0:0:0:0:0:0001"
+        msg = attributes._validate_ip_address(ip_addr)
+        self.assertIsNone(msg)
+
     def test_validate_ip_address_bsd(self):
         # NOTE(yamamoto):  On NetBSD and OS X, netaddr.IPAddress() accepts
         # '1' * 59 as a valid address.  The behaviour is inherited from
@@ -279,7 +302,8 @@ class TestAttributes(base.BaseTestCase):
         ip_addr = '1' * 59
         with mock.patch('netaddr.IPAddress') as ip_address_cls:
             msg = attributes._validate_ip_address(ip_addr)
-        ip_address_cls.assert_called_once_with(ip_addr)
+        ip_address_cls.assert_called_once_with(ip_addr,
+                                               flags=netaddr.core.ZEROFILL)
         self.assertEqual("'%s' is not a valid IP address" % ip_addr, msg)
 
     def test_validate_ip_pools(self):