tests: Verify correct VLAN operation in multi-BSS multi-VLAN case
authorMichael Braun <michael-dev@fami-braun.de>
Mon, 27 Apr 2015 07:08:04 +0000 (09:08 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 14 Jun 2015 10:40:50 +0000 (13:40 +0300)
This adds hwsim test ap_vlan_iface_cleanup_multibss. It connects two
stations in different BSS but the same hostapd process. First both
stations are in VLAN 1, then they get reauthenticated into VLAN 2. Due
to the ordering of the stations moving around, this test checks that
bridge and tagged interface referencing counting is done globally, such
that the tagged interface is not removed too early and no bridge is
left over.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
tests/hwsim/hostapd.py
tests/hwsim/multi-bss-iface.conf [new file with mode: 0644]
tests/hwsim/test_ap_vlan.py
tests/hwsim/utils.py
tests/hwsim/vm/kernel-config

index 83defb0..9d3ca87 100644 (file)
@@ -49,16 +49,17 @@ class HostapdGlobal:
 
 
 class Hostapd:
-    def __init__(self, ifname):
+    def __init__(self, ifname, bssidx=0):
         self.ifname = ifname
         self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
         self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
         self.mon.attach()
         self.bssid = None
+        self.bssidx = bssidx
 
     def own_addr(self):
         if self.bssid is None:
-            self.bssid = self.get_status_field('bssid[0]')
+            self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
         return self.bssid
 
     def request(self, cmd):
diff --git a/tests/hwsim/multi-bss-iface.conf b/tests/hwsim/multi-bss-iface.conf
new file mode 100644 (file)
index 0000000..6b6167f
--- /dev/null
@@ -0,0 +1,40 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
index 5a8af73..822bf99 100644 (file)
@@ -11,8 +11,15 @@ import subprocess
 import logging
 logger = logging.getLogger(__name__)
 
+try:
+    import netifaces
+    netifaces_imported = True
+except ImportError:
+    netifaces_imported = False
+
 import hwsim_utils
 import hostapd
+from utils import iface_is_in_bridge, HwsimSkip
 
 def test_ap_vlan_open(dev, apdev):
     """AP VLAN with open network"""
@@ -195,3 +202,183 @@ def test_ap_vlan_tagged(dev, apdev):
     hwsim_utils.test_connectivity_iface(dev[0], hapd, "brlo.1")
     hwsim_utils.test_connectivity_iface(dev[1], hapd, "brlo.2")
     hwsim_utils.test_connectivity(dev[2], hapd)
+
+def ap_vlan_iface_cleanup_multibss_cleanup():
+    subprocess.call(['ifconfig', 'dummy0', 'down'],
+                    stderr=open('/dev/null', 'w'))
+    ifnames = [ 'wlan3.1', 'wlan3.2', 'wlan3-2.1', 'wlan3-2.2', 'dummy0.2',
+                'dummy0.1', 'dummy0', 'brvlan1', 'brvlan2' ]
+    for ifname in ifnames:
+        subprocess.call(['ip', 'link', 'del', ifname],
+                        stderr=open('/dev/null', 'w'))
+
+def ap_vlan_iface_test_and_prepare_environ():
+    ifaces = netifaces.interfaces()
+    if "dummy0" in ifaces:
+        raise Exception("dummy0 already exists before")
+    ifaces = netifaces.interfaces()
+    if "dummy0.1" in ifaces:
+        raise Exception("dummy0.1 already exists before")
+
+    subprocess.call(['ip', 'link', 'add', 'dummy0', 'type', 'dummy'])
+    subprocess.call(['ifconfig', 'dummy0', 'up'])
+
+    ifaces = netifaces.interfaces()
+    if not("dummy0" in ifaces):
+        raise HwsimSkip("failed to add dummy0 - missing kernel config DUMMY ?")
+
+    subprocess.call(['ip', 'link', 'add', 'link', 'dummy0', 'name', 'dummy0.1',
+                     'type', 'vlan', 'id', '1'])
+
+    ifaces = netifaces.interfaces()
+    if not("dummy0.1" in ifaces):
+        raise HwsimSkip("failed to add dummy0.1 - missing kernel config VLAN_8021Q ?")
+
+    subprocess.call(['ip', 'link', 'del', 'dummy0.1'])
+
+    ifaces = netifaces.interfaces()
+    if "dummy0.1" in ifaces:
+        raise Exception("dummy0.1 was not removed before testing")
+
+def test_ap_vlan_iface_cleanup_multibss(dev, apdev):
+    """AP VLAN operation in multi-BSS multi-VLAN case"""
+
+    # AP VLAN with WPA2-Enterprise and RADIUS attributes changing VLANID
+    # check that multiple bss do not interfere with each other with respect
+    # to deletion of bridge and tagged interface.
+
+    if not netifaces_imported:
+        raise HwsimSkip("python module netifaces not available")
+
+    try:
+        ap_vlan_iface_cleanup_multibss_cleanup()
+        ap_vlan_iface_test_and_prepare_environ()
+
+        as_params = { "ssid": "as",
+                      "beacon_int": "2000",
+                      "radius_server_clients": "auth_serv/radius_clients.conf",
+                      "radius_server_auth_port": '18128',
+                      "eap_server": "1",
+                      "eap_user_file": "auth_serv/eap_user.conf",
+                      "ca_cert": "auth_serv/ca.pem",
+                      "server_cert": "auth_serv/server.pem",
+                      "private_key": "auth_serv/server.key",
+                      "vlan_naming": "1" }
+        authserv = hostapd.add_ap(apdev[1]['ifname'], as_params)
+
+        ifname = apdev[0]['ifname']
+
+        # start the actual test
+        hostapd.add_iface(ifname, 'multi-bss-iface.conf')
+        hapd = hostapd.Hostapd(ifname)
+        hapd1 = hostapd.Hostapd("wlan3-2", 1)
+        hapd1.enable()
+
+        ifaces = netifaces.interfaces()
+        if "brvlan1" in ifaces:
+            raise Exception("bridge brvlan1 already exists before")
+        if "brvlan2" in ifaces:
+            raise Exception("bridge brvlan2 already exists before")
+
+        dev[0].connect("bss-1", key_mgmt="WPA-EAP", eap="PAX",
+                       identity="vlan1",
+                       password_hex="0123456789abcdef0123456789abcdef",
+                       scan_freq="2412")
+
+        ifaces = netifaces.interfaces()
+        if not("brvlan1" in ifaces):
+            raise Exception("bridge brvlan1 was not created")
+
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+        if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+            raise Exception("dummy0.1 not in brvlan1")
+
+        dev[1].connect("bss-2", key_mgmt="WPA-EAP", eap="PAX",
+                       identity="vlan1",
+                       password_hex="0123456789abcdef0123456789abcdef",
+                       scan_freq="2412")
+
+        hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan1")
+        if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+            raise Exception("dummy0.1 not in brvlan1")
+
+        authserv.disable()
+        authserv.set('eap_user_file', "auth_serv/eap_user_vlan.conf")
+        authserv.enable()
+
+        logger.info("wlan0 -> VLAN 2")
+
+        dev[0].dump_monitor()
+        dev[0].request("REAUTHENTICATE")
+        ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("EAP reauthentication timed out")
+        ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=5)
+        if ev is None:
+            raise Exception("4-way handshake after reauthentication timed out")
+        state = dev[0].get_status_field('wpa_state')
+        if state != "COMPLETED":
+            raise Exception("Unexpected state after reauth: " + state)
+
+        ifaces = netifaces.interfaces()
+        if not ("brvlan1" in ifaces):
+            raise Exception("bridge brvlan1 has been removed too early")
+
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2",
+                                            max_tries=5)
+
+        if not iface_is_in_bridge("brvlan2", "dummy0.2"):
+            raise Exception("dummy0.2 not in brvlan2")
+
+        logger.info("test wlan1 == VLAN 1")
+        hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan1")
+        if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+            raise Exception("dummy0.1 not in brvlan1")
+
+        logger.info("wlan1 -> VLAN 2")
+
+        dev[1].dump_monitor()
+        dev[1].request("REAUTHENTICATE")
+        ev = dev[1].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+        if ev is None:
+            raise Exception("EAP reauthentication timed out")
+        ev = dev[1].wait_event(["WPA: Key negotiation completed"], timeout=5)
+        if ev is None:
+            raise Exception("4-way handshake after reauthentication timed out")
+        state = dev[1].get_status_field('wpa_state')
+        if state != "COMPLETED":
+            raise Exception("Unexpected state after reauth: " + state)
+
+        # it can take some time for data connectivity to be updated
+        hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan2",
+                                            max_tries=5)
+        logger.info("test wlan0 == VLAN 2")
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2")
+
+        if not iface_is_in_bridge("brvlan2", "dummy0.2"):
+            raise Exception("dummy0.2 not in brvlan2")
+
+        ifaces = netifaces.interfaces()
+        if "brvlan1" in ifaces:
+            raise Exception("bridge brvlan1 has not been cleaned up")
+
+        # disconnect dev0 first to test a corner case
+        dev[0].request("DISCONNECT")
+        dev[0].wait_disconnected()
+        dev[1].request("DISCONNECT")
+        dev[1].wait_disconnected()
+
+        # station removal needs some time
+        for i in range(5):
+            time.sleep(1)
+            ifaces = netifaces.interfaces()
+            if "brvlan2" not in ifaces:
+                break
+
+        ifaces = netifaces.interfaces()
+        if "brvlan2" in ifaces:
+            raise Exception("bridge brvlan2 has not been cleaned up")
+
+        hapd.request("DISABLE")
+    finally:
+        ap_vlan_iface_cleanup_multibss_cleanup()
index 43dd331..2769e19 100644 (file)
@@ -4,6 +4,8 @@
 # This software may be distributed under the terms of the BSD license.
 # See README for more details.
 
