Beispiel #1
0
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))
Beispiel #2
0
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))
Beispiel #3
0
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)
Beispiel #4
0
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)
Beispiel #5
0
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))
Beispiel #6
0
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)
Beispiel #7
0
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)
Beispiel #8
0
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
Beispiel #9
0
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)
Beispiel #10
0
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
Beispiel #11
0
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
Beispiel #12
0
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))