Example #1
0
    def handle_response(self, message):
        """ Handle an incoming RSSI_TRIGGER message.
        Args:
            message, a RSSI_TRIGGER message
        Returns:
            None
        """

        wtp_addr = EtherAddress(message.wtp)

        if wtp_addr not in RUNTIME.wtps:
            return

        wtp = RUNTIME.wtps[wtp_addr]

        if wtp_addr not in RUNTIME.tenants[self.tenant_id].wtps:
            return

        incoming = ResourcePool()
        hwaddr = EtherAddress(message.hwaddr)
        channel = message.channel
        band = message.band
        incoming.add(ResourceBlock(wtp, hwaddr, channel, band))

        matches = wtp.supports & incoming

        self.event = \
            {'block': matches.pop(),
             'timestamp': datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
             'current': message.current}

        self.handle_callback(self)
Example #2
0
    def handle_poller_response(self, message):
        """Handle an incoming poller response message.
        Args:
            message, a poller response message
        Returns:
            None
        """

        if message.poller_id not in self.modules:
            return

        wtp_addr = EtherAddress(message.wtp)

        if wtp_addr not in RUNTIME.wtps:
            return

        wtp = RUNTIME.wtps[wtp_addr]
        hwaddr = EtherAddress(message.hwaddr)
        incoming = ResourcePool()
        incoming.add(ResourceBlock(wtp, hwaddr, message.channel, message.band))

        matching = (wtp.supports & incoming).pop()

        LOG.info("Received %s response from %s (id=%u)",
                 self.MODULE_NAME,
                 matching,
                 message.poller_id)

        # find poller object
        poller = self.modules[message.poller_id]

        # handle the message
        map_type = self.MODULE_NAME
        setattr(poller.block, map_type, CQM())
        map_entry = getattr(poller.block, map_type)

        for entry in message.img_entries:

            addr = EtherAddress(entry[0])

            if not addr.match(poller.addrs):
                continue

            value = {'addr': addr,
                     'last_rssi_std': entry[1] / 1000.0,
                     'last_rssi_avg': entry[2] / 1000.0,
                     'last_packets': entry[3],
                     'hist_packets': entry[4],
                     'ewma_rssi': entry[5] / 1000.0,
                     'sma_rssi': entry[6] / 1000.0}

            map_entry[addr] = value

        # call callback
        handle_callback(poller, poller)
Example #3
0
    def blocks(self, lvap=None, limit=None):
        """Return all blocks in this Tenant."""

        # Initialize the Resource Pool
        pool = ResourcePool()

        # Update the Resource Pool with all
        # the available Resourse Blocks
        for wtp in self.wtps():
            for block in wtp.supports:
                pool.add(block)

        return pool
Example #4
0
    def handle_response(self, response):
        """Handle an incoming poller response message.
        Args:
            message, a poller response message
        Returns:
            None
        """

        wtp_addr = EtherAddress(response.wtp)

        if wtp_addr not in RUNTIME.wtps:
            return

        wtp = RUNTIME.wtps[wtp_addr]

        hwaddr = EtherAddress(response.hwaddr)
        block = ResourceBlock(wtp, hwaddr, response.channel, response.band)
        incoming = ResourcePool()
        incoming.add(block)

        matching = (wtp.supports & incoming).pop()

        if not matching:
            return

        # handle the message
        setattr(self.block, self.MODULE_NAME, CQM())
        map_entry = getattr(self.block, self.MODULE_NAME)

        for entry in response.img_entries:

            addr = EtherAddress(entry[0])

            if not addr.match(self.addrs):
                continue

            value = {'addr': addr,
                     'last_rssi_std': entry[1] / 1000.0,
                     'last_rssi_avg': entry[2] / 1000.0,
                     'last_packets': entry[3],
                     'hist_packets': entry[4],
                     'ewma_rssi': entry[5] / 1000.0,
                     'sma_rssi': entry[6] / 1000.0}

            map_entry[addr] = value

        # call callback
        self.handle_callback(self)
def handover(lvap, wtps):
    """ Handover the LVAP to a WTP with
    an RSSI higher that -65dB. """

    # Initialize the Resource Pool
    pool = ResourcePool()

    # Update the Resource Pool with all
    # the available Resourse Blocks
    for wtp in wtps:
        pool = pool | wtp.supports

    # Select matching Resource Blocks
    matches = pool & lvap.scheduled_on

    # Filter Resource Blocks by RSSI
    valid = [block for block in matches
             if block.ucqm[lvap.addr]['ewma_rssi'] >= -55]

    # Perform the handover
    new_block = valid.pop() if valid else None
    LOG.info("LVAP %s setting new block %s" % (lvap.addr, new_block))
    lvap.scheduled_on = new_block

    # Set port
    for block in lvap.scheduled_on:
        port = lvap.scheduled_on[block]
        port.no_ack = True
        port.tx_power = 20
        port.rts_cts = 3500
        port.mcs = [6, 12]
Example #6
0
    def wtp(self, wtp):
        """Assigns LVAP to new wtp."""

        # Initialize the Resource Pool
        pool = ResourcePool()

        # Update the pool with all the available ResourseBlocks
        for block in wtp.blocks.values():
            pool.append(block)

        # Filter blocks so we are sure to go to the same interface and band
        blocks = pool.filter_by_channel(self.blocks[0].channel) \
                     .filter_by_band(self.blocks[0].band) \
                     .first()

        self.blocks = blocks
    def _on_disconnect(self):
        """ Handle WTP disconnection """

        if not self.wtp:
            return

        LOG.info("WTP disconnected: %s", self.wtp.addr)

        # remove hosted lvaps
        for addr in list(RUNTIME.lvaps.keys()):
            lvap = RUNTIME.lvaps[addr]
            dl_wtps = [block.radio for block in lvap.downlink.keys()]
            ul_wtps = [block.radio for block in lvap.uplink.keys()]
            # in case the downlink went down, the remove also the uplinks
            if self.wtp in dl_wtps:
                RUNTIME.remove_lvap(lvap.addr)
            elif self.wtp in ul_wtps:
                LOG.info("Deleting LVAP (UL): %s", lvap.addr)
                lvap.clear_uplink()

        # remove hosted vaps
        for tenant_id in RUNTIME.tenants.keys():
            for vap_id in list(RUNTIME.tenants[tenant_id].vaps.keys()):
                vap = RUNTIME.tenants[tenant_id].vaps[vap_id]
                if vap.wtp == self.wtp:
                    LOG.info("Deleting VAP: %s", vap.net_bssid)
                    del RUNTIME.tenants[tenant_id].vaps[vap.net_bssid]

        # reset state
        self.wtp.last_seen = 0
        self.wtp.connection = None
        self.wtp.ports = {}
        self.wtp.supports = ResourcePool()
        self.wtp = None
