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)
def scheduled_on(self, blocks): """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 """ if not blocks: return if isinstance(blocks, ResourcePool): pool = blocks elif isinstance(blocks, ResourceBlock): pool = ResourcePool() pool.add(blocks) else: raise TypeError("Invalid type: %s", type(blocks)) # set downlink block self.downlink = pool.pop() # set uplink blocks self.uplink = pool
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 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)
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
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 _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)
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()
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()
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()
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_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 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 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 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)
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_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)
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)
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()
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()