Source code for wlan_exp.ltg

# -*- coding: utf-8 -*-
"""
------------------------------------------------------------------------------
Mango 802.11 Reference Design Experiments Framework 
    - Local Traffic Generation (LTG)
------------------------------------------------------------------------------
License:   Copyright 2019 Mango Communications, Inc. All rights reserved.
           Use and distribution subject to terms in LICENSE.txt
------------------------------------------------------------------------------

This module provides definitions for Local Traffic Generation (LTG) on
wlan_exp nodes.
"""

from wlan_exp.info import InfoStruct
import wlan_exp.util as util

__all__ = ['Schedule', 'SchedulePeriodic', 'ScheduleRandom',
           'Payload', 'PayloadMSDU', 'PayloadRandLengthMSDU', 'PayloadRawMPDU',]

# LTG Schedule IDs - must match ltg_sched_type_t enum in wlan_mac_ltg.h
LTG_SCHED_TYPE_PERIODIC                = 1
LTG_SCHED_TYPE_UNIFORM_RAND            = 2

# LTG Payload IDs - must match ltg_pyld_type_t enum in wlan_mac_ltg.h
LTG_PYLD_TYPE_MSDU_FIXED_LEN           = 1
LTG_PYLD_TYPE_MSDU_RAND_LEN            = 2
LTG_PYLD_TYPE_MPDU_RAW                 = 3

# LTG IDs are u32 in C; define a special value that means "all LTG IDs" for more
#  efficient implementation of start_all/stop_all/remove_all commands
LTG_ID_ALL = 0xFFFFFFFF

class LTGConfig(InfoStruct):
    """LTG configuration common to all LTG types. This class is created by the LTGConfigure
    command when constructing the over-the-wire command message. User code should not created
    instances of this class diractly."""

    flags = {'LTG_START_ON_CREATE': 0x00000001}

    # Must match _ltg_config_otw_t typedef
    struct_fields = [
        ('duration_usec',    'I', 'uint32', 'LTG duration in microseconds'),
        ('start_delay_usec', 'I', 'uint32', 'LTG start delay in microseconds'),
        ('ltg_config_flags', 'I', 'uint32', 'Flags defining LTG behavior'),
    ]

    def __init__(self, start, start_delay, duration):
        super(LTGConfig, self).__init__(field_sets=None, field_defs=self.struct_fields)

        self['ltg_config_flags'] = 0
        self['duration_usec'] = duration
        self['start_delay_usec'] = start_delay

        if(start):
            self['ltg_config_flags'] |= self.flags['LTG_START_ON_CREATE']

#-----------------------------------------------------------------------------
# LTG Schedules
#-----------------------------------------------------------------------------
class Schedule(InfoStruct):
    """Base class for LTG Schedules."""

    def __init__(self, field_defs):
        super(Schedule, self).__init__(field_sets=None, field_defs=field_defs)