Example #8
0
    def _handle_status_port(cls, status):
        """Handle an incoming PORT message.
        Args:
            status, a STATUS_PORT message
        Returns:
            None
        """

        wtp_addr = EtherAddress(status.wtp)

        try:
            wtp = RUNTIME.wtps[wtp_addr]
        except KeyError:
            LOG.info("Status from unknown WTP %s", wtp_addr)
            return

        if not wtp.connection:
            LOG.info("Status from disconnected WTP %s", wtp_addr)
            return

        sta_addr = EtherAddress(status.sta)
        hwaddr = EtherAddress(status.hwaddr)
        block = ResourceBlock(wtp, hwaddr, status.channel, status.band)

        # incoming block
        pool = ResourcePool()
        pool.add(block)

        match = wtp.supports & pool

        if not match:
            LOG.error("Incoming block %s is invalid", block)
            return

        block = match.pop()

        LOG.info("Port status from %s, station %s", wtp_addr, sta_addr)

        tx_policy = block.tx_policies[sta_addr]

        tx_policy._mcs = set([float(x) / 2 for x in status.mcs])
        tx_policy._rts_cts = int(status.rts_cts)
        tx_policy._mcast = int(status.tx_mcast)
        tx_policy._ur_count = int(status.ur_mcast_count)
        tx_policy._no_ack = bool(status.flags.no_ack)

        LOG.info("Port status %s", tx_policy)
    def _handle_status_port(cls, status):
        """Handle an incoming PORT message.
        Args:
            status, a STATUS_PORT message
        Returns:
            None
        """

        wtp_addr = EtherAddress(status.wtp)

        try:
            wtp = RUNTIME.wtps[wtp_addr]
        except KeyError:
            LOG.info("Status from unknown WTP %s", wtp_addr)
            return

        if not wtp.connection:
            LOG.info("Status from disconnected WTP %s", wtp_addr)
            return

        sta_addr = EtherAddress(status.sta)
        hwaddr = EtherAddress(status.hwaddr)
        block = ResourceBlock(wtp, hwaddr, status.channel, status.band)

        # incoming block
        pool = ResourcePool()
        pool.add(block)

        match = wtp.supports & pool

        if not match:
            LOG.error("Incoming block %s is invalid", block)
            return

        block = match.pop()

        LOG.info("Port status from %s, station %s", wtp_addr, sta_addr)

        tx_policy = block.tx_policies[sta_addr]

        tx_policy._mcs = set([float(x)/2 for x in status.mcs])
        tx_policy._rts_cts = int(status.rts_cts)
        tx_policy._mcast = int(status.tx_mcast)
        tx_policy._ur_count = int(status.ur_mcast_count)
        tx_policy._no_ack = bool(status.flags.no_ack)

        LOG.info("Port status %s", tx_policy)
Example #10
0
    def __init__(self, lvap, block):

        self._lvap = lvap
        self._block = block
        self._no_ack = False
        self._rts_cts = 2346

        match = (lvap.supports & ResourcePool([block])).pop()
        self._mcs = block.supports & match.supports
    def put(self, *args, **kwargs):
        """Modify the LVAP

        Args:

            [0], the project id (mandatory)
            [1]: the lvap address (mandatory)

        Example URLs:

            PUT /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/lvaps/
                60:F4:45:D0:3B:FC

            {
                "version": "1.0",
                "wtp": "04:F0:21:09:F9:AA"
            }
        """

        project_id = uuid.UUID(args[0])
        project = self.service.projects[project_id]

        lvap = project.lvaps[EtherAddress(args[0])]

        if "blocks" in kwargs:

            addr = EtherAddress(kwargs['wtp'])
            wtp = self.service.devices[addr]
            pool = ResourcePool()

            for block_id in kwargs["blocks"]:
                pool.append(wtp.blocks[block_id])

            lvap.blocks = pool

        elif "wtp" in kwargs:

            wtp = self.service.devices[EtherAddress(kwargs['wtp'])]
            lvap.wtp = wtp

        if "encap" in kwargs:

            encap = EtherAddress(kwargs["encap"])
            lvap.encap = encap
Example #12
0
    def block(self, value):

        if not isinstance(value, ResourceBlock):
            raise ValueError("Expected ResourceBlock, got %s", type(value))

        wtp = RUNTIME.wtps[value.radio.addr]

        requested = ResourcePool()
        requested.add(value)

        match = wtp.supports & requested

        if len(match) > 1:
            raise ValueError("More than one block specified")

        if not match:
            raise ValueError("No block specified")

        self._block = match.pop()
Example #13
0
    def block(self, value):

        if not isinstance(value, ResourceBlock):
            raise ValueError("Expected ResourceBlock, got %s", type(value))

        wtp = RUNTIME.wtps[value.radio.addr]

        requested = ResourcePool()
        requested.add(value)

        match = wtp.supports & requested

        if len(match) > 1:
            raise ValueError("More than one block specified")

        if not match:
            raise ValueError("No block specified")

        self._block = match.pop()
Example #14
0
    def uplink(self, blocks):
        """Set downlink block.

        Set the downlink block. Must receive as input a single resource block.
        """

        if isinstance(blocks, ResourcePool):
            pool = blocks
        elif isinstance(blocks, ResourceBlock):
            pool = ResourcePool()
            pool.add(blocks)
        else:
            raise TypeError("Invalid type: %s", type(blocks))

        if len(pool) > 5:
            raise ValueError("Downlink, too many blocks (%u)", len(blocks))

        current = ResourcePool(list(self._uplink.keys()))

        # Null operation, just return
        if current == pool:
            return

        # make sure we are not overwriting a downlink block
        downlink = ResourcePool(list(self._downlink.keys()))
        pool = pool - downlink

        # clear uplink blocks
        for block in list(self._uplink.keys()):
            del self._uplink[block]

        # assign uplink blocks
        for block in pool:
            self._uplink[block] = RadioPort(self, block)
    def put(self, *args, **kwargs):
        """Modify the LVAP

        Args:

            [0]: the lvap address (mandatory)

        Example URLs:

            PUT /api/v1/lvaps/60:F4:45:D0:3B:FC

            {
                "version": "1.0",
                "wtp": "04:F0:21:09:F9:AA"
            }
        """

        lvap = self.service.lvaps[EtherAddress(args[0])]

        if "blocks" in kwargs:

            addr = EtherAddress(kwargs['wtp'])
            wtp = self.service.devices[addr]
            pool = ResourcePool()

            for block_id in kwargs["blocks"]:
                pool.append(wtp.blocks[block_id])

            lvap.blocks = pool

        elif "wtp" in kwargs:

            wtp = self.service.devices[EtherAddress(kwargs['wtp'])]
            lvap.wtp = wtp

        if "encap" in kwargs:

            encap = EtherAddress(kwargs["encap"])
            lvap.encap = encap
