swift/test/unit/common/utils/test_ipaddrs.py
Tim Burke 23fa18d302 Remove hard dependency on netifaces
The project was archived in 2021, and we can fairly easily replace it
with some ctypes code to call getifaddrs ourselves.

Be willing to fall back to netifaces (with a warning) in case getifaddrs
is not available, but I'm fairly certain it will be for all platforms we
support.

Could maybe use some more testing on big-endian arches / BSDs, but an
attempt was at least made at supporting them.

Partial-Bug: #2019233
Change-Id: I1189a60204cf96c291619f8d8ec957ed8a5be1ce
2023-05-23 14:35:48 -07:00

217 lines
9.2 KiB
Python

# Copyright (c) 2010-2012 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import ctypes
from mock import patch
import socket
import unittest
# Continue importing from utils, as 3rd parties may depend on those imports
from swift.common import utils
from swift.common.utils import ipaddrs as utils_ipaddrs
class TestIsValidIP(unittest.TestCase):
def test_is_valid_ip(self):
self.assertTrue(utils.is_valid_ip("127.0.0.1"))
self.assertTrue(utils.is_valid_ip("10.0.0.1"))
ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156"
self.assertTrue(utils.is_valid_ip(ipv6))
ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156"
self.assertTrue(utils.is_valid_ip(ipv6))
ipv6 = "fe80::204:61ff:fe9d:f156"
self.assertTrue(utils.is_valid_ip(ipv6))
ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86"
self.assertTrue(utils.is_valid_ip(ipv6))
ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86"
self.assertTrue(utils.is_valid_ip(ipv6))
ipv6 = "fe80::204:61ff:254.157.241.86"
self.assertTrue(utils.is_valid_ip(ipv6))
ipv6 = "fe80::"
self.assertTrue(utils.is_valid_ip(ipv6))
ipv6 = "::1"
self.assertTrue(utils.is_valid_ip(ipv6))
not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a"
self.assertFalse(utils.is_valid_ip(not_ipv6))
not_ipv6 = "1:2:3:4:5:6::7:8"
self.assertFalse(utils.is_valid_ip(not_ipv6))
def test_is_valid_ipv4(self):
self.assertTrue(utils.is_valid_ipv4("127.0.0.1"))
self.assertTrue(utils.is_valid_ipv4("10.0.0.1"))
ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156"
self.assertFalse(utils.is_valid_ipv4(ipv6))
ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156"
self.assertFalse(utils.is_valid_ipv4(ipv6))
ipv6 = "fe80::204:61ff:fe9d:f156"
self.assertFalse(utils.is_valid_ipv4(ipv6))
ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86"
self.assertFalse(utils.is_valid_ipv4(ipv6))
ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86"
self.assertFalse(utils.is_valid_ipv4(ipv6))
ipv6 = "fe80::204:61ff:254.157.241.86"
self.assertFalse(utils.is_valid_ipv4(ipv6))
ipv6 = "fe80::"
self.assertFalse(utils.is_valid_ipv4(ipv6))
ipv6 = "::1"
self.assertFalse(utils.is_valid_ipv4(ipv6))
not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a"
self.assertFalse(utils.is_valid_ipv4(not_ipv6))
not_ipv6 = "1:2:3:4:5:6::7:8"
self.assertFalse(utils.is_valid_ipv4(not_ipv6))
def test_is_valid_ipv6(self):
self.assertFalse(utils.is_valid_ipv6("127.0.0.1"))
self.assertFalse(utils.is_valid_ipv6("10.0.0.1"))
ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156"
self.assertTrue(utils.is_valid_ipv6(ipv6))
ipv6 = "fe80:0:0:0:204:61ff:fe9d:f156"
self.assertTrue(utils.is_valid_ipv6(ipv6))
ipv6 = "fe80::204:61ff:fe9d:f156"
self.assertTrue(utils.is_valid_ipv6(ipv6))
ipv6 = "fe80:0000:0000:0000:0204:61ff:254.157.241.86"
self.assertTrue(utils.is_valid_ipv6(ipv6))
ipv6 = "fe80:0:0:0:0204:61ff:254.157.241.86"
self.assertTrue(utils.is_valid_ipv6(ipv6))
ipv6 = "fe80::204:61ff:254.157.241.86"
self.assertTrue(utils.is_valid_ipv6(ipv6))
ipv6 = "fe80::"
self.assertTrue(utils.is_valid_ipv6(ipv6))
ipv6 = "::1"
self.assertTrue(utils.is_valid_ipv6(ipv6))
not_ipv6 = "3ffe:0b00:0000:0001:0000:0000:000a"
self.assertFalse(utils.is_valid_ipv6(not_ipv6))
not_ipv6 = "1:2:3:4:5:6::7:8"
self.assertFalse(utils.is_valid_ipv6(not_ipv6))
class TestExpandIPv6(unittest.TestCase):
def test_expand_ipv6(self):
expanded_ipv6 = "fe80::204:61ff:fe9d:f156"
upper_ipv6 = "fe80:0000:0000:0000:0204:61ff:fe9d:f156"
self.assertEqual(expanded_ipv6, utils.expand_ipv6(upper_ipv6))
omit_ipv6 = "fe80:0000:0000::0204:61ff:fe9d:f156"
self.assertEqual(expanded_ipv6, utils.expand_ipv6(omit_ipv6))
less_num_ipv6 = "fe80:0:00:000:0204:61ff:fe9d:f156"
self.assertEqual(expanded_ipv6, utils.expand_ipv6(less_num_ipv6))
class TestWhatAreMyIPs(unittest.TestCase):
def test_whataremyips(self):
myips = utils.whataremyips()
self.assertTrue(len(myips) > 1)
self.assertIn('127.0.0.1', myips)
def test_whataremyips_bind_to_all(self):
for any_addr in ('0.0.0.0', '0000:0000:0000:0000:0000:0000:0000:0000',
'::0', '::0000', '::',
# Wacky parse-error input produces all IPs
'I am a bear'):
myips = utils.whataremyips(any_addr)
self.assertTrue(len(myips) > 1)
self.assertIn('127.0.0.1', myips)
def test_whataremyips_bind_ip_specific(self):
self.assertEqual(['1.2.3.4'], utils.whataremyips('1.2.3.4'))
def test_whataremyips_getifaddrs(self):
def mock_getifaddrs(ptr):
addrs = [
utils_ipaddrs.ifaddrs(None, b'lo', 0, ctypes.pointer(
utils_ipaddrs.sockaddr_in4(
sin_family=socket.AF_INET,
sin_addr=(127, 0, 0, 1)))),
utils_ipaddrs.ifaddrs(None, b'lo', 0, ctypes.cast(
ctypes.pointer(utils_ipaddrs.sockaddr_in6(
sin6_family=socket.AF_INET6,
sin6_addr=(
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1))),
ctypes.POINTER(utils_ipaddrs.sockaddr_in4))),
utils_ipaddrs.ifaddrs(None, b'eth0', 0, ctypes.pointer(
utils_ipaddrs.sockaddr_in4(
sin_family=socket.AF_INET,
sin_addr=(192, 168, 50, 63)))),
utils_ipaddrs.ifaddrs(None, b'eth0', 0, ctypes.cast(
ctypes.pointer(utils_ipaddrs.sockaddr_in6(
sin6_family=socket.AF_INET6,
sin6_addr=(
254, 128, 0, 0, 0, 0, 0, 0,
106, 191, 199, 168, 109, 243, 41, 35))),
ctypes.POINTER(utils_ipaddrs.sockaddr_in4))),
# MAC address will be ignored
utils_ipaddrs.ifaddrs(None, b'eth0', 0, ctypes.cast(
ctypes.pointer(utils_ipaddrs.sockaddr_in6(
sin6_family=getattr(socket, 'AF_PACKET', 17),
sin6_port=0,
sin6_flowinfo=2,
sin6_addr=(
1, 0, 0, 6, 172, 116, 177, 85,
64, 146, 0, 0, 0, 0, 0, 0))),
ctypes.POINTER(utils_ipaddrs.sockaddr_in4))),
# Seen in the wild: no addresses at all
utils_ipaddrs.ifaddrs(None, b'cscotun0', 69841),
]
for cur, nxt in zip(addrs, addrs[1:]):
cur.ifa_next = ctypes.pointer(nxt)
ptr._obj.contents = addrs[0]
with patch.object(utils_ipaddrs, 'getifaddrs', mock_getifaddrs), \
patch('swift.common.utils.ipaddrs.freeifaddrs') as mock_free:
self.assertEqual(utils.whataremyips(), [
'127.0.0.1',
'::1',
'192.168.50.63',
'fe80::6abf:c7a8:6df3:2923',
])
self.assertEqual(len(mock_free.mock_calls), 1)
def test_whataremyips_netifaces_error(self):
class FakeNetifaces(object):
@staticmethod
def interfaces():
return ['eth0']
@staticmethod
def ifaddresses(interface):
raise ValueError
with patch.object(utils_ipaddrs, 'getifaddrs', None), \
patch.object(utils_ipaddrs, 'netifaces', FakeNetifaces):
self.assertEqual(utils.whataremyips(), [])
def test_whataremyips_netifaces_ipv6(self):
test_ipv6_address = '2001:6b0:dead:beef:2::32'
test_interface = 'eth0'
class FakeNetifaces(object):
AF_INET = int(socket.AF_INET)
AF_INET6 = int(socket.AF_INET6)
@staticmethod
def interfaces():
return ['eth0']
@staticmethod
def ifaddresses(interface):
return {int(socket.AF_INET6): [
{'netmask': 'ffff:ffff:ffff:ffff::',
'addr': '%s%%%s' % (test_ipv6_address, test_interface)}]}
with patch.object(utils_ipaddrs, 'getifaddrs', None), \
patch.object(utils_ipaddrs, 'netifaces', FakeNetifaces):
myips = utils.whataremyips()
self.assertEqual(len(myips), 1)
self.assertEqual(myips[0], test_ipv6_address)