def unpack_payload_from_bytes(sub_id, buf): """Unpack the payload portion of the message provided. Args: sub_id (int): the type of the message buf (str): the entire payload to be unpacked Returns: tuple containing the unpacked header and message ID: payload (namedtuple): the unpacked message as a namedtuple of the right type MessageID (Enum): the type of the message converted to an Enum Raises: :class:`MessageMalformedError`: Invalid message """ if sub_id == MessageID.RAW_MU.value: unpacker = _RawMU constructor = RawMU elif sub_id == MessageID.RAW_LC.value: unpacker = _RawLC constructor = RawLC elif sub_id == MessageID.GET_MU.value: unpacker = _GetMU constructor = GetMU elif sub_id == MessageID.GET_LC.value: unpacker = _GetLC constructor = GetLC elif sub_id == MessageID.EVENTS.value: unpacker = _Events constructor = Events elif sub_id == MessageID.SUMMARY.value: unpacker = _Summary constructor = Summary elif sub_id == MessageID.RAW_HOST_IF.value: unpacker = _RawHostIF constructor = RawHostIF else: raise MessageMalformedError("Unsupported message ID: %d" % sub_id) if len(buf) < unpacker.size: raise MessageMalformedError("Message too short: %d (need %d)" % (len(buf), unpacker.size)) fields = unpacker.unpack(buf[0:unpacker.size]) if sub_id == MessageID.RAW_LC.value: # Variable length message # First unpack the fixed size portion fields = list(fields) + [0] payload = constructor._make(fields) buf = buf[unpacker.size:] # Now unpack the links payload = common.unpack_link(payload, buf, 'num_links', 'links', _RawLCLink, RawLCLink) else: payload = constructor._make(fields) payload = common.replace_mac_address_with_string(payload) return (payload, MessageID(sub_id))
def unpack_payload_from_bytes(sub_id, buf): """Unpack the payload portion of the message provided. Args: sub_id (int): the type of the message buf (str): the entire payload to be unpacked Returns: tuple containing the unpacked header and message ID: payload (namedtuple): the unpacked message as a namedtuple of the right type MessageID (Enum): the type of the message converted to an Enum Raises: :class:`MessageMalformedError`: Invalid message """ if sub_id == MessageID.LINK_UP.value or sub_id == MessageID.LINK_DOWN.value: if sub_id == MessageID.LINK_UP.value: link_event = LinkUp("") else: link_event = LinkDown("") (link_event, buf) = get_variable_length_str(link_event, buf, None, 'iface', 2) return (link_event, MessageID(sub_id)) elif sub_id == MessageID.REMOTE_IF_UP.value: unpacker = _tdMacAddrUnpacker constructor = RemoteIfaceUp elif sub_id == MessageID.REMOTE_IF_DOWN.value: unpacker = _tdMacAddrUnpacker constructor = RemoteIfaceDown elif sub_id == MessageID.REMOTE_DEV_UP.value: unpacker = _tdMacAddrUnpacker constructor = RemoteDevUp elif sub_id == MessageID.REMOTE_DEV_DOWN.value: unpacker = _tdMacAddrUnpacker constructor = RemoteDevDown elif sub_id == MessageID.AGING.value: # Aging is an empty event return (None, MessageID.AGING) elif sub_id == MessageID.BDA_CHANGE.value: unpacker = _tdMacAddrUnpacker constructor = BridgedDAChange elif sub_id == MessageID.ENTRY.value: return (unpack_td_entry(buf), MessageID.ENTRY) else: raise MessageMalformedError("Unsupported message ID: %d" % sub_id) if len(buf) < unpacker.size: raise MessageMalformedError("Message too short: %d (need %d)" % (len(buf), unpacker.size)) fields = unpacker.unpack(buf[0:unpacker.size]) payload = constructor._make(fields) payload = common.replace_mac_address_with_string(payload) return (payload, MessageID(sub_id))
def unpack_payload_from_bytes(sub_id, buf): """Unpack the payload portion of the message provided. Args: sub_id (int): the type of the message buf (str): the entire payload to be unpacked Returns: tuple containing the unpacked header and message ID: payload (namedtuple): the unpacked message as a namedtuple of the right type MessageID (Enum): the type of the message converted to an Enum Raises: :class:`MessageMalformedError`: Invalid message """ # Version is a special case - there is no payload, and the sub_id # contains the version number try: v = Version(MessageID(sub_id)) except: raise MessageMalformedError("Unsupported version: %d" % sub_id) return (v, v.version)
def unpack_payload_from_bytes(sub_id, buf): """Unpack the payload portion of the message provided. Args: sub_id (int): the type of the message buf (str): the entire payload to be unpacked Returns: tuple containing the unpacked header and message ID: payload (namedtuple): the unpacked message as a namedtuple of the right type MessageID (Enum): the type of the message converted to an Enum Raises: :class:`MessageMalformedError`: Invalid message """ # The sub_id is the length of the string if len(buf) == 0 or sub_id == 0: raise MessageMalformedError("Custom string is missing") string_val = struct.unpack('%ds' % sub_id, buf[:sub_id]) msg = CustomMessage._make(string_val) return (msg, MessageID.CUSTOM_MESSAGE)
def unpack_payload_from_bytes(sub_id, buf): """Unpack the payload portion of the message provided. Args: sub_id (int): the type of the message buf (str): the entire payload to be unpacked Returns: tuple containing the unpacked header and message ID: payload (namedtuple): the unpacked message as a namedtuple of the right type MessageID (Enum): the type of the message converted to an Enum Raises: :class:`MessageMalformedError`: Invalid message """ if sub_id == MessageID.SUMMARY.value: unpacker = _Summary constructor = Summary elif sub_id == MessageID.DETAIL.value: return unpack_hactive_table(buf) else: raise MessageMalformedError("Unsupported message ID: %d" % sub_id) if len(buf) < unpacker.size: raise MessageMalformedError("Message too short: %d (need %d)" % (len(buf), unpacker.size)) fields = unpacker.unpack(buf[0:unpacker.size]) payload = constructor._make(fields) # Replace the interface type with the correct string payload = common.replace_iface_char_with_string(payload) # Replace the MAC address with the correct format payload = common.replace_mac_address_with_string(payload) return (payload, MessageID(sub_id))
def unpack_payload_from_bytes(sub_id, buf): """Unpack the payload portion of the message provided. Args: sub_id (int): the type of the message buf (str): the entire payload to be unpacked Returns: tuple containing the unpacked header and message ID: payload (namedtuple): the unpacked message as a namedtuple of the right type MessageID (Enum): the type of the message converted to an Enum Raises: :class:`MessageMalformedError`: Invalid message """ if sub_id == MessageID.HACTIVE.value: return unpack_hactive_table(buf) else: raise MessageMalformedError("Unsupported message ID: %d" % sub_id)
def unpack_header_from_bytes(buf): """Unpack the header portion of the message provided. Args: buf (str): the entire message to be unpacked Returns: tuple containing the unpacked header and number of bytes consumed: hdr (:obj:`Header`): the fields in the header n (int): the number of bytes consumed in buf """ fields = _HeaderFormat.unpack(buf[0:_HeaderFormat.size]) # Convert the module ID into an enum value. fields = list(fields) # Check if the more_fragments bit is set if fields[0] & MORE_FRAGMENTS: more_fragments = True fields[0] &= ~MORE_FRAGMENTS else: more_fragments = False fields.append(more_fragments) try: fields[0] = ModuleID(fields[0]) except: raise MessageMalformedError("Unsupported module ID: %d" % fields[0]) header = Header._make(itertools.chain(fields)) header = replace_mac_address_with_string(header) return (_HeaderFormat.size, header)
def replace_iface_char_with_string(payload, attr_name='iface_type'): """Replace the interface character in the tuple with a string. Args: payload (tuple): tuple containing a message attr_name (str): string containing the name of the interface type field Returns: tuple updated with the interface type in attr_name (if present) replaced with a string representation """ if hasattr(payload, attr_name): iface_type = getattr(payload, attr_name) try: iface = InterfaceType(iface_type) except: raise MessageMalformedError("Unsupported interface type: %c" % chr(iface_type)) vals = {attr_name: iface} payload = payload._replace(**vals) return payload
def unpack_msg(buf): """Unpack a single diagnostic logging message. Args: buf (str): the raw message to unpack Returns: tuple of the diagnostic logging header and the payload, each as an appropriate namedtuple Raises: :class:`MessageMalformedError`: Message could not be parsed """ n_bytes, header = common.unpack_header_from_bytes(buf) payload_bytes = buf[n_bytes:] if header.id == common.ModuleID.VER: (payload, sub_id) = ver.unpack_payload_from_bytes(header.sub_id, payload_bytes) # Update the version based on this message (should always be the first received) version[header.mac] = sub_id logging.info("%s: hyd using version %d", header.mac, sub_id.value) elif header.id == common.ModuleID.MSG: (payload, sub_id) = msg.unpack_payload_from_bytes(header.sub_id, payload_bytes) elif header.id == common.ModuleID.PCW2: msg_version = get_version(header.mac) (payload, sub_id) = pcwservice.unpack_payload_from_bytes( header.sub_id, payload_bytes, msg_version) elif header.id == common.ModuleID.PCW5: msg_version = get_version(header.mac) (payload, sub_id) = pcwservice.unpack_payload_from_bytes( header.sub_id, payload_bytes, msg_version) elif header.id == common.ModuleID.PCP: (payload, sub_id) = pcpservice.unpack_payload_from_bytes( header.sub_id, payload_bytes) elif header.id == common.ModuleID.HE: (payload, sub_id) = heservice.unpack_payload_from_bytes(header.sub_id, payload_bytes) elif header.id == common.ModuleID.HE_TABLES: (payload, sub_id) = hetables.unpack_payload_from_bytes(header.sub_id, payload_bytes) elif header.id == common.ModuleID.PS: msg_version = get_version(header.mac) (payload, sub_id) = psservice.unpack_payload_from_bytes(header.sub_id, payload_bytes, msg_version) elif header.id == common.ModuleID.PS_TABLES: (payload, sub_id) = pstables.unpack_payload_from_bytes(header.sub_id, payload_bytes) elif header.id == common.ModuleID.TD: (payload, sub_id) = tdservice.unpack_payload_from_bytes(header.sub_id, payload_bytes) else: raise MessageMalformedError("Unhandled module ID %d" % header.id.value) # Replace the sub_id in the header with the correct enum depending on # id type vals = {"sub_id": sub_id} header = header._replace(**vals) return (header, payload)
def unpack_td_entry(buf): """Unpack a td entry. Args: buf (str): the entire payload to be unpacked Returns: entry (namedtuple): the unpacked message as a namedtuple Raises: :class:`MessageMalformedError`: Invalid message """ # Needs to be at least big enough for 1 MAC address, the interface # list length, and the bridged DA list length if len(buf) < 14: raise MessageMalformedError("Message too short: %d (need 14)" % (len(buf))) # Get the MAC address mac = _tdMacAddrUnpacker.unpack(buf[0:_tdMacAddrUnpacker.size]) mac = common.ether_ntoa(mac[0]) buf = buf[_tdMacAddrUnpacker.size:] # Get the interface list length num_ifaces = struct.unpack('>I', buf[:4]) num_ifaces = num_ifaces[0] buf = buf[4:] # Read the interface list # Make sure there is enough buffer remaining - need at least 10 bytes per interface, # plus the length of the bridged DA list min_len_remaining = (10 * num_ifaces + 4) if len(buf) < min_len_remaining: raise MessageMalformedError( "Message too short: %d (need %d), %d interfaces present" % (len(buf), min_len_remaining, num_ifaces)) iface_list = [] for i in range(0, num_ifaces): # Unpack the interface type string (buf, length, type_string) = get_variable_length_str_raw(buf) # Unpack whether or not the interface is connected connected = struct.unpack('>H', buf[:2]) buf = buf[2:] # Unpack the MAC address iface_name = _tdMacAddrUnpacker.unpack(buf[0:_tdMacAddrUnpacker.size]) buf = buf[_tdMacAddrUnpacker.size:] iface = Interface(type_string, connected[0], common.ether_ntoa(iface_name[0])) iface_list.append(iface) # Read the number of bridged DAs num_bridged_das = struct.unpack('>I', buf[:4]) num_bridged_das = num_bridged_das[0] buf = buf[4:] # Read the bridged DA list if len(buf) < 6 * num_bridged_das: raise MessageMalformedError( "Message too short: %d (need %d), %d bridged DAs present" % (len(buf), min_len_remaining, num_bridged_das)) bridged_da_list = [] for i in range(0, num_bridged_das): da = _tdMacAddrUnpacker.unpack(buf[0:_tdMacAddrUnpacker.size]) buf = buf[_tdMacAddrUnpacker.size:] bridged_da_list.append(common.ether_ntoa(da[0])) entry = Entry(mac, num_ifaces, iface_list, num_bridged_das, bridged_da_list) return entry
def unpack_table(buf, unpacker, constructor, mac_names, iface_types, variable_length_string_names, sub_class=None): """Unpack a table from the buffer, updating some fields as needed The entire buffer should be consumed, there is no length field indicating the number of rows. Args: buf (str): the entire message to be unpacked unpacker (Struct): struct to use to unpack each row constructor (tuple): constructor to use to make each row mac_names (list): list of field names that contain MAC addresses iface_types (list): list of field names that interface types variable_length_string_names (list): list of field names that contain variable length strings sub_class (str): string containing the name of the sub_class (transport protocol) field Returns: array of table rows Raises: :class:`MessageMalformedError`: There are not an integer number of rows in the table """ rows = [] while len(buf) >= unpacker.size: fields = unpacker.unpack(buf[:unpacker.size]) fields = list(fields) for string_name in variable_length_string_names: fields.append("") row = constructor._make(fields) buf = buf[unpacker.size:] # now read out the variable length fields for string_name in variable_length_string_names: (row, buf) = get_variable_length_str(row, buf, None, string_name, 2) # Update the MAC names for name in mac_names: row = replace_mac_address_with_string(row, name) # Update the interface types for iface_type in iface_types: row = replace_iface_char_with_string(row, iface_type) # Update the subclass if sub_class: vals = {sub_class: TRANSPORT_PROTOCOL(row.sub_class)} row = row._replace(**vals) rows.append(row) # Should have consumed all of buf at this point if len(buf): raise MessageMalformedError( "Non-integer number of rows: %d bytes remain" % len(buf)) return rows
def unpack_payload_from_bytes(sub_id, buf, version): """Unpack the payload portion of the message provided. Args: sub_id (int): the type of the message buf (str): the entire payload to be unpacked version (Enum): version to use for parsing this message Returns: tuple containing the unpacked header and message ID: payload (namedtuple): the unpacked message as a namedtuple of the right type MessageID (Enum): the type of the message converted to an Enum Raises: :class:`MessageMalformedError`: Invalid message """ if sub_id == MessageID.RAW_STA.value: if version == MessageVersion.VERSION_1: unpacker = _RawSTA constructor = RawSTA else: unpacker = _RawSTA_v2 constructor = RawSTA_v2 elif sub_id == MessageID.RAW_AP.value: if version == MessageVersion.VERSION_1: unpacker = _RawAP constructor = RawAP else: unpacker = _RawAP_v2 constructor = RawAP_v2 elif sub_id == MessageID.RAW_STA_AP.value: if version == MessageVersion.VERSION_1: unpacker = _RawSTAAP constructor = RawSTAAP else: unpacker = _RawSTAAP_v2 constructor = RawSTAAP_v2 elif sub_id == MessageID.GET_MU.value: # Should only be generated in version 1 if version != MessageVersion.VERSION_1: raise MessageMalformedError( "GET_MU should only be generated in version 1") unpacker = _MediumUtilization constructor = MediumUtilization elif sub_id == MessageID.GET_LC.value: # Should only be generated in version 1 if version != MessageVersion.VERSION_1: raise MessageMalformedError( "GET_LC should only be generated in version 1") unpacker = _LinkCapacity constructor = LinkCapacity elif sub_id == MessageID.EVENTS.value: unpacker = _Events constructor = Events elif sub_id == MessageID.SUMMARY.value: unpacker = _Summary constructor = Summary else: raise MessageMalformedError("Unsupported message ID: %d" % sub_id) if len(buf) < unpacker.size: raise MessageMalformedError("Message too short: %d (need %d)" % (len(buf), unpacker.size)) fields = unpacker.unpack(buf[0:unpacker.size]) if sub_id == MessageID.RAW_AP.value or sub_id == MessageID.RAW_STA_AP.value: # Variable length messages # First unpack the fixed size portion fields = list(fields) + [0] payload = constructor._make(fields) buf = buf[unpacker.size:] # Now unpack the links if sub_id == MessageID.RAW_AP.value: payload = common.unpack_link(payload, buf, 'num_links', 'ap_links', _APLink, APLink) else: payload = common.unpack_link(payload, buf, 'num_links', 'ap_links', _STAAPLink, STAAPLink) else: payload = constructor._make(list(fields)) if sub_id == MessageID.EVENTS.value: payload = common.replace_mac_address_with_string( payload, 'bad_link_da') else: payload = common.replace_mac_address_with_string(payload) if version != MessageVersion.VERSION_1 and ( sub_id == MessageID.RAW_AP.value or sub_id == MessageID.RAW_STA_AP.value or sub_id == MessageID.RAW_STA.value): payload = common.replace_mac_address_with_string(payload, 'iface') return (payload, MessageID(sub_id))