Example #16
0
    def __init__(self, addr, net_bssid_addr, lvap_bssid_addr):

        # read only params
        self.addr = addr
        self.net_bssid = net_bssid_addr

        # lvap bssid, this is the bssid to which the client is currently
        # attached, it can only be updated as a result of an auth request
        # message
        self._lvap_bssid = lvap_bssid_addr

        # the following parameters are only updated upon RX of a lvap status
        # update message from an agent
        self.authentication_state = False
        self.association_state = False

        # the following parameters are only updated by the controller, which
        # will then dispatch an add lvap message in order to propagate the
        # change to the agent
        self._ssids = []
        self._encap = None
        self._group = 6000

        # the following parameters can be updated by both agent and
        # controller. The controller sets them when a client successfully
        # associate. The agent sets them upon disassociation.
        self._assoc_id = 0
        self._tenant = None

        # only one block supported, default block points to this
        self._downlink = DownlinkPort()

        # multiple blocks supported, no port configuration supported
        self._uplink = UplinkPort()

        # counters
        self.tx_samples = []
        self.rx_samples = []

        # rates statistics
        self.rates = {}

        # virtual ports (VNFs)
        self.__ports = {}

        # downlink intent uuid
        self.poa_uuid = None

        # supported resource blocks
        self.supported = ResourcePool()
Example #17
0
    def block(self, value):
        """Set block."""

        if isinstance(value, ResourceBlock):

            self._block = value

        elif isinstance(value, dict):

            wtp = RUNTIME.wtps[EtherAddress(value['wtp'])]

            if 'hwaddr' not in value:
                raise ValueError("Missing field: hwaddr")

            if 'channel' not in value:
                raise ValueError("Missing field: channel")

            if 'band' not in value:
                raise ValueError("Missing field: band")

            if 'wtp' not in value:
                raise ValueError("Missing field: wtp")

            incoming = ResourcePool()
            block = ResourceBlock(wtp, EtherAddress(value['hwaddr']),
                                  int(value['channel']), int(value['band']))
            incoming.add(block)

            match = wtp.supports & incoming

            if not match:
                raise ValueError("No block specified")

            if len(match) > 1:
                raise ValueError("More than one block specified")

            self._block = match.pop()
Example #18
0
    def block(self, value):
        """Set block."""

        if isinstance(value, ResourceBlock):

            self._block = value

        elif isinstance(value, dict):

            if 'hwaddr' not in value:
                raise ValueError("Missing field: hwaddr")

            if 'channel' not in value:
                raise ValueError("Missing field: channel")

            if 'band' not in value:
                raise ValueError("Missing field: band")

            if 'wtp' not in value:
                raise ValueError("Missing field: wtp")

            wtp = RUNTIME.wtps[EtherAddress(value['wtp'])]

            incoming = ResourcePool()
            block = ResourceBlock(wtp, EtherAddress(value['hwaddr']),
                                  int(value['channel']), int(value['band']))
            incoming.add(block)

            match = wtp.supports & incoming

            if not match:
                raise ValueError("No block specified")

            if len(match) > 1:
                raise ValueError("More than one block specified")

            self._block = match.pop()
Example #19
0
    def handover(self, lvap):
        """ Handover the LVAP to a WTP with
        an RSSI higher that -65dB. """

        self.log.info("Running handover...")

        # Initialize the Resource Pool
        pool = ResourcePool()

        # Update the Resource Pool with all
        # the available Resourse Blocks
        for wtp in self.wtps():
            pool = pool | wtp.supports

        # Select matching Resource Blocks
        matches = pool & lvap.scheduled_on

        # Filter Resource Blocks by RSSI
        valid = [
            block for block in matches
            if block.ucqm[lvap.addr]['ewma_rssi'] >= -85
        ]

        if not valid:
            return

        new_block = max(valid, key=lambda x: x.ucqm[lvap.addr]['ewma_rssi'])
        self.log.info("LVAP %s setting new block %s" % (lvap.addr, new_block))

        lvap.scheduled_on = new_block

        # Set port
        for block in lvap.scheduled_on:
            port = lvap.scheduled_on[block]
            port.no_ack = True
            port.rts_cts = 3500
            port.mcs = [6, 12, 54]
Example #20
0
    def _on_disconnect(self):
        """ Handle WTP disconnection """

        if not self.wtp:
            return

        LOG.info("WTP disconnected: %s", self.wtp.addr)

        # reset state
        self.wtp.last_seen = 0
        self.wtp.connection = None
        self.wtp.ports = {}
        self.wtp.supports = ResourcePool()

        # remove hosted LVAPs
        to_be_removed = []
        for lvap in RUNTIME.lvaps.values():
            if lvap.wtp == self.wtp:
                to_be_removed.append(lvap)

        for lvap in to_be_removed:
            LOG.info("Deleting LVAP: %s", lvap.addr)
            for handler in self.server.pt_types_handlers[PT_LVAP_LEAVE]:
                handler(lvap)
            lvap.clear_ports()
            del RUNTIME.lvaps[lvap.addr]

        # remove hosted vaps
        to_be_removed = []
        for tenant in RUNTIME.tenants.values():
            for vap in tenant.vaps.values():
                if vap.wtp == self.wtp:
                    to_be_removed.append(vap)

        for vap in to_be_removed:
            LOG.info("Deleting VAP: %s", vap.net_bssid)
            del RUNTIME.tenants[vap.tenant_id].vaps[vap.net_bssid]
    def _handle_probe_request(self, wtp, request):
        """Handle an incoming PROBE_REQUEST message.
        Args:
            request, a PROBE_REQUEST message
        Returns:
            None
        """

        if not wtp.connection:
            LOG.info("Probe request from disconnected WTP %s", wtp.addr)
            self.stream.close()
            return

        if not wtp.port():
            LOG.info("WTP %s not ready", wtp.addr)
            return

        sta = EtherAddress(request.sta)

        if sta in RUNTIME.lvaps:
            return

        if not RUNTIME.is_allowed(sta):
            return

        if RUNTIME.is_denied(sta):
            return

        ssid = SSID(request.ssid)

        if request.ssid == b'':
            LOG.info("Probe request from %s ssid %s", sta, "Broadcast")
        else:
            LOG.info("Probe request from %s ssid %s", sta, ssid)

        # generate list of available SSIDs
        ssids = set()

        for tenant in RUNTIME.tenants.values():
            if tenant.bssid_type == T_TYPE_SHARED:
                continue
            for wtp_in_tenant in tenant.wtps.values():
                if wtp.addr == wtp_in_tenant.addr:
                    ssids.add(tenant.tenant_name)

        if not ssids:
            LOG.info("No SSIDs available at this WTP")
            return

        # spawn new LVAP
        LOG.info("Spawning new LVAP %s on %s", sta, wtp.addr)
        net_bssid = generate_bssid(BASE_MAC, sta)
        lvap = LVAP(sta, net_bssid, net_bssid)
        lvap.set_ssids(list(ssids))

        RUNTIME.lvaps[sta] = lvap

        # This will trigger an LVAP ADD message (and REMOVE if necessary)
        lvap.supported = ResourcePool()
        hwaddr = EtherAddress(request.hwaddr)
        channel = request.channel
        band = request.band
        lvap.supported.add(ResourceBlock(lvap, hwaddr, channel, band))

        lvap.scheduled_on = wtp.supports & lvap.supported

        LOG.info("Sending probe response to %s", lvap.addr)
        self.send_probe_response(lvap)
