def send_message(self, msg_type, msg, callback=None): """Send message and set common parameters.""" parser = self.proto.PT_TYPES[msg_type] if self.stream.closed(): self.log.warning("Stream closed, unabled to send %s message to %s", parser.name, self.device) return 0 msg.version = self.proto.PT_VERSION msg.seq = self.seq msg.type = msg_type msg.xid = self.xid if not self.device: msg.device = EtherAddress("00:00:00:00:00:00").to_raw() else: msg.device = self.device.addr.to_raw() addr = self.stream.socket.getpeername() self.log.debug("Sending %s message to %s seq %u", parser.name, addr[0], msg.seq) self.stream.write(parser.build(msg)) if callback: self.xids[msg.xid] = (msg, callback) return msg.xid
def bssid(self, bssid): """ Set the bssid. """ if bssid == EtherAddress("00:00:00:00:00:00"): self._bssid = None else: self._bssid = bssid
def post(self, *args, **kwargs): """Add entry in ACL. Args: [0], the project id (mandatory) Example URLs: POST /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/ wifi_acl { "addr": "60:57:18:B1:A4:B8", "desc": "Dell Laptop" } """ project_id = uuid.UUID(args[0]) project = self.service.projects[project_id] desc = "Generic Station" if 'desc' not in kwargs else kwargs['desc'] addr = EtherAddress(kwargs['addr']) acl = project.upsert_acl(addr, desc) url = "/api/v1/projects/%s/wifi_acl/%s" % (project_id, acl.addr) self.set_header("Location", url)
def put(self, *args, **kwargs): """Update entry in ACL. Args: [0], the project id (mandatory) [1]: the device address (mandatory) Example URLs: PUT /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/ wifi_acl/60:57:18:B1:A4:B8 { "desc": "Dell Laptop" } """ project_id = uuid.UUID(args[0]) project = self.service.projects[project_id] desc = "Generic Station" if 'desc' not in kwargs else kwargs['desc'] addr = EtherAddress(args[1]) project.upsert_acl(addr, desc)
def post(self, *args, **kwargs): """Add a new device. Request: version: protocol version (1.0) addr: the device address (mandatory) desc: a human readable description of the device (optional) Example URLs: POST /api/v1/vbses { "version":"1.0", "addr": "00:00:00:00:00:01" } POST /api/v1/vbses { "version":"1.0", "addr": "00:00:00:00:00:01", "desc": "Ettus B210" } """ addr = EtherAddress(kwargs['addr']) if 'desc' in kwargs: device = self.service.create(addr, kwargs['desc']) else: device = self.service.create(addr) self.set_header("Location", "/api/v1/vbses/%s" % device.addr)
def _handle_caps_response(self, caps): """Handle an incoming CAPS message.""" for block in caps.blocks: self.device.blocks[block.block_id] = \ ResourceBlock(self.device, block.block_id, EtherAddress(block.hwaddr), block.channel, block.band) # set state to online self.device.set_online() # fetch active lvaps self.send_lvap_status_request() # fetch active vaps self.send_vap_status_request() # fetch active traffic rules self.send_slice_status_request() # fetch active tramission policies self.send_tx_policy_status_request() # send vaps self.update_vaps() # send slices self.update_slices()
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[1])] if "blocks" in kwargs: wtp = project.wtps[EtherAddress(kwargs['wtp'])] pool = ResourcePool() for block_id in kwargs["blocks"]: pool.append(wtp.blocks[block_id]) lvap.blocks = pool elif "wtp" in kwargs: lvap.wtp = project.wtps[EtherAddress(kwargs['wtp'])] if "encap" in kwargs: encap = EtherAddress(kwargs["encap"]) lvap.encap = encap
def generate_bssid(self, mac): """ Generate a new BSSID address. """ base_mac = self.get_prefix() base = str(base_mac).split(":")[0:3] unicast_addr_mask = int(base[0], 16) & 0xFE base[0] = str(format(unicast_addr_mask, 'X')) suffix = str(mac).split(":")[3:6] return EtherAddress(":".join(base + suffix))
def upsert_mcast_service(self, ipaddress, receivers, status, service_type): """Update/insert new mcast services. Expected input: { "ip": "224.0.1.200", "receivers": ["ff:ff:ff:ff:ff:ff"], "status": True, "type": "emergency" } """ addr = self.mcast_ip_to_ether(ipaddress) if addr not in self.mcast_services: schedule = self.schedule[-self._services_registered:] + \ self.schedule[:-self._services_registered] self.mcast_services[addr] = { "addr": addr, "ipaddress": ipaddress, "mcs": 6, "schedule": schedule, "receivers": [EtherAddress(x) for x in receivers], "status": status, "service_type": service_type } self._services_registered += 1 else: self.mcast_services[addr]["receivers"] = \ [EtherAddress(x) for x in receivers] self.mcast_services[addr]["status"] = status self.mcast_services[addr]["service_type"] = service_type return addr
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
def __init__(self, slice_id, properties=None, devices=None): # set read-only parameters self.slice_id = int(slice_id) # logger :) self.log = logging.getLogger(self.__class__.__module__) # parse properties self.properties = self._parse_properties(properties) # parse per device properties self.devices = {} if devices: for device in devices: self.devices[EtherAddress(device)] = \ self._parse_properties(devices[device])
def delete(self, *args, **kwargs): """Delete one or all devices. Args: [0]: the device address Example URLs: DELETE /api/v1/vbses DELETE /api/v1/vbses/00:00:00:00:00:01 """ if args: self.service.remove(EtherAddress(args[0])) else: self.service.remove_all()
def delete(self, *args, **kwargs): """Delete one or all devices. Args: [0]: the device address (mandatory) Example URLs: DELETE /api/v1/wtps DELETE /api/v1/wtps/00:0D:B9:2F:56:64 """ if args: self.service.remove(EtherAddress(args[0])) else: self.service.remove_all()
def delete(self, *args, **kwargs): """Delete the mcast_services . Args: [0]: the project id (mandatory) [1]: the app id (mandatory) [3]: the mcast service MAC address (mandatory) Example URLs: DELETE /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/apps/ mcast_services/01:00:5E:00:01:C8 """ self.service.delete_mcast_service(EtherAddress(args[2])) self.service.save_service_state()
def get(self, *args, **kwargs): """Lists all clients in the ACL. Args: [0], the project id (mandatory) [0]: the device address (optional) Example URLs: GET /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/ wifi_acl/ { "60:57:18:B1:A4:B8": { "addr": "60:57:18:B1:A4:B8", "desc": "Dell Laptop" }, "18:5E:0F:E3:B8:68": { "addr": "18:5E:0F:E3:B8:68", "desc": "Dell Laptop" }, "60:F4:45:D0:3B:FC": { "addr": "60:F4:45:D0:3B:FC", "desc": "Roberto's iPhone" } } GET /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/ wifi_acl/60:57:18:B1:A4:B8 { "addr": "60:57:18:B1:A4:B8", "desc": "Dell Laptop" } """ project_id = uuid.UUID(args[0]) project = self.service.projects[project_id] allowed = project.wifi_props.allowed return allowed if not args else allowed[str(EtherAddress(args[1]))]
def _handle_tx_policy_status_response(self, status): """Handle an incoming TX_POLICY_STATUS_RESPONSE message.""" block = self.device.blocks[status.iface_id] addr = EtherAddress(status.sta) if addr not in block.tx_policies: block.tx_policies[addr] = TxPolicy(addr, block) txp = block.tx_policies[addr] txp.set_mcs([float(x) / 2 for x in status.mcs]) txp.set_ht_mcs([int(x) for x in status.mcs_ht]) txp.set_rts_cts(status.rts_cts) txp.set_max_amsdu_len(status.max_amsdu_len) txp.set_mcast(status.tx_mcast) txp.set_no_ack(status.flags.no_ack) self.log.info("TX policy status: %s", txp)
def handle_ucqm_response(self, response, wtp, _): """Handle UCQM_RESPONSE message.""" block = wtp.blocks[response.iface_id] block.ucqm = {} # generate data points points = [] timestamp = datetime.utcnow() for entry in response.entries: addr = EtherAddress(entry['addr']) block.ucqm[addr] = { 'addr': addr, 'last_rssi_std': entry['last_rssi_std'], 'last_rssi_avg': entry['last_rssi_avg'], 'last_packets': entry['last_packets'], 'hist_packets': entry['hist_packets'], 'mov_rssi': entry['mov_rssi'] } tags = dict(self.params) tags["wtp"] = wtp.addr tags["block_id"] = response.iface_id tags["addr"] = addr sample = { "measurement": self.name, "tags": tags, "time": timestamp, "fields": block.ucqm[addr] } points.append(sample) # save to db self.write_points(points) self.ucqm[block.block_id] = block.ucqm # handle callbacks self.handle_callbacks()
def get(self, *args, **kwargs): """Access the mcast_services . Args: [0]: the project id (mandatory) [1]: the app id (mandatory) [2]: the mcast service MAC address (optional) Example URLs: GET /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/apps/ mcast_services/01:00:5E:00:01:C8 { addr: "01:00:5E:00:01:C8", ipaddress: "224.0.1.200", mcs: 0, schedule: [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], receivers: [ "FF:FF:FF:FF:FF:FF" ], status: "true", service_type: "emergency" } """ if len(args) == 2: return self.service.mcast_services return self.service.mcast_services[EtherAddress(args[2])]
def delete(self, *args, **kwargs): """Delete an entry in ACL. Args: [0], the project id (mandatory) [0]: the device address (mandatory) Example URLs: DELETE /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/ wifi_acl/60:57:18:B1:A4:B8 """ project_id = uuid.UUID(args[0]) project = self.service.projects[project_id] if len(args) == 2: project.remove_acl(EtherAddress(args[1])) else: project.remove_acl()
def handle_ncqm_response(self, response, wtp, _): """Handle NCQM_RESPONSE message.""" block = wtp.blocks[response.iface_id] block.ncqm = {} for entry in response.entries: addr = EtherAddress(entry['addr']) block.ncqm[addr] = { 'addr': addr, 'last_rssi_std': entry['last_rssi_std'], 'last_rssi_avg': entry['last_rssi_avg'], 'last_packets': entry['last_packets'], 'hist_packets': entry['hist_packets'], 'mov_rssi': entry['mov_rssi'] } self.ncqm = block.ncqm # handle callbacks self.handle_callbacks()
def handle_response(self, response, *_): """Handle WIFI_SLICE_STATS_RESPONSE message.""" wtp = EtherAddress(response.device) # update this object if wtp not in self.stats: self.stats[wtp] = {} # generate data points points = [] timestamp = datetime.utcnow() for entry in response.stats: self.stats[wtp][entry.iface_id] = { 'deficit_used': entry.deficit_used, 'max_queue_length': entry.max_queue_length, 'tx_packets': entry.tx_packets, 'tx_bytes': entry.tx_bytes, } tags = dict(self.params) tags["wtp"] = wtp tags["iface_id"] = entry.iface_id sample = { "measurement": self.name, "tags": tags, "time": timestamp, "fields": self.stats[wtp][entry.iface_id] } points.append(sample) # save to db self.write_points(points) # handle callbacks self.handle_callbacks()
def get(self, *args, **kwargs): """List devices. Args: [0]: the device address (optional) Example URLs: GET /api/v1/vbses [ { "addr": "00:00:00:00:00:01", "cells": {}, "connection": null, "desc": "Ettus B210", "last_seen": 0, "last_seen_ts": "1970-01-01T01:00:00.000000Z", "period": 0, "state": "disconnected" } ] GET /api/v1/vbses/00:00:00:00:00:01 { "addr": "00:00:00:00:00:01", "cells": {}, "connection": null, "desc": "Ettus B210", "last_seen": 0, "last_seen_ts": "1970-01-01T01:00:00.000000Z", "period": 0, "state": "disconnected" } """ return self.service.devices \ if not args else self.service.devices[EtherAddress(args[0])]
def mcast_ip_to_ether(cls, ip_mcast_addr): """Transform an IP multicast address into an Ethernet one.""" if ip_mcast_addr is None: return '\x00' * 6 # The first 24 bits are fixed according to class D IP # and IP multicast address convenctions mcast_base = '01:00:5e' # The 23 low order bits are mapped. ip_addr_bytes = str(ip_mcast_addr).split('.') # The first IP byte is not use, # and only the last 7 bits of the second byte are used. second_byte = int(ip_addr_bytes[1]) & 127 third_byte = int(ip_addr_bytes[2]) fourth_byte = int(ip_addr_bytes[3]) mcast_upper = format(second_byte, '02x') + ':' + \ format(third_byte, '02x') + ':' + \ format(fourth_byte, '02x') return EtherAddress(mcast_base + ':' + mcast_upper)
def put(self, *args, **kwargs): """Update the description of the device. Request: version: protocol version (1.0) desc: a human readable description of the device (optional) Example URLs: PUT /api/v1/vbses/00:0D:B9:2F:56:64 { "version":"1.0", "desc": "SrsLTE eNB" } """ addr = EtherAddress(args[0]) if 'desc' in kwargs: self.service.update(addr, kwargs['desc']) else: self.service.update(addr)
def _handle_vap_status_response(self, status): """Handle an incoming STATUS_VAP message.""" bssid = EtherAddress(status.bssid) ssid = SSID(status.ssid) project = srv_or_die("projectsmanager").load_project_by_ssid(ssid) if not project: self.log.warning("Unable to find SSID %s", ssid) self.send_del_vap(bssid) return # If the VAP does not exists, then create a new one if bssid not in self.manager.vaps: incoming = self.device.blocks[status.iface_id] self.manager.vaps[bssid] = VAP(bssid, incoming, project.wifi_props.ssid) vap = self.manager.vaps[bssid] self.log.info("VAP status: %s", vap)
def send_add_lvap_request(self, lvap, block, set_mask): """Send a ADD_LVAP message.""" flags = Container(ht_caps=lvap.ht_caps, authenticated=lvap.authentication_state, associated=lvap.association_state, set_mask=set_mask) encap = EtherAddress("00:00:00:00:00:00") if lvap.encap: encap = lvap.encap bssid = EtherAddress() if lvap.bssid: bssid = lvap.bssid ssid = SSID() if lvap.ssid: ssid = lvap.ssid msg = Container(length=80, flags=flags, assoc_id=lvap.assoc_id, iface_id=block.block_id, ht_caps_info=Container(**lvap.ht_caps_info), sta=lvap.addr.to_raw(), encap=encap.to_raw(), bssid=bssid.to_raw(), ssid=ssid.to_raw(), networks=[]) for network in lvap.networks: msg.length = msg.length + 6 + WIFI_NWID_MAXSIZE + 1 msg.networks.append( Container(bssid=network[0].to_raw(), ssid=network[1].to_raw())) return self.send_message(self.proto.PT_ADD_LVAP_REQUEST, msg, lvap.handle_add_lvap_response)
def get(self, *args, **kwargs): """List the LVAPs. Args: [0], the project id (mandatory) [1]: the lvap address (optional) Example URLs: GET /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/lvaps [ { "addr": "60:F4:45:D0:3B:FC", "assoc_id": 732, "association_state": true, "authentication_state": true, "blocks": [ ... ], "bssid": "52:31:3E:D0:3B:FC", "encap": "00:00:00:00:00:00", "ht_caps": true, "ht_caps_info": { "DSSS_CCK_Mode_in_40_MHz": false, "Forty_MHz_Intolerant": false, "HT_Delayed_Block_Ack": false, "HT_Greenfield": false, "LDPC_Coding_Capability": true, "L_SIG_TXOP_Protection_Support": false, "Maximum_AMSDU_Length": false, "Reserved": false, "Rx_STBC": 0, "SM_Power_Save": 3, "Short_GI_for_20_MHz": true, "Short_GI_for_40_MHz": true, "Supported_Channel_Width_Set": true, "Tx_STBC": false }, "networks": [ [ "52:31:3E:D0:3B:FC", "EmPOWER" ] ], "pending": [], "ssid": "EmPOWER", "state": "running", "wtp": { ... } } ] GET /api/v1/projects/52313ecb-9d00-4b7d-b873-b55d3d9ada26/lvaps/ 60:F4:45:D0:3B:FC { "addr": "60:F4:45:D0:3B:FC", "assoc_id": 732, "association_state": true, "authentication_state": true, "blocks": [ ... ], "bssid": "52:31:3E:D0:3B:FC", "encap": "00:00:00:00:00:00", "ht_caps": true, "ht_caps_info": { "DSSS_CCK_Mode_in_40_MHz": false, "Forty_MHz_Intolerant": false, "HT_Delayed_Block_Ack": false, "HT_Greenfield": false, "LDPC_Coding_Capability": true, "L_SIG_TXOP_Protection_Support": false, "Maximum_AMSDU_Length": false, "Reserved": false, "Rx_STBC": 0, "SM_Power_Save": 3, "Short_GI_for_20_MHz": true, "Short_GI_for_40_MHz": true, "Supported_Channel_Width_Set": true, "Tx_STBC": false }, "networks": [ [ "52:31:3E:D0:3B:FC", "EmPOWER" ] ], "pending": [], "ssid": "EmPOWER", "state": "running", "wtp": { ... } } """ project_id = uuid.UUID(args[0]) project = self.service.projects[project_id] return project.lvaps \ if len(args) == 1 else project.lvaps[EtherAddress(args[1])]
def on_read(self, future): """Assemble message from agent. Appends bytes read from socket to a buffer. Once the full packet has been read the parser is invoked and the buffers is cleared. The parsed packet is then passed to the suitable method or dropped if the packet type in unknown. """ try: self.buffer = self.buffer + future.result() except StreamClosedError as stream_ex: self.log.error(stream_ex) return hdr = self.proto.HEADER.parse(self.buffer) if len(self.buffer) < hdr.length: remaining = hdr.length - len(self.buffer) future = self.stream.read_bytes(remaining) future.add_done_callback(self.on_read) return # Check if we know the message type if hdr.type not in self.proto.PT_TYPES: self.log.warning("Unknown message type %u, ignoring.", hdr.type) return # Check if the Device is among the ones we known addr = EtherAddress(hdr.device) if addr not in self.manager.devices: self.log.warning("Unknown Device %s, closing connection.", addr) self.stream.close() return device = self.manager.devices[addr] # Log message informations parser = self.proto.PT_TYPES[hdr.type] msg = parser.parse(self.buffer) self.log.debug("Got %s message from %s seq %u", parser.name, EtherAddress(addr), hdr.seq) # If Device is not online and is not connected, then the only message # type we can accept is HELLO_RESPONSE if not device.is_connected(): if msg.type != self.proto.PT_HELLO_REQUEST: if not self.stream.closed(): self.wait() return # This is a new connection, set pointer to the device self.device = device # The set pointer from device connection to this object device.connection = self # Transition to connected state device.set_connected() # Start hb worker self.hb_worker.start() # Send caps request self.send_caps_request() # If device is not online but it is connected, then we can accept both # HELLO_RESPONSE and CAP_RESPONSE message if device.is_connected() and not device.is_online(): valid = (self.proto.PT_HELLO_REQUEST, self.proto.PT_CAPS_RESPONSE) if msg.type not in valid: if not self.stream.closed(): self.wait() return # Otherwise handle message try: method = self.proto.PT_TYPES[msg.type].name self.handle_message(method, msg) except Exception as ex: self.log.exception(ex) self.stream.close() if not self.stream.closed(): self.wait()
def _handle_assoc_request(self, request): """Handle an incoming ASSOC_REQUEST message.""" sta = EtherAddress(request.sta) ht_caps = request.flags.ht_caps ht_caps_info = dict(request.ht_caps_info) del ht_caps_info['_io'] if sta not in self.manager.lvaps: self.log.info("Assoc request from unknown LVAP %s", sta) return lvap = self.manager.lvaps[sta] incoming_bssid = EtherAddress(request.bssid) if lvap.bssid != incoming_bssid: self.log.info("Assoc request for invalid BSSID %s", incoming_bssid) return incoming_ssid = SSID(request.ssid) # Check if the requested SSID is from a unique project for project in srv_or_die("projectsmanager").projects.values(): if not project.wifi_props: continue if project.wifi_props.bssid_type == T_BSSID_TYPE_SHARED: continue bssid = project.generate_bssid(lvap.addr) if bssid != incoming_bssid: self.log.info("Invalid BSSID %s", incoming_bssid) continue if project.wifi_props.ssid == incoming_ssid: lvap.bssid = incoming_bssid lvap.authentication_state = True lvap.association_state = True lvap.ssid = incoming_ssid lvap.ht_caps = ht_caps lvap.ht_caps_info = ht_caps_info lvap.commit() self.send_assoc_response(lvap) return # Check if the requested SSID is from a unique project for project in srv_or_die("projectsmanager").projects.values(): if not project.wifi_props: continue if project.wifi_props.bssid_type == T_BSSID_TYPE_UNIQUE: continue if incoming_bssid not in project.vaps: self.log.info("Invalid BSSID %s", incoming_bssid) continue if project.wifi_props.ssid == incoming_ssid: lvap.bssid = incoming_bssid lvap.authentication_state = True lvap.association_state = True lvap.ssid = incoming_ssid lvap.ht_caps = ht_caps lvap.ht_caps_info = ht_caps_info lvap.commit() self.send_assoc_response(lvap) return self.log.info("Unable to find SSID %s", incoming_ssid)
def _handle_lvap_status_response(self, status): """Handle an incoming LVAP_STATUS_RESPONSE message.""" sta = EtherAddress(status.sta) # If the LVAP does not exists, then create a new one if sta not in self.manager.lvaps: self.manager.lvaps[sta] = \ LVAP(sta, assoc_id=status.assoc_id, state=PROCESS_RUNNING) lvap = self.manager.lvaps[sta] # update LVAP params lvap.encap = EtherAddress(status.encap) lvap.authentication_state = bool(status.flags.authenticated) lvap.association_state = bool(status.flags.associated) lvap.ht_caps = bool(status.flags.ht_caps) lvap.ht_caps_info = dict(status.ht_caps_info) del lvap.ht_caps_info['_io'] ssid = SSID(status.ssid) if ssid == SSID(): ssid = None bssid = EtherAddress(status.bssid) if bssid == EtherAddress("00:00:00:00:00:00"): bssid = None lvap.bssid = bssid incoming = self.device.blocks[status.iface_id] if status.flags.set_mask: lvap.downlink = incoming else: lvap.uplink.append(incoming) # if this is not a DL+UL block then stop here if not status.flags.set_mask: return # if an SSID is set and the incoming SSID is different from the # current one then raise an LVAP leave event if lvap.ssid and ssid != lvap.ssid: self.send_client_leave_message_to_self(lvap) lvap.ssid = None # if the incoming ssid is not none then raise an lvap join event if ssid: lvap.ssid = ssid self.send_client_join_message_to_self(lvap) # udpate networks networks = list() for network in status.networks: incoming = (EtherAddress(network.bssid), SSID(network.ssid)) networks.append(incoming) lvap.networks = networks self.log.info("LVAP status: %s", lvap)