diff --git a/autotests/testNetconfig/connection_test.py b/autotests/testNetconfig/connection_test.py index 672efb72..0334267b 100644 --- a/autotests/testNetconfig/connection_test.py +++ b/autotests/testNetconfig/connection_test.py @@ -11,7 +11,7 @@ from iwd import NetworkType from hostapd import HostapdCLI import testutil from config import ctx -import os, time +import os import subprocess class Test(unittest.TestCase): @@ -19,8 +19,9 @@ class Test(unittest.TestCase): def test_connection_success(self): def check_addr(device): try: - subprocess.check_output('ip addr show ' + device.name + \ - ' | grep \'inet6 3ffe:501:ffff:100::\'', shell=True) + # DHCPv6 addresses always have a prefix length of 128 bits, the actual + # subnet's prefix length is in the route. + testutil.test_ip_address_match(device.name, '3ffe:501:ffff:100::1', 128, 112) except: return False @@ -49,6 +50,7 @@ class Test(unittest.TestCase): testutil.test_iface_operstate() testutil.test_ifaces_connected() + testutil.test_ip_address_match(device.name, '192.168.1.10', 17, 24) ctx.non_block_wait(check_addr, 10, device, exception=Exception("IPv6 address was not set")) @@ -78,14 +80,14 @@ class Test(unittest.TestCase): # TODO: This could be moved into test-runner itself if other tests ever # require this functionality (p2p, FILS, etc.). Since its simple # enough it can stay here for now. - ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.255.0', + ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.128.0', 'dev', hapd.ifname,]).wait() ctx.start_process(['touch', '/tmp/dhcpd.leases']).wait() cls.dhcpd_pid = ctx.start_process(['dhcpd', '-f', '-cf', '/tmp/dhcpd.conf', '-lf', '/tmp/dhcpd.leases', hapd.ifname], cleanup=remove_lease4) - ctx.start_process(['ip', 'addr', 'add', '3ffe:501:ffff:100::1/64', + ctx.start_process(['ip', 'addr', 'add', '3ffe:501:ffff:100::1/72', 'dev', hapd.ifname]).wait() ctx.start_process(['touch', '/tmp/dhcpd6.leases']).wait() cls.dhcpd6_pid = ctx.start_process(['dhcpd', '-6', '-f', '-cf', '/tmp/dhcpd-v6.conf', diff --git a/autotests/testNetconfig/dhcpd-v6.conf b/autotests/testNetconfig/dhcpd-v6.conf index 7a22cc65..917ee8a3 100644 --- a/autotests/testNetconfig/dhcpd-v6.conf +++ b/autotests/testNetconfig/dhcpd-v6.conf @@ -1,4 +1,4 @@ -subnet6 3ffe:501:ffff:100::/64 +subnet6 3ffe:501:ffff:100::/72 { option dhcp6.name-servers 3ffe:501:ffff:100::1; range6 3ffe:501:ffff:100::10 3ffe:501:ffff:100::20; diff --git a/autotests/testNetconfig/dhcpd.conf b/autotests/testNetconfig/dhcpd.conf index 0c4fe9b9..ea9957a3 100644 --- a/autotests/testNetconfig/dhcpd.conf +++ b/autotests/testNetconfig/dhcpd.conf @@ -1,14 +1,14 @@ default-lease-time 600; # 10 minutes max-lease-time 7200; # 2 hours -option broadcast-address 192.168.1.255; +option broadcast-address 192.168.127.255; option routers 192.168.1.254; -option subnet-mask 255.255.255.0; +option subnet-mask 255.255.128.0; -subnet 192.168.1.0 netmask 255.255.255.0 +subnet 192.168.0.0 netmask 255.255.128.0 { option routers 192.168.1.1; - option subnet-mask 255.255.255.0; + option subnet-mask 255.255.128.0; option domain-name-servers 192.168.1.1; range 192.168.1.10 192.168.1.20; range 192.168.1.100 192.168.1.200; diff --git a/autotests/testNetconfig/ssidTKIP.psk b/autotests/testNetconfig/ssidTKIP.psk index 64e84f12..eea59cda 100644 --- a/autotests/testNetconfig/ssidTKIP.psk +++ b/autotests/testNetconfig/ssidTKIP.psk @@ -1,5 +1,6 @@ [IPv4] Address=192.168.1.10 +Netmask=255.255.255.128 Gateway=192.168.1.1 [Settings] diff --git a/autotests/testNetconfig/static_test.py b/autotests/testNetconfig/static_test.py index bba2c644..e82dbaeb 100644 --- a/autotests/testNetconfig/static_test.py +++ b/autotests/testNetconfig/static_test.py @@ -45,7 +45,7 @@ class Test(unittest.TestCase): testutil.test_iface_operstate() testutil.test_ifaces_connected() - testutil.test_ip_address_match(dev1.name, '192.168.1.10') + testutil.test_ip_address_match(dev1.name, '192.168.1.10', 25) ordered_network = dev2.get_ordered_network('ssidTKIP') @@ -79,9 +79,9 @@ class Test(unittest.TestCase): hapd = HostapdCLI() # TODO: This could be moved into test-runner itself if other tests ever - # require this functionality (p2p, FILS, etc.). Since its simple + # require this functionality (p2p, FILS, etc.). Since it's simple # enough it can stay here for now. - ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.255.0', + ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.128.0', 'dev', hapd.ifname]).wait() ctx.start_process(['touch', '/tmp/dhcpd.leases']).wait() cls.dhcpd_pid = ctx.start_process(['dhcpd', '-f', '-cf', '/tmp/dhcpd.conf', diff --git a/autotests/util/testutil.py b/autotests/util/testutil.py index 01c42654..99cd58f0 100644 --- a/autotests/util/testutil.py +++ b/autotests/util/testutil.py @@ -4,6 +4,7 @@ import socket import fcntl import struct import select +import codecs import iwd from config import ctx @@ -131,6 +132,7 @@ def test_ifaces_connected(if0=None, if1=None, group=True, expect_fail=False): SIOCGIFFLAGS = 0x8913 SIOCGIFADDR = 0x8915 +SIOCGIFNETMASK = 0x891b IFF_UP = 1 << 0 IFF_RUNNING = 1 << 6 @@ -156,19 +158,68 @@ def test_iface_operstate(intf=None): ctx.non_block_wait(_test_operstate, 10, intf, exception=Exception(intf + ' operstate wrong')) -def test_ip_address_match(intf, ip): - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - addr = fcntl.ioctl(s.fileno(), SIOCGIFADDR, struct.pack('256s', intf.encode('utf-8'))) - addr = socket.inet_ntoa(addr[20:24]) - except OSError as e: - if e.errno != 99 or ip != None: - raise Exception('SIOCGIFADDR failed with %d' % e.errno) +def get_addrs6(ifname): + f = open('/proc/net/if_inet6', 'r') + lines = f.readlines() + f.close() + for line in lines: + addr_str, _, plen, _, _, addr_ifname = line.split() + if ifname is not None and addr_ifname != ifname: + continue - return + yield (codecs.decode(addr_str, 'hex'), int(plen, 16), addr_ifname) - if ip != addr: - raise Exception('IP for %s did not match %s (was %s)' % (intf, ip, addr)) +def test_ip_address_match(intf, expected_addr_str, expected_plen=None, match_plen=None): + def mask_addr(addr, plen): + if plen is None or len(addr) * 8 <= plen: + return addr + bytelen = int(plen / 8) + return addr[0:bytelen] + bytes([addr[bytelen] & (0xff00 >> (plen & 7))]) + b'\0' * (len(addr) - bytelen - 1) + if expected_addr_str is not None: + try: + expected_addr = socket.inet_pton(socket.AF_INET, expected_addr_str) + family = socket.AF_INET + except OSError as e: + try: + expected_addr = socket.inet_pton(socket.AF_INET6, expected_addr_str) + family = socket.AF_INET6 + except OSError as e2: + raise e2 from None + expected_addr = mask_addr(expected_addr, match_plen) + else: + expected_addr = None + family = socket.AF_INET + + if family == socket.AF_INET: + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + out = fcntl.ioctl(s.fileno(), SIOCGIFADDR, struct.pack('256s', intf.encode('utf-8'))) + actual_addr = mask_addr(out[20:24], match_plen) + out = fcntl.ioctl(s.fileno(), SIOCGIFNETMASK, struct.pack('256s', intf.encode('utf-8'))) + actual_plen = sum([sum([(byte >> bit) & 1 for bit in range(0, 8)]) for byte in out[20:24]]) # count bits + except OSError as e: + if e.errno == 99 and expected_addr is None: + return + + raise Exception('SIOCGIFADDR/SIOCGIFNETMASK failed with %d' % e.errno) + else: + # The "get" ioctls don't work for IPv6, netdevice(7) recommends reading /proc/net instead, + # which on the other hand works *only* for IPv6 + actual_addr = None + actual_plen = None + for addr, plen, _ in get_addrs6(intf): + actual_addr = mask_addr(addr, match_plen) + actual_plen = plen + if actual_addr == expected_addr: + break + + if expected_addr != actual_addr: + raise Exception('IP for %s did not match %s (was %s)' % + (intf, expected_addr_str, socket.inet_ntop(family, actual_addr))) + + if expected_plen is not None and expected_plen != actual_plen: + raise Exception('Prefix Length for %s did not match %i (was %i)' % + (intf, expected_plen, actual_plen)) def test_ip_connected(tup0, tup1): ip0, ns0 = tup0