Example #22
0
    def _handle_probe_request(self, request):
        """Handle an incoming PROBE_REQUEST message.
        Args:
            request, a PROBE_REQUEST message
        Returns:
            None
        """

        wtp_addr = EtherAddress(request.wtp)

        try:
            wtp = RUNTIME.wtps[wtp_addr]
        except KeyError:
            LOG.info("Probe request from unknown WTP (%s)", wtp_addr)
            return

        if not wtp.connection:
            LOG.info("Probe request from disconnected WTP %s", wtp_addr)
            return

        sta = EtherAddress(request.sta)

        if sta in RUNTIME.lvaps:
            return

        if not RUNTIME.is_allowed(sta):
            return

        if RUNTIME.is_denied(sta):
            return

        ssid = SSID(request.ssid)

        if request.ssid == b'':
            LOG.info("Probe request from %s ssid %s", sta, "Broadcast")
        else:
            LOG.info("Probe request from %s ssid %s", sta, ssid)

        # generate list of available SSIDs
        ssids = set()

        for tenant in RUNTIME.tenants.values():
            if tenant.bssid_type == T_TYPE_SHARED:
                continue
            for wtp_in_tenant in tenant.wtps.values():
                if wtp_addr == wtp_in_tenant.addr:
                    ssids.add(tenant.tenant_name)

        if not ssids:
            LOG.info("No SSIDs available at this WTP")
            return

        # spawn new LVAP
        LOG.info("Spawning new LVAP %s on %s", sta, wtp.addr)
        net_bssid = generate_bssid(BASE_MAC, sta)
        lvap = LVAP(sta, net_bssid, net_bssid)
        lvap.set_ssids(ssids)

        RUNTIME.lvaps[sta] = lvap

        # TODO: This should be built starting from the probe request
        lvap.supports.add(ResourceBlock(lvap, sta, 1, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 2, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 3, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 4, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 5, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 6, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 7, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 8, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 9, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 10, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 11, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 36, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 48, BT_L20))

        # This will trigger an LVAP ADD message (and REMOVE if necessary)
        requested = ResourcePool()
        hwaddr = EtherAddress(request.hwaddr)
        channel = request.channel
        band = request.band
        requested.add(ResourceBlock(wtp, hwaddr, channel, band))

        lvap.scheduled_on = wtp.supports & requested

        LOG.info("Sending probe response to %s", lvap.addr)
        self.send_probe_response(lvap)
Example #23
0
    def wtp(self, wtp):
        """Assigns LVAP to new wtp."""

        default_block = next(iter(self.scheduled_on.keys()))
        matching = wtp.supports & ResourcePool([default_block])
        self.scheduled_on = matching.pop() if matching else None
Example #24
0
    def put(self, *args, **kwargs):
        """ Set the WTP for a given LVAP, effectivelly hands-over the LVAP to
        another WTP

        Args:
            pool_id: the pool address
            lvap_id: the lvap address

        Request:
            version: the protocol version (1.0)
            wtp: the new wtp address

        Example URLs:
            PUT /api/v1/pools/EmPOWER/lvaps/11:22:33:44:55:66
        """

        try:

            if len(args) != 2:
                raise ValueError("Invalid URL")

            request = tornado.escape.json_decode(self.request.body)

            if "version" not in request:
                raise ValueError("missing version element")

            if "wtp" not in request and "scheduled_on" not in request and \
               "encap" not in request:

                raise ValueError("missing wtp/scheduled_on element")

            tenant_id = uuid.UUID(args[0])
            lvap_addr = EtherAddress(args[1])

            tenant = RUNTIME.tenants[tenant_id]
            lvap = tenant.lvaps[lvap_addr]

            if "wtp" in request:

                wtp_addr = EtherAddress(request['wtp'])
                wtp = tenant.wtps[wtp_addr]
                lvap.wtp = wtp

            if "scheduled_on" in request:

                pool = ResourcePool()

                for block in request["scheduled_on"]:

                    wtp_addr = EtherAddress(block['wtp'])
                    wtp = RUNTIME.wtps[wtp_addr]
                    channel = int(block['channel'])
                    band = int(block['band'])

                    rb = ResourceBlock(wtp, channel, band)
                    pool.add(rb)

                lvap.scheduled_on = pool

            if "encap" in request:

                encap = EtherAddress(request["encap"])
                lvap.encap = encap

        except KeyError as ex:
            self.send_error(404, message=ex)
        except ValueError as ex:
            self.send_error(400, message=ex)

        self.set_status(204, None)
Example #25
0
    def downlink(self, downlink):
        """Assign default resource block to LVAP.

        Assign default resource block to LVAP. Accepts as input either a
        ResourcePool or a ResourceBlock. If the resource pool has more than
        one resource block then one random resource block is assigned as both
        downlink and default uplink. The remaining resource blocks are
        assigned as uplink only.

        Args:
            downlink: A ResourcePool or a ResourceBlock
        """

        # Null operation, just return
        if not downlink:
            return

        if isinstance(downlink, ResourcePool):

            pool = downlink

        elif isinstance(downlink, ResourceBlock):

            pool = ResourcePool()
            pool.add(downlink)

        else:

            raise TypeError("Expected ResourcePool, ResourceBlock, got %s",
                            type(downlink))

        current = ResourcePool(list(self.downlink.keys()) +
                               list(self.uplink.keys()))

        # Null operation, just return, but before re-send configuration
        # commands (ports and of tables)
        if current == pool:
            self.set_ports()
            return

        # clear downlink blocks
        for block in list(self.downlink.keys()):
            del self.downlink[block]

        # clear uplink blocks
        for block in list(self.uplink.keys()):
            del self.uplink[block]

        # pick default resource block
        default_block = pool.pop()

        # assign default port policy to downlink resource block, this will
        # trigger a send_add_lvap and a set_port (radio) message
        self.downlink[default_block] = RadioPort(self, default_block)

        # assign remaining blocks (if any) to the uplink, this could
        # trigger one or more send_add_lvap and a set_port (radio) messages
        for block in pool:
            self.uplink[block] = RadioPort(self, block)

        # set ports
        self.set_ports()