[docs]class SchedulePeriodic(Schedule): """Generates a new packet every `interval` seconds. Args: interval (float): Interval between packet creation in float seconds; actual packet creation interval will be quantized to the LTG polling interval in CPU High (typically 64 usec). """ # Must match _ltg_sched_periodic_otw typedef struct_fields = [ ('sched_type', 'I', 'uint32', 'LTG Schedule spec type'), ('interval', 'I', 'uint32', 'Interval between packet creation, in microseconds'), ] def __init__(self, interval): super(SchedulePeriodic, self).__init__(field_defs=self.struct_fields) self['sched_type'] = LTG_SCHED_TYPE_PERIODIC # Convert argument from float seconds to integer microseconds intvl = int(float(interval) * 10**6) if(intvl >= 2**32): raise Exception('ERROR: interval {0} is larger than maximum {1}'.format(interval, 2**32/10**6)) self['interval'] = int(float(interval) * 10**6)
[docs]class ScheduleRandom(Schedule): """Generates a packet at a uniformaly-distributed random interval. Args: min_interval (numeric): Minimum interval between packets in seconds max_interval (numeric): Maximum interval between packets in seconds """ # Must match _ltg_sched_uniform_rand_otw typedef struct_fields = [ ('sched_type', 'I', 'uint32', 'LTG Schedule spec type'), ('min_interval', 'I', 'uint32', 'Minimum interval between packet creation, in microseconds'), ('max_interval', 'I', 'uint32', 'Maximum interval between packet creation, in microseconds'), ] def __init__(self, min_interval, max_interval): super(ScheduleRandom, self).__init__(field_defs=self.struct_fields) self['sched_type'] = LTG_SCHED_TYPE_UNIFORM_RAND min_intvl_usec = int(float(min_interval) * 10**6) max_intvl_usec = int(float(max_interval) * 10**6) if(min_intvl_usec >= 2**32): raise Exception('ERROR: interval {0} is larger than maximum {1}'.format(min_interval, 2**32/10**6)) if(max_intvl_usec >= 2**32): raise Exception('ERROR: interval {0} is larger than maximum {1}'.format(max_interval, 2**32/10**6)) # Convert intervals from float seconds to integer usec self['min_interval'] = min_intvl_usec self['max_interval'] = max_intvl_usec
#----------------------------------------------------------------------------- # LTG Payloads #----------------------------------------------------------------------------- class Payload(InfoStruct): """Base class for LTG Payloads. Should not be instantiated directly.""" def __init__(self, field_defs): # InfoStruct constructor accepts either pre-defined fields in Info.py via 'field_sets' # or caller-specified fields via 'field_defs' super(Payload, self).__init__(field_sets=None, field_defs=field_defs)
[docs]class PayloadMSDU(Payload): """ LTG payload of a fixed-length MSDU with the sepcified destination address. On each execution this LTG payload will create a single pseudo-Ethernet packet and inject the packet into the high MAC framework at the same point as real Ethernet receptions. The pseudo-Ethernet packet has contents: * ``header.da = dest_addr`` * ``header.sa =`` MAC address of the transmittig node * ``header.ethertype = 0x9090`` * ``payload = <LTG ID, uniq_seq, arbitrary bytes>`` The pseudo-Ethernet packet will be passed to the high MAC framework's Ethernet encapsulation functions. The behavior of the encapsulation flow depends on the MAC application. * AP: if the ``dest_addr`` matches the MAC address of an associated node, the new packet will be enqueued for transmission to that node. If the ``dest_addr`` is a multicast address the new packet will be enqueued in the dedicated multicast queue. Any other ``dest_addr`` will cause the packet to be discarded. * STA: the new packet will be enqueued for transmission to the associated AP, like all other data packets * IBSS: the new packet will be enqueued for transmission to the specified address The payload of the LTG MSDU contains the packet's ``uniq_seq`` (64-bit unique sequence number) and the integer ID of the LTG. The remainder of the payload (up to ``length`` bytes) is random. Args: dest_addr (u64 int): MSDU Destination MAC address length (int): MSDU payload length in bytes """ # Must match _ltg_pyld_msdu_fixed_otw typedef struct_fields = [ ('pyld_type', 'I', 'uint32', 'LTG Payload spec type'), ('dest_addr', '6s', '6uint8', 'MSDU Destination MAC address'), ('length', 'H', 'uint16', 'Length of payload in bytes for LTG packets'), ] def __init__(self, dest_addr, length): super(PayloadMSDU, self).__init__(field_defs=self.struct_fields) self['pyld_type'] = LTG_PYLD_TYPE_MSDU_FIXED_LEN self['dest_addr'] = util.mac_addr_to_byte_str(dest_addr) # FIXME: how to validate MSDU payload lengths self['length'] = length
[docs]class PayloadRandLengthMSDU(Payload): """LTG payload of an MSDU with the sepcified destination address and uniform random payload length. This LTG payload has the same behavior as ``PayloadMSDU`` but with random payload length. Args: dest_addr (u64 int): MSDU Destination MAC address min_length (int): Minimum length of the MSDU payload in bytes max_length (int): Maximum length of the MSDU payload in bytes """ # Must match _ltg_pyld_msdu_randlen_otw typedef struct_fields = [ ('pyld_type', 'I', 'uint32', 'LTG Payload spec type'), ('dest_addr', '6s', '6uint8', 'MSDU Destination MAC address'), ('min_length', 'H', 'uint16', 'Minimum length of the MSDU payload in bytes'), ('max_length', 'H', 'uint16', 'Maximum length of the MSDU payload in bytes'), ] def __init__(self, dest_addr, min_length, max_length): super(PayloadRandLengthMSDU, self).__init__(field_defs=self.struct_fields) self['pyld_type'] = LTG_PYLD_TYPE_MSDU_RAND_LEN self['dest_addr'] = util.mac_addr_to_byte_str(dest_addr) # FIXME: how to validate MSDU payload lengths self['min_length'] = min_length self['max_length'] = max_length
[docs]class PayloadRawMPDU(Payload): """LTG payload of an arbitrary MPDU. The MPDU payload can be any byte sequence. The Raw MPDU LTG flow creates packets with arbitrary contents and enqueues the packets for transmission without any further proecssing. Raw MPDUs bypass all high MAC framework functions (i.e. matching destination addresses to known stations (AP), Tx/Rx counts, source MAC address substitution (STA/IBSS), etc.). Packets created by a Raw MPDU LTG also bypass robustness functions in the lower MAC (no RTS/CTS, no retranmissions). The Raw MPDU LTG is useful for creating arbitrary transmissions of 802.11 control packets. It can also be used to transmit packets as 802.11 waveforms which contain invalid 802.11 header/payloads. Args: payload (byte array): MPDU payload, must be iterable of numeric values; each value is cast to a u8. The length of this argument defines the MPDU length. The actual MPDU will be 4 bytes longer than this argument due to the FCS appended before transmission. """ # Must match _ltg_pyld_mpdu_raw_otw typedef struct_fields = [ ('pyld_type', 'I', 'uint32', 'LTG Payload spec type'), ('pyld_flags', 'H', 'uint16', 'Flags for packet construction options'), ('length', 'H', 'uint16', 'Length of payload in bytes'), ('mcs', 'B', 'uint8', 'MCS for MPDU waveform'), ('phy_mode', 'B', 'uint8', 'PHY mode for MPDU waveform'), ('payload', '1400s', '1400uint8', 'MPDU payload'), ] flags = {'OVERRIDE_DEFAULT_MCS': 0x00000001} def __init__(self, payload, mcs=None, phy_mode=None): super(PayloadRawMPDU, self).__init__(field_defs=self.struct_fields) self['pyld_type'] = LTG_PYLD_TYPE_MPDU_RAW self['pyld_flags'] = 0 # Check arguments if mcs is None and phy_mode is not None: raise Exception('ERROR: must specify phy_mode when setting mcs') if mcs is not None and phy_mode is None: raise Exception('ERROR: must specify mcs when setting phy_mode') # Check if user set an explicit MCS if mcs is not None: self['mcs'] = int(mcs) #FIXME: validate? self['phy_mode'] = int(phy_mode) #FIXME: validate? self['pyld_flags'] |= self.flags['OVERRIDE_DEFAULT_MCS'] # FIXME: how to validate MPDU payload lengths pyld_len = len(payload) self['length'] = pyld_len # Initialize payload to all zeros, then fill in user-supplied payload bytes # bytearray() call will throw exception if any element of payload is invalid byte value pyld = 1400*[0] pyld[0:pyld_len] = bytearray(payload) self['payload'] = pyld
# Method that mimics constructor for old FlowConfigCBR class used in old wlan_exp code # This was the most (only?) used LTG FlowConfig # Leave this method for a few releases to assist users in updating scripts to use the new # ltg_create(payload,schedule) syntax for creating LTGs def FlowConfigCBR(dest_addr=None, payload_length=None, interval=None, duration=None): msg = "FlowConfigCBR has been deprecated. LTG payload and schedule params are now specified " msg += "as explicit arguments to ltg_create(). A CBR flow can be created using the PayloadMSDU and " msg += "SchedulePeriodic classes as arguments to ltg_create()." raise Exception(msg)