Allow to convert IPV4 addresse from text to binary

Bug #1914386 reported by Herve Beraud
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
oslo.utils
Fix Released
Undecided
Unassigned

Bug Description

Heat Common constraints accept an arbitrary string of 1 to 10 numbers as a valid IP.

Using the custom_constraint ip_addr in a template does not work as expect.

Using something like
- notanipadress
- 1024.1024.10.24

Produce a parameter error like expected
~~~
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack10 --parameter ip_address=notanipadress --parameter demo=demo --parameter demopassword=password
ERROR: Parameter 'ip_address' is invalid: Error validating value 'notanipadress': Invalid IP address
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack10 --parameter ip_address=1024.1024.10.24 --parameter demo=demo --parameter demopassword=password
ERROR: Parameter 'ip_address' is invalid: Error validating value '1024.1024.10.24': Invalid IP address
~~~

But using arbitrary strings of numbers less then 11 numbers does not

~~~
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack10 --parameter ip_address=1024 --parameter demo=demo --parameter demopassword=password
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| id | 8cca736a-1f3c-4739-a85b-bd770ff7d561 |
| stack_name | teststack10 |
| description | Simple demonstration on what happens when use a function that is from a template version which higher then the version specified in the template |
| creation_time | 2021-02-03T06:24:01Z |
| updated_time | None |
| stack_status | CREATE_COMPLETE |
| stack_status_reason | Stack CREATE completed successfully |
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack11 --parameter ip_address=1 --parameter demo=demo --parameter demopassword=password
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| id | f6739aad-285c-423e-a0ea-1668b4e340b5 |
| stack_name | teststack11 |
| description | Simple demonstration on what happens when use a function that is from a template version which higher then the version specified in the template |
| creation_time | 2021-02-03T06:24:17Z |
| updated_time | None |
| stack_status | CREATE_COMPLETE |
| stack_status_reason | Stack CREATE completed successfully |
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+