Example #26
0
    def scheduled_on(self, downlink):
        """Assign default resource block to LVAP.

        Assign default resource block to LVAP. Accepts as input either a
        ResourcePool or a ResourceBlock. If the resource pool has more than
        one resource block then one random resource block is assigned as both
        downlink and default uplink. The remaining resource blocks are
        assigned as uplink only.

        Args:
            downlink: A ResourcePool or a ResourceBlock
        """

        # Null operation, just return
        if not downlink:
            return

        if isinstance(downlink, ResourcePool):

            pool = downlink

        elif isinstance(downlink, ResourceBlock):

            pool = ResourcePool()
            pool.add(downlink)

        else:

            raise TypeError("Expected ResourcePool, ResourceBlock, got %s",
                            type(downlink))

        current = ResourcePool(
            list(self.downlink.keys()) + list(self.uplink.keys()))

        # Null operation, just return, but before re-send configuration
        # commands (ports and of tables)
        if current == pool:
            self.set_ports()
            return

        # clear downlink blocks
        for block in list(self.downlink.keys()):
            del self.downlink[block]

        # clear uplink blocks
        for block in list(self.uplink.keys()):
            del self.uplink[block]

        # pick default resource block
        default_block = pool.pop()

        # If lvap is associated to a shared tenant. I need to reset the lvap
        # before moving it.
        if self._tenant and self._tenant.bssid_type == T_TYPE_SHARED:

            # check if tenant is available at target block
            base_bssid = self._tenant.get_prefix()
            net_bssid = generate_bssid(base_bssid, default_block.hwaddr)

            # if not ignore request
            if net_bssid not in self._tenant.vaps:
                LOG.error("VAP %s not found on tenant %s", net_bssid,
                          self._tenant.tenant_name)
                self.set_ports()
                return

            # check if vap is available at target block
            if net_bssid != self._tenant.vaps[net_bssid].net_bssid:
                LOG.error("VAP %s not available at target block %s", net_bssid,
                          default_block)
                self.set_ports()
                return

            # otherwise reset lvap
            self._tenant = None
            self.association_state = False
            self.authentication_state = False
            self._assoc_id = 0
            self._lvap_bssid = net_bssid

        else:

            self._lvap_bssid = self.net_bssid

        # assign default port policy to downlink resource block, this will
        # trigger a send_add_lvap and a set_port (radio) message
        self.downlink[default_block] = RadioPort(self, default_block)

        # assign remaining blocks (if any) to the uplink, this could
        # trigger one or more send_add_lvap and a set_port (radio) messages
        for block in pool:
            self.uplink[block] = RadioPort(self, block)

        # set ports
        self.set_ports()
    def _handle_probe_request(self, request):
        """Handle an incoming PROBE_REQUEST message.
        Args:
            request, a PROBE_REQUEST message
        Returns:
            None
        """

        wtp_addr = EtherAddress(request.wtp)

        try:
            wtp = RUNTIME.wtps[wtp_addr]
        except KeyError:
            LOG.info("Probe request from unknown WTP (%s)", wtp_addr)
            return

        if not wtp.connection:
            LOG.info("Probe request from disconnected WTP %s", wtp_addr)
            return

        sta = EtherAddress(request.sta)

        if sta in RUNTIME.lvaps:
            return

        if not RUNTIME.is_allowed(sta):
            return

        if RUNTIME.is_denied(sta):
            return

        ssid = SSID(request.ssid)

        if request.ssid == b'':
            LOG.info("Probe request from %s ssid %s", sta, "Broadcast")
        else:
            LOG.info("Probe request from %s ssid %s", sta, ssid)

        # generate list of available SSIDs
        ssids = set()

        for tenant in RUNTIME.tenants.values():
            if tenant.bssid_type == T_TYPE_SHARED:
                continue
            for wtp_in_tenant in tenant.wtps.values():
                if wtp_addr == wtp_in_tenant.addr:
                    ssids.add(tenant.tenant_name)

        if not ssids:
            LOG.info("No SSIDs available at this WTP")
            return

        # spawn new LVAP
        LOG.info("Spawning new LVAP %s on %s", sta, wtp.addr)
        net_bssid = generate_bssid(BASE_MAC, sta)
        lvap = LVAP(sta, net_bssid, net_bssid)
        lvap._ssids = ssids

        RUNTIME.lvaps[sta] = lvap

        # TODO: This should be built starting from the probe request
        lvap.supports.add(ResourceBlock(lvap, sta, 1, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 2, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 3, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 4, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 5, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 6, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 7, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 8, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 9, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 10, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 11, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 36, BT_L20))
        lvap.supports.add(ResourceBlock(lvap, sta, 48, BT_L20))

        # This will trigger an LVAP ADD message (and REMOVE if necessary)
        requested = ResourcePool()
        hwaddr = EtherAddress(request.hwaddr)
        channel = request.channel
        band = request.band
        requested.add(ResourceBlock(wtp, hwaddr, channel, band))

        lvap.scheduled_on = wtp.supports & requested

        LOG.info("Sending probe response to %s", lvap.addr)
        self.send_probe_response(lvap)
    def _handle_status_lvap(self, wtp, status):
        """Handle an incoming STATUS_LVAP message.
        Args:
            status, a STATUS_LVAP message
        Returns:
            None
        """

        if not wtp.connection:
            LOG.info("Status from disconnected WTP %s", wtp.addr)
            return

        sta_addr = EtherAddress(status.sta)
        set_mask = bool(status.flags.set_mask)

        lvap = None

        LOG.info("LVAP status update from %s", sta_addr)

        # If the LVAP does not exists, then create a new one
        if sta_addr not in RUNTIME.lvaps:

            net_bssid_addr = EtherAddress(status.net_bssid)
            lvap_bssid_addr = EtherAddress(status.lvap_bssid)
            lvap = LVAP(sta_addr, net_bssid_addr, lvap_bssid_addr)

            RUNTIME.lvaps[sta_addr] = lvap

        lvap = RUNTIME.lvaps[sta_addr]

        # incoming block
        lvap.supported = ResourcePool()
        hwaddr = EtherAddress(status.hwaddr)
        channel = status.channel
        band = status.band
        lvap.supported.add(ResourceBlock(lvap, hwaddr, channel, band))

        match = wtp.supports & lvap.supported

        if not match:
            LOG.error("Incoming block %s is invalid", block)
            return

        block = match.pop()

        # this will try to updated the lvap object with the resource block
        # coming in this status update message.
        try:

            if set_mask:

                # set downlink+uplink block
                lvap._downlink.setitem(block, RadioPort(lvap, block))

            else:

                # set uplink only blocks
                lvap._uplink.setitem(block, RadioPort(lvap, block))

        except ValueError:
            LOG.error("Error while importing block %s, removing.", block)
            block.radio.connection.send_del_lvap(lvap)
            return

        lvap.authentication_state = bool(status.flags.authenticated)
        lvap.association_state = bool(status.flags.associated)

        lvap._assoc_id = status.assoc_id
        lvap._encap = EtherAddress(status.encap)
        ssids = [SSID(x.ssid) for x in status.ssids]

        if lvap.ssid:

            # Raise LVAP leave event
            self.server.send_lvap_leave_message_to_self(lvap)

            # removing LVAP from tenant, need first to look for right tenant
            if lvap.addr in lvap.tenant.lvaps:
                LOG.info("Removing %s from tenant %s", lvap.addr, lvap.ssid)
                del lvap.tenant.lvaps[lvap.addr]

        lvap._tenant = None

        if ssids[0]:

            tenant = RUNTIME.load_tenant(ssids[0])

            if not tenant:
                LOG.info("LVAP %s from unknown tenant %s", lvap.addr, ssids[0])
                RUNTIME.remove_lvap(lvap.addr)
                return

            # setting tenant without seding out add lvap message
            lvap._tenant = tenant

            # adding LVAP to tenant
            LOG.info("Adding %s to tenant %s", lvap.addr, ssids[0])
            lvap.tenant.lvaps[lvap.addr] = lvap

            # Raise LVAP join event
            self.server.send_lvap_join_message_to_self(lvap)

        # update remaining ssids
        lvap._ssids = ssids[1:]

        # set ports
        lvap.set_ports()

        LOG.info("LVAP status %s", lvap)
