From f53dbf90eae9dabd7fc182707d5907cb49ec0ffb Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Thu, 13 Oct 2022 14:16:13 -0500 Subject: [PATCH] Don't assume column index for netmask and broadcast (#79121) * Don't assume column index for netmask and broadcast. Fixes #79117 * fix typo --- .../fragments/79117-bsd-ifconfig-inet-fix.yml | 4 ++ .../module_utils/facts/network/generic_bsd.py | 27 ++++++++---- .../facts/network/test_generic_bsd.py | 42 +++++++++++++++++++ 3 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 changelogs/fragments/79117-bsd-ifconfig-inet-fix.yml diff --git a/changelogs/fragments/79117-bsd-ifconfig-inet-fix.yml b/changelogs/fragments/79117-bsd-ifconfig-inet-fix.yml new file mode 100644 index 00000000000..ae211a0e668 --- /dev/null +++ b/changelogs/fragments/79117-bsd-ifconfig-inet-fix.yml @@ -0,0 +1,4 @@ +bugfixes: +- BSD network facts - Do not assume column indexes, look for ``netmask`` and + ``broadcast`` for determining the correct columns when parsing ``inet`` line + (https://github.com/ansible/ansible/issues/79117) diff --git a/lib/ansible/module_utils/facts/network/generic_bsd.py b/lib/ansible/module_utils/facts/network/generic_bsd.py index 8f4d145f8c1..8d640f2152f 100644 --- a/lib/ansible/module_utils/facts/network/generic_bsd.py +++ b/lib/ansible/module_utils/facts/network/generic_bsd.py @@ -221,24 +221,35 @@ class GenericBsdIfconfigNetwork(Network): address['broadcast'] = words[3] else: + # Don't just assume columns, use "netmask" as the index for the prior column + try: + netmask_idx = words.index('netmask') + 1 + except ValueError: + netmask_idx = 3 + # deal with hex netmask - if re.match('([0-9a-f]){8}', words[3]) and len(words[3]) == 8: - words[3] = '0x' + words[3] - if words[3].startswith('0x'): - address['netmask'] = socket.inet_ntoa(struct.pack('!L', int(words[3], base=16))) + if re.match('([0-9a-f]){8}$', words[netmask_idx]): + netmask = '0x' + words[netmask_idx] + else: + netmask = words[netmask_idx] + + if netmask.startswith('0x'): + address['netmask'] = socket.inet_ntoa(struct.pack('!L', int(netmask, base=16))) else: # otherwise assume this is a dotted quad - address['netmask'] = words[3] + address['netmask'] = netmask # calculate the network address_bin = struct.unpack('!L', socket.inet_aton(address['address']))[0] netmask_bin = struct.unpack('!L', socket.inet_aton(address['netmask']))[0] address['network'] = socket.inet_ntoa(struct.pack('!L', address_bin & netmask_bin)) if 'broadcast' not in address: # broadcast may be given or we need to calculate - if len(words) > 5: - address['broadcast'] = words[5] - else: + try: + broadcast_idx = words.index('broadcast') + 1 + except ValueError: address['broadcast'] = socket.inet_ntoa(struct.pack('!L', address_bin | (~netmask_bin & 0xffffffff))) + else: + address['broadcast'] = words[broadcast_idx] # add to our list of addresses if not words[1].startswith('127.'): diff --git a/test/units/module_utils/facts/network/test_generic_bsd.py b/test/units/module_utils/facts/network/test_generic_bsd.py index afb698c5718..f061f04d8a9 100644 --- a/test/units/module_utils/facts/network/test_generic_bsd.py +++ b/test/units/module_utils/facts/network/test_generic_bsd.py @@ -173,3 +173,45 @@ class TestGenericBsdNetworkNetBSD(unittest.TestCase): 'filter': '*'} mock_module.get_bin_path = Mock(return_value=None) return mock_module + + def test_ensure_correct_netmask_parsing(self): + n = generic_bsd.GenericBsdIfconfigNetwork(None) + lines = [ + 'inet 192.168.7.113 netmask 0xffffff00 broadcast 192.168.7.255', + 'inet 10.109.188.206 --> 10.109.188.206 netmask 0xffffe000', + ] + expected = [ + ( + { + 'ipv4': [ + { + 'address': '192.168.7.113', + 'netmask': '255.255.255.0', + 'network': '192.168.7.0', + 'broadcast': '192.168.7.255' + } + ] + }, + {'all_ipv4_addresses': ['192.168.7.113']}, + ), + ( + { + 'ipv4': [ + { + 'address': '10.109.188.206', + 'netmask': '255.255.224.0', + 'network': '10.109.160.0', + 'broadcast': '10.109.191.255' + } + ] + }, + {'all_ipv4_addresses': ['10.109.188.206']}, + ), + ] + for i, line in enumerate(lines): + words = line.split() + current_if = {'ipv4': []} + ips = {'all_ipv4_addresses': []} + n.parse_inet_line(words, current_if, ips) + self.assertDictEqual(current_if, expected[i][0]) + self.assertDictEqual(ips, expected[i][1])