(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack12 --parameter ip_address=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 --parameter demo=demo --parameter demopassword=password
ERROR: Parameter 'ip_address' is invalid: Error validating value '111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111': Invalid IP address
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack13 --parameter ip_address=0101010101010 --parameter demo=demo --parameter demopassword=password
ERROR: Parameter 'ip_address' is invalid: Error validating value '0101010101010': Invalid IP address
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack13 --parameter ip_address=010101010101 --parameter demo=demo --parameter demopassword=password
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| id | 3b60f530-84ab-4daa-bbb5-5647374e2079 |
| stack_name | teststack13 |
| description | Simple demonstration on what happens when use a function that is from a template version which higher then the version specified in the template |
| creation_time | 2021-02-03T06:28:17Z |
| updated_time | None |
| stack_status | CREATE_COMPLETE |
| stack_status_reason | Stack CREATE completed successfully |
~~~

Version-Release number of selected component (if applicable):
- python2-oslo-utils-3.35.1-2.el7ost.noarch
- openstack-heat-common-10.0.3-15.el7ost.noarch
- python2-netaddr-0.7.19-5.el7ost.noarch

How reproducible:

Every time

Steps to Reproduce:
1. use the a template like the following

openstack stack create -t Parameterconstraints.yaml teststack --parameter ip_address=010101010101 --parameter demo=demo --parameter demopassword=password
~~~
heat_template_version: 2013-05-23

description: Simple demonstration of heat parameter constraints

parameters:
  demo:
    type: string
    label: demo
    description: just a useless var
  demopassword:
    type: string
    label: demo password
    description: just a useless var
    default: demopassword
    constraints:
      - length: { min: 8, max: 24 }
      - allowed_pattern: "[a-zA-Z0-9]*"
  ip_address:
    type: string
    label: IP ip_address
    constraints:
      - custom_constraint: ip_addr
resources:
  cloud_config:
    type: OS::Heat::CloudConfig
    properties:
      cloud_config:
        users:
          - name: {get_param: demo}
            passwd: { digest: ['sha512', {get_param: demopassword}]}
            ip_address: { get_param: ip_address }

~~~

Actual results:

~~~
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack13 --parameter ip_address=010101010101 --parameter demo=demo --parameter demopassword=password
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+---------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| id | 3b60f530-84ab-4daa-bbb5-5647374e2079 |
| stack_name | teststack13 |
| description | Simple demonstration on what happens when use a function that is from a template version which higher then the version specified in the template |
| creation_time | 2021-02-03T06:28:17Z |
| updated_time | None |
| stack_status | CREATE_COMPLETE |
| stack_status_reason | Stack CREATE completed successfully
~~~
Expected results:

~~~
(overcloud) [stack@undercloud-0 Parameter]$ openstack stack create -t Parameterconstraints.yaml teststack13 --parameter ip_address=010101010101 --parameter demo=demo --parameter demopassword=password
ERROR: Parameter 'ip_address' is invalid: Error validating value '010101010101': Invalid IP address
~~~

Additional info:

/usr/lib/python2.7/site-packages/heat/engine/constraint/common_constraints.py
~~~
from oslo_utils import netutils
from oslo_utils import timeutils

from heat.common.i18n import _
from heat.common import netutils as heat_netutils
from heat.engine import constraints

class TestConstraintDelay(constraints.BaseCustomConstraint):

    def validate_with_client(self, client, value):
        eventlet.sleep(value)

class IPConstraint(constraints.BaseCustomConstraint):

    def validate(self, value, context, template=None):
        self._error_message = 'Invalid IP address'
        return netutils.is_valid_ip(value)
~~~
/usr/lib/python2.7/site-packages/oslo_utils/netutils.py
~~~
def is_valid_ipv4(address):
    """Verify that address represents a valid IPv4 address.

    :param address: Value to verify
    :type address: string
    :returns: bool

    .. versionadded:: 1.1
    """
    try:
        return netaddr.valid_ipv4(address)
    except netaddr.AddrFormatError:
        return False

def is_valid_ipv6(address):
    """Verify that address represents a valid IPv6 address.

    :param address: Value to verify
    :type address: string
    :returns: bool

    .. versionadded:: 1.1
    """
    if not address:
        return False

    parts = address.rsplit("%", 1)
    address = parts[0]
    scope = parts[1] if len(parts) > 1 else None
    if scope is not None and (len(scope) < 1 or len(scope) > 15):
        return False

    try:
        return netaddr.valid_ipv6(address, netaddr.core.INET_PTON)
    except netaddr.AddrFormatError:
        return False

...................

def is_valid_ip(address):
    """Verify that address represents a valid IP address.

    :param address: Value to verify
    :type address: string
    :returns: bool

    .. versionadded:: 1.1
    """
    return is_valid_ipv4(address) or is_valid_ipv6(address)
~~~~

At first glance this behave come from `netaddr` and not from `oslo.utils`:

```
$ tox -e venv -- python
>>> from oslo_utils import netutils
>>> netutils.is_valid_ipv4(1)
False
>>> netutils.is_valid_ipv4("1")
True
>>> import netaddr
>>> netaddr.valid_ipv4(1)
False
>>> netaddr.valid_ipv4("1")
True
```

Oslo.utils just wrap the `netaddr`[1] module and return its outcome [2].

This is behavior is something expected.

This is not an invalid input.

By default, valid_ipv4() uses the inet_aton() C function to verify input which follows the following input formatting rules:

       a.b.c.d Each of the four numeric parts specifies a byte of the address; the bytes are assigned in left-to-right order to produce the binary address.

       a.b.c Parts a and b specify the first two bytes of the binary address. Part c is interpreted as a 16-bit value that defines the rightmost two bytes of the binary address. This notation is suitable for specifying (outmoded) Class B network addresses.

       a.b Part a specifies the first byte of the binary address. Part b is interpreted as a 24-bit value that defines the rightmost three bytes of the binary address. This notation is suitable for specifying (outmoded) Class A network addresses.

       a The value is interpreted as a 32-bit value that is stored directly into the binary address without any byte rearrangement.

ref: http://man7.org/linux/man-pages/man3/inet_addr.3.html

Therefore, '10' falls under the 4th option (a) where the number is treated as a 32-bit value. Written in dotted decimal format it would be '0.0.0.10' or in hex \x00\x00\x00\x0A Python3 shows it as a byte array b'\x00 \x00\x00\n' (\n newline being the ASCII character 10)

To validate the dotted decimal notation pass the INET_PTON flag to have the function use inet_pton() instead of inet_aton() which evaluates ipv4 and ipv6 addresses using proper formatting requirements:

```
>>> import netaddr
>>> netaddr.valid_ipv4('10')
True
>>> netaddr.valid_ipv4('10', flags=netaddr.INET_PTON)
False
```

However oslo.utils doesn't accept `flags` so it could be an improvement if you decide to use `INET_PTON` rather than `INET_ATON` that's the default:

https://github.com/netaddr/netaddr/blob/master/netaddr/strategy/ipv4.py#L99,L105

For further details please take a look too:

https://github.com/netaddr/netaddr/issues/186

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix included in openstack/oslo.utils 4.8.0

This issue was fixed in the openstack/oslo.utils 4.8.0 release.

Changed in oslo.utils:
status: New → Fix Released
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Related fix proposed to oslo.utils (master)

Related fix proposed to branch: master
Review: https://review.opendev.org/c/openstack/oslo.utils/+/903924

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.