Example #29
0
 def __init__(self, addr, label):
     super().__init__(addr, label)
     self.supports = ResourcePool()
Example #30
0
    def downlink(self, blocks):
        """Set downlink block.

        Set the downlink block. Must receive as input a single resource block.
        """

        if isinstance(blocks, ResourcePool):
            pool = blocks
        elif isinstance(blocks, ResourceBlock):
            pool = ResourcePool()
            pool.add(blocks)
        else:
            raise TypeError("Invalid type: %s", type(blocks))

        if len(pool) > 1:
            raise ValueError("Downlink, too many blocks (%u)", len(blocks))

        current = ResourcePool(list(self._downlink.keys()))

        # Null operation, just return, but before re-send intent configuration
        if current == pool:
            self.set_ports()
            return

        # clear downlink blocks
        for block in list(self._downlink.keys()):
            del self._downlink[block]

        # downlink block
        default_block = pool.pop()

        # check if block is also in the uplink, if so remove it
        if default_block in self._uplink:
            del self._uplink[default_block]

        # If lvap is associated to a shared tenant. I need to reset the lvap
        # before moving it.
        if self._tenant and self._tenant.bssid_type == T_TYPE_SHARED:

            # check if tenant is available at target block
            base_bssid = self._tenant.get_prefix()
            net_bssid = generate_bssid(base_bssid, default_block.hwaddr)

            # if not ignore request
            if net_bssid not in self._tenant.vaps:
                LOG.error("VAP %s not found on tenant %s", net_bssid,
                          self._tenant.tenant_name)
                self.set_ports()
                return

            # check if vap is available at target block
            if net_bssid != self._tenant.vaps[net_bssid].net_bssid:
                LOG.error("VAP %s not available at target block %s", net_bssid,
                          default_block)
                self.set_ports()
                return

            # otherwise reset lvap
            self._tenant = None
            self.association_state = False
            self.authentication_state = False
            self._assoc_id = 0
            self._lvap_bssid = net_bssid

        # assign default port policy to downlink resource block, this will
        # trigger a send_add_lvap and a set_port (radio) message
        self._downlink[default_block] = RadioPort(self, default_block)

        # set ports
        self.set_ports()
    def put(self, *args, **kwargs):
        """ Set the WTP for a given LVAP, effectivelly hands-over the LVAP to
        another WTP

        Args:
            lvap_id: the lvap address

        Request:
            version: the protocol version (1.0)

        Example URLs:
            PUT /api/v1/lvaps/11:22:33:44:55:66
        """

        try:

            if len(args) != 1:
                raise ValueError("Invalid URL")

            request = tornado.escape.json_decode(self.request.body)

            if "version" not in request:
                raise ValueError("missing version element")

            lvap_addr = EtherAddress(args[0])

            lvap = RUNTIME.lvaps[lvap_addr]

            if "wtp" in request:

                wtp_addr = EtherAddress(request['wtp'])
                wtp = RUNTIME.wtps[wtp_addr]
                lvap.wtp = wtp

            elif "scheduled_on" in request:

                pool = ResourcePool()

                for block in request["scheduled_on"]:

                    wtp_addr = EtherAddress(block['wtp'])
                    wtp = RUNTIME.wtps[wtp_addr]
                    hwaddr = EtherAddress(block['hwaddr'])
                    channel = int(block['channel'])
                    band = int(block['band'])

                    r_block = ResourceBlock(wtp, hwaddr, channel, band)
                    pool.add(r_block)

                lvap.scheduled_on = pool

            elif "downlink" in request:

                pool = ResourcePool()

                for block in request["downlink"]:

                    wtp_addr = EtherAddress(block['wtp'])
                    wtp = RUNTIME.wtps[wtp_addr]
                    hwaddr = EtherAddress(block['hwaddr'])
                    channel = int(block['channel'])
                    band = int(block['band'])

                    r_block = ResourceBlock(wtp, hwaddr, channel, band)
                    pool.add(r_block)

                lvap.downlink = pool

            elif "uplink" in request:

                pool = ResourcePool()

                for block in request["uplink"]:

                    wtp_addr = EtherAddress(block['wtp'])
                    wtp = RUNTIME.wtps[wtp_addr]
                    hwaddr = EtherAddress(block['hwaddr'])
                    channel = int(block['channel'])
                    band = int(block['band'])

                    r_block = ResourceBlock(wtp, hwaddr, channel, band)
                    pool.add(r_block)

                lvap.uplink = pool

            if "encap" in request:

                encap = EtherAddress(request["encap"])
                lvap.encap = encap

        except KeyError as ex:
            self.send_error(404, message=ex)
        except ValueError as ex:
            self.send_error(400, message=ex)

        self.set_status(204, None)
Example #32
0
    def scheduled_on(self):
        """ Get the resource blocks assigned to this LVAP in the uplink. """

        return ResourcePool(
            list(self._downlink.keys()) + list(self._uplink.keys()))