+import os
+
 def get_ifnames():
     ifnames = []
     with open("/proc/net/dev", "r") as f:
@@ -39,3 +41,14 @@ def require_under_vm():
         cmd = f.read()
         if "inside.sh" not in cmd:
             raise HwsimSkip("Not running under VM")
+
+def iface_is_in_bridge(bridge, ifname):
+    fname = "/sys/class/net/"+ifname+"/brport/bridge"
+    if not os.path.exists(fname):
+        return False
+    if not os.path.islink(fname):
+        return False
+    truebridge = os.path.basename(os.readlink(fname))
+    if bridge == truebridge:
+        return True
+    return False
index 487c4fd..08fc7a9 100644 (file)
@@ -552,7 +552,7 @@ CONFIG_STP=y
 CONFIG_BRIDGE=y
 CONFIG_BRIDGE_IGMP_SNOOPING=y
 CONFIG_HAVE_NET_DSA=y
-# CONFIG_VLAN_8021Q is not set
+CONFIG_VLAN_8021Q=y
 # CONFIG_DECNET is not set
 CONFIG_LLC=y
 # CONFIG_LLC2 is not set
@@ -726,7 +726,8 @@ CONFIG_SCSI_MOD=y
 # CONFIG_I2O is not set
 # CONFIG_MACINTOSH_DRIVERS is not set
 CONFIG_NETDEVICES=y
-# CONFIG_NET_CORE is not set
+CONFIG_NET_CORE=y
+CONFIG_DUMMY=y
 # CONFIG_ARCNET is not set
 
 #