Example #33
0
    def put(self, *args, **kwargs):
        """ Set the WTP for a given LVAP, effectivelly hands-over the LVAP to
        another WTP

        Args:
            lvap_id: the lvap address

        Request:
            version: the protocol version (1.0)

        Example URLs:
            PUT /api/v1/lvaps/11:22:33:44:55:66
        """

        try:

            if len(args) != 1:
                raise ValueError("Invalid URL")

            request = tornado.escape.json_decode(self.request.body)

            if "version" not in request:
                raise ValueError("missing version element")

            lvap_addr = EtherAddress(args[0])

            lvap = RUNTIME.lvaps[lvap_addr]

            if "wtp" in request:

                wtp_addr = EtherAddress(request['wtp'])
                wtp = RUNTIME.wtps[wtp_addr]
                lvap.wtp = wtp

            elif "scheduled_on" in request:

                pool = ResourcePool()

                for block in request["scheduled_on"]:

                    wtp_addr = EtherAddress(block['wtp'])
                    wtp = RUNTIME.wtps[wtp_addr]
                    hwaddr = EtherAddress(block['hwaddr'])
                    channel = int(block['channel'])
                    band = int(block['band'])

                    r_block = ResourceBlock(wtp, hwaddr, channel, band)
                    pool.add(r_block)

                lvap.scheduled_on = pool

            elif "downlink" in request:

                pool = ResourcePool()

                for block in request["downlink"]:

                    wtp_addr = EtherAddress(block['wtp'])
                    wtp = RUNTIME.wtps[wtp_addr]
                    hwaddr = EtherAddress(block['hwaddr'])
                    channel = int(block['channel'])
                    band = int(block['band'])

                    r_block = ResourceBlock(wtp, hwaddr, channel, band)
                    pool.add(r_block)

                lvap.downlink = pool

            elif "uplink" in request:

                pool = ResourcePool()

                for block in request["uplink"]:

                    wtp_addr = EtherAddress(block['wtp'])
                    wtp = RUNTIME.wtps[wtp_addr]
                    hwaddr = EtherAddress(block['hwaddr'])
                    channel = int(block['channel'])
                    band = int(block['band'])

                    r_block = ResourceBlock(wtp, hwaddr, channel, band)
                    pool.add(r_block)

                lvap.uplink = pool

            if "encap" in request:

                encap = EtherAddress(request["encap"])
                lvap.encap = encap

        except KeyError as ex:
            self.send_error(404, message=ex)
        except ValueError as ex:
            self.send_error(400, message=ex)

        self.set_status(204, None)
Example #34
0
    def scheduled_on(self, downlink):
        """Assign default resource block to LVAP.

        Assign default resource block to LVAP. Accepts as input either a
        ResourcePool or a ResourceBlock. If the resource pool has more than
        one resource block then one random resource block is assigned as both
        downlink and default uplink. The remaining resource blocks are
        assigned as uplink only.

        Args:
            downlink: A ResourcePool or a ResourceBlock
        """

        # Null operation, just return
        if not downlink:
            return

        if isinstance(downlink, ResourcePool):

            pool = downlink

        elif isinstance(downlink, ResourceBlock):

            pool = ResourcePool()
            pool.add(downlink)

        else:

            raise TypeError("Expected ResourcePool, ResourceBlock, got %s",
                            type(downlink))

        current = ResourcePool(list(self.downlink.keys()) +
                               list(self.uplink.keys()))

        # Null operation, just return, but before re-send configuration
        # commands (ports and of tables)
        if current == pool:
            self.set_ports()
            return

        # clear downlink blocks
        for block in list(self.downlink.keys()):
            del self.downlink[block]

        # clear uplink blocks
        for block in list(self.uplink.keys()):
            del self.uplink[block]

        # pick default resource block
        default_block = pool.pop()

        # If lvap is associated to a shared tenant. I need to reset the lvap
        # before moving it.
        if self._tenant and self._tenant.bssid_type == T_TYPE_SHARED:

            # check if tenant is available at target block
            base_bssid = self._tenant.get_prefix()
            net_bssid = generate_bssid(base_bssid, default_block.hwaddr)

            # if not ignore request
            if net_bssid not in self._tenant.vaps:
                LOG.error("VAP %s not found on tenant %s", net_bssid,
                          self._tenant.tenant_name)
                self.set_ports()
                return

            # check if vap is available at target block
            if net_bssid != self._tenant.vaps[net_bssid].net_bssid:
                LOG.error("VAP %s not available at target block %s",
                          net_bssid, default_block)
                self.set_ports()
                return

            # otherwise reset lvap
            self._tenant = None
            self.association_state = False
            self.authentication_state = False
            self._assoc_id = 0
            self._lvap_bssid = net_bssid

        else:

            self._lvap_bssid = self.net_bssid

        # assign default port policy to downlink resource block, this will
        # trigger a send_add_lvap and a set_port (radio) message
        self.downlink[default_block] = RadioPort(self, default_block)

        # assign remaining blocks (if any) to the uplink, this could
        # trigger one or more send_add_lvap and a set_port (radio) messages
        for block in pool:
            self.uplink[block] = RadioPort(self, block)

        # set ports
        self.set_ports()
Example #35
0
    def _handle_status_lvap(self, status):
        """Handle an incoming STATUS_LVAP message.
        Args:
            status, a STATUS_LVAP message
        Returns:
            None
        """

        wtp_addr = EtherAddress(status.wtp)

        try:
            wtp = RUNTIME.wtps[wtp_addr]
        except KeyError:
            LOG.info("Status from unknown WTP %s", wtp_addr)
            return

        if not wtp.connection:
            LOG.info("Status from disconnected WTP %s", wtp_addr)
            return

        sta_addr = EtherAddress(status.sta)
        set_mask = bool(status.flags.set_mask)

        lvap = None
        hwaddr = EtherAddress(status.hwaddr)
        block = ResourceBlock(wtp, hwaddr, status.channel, status.band)

        LOG.info("LVAP status update from %s", sta_addr)

        # If the LVAP does not exists, then create a new one
        if sta_addr not in RUNTIME.lvaps:

            net_bssid_addr = EtherAddress(status.net_bssid)
            lvap_bssid_addr = EtherAddress(status.lvap_bssid)
            lvap = LVAP(sta_addr, net_bssid_addr, lvap_bssid_addr)

            # TODO: This should be built starting from the status message
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 1, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 2, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 3, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 4, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 5, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 6, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 7, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 8, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 9, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 10, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 11, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 36, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 48, BT_L20))

            RUNTIME.lvaps[sta_addr] = lvap

        lvap = RUNTIME.lvaps[sta_addr]

        # incoming block
        pool = ResourcePool()
        pool.add(block)

        match = wtp.supports & pool

        if not match:
            LOG.error("Incoming block %s is invalid", block)
            return

        block = match.pop()

        # this will try to updated the lvap object with the resource block
        # coming in this status update message.
        try:

            if set_mask:

                # set downlink+uplink block
                lvap._downlink.setitem(block, RadioPort(lvap, block))

            else:

                # set uplink only blocks
                lvap._uplink.setitem(block, RadioPort(lvap, block))

        except ValueError:
            LOG.error("Error while importing block %s, removing.", block)
            block.radio.connection.send_del_lvap(lvap)
            return

        lvap.authentication_state = bool(status.flags.authenticated)
        lvap.association_state = bool(status.flags.associated)

        lvap._assoc_id = status.assoc_id
        lvap._encap = EtherAddress(status.encap)
        ssids = [SSID(x.ssid) for x in status.ssids]

        if lvap.ssid:

            # Raise LVAP leave event
            LOG.info("LVAP LEAVE %s (%s)", lvap.addr, lvap.ssid)
            for handler in self.server.pt_types_handlers[PT_LVAP_LEAVE]:
                handler(lvap)

            # removing LVAP from tenant, need first to look for right tenant
            if lvap.addr in lvap.tenant.lvaps:
                LOG.info("Removing %s from tenant %s", lvap.addr, lvap.ssid)
                del lvap.tenant.lvaps[lvap.addr]

        lvap._tenant = None

        if ssids[0]:

            # setting tenant without seding out add lvap message
            lvap._tenant = RUNTIME.load_tenant(ssids[0])

            # adding LVAP to tenant
            LOG.info("Adding %s to tenant %s", lvap.addr, ssids[0])
            lvap.tenant.lvaps[lvap.addr] = lvap

            # Raise LVAP join event
            LOG.info("LVAP JOIN %s (%s)", lvap.addr, lvap.ssid)
            for handler in self.server.pt_types_handlers[PT_LVAP_JOIN]:
                handler(lvap)

        # update remaining ssids
        lvap._ssids = ssids[1:]

        # set ports
        lvap.set_ports()

        LOG.info("LVAP status %s", lvap)
    def _handle_status_lvap(self, status):
        """Handle an incoming STATUS_LVAP message.
        Args:
            status, a STATUS_LVAP message
        Returns:
            None
        """

        wtp_addr = EtherAddress(status.wtp)

        try:
            wtp = RUNTIME.wtps[wtp_addr]
        except KeyError:
            LOG.info("Status from unknown WTP %s", wtp_addr)
            return

        if not wtp.connection:
            LOG.info("Status from disconnected WTP %s", wtp_addr)
            return

        sta_addr = EtherAddress(status.sta)
        set_mask = bool(status.flags.set_mask)

        lvap = None
        hwaddr = EtherAddress(status.hwaddr)
        block = ResourceBlock(wtp, hwaddr, status.channel, status.band)

        LOG.info("LVAP status update from %s", sta_addr)

        # If the LVAP does not exists, then create a new one
        if sta_addr not in RUNTIME.lvaps:

            net_bssid_addr = EtherAddress(status.net_bssid)
            lvap_bssid_addr = EtherAddress(status.lvap_bssid)
            lvap = LVAP(sta_addr, net_bssid_addr, lvap_bssid_addr)

            # TODO: This should be built starting from the status message
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 1, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 2, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 3, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 4, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 5, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 6, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 7, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 8, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 9, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 10, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 11, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 36, BT_L20))
            lvap.supports.add(ResourceBlock(lvap, sta_addr, 48, BT_L20))

            RUNTIME.lvaps[sta_addr] = lvap

        lvap = RUNTIME.lvaps[sta_addr]

        # incoming block
        pool = ResourcePool()
        pool.add(block)

        match = wtp.supports & pool

        if not match:
            LOG.error("Incoming block %s is invalid", block)
            return

        # this will try to updated the lvap object with the resource block
        # coming in this status update message.
        try:

            if set_mask:

                # set downlink+uplink block
                lvap._downlink.setitem(block, RadioPort(lvap, block))

            else:

                # set uplink only blocks
                lvap._uplink.setitem(block, RadioPort(lvap, block))

        except ValueError:
            LOG.error("Error while importing block %s, removing.", block)
            block.radio.connection.send_del_lvap(lvap)
            return

        lvap.authentication_state = bool(status.flags.authenticated)
        lvap.association_state = bool(status.flags.associated)

        lvap._assoc_id = status.assoc_id
        lvap._encap = EtherAddress(status.encap)
        ssids = [SSID(x.ssid) for x in status.ssids]

        if lvap.ssid:

            # Raise LVAP leave event
            LOG.info("LVAP LEAVE %s (%s)", lvap.addr, lvap.ssid)
            for handler in self.server.pt_types_handlers[PT_LVAP_LEAVE]:
                handler(lvap)

            # removing LVAP from tenant, need first to look for right tenant
            if lvap.addr in lvap.tenant.lvaps:
                LOG.info("Removing %s from tenant %s", lvap.addr, lvap.ssid)
                del lvap.tenant.lvaps[lvap.addr]

        lvap._tenant = None

        if ssids[0]:

            # setting tenant without seding out add lvap message
            lvap._tenant = RUNTIME.load_tenant(ssids[0])

            # adding LVAP to tenant
            LOG.info("Adding %s to tenant %s", lvap.addr, ssids[0])
            lvap.tenant.lvaps[lvap.addr] = lvap

            # Raise LVAP join event
            LOG.info("LVAP JOIN %s (%s)", lvap.addr, lvap.ssid)
            for handler in self.server.pt_types_handlers[PT_LVAP_JOIN]:
                handler(lvap)

        # update remaining ssids
        lvap._ssids = ssids[1:]

        # set ports
        lvap.set_ports()

        LOG.info("LVAP status %s", lvap)
    def _handle_assoc_request(self, wtp, request):
        """Handle an incoming ASSOC_REQUEST message.
        Args:
            request, a ASSOC_REQUEST message
        Returns:
            None
        """

        if not wtp.connection:
            LOG.info("Assoc request from disconnected WTP %s", wtp.addr)
            return

        sta = EtherAddress(request.sta)

        if sta not in RUNTIME.lvaps:
            LOG.info("Assoc request from unknown LVAP %s", sta)
            return

        lvap = RUNTIME.lvaps[sta]

        if not RUNTIME.is_allowed(sta):
            LOG.info("Assoc request from %s ignored (white list)", sta)
            return

        if RUNTIME.is_denied(sta):
            LOG.info("Assoc request from %s ignored (black list)", sta)
            return

        ssid = SSID(request.ssid.decode('UTF-8'))
        bssid = EtherAddress(request.bssid)

        tenant_name = None

        # look for ssid in shared tenants
        for tenant_id in RUNTIME.tenants:

            tenant = RUNTIME.tenants[tenant_id]

            if tenant.bssid_type == T_TYPE_UNIQUE:
                continue

            if bssid in tenant.vaps and ssid == tenant.tenant_name:
                tenant_name = tenant.tenant_name

        # otherwise this must be the lvap unique bssid
        if lvap.net_bssid == bssid and ssid in lvap.ssids:
            tenant_name = ssid

        if not tenant_name:
            LOG.info("Assoc request sta %s for ssid %s bssid %s, ignoring",
                     lvap.addr, lvap.ssid, lvap.lvap_bssid)
            return

        # this will trigger an add lvap message to update the ssid
        lvap.tenant = RUNTIME.load_tenant(tenant_name)

        # update supported blocks field
        lvap.supported = ResourcePool()
        hwaddr = EtherAddress(request.hwaddr)
        channel = request.channel
        band = request.band
        lvap.supported.add(ResourceBlock(lvap, hwaddr, channel, band))

        # this will trigger an add lvap message to update the assoc id
        lvap.assoc_id = self.server.assoc_id

        LOG.info("Assoc request sta %s ssid %s bssid %s assoc id %u, replying",
                 lvap.addr, lvap.ssid, lvap.lvap_bssid, lvap.assoc_id)

        self.send_assoc_response(lvap)