示例#1
0
    def __dissect_pkt(self, packet):
        '''
        internal routine to deconstruct serial text received from device
        packet will be in the format: "{{num}   {oflo}  {seq}   {per}  {err} {lqi}  {rssi}{ed}   {gain}       {status} {time}       {fp}{length}{payload}}"
        @type packet: String
        @param: packet: The raw packet to dissect
        @rtype: List
        @return: Returns None if packet is not in correct format.  When a packet is correct,
                 a list is returned, in the form [ String: Frame | Bool: Valid CRC | Int: Unscaled RSSI ]
        '''
        data = packet[1:].replace('{', ' ').replace('}', ' ').split()
        # should be 12 fields + payload length + payload
        if not data or not len(data[13:]) == int(data[12], 16):
            print "Error parsing stream received from device (payload size error):", packet
            return None
        # payload is in the form e.g. "0x03 0x08 0xA3 0xFF 0xFF 0xFF 0xFF 0x07" so we need to convert to a string
        frame = ''
        for x in data[13:]:
            try:
                frame += chr(int(x, 16))
            except:
                print "Error parsing stream received from device (invalid payload):", packet
                return None

        # Parse other useful fields
        try:
            rssi = int(data[6])
            # sniffer doesn't give us the CRC so we must add it, but must be correct or we would not have received it
            validcrc = True
            frame += makeFCS(frame)
        except:
            print "Error parsing stream received from device (invalid rssi or FCS build error):", packet
            return None
        return [frame, validcrc, rssi]
示例#2
0
    def pnext(self, timeout=100):
        '''
        Returns a dictionary containing packet data, else None.
        @type timeout: Integer
        @param timeout: Timeout to wait for packet reception in usec
        @rtype: List
        @return: Returns None is timeout expires and no packet received.  When a packet is received, a dictionary is returned with the keys bytes (string of packet bytes), validcrc (boolean if a vaid CRC), rssi (unscaled RSSI), and location (may be set to None). For backwards compatibility, keys for 0,1,2 are provided such that it can be treated as if a list is returned, in the form [ String: packet contents | Bool: Valid CRC | Int: Unscaled RSSI ]
        '''
        if self.__stream_open == False:
            self.sniffer_on() #start sniffing

        packet = None;
        start = datetime.utcnow()

        while (packet is None and (start + timedelta(microseconds=timeout) > datetime.utcnow())):
            packet = self.handle.RF_rxpacket()
            rssi = self.handle.RF_getrssi() #TODO calibrate

        if packet is None:
            return None

        frame = packet[1:]
        if frame[-2:] == makeFCS(frame[:-2]): validcrc = True
        else: validcrc = False
        #Return in a nicer dictionary format, so we don't have to reference by number indicies.
        #Note that 0,1,2 indicies inserted twice for backwards compatibility.
        result = {0:frame, 1:validcrc, 2:rssi, 'bytes':frame, 'validcrc':validcrc, 'rssi':rssi, 'location':None}
        result['dbm'] = rssi - 45 #TODO tune specifically to the Apimote platform (does ext antenna need to different?)
        result['datetime'] = datetime.utcnow()
        return result
示例#3
0
    def pnext(self, timeout=100):
        '''
        Returns a dictionary containing packet data, else None.
        @type timeout: Integer
        @param timeout: Timeout to wait for packet reception in usec
        @rtype: List
        @return: Returns None is timeout expires and no packet received.  When a packet is received, a dictionary is returned with the keys bytes (string of packet bytes), validcrc (boolean if a vaid CRC), rssi (unscaled RSSI), and location (may be set to None). For backwards compatibility, keys for 0,1,2 are provided such that it can be treated as if a list is returned, in the form [ String: packet contents | Bool: Valid CRC | Int: Unscaled RSSI ]
        '''
        if self.__stream_open == False:
            self.sniffer_on()

        packet = None
        start = datetime.utcnow()

        while (packet is None and (start + timedelta(microseconds=timeout) > datetime.utcnow())):
            packet = self.handle.RF_rxpacket()
            rssi = self.handle.RF_getrssi() #TODO calibrate

        if packet is None:
            return None

        frame = packet[1:]
        validcrc = False
        if frame[-2:] == makeFCS(frame[:-2]):
            validcrc = True
        #Return in a nicer dictionary format, so we don't have to reference by number indicies.
        #Note that 0,1,2 indicies inserted twice for backwards compatibility.
        result = {0:frame, 1:validcrc, 2:rssi, 'bytes':frame, 'validcrc':validcrc, 'rssi':rssi, 'location':None}
        result['dbm'] = rssi - 45 #TODO tune specifically to the Apimote platform (does ext antenna need to different?)
        result['datetime'] = datetime.utcnow()
        return result
示例#4
0
    def indirect_inject(self, packet, channel=None, oneshot=False):
        '''
        Preloads ApiMote with payload provided as packet argument.
        Reflexively jams all traffic while serving forged frame pending ACKs and the preloaded cmd.
        @type packet: String
        @param packet: Packet contents to pre-load, excluding length and FCS (generated below).
        @type channel: Integer
        @param channel: Sets the channel.  Optional.
        @type oneshot: Bool
        @param oneshot: Inject sequence indefinitely if true, exactly once if false.  Optional.
        '''
        # Hardware must be able to detect the beginning of a packet; through SFD pin for ApiMote
        self.capabilities.require(KBCapabilities.PHYJAM_REFLEX)

        self.handle.RF_promiscuity(1)    #Configure the radio
        self.handle.RF_autocrc(0)
        if channel != None:
            self.set_channel(channel)
        self.handle.CC_RFST_RX()         #Put the radio in RX mode to begin

        #Prepare command by calculating/appending the FCS and prepending the oneshot value and length.
        fcs = [ord(x) for x in makeFCS(packet)]
        gfready = [ord(x) for x in packet]
        gfready.append(fcs[0])
        gfready.append(fcs[1])
        gfready.insert(0, len(gfready))
        gfready.insert(0, 0x00 if oneshot is False else 0x01)

        #Send the command to the ApiMote
        self.handle.RF_reflexjam_indirect(gfready)
示例#5
0
    def pnext_rec(self, timeout=100):
        pdata = ''
        ldata = ''

        self.handle.timeout = timeout  # Allow pySerial to handle timeout
        startChar = self.handle.read()
        if startChar == None: return None  # Sense timeout case and return

        # Listens for serial message of general format R!<data>;
        if startChar == "R":  # Get packet data
            if self.handle.read() == "!":
                x = self.handle.read()
                while (x != ";"):
                    pdata += x
                    x = self.handle.read()
        if startChar == "L":  # Get location data
            if self.handle.read() == "!":
                x = self.handle.read()
                while (x != ";"):
                    ldata += x
                    x = self.handle.read()
        if startChar == "[":  # Sense when done reading from EEPROM
            if self.handle.read(
                    40) == "{[ DONE READING BACK ALL LOGGED DATA ]}]":
                raise StopIteration("All Data Read")

        # If location received, update our local variables:
        if ldata != None and ldata != '':
            self.processLocationUpdate(ldata)

        if pdata == None or pdata == '':
            return None

        # Parse received data as <rssi>!<time>!<packtlen>!<frame>
        data = pdata.split("!", 3)
        try:
            rssi = ord(data[0])
            frame = data[3]
            if frame[-2:] == makeFCS(frame[:-2]): validcrc = True
            else: validcrc = False
        except:
            print "Error parsing stream received from device:", pdata, data
            return None
        #Return in a nicer dictionary format, so we don't have to reference by number indicies.
        #Note that 0,1,2 indicies inserted twice for backwards compatibility.
        result = {
            0: frame,
            1: validcrc,
            2: rssi,
            'bytes': frame,
            'validcrc': validcrc,
            'rssi': rssi
        }
        result[
            'dbm'] = None  #TODO calculate dBm antenna signal based on RSSI formula
        result['datetime'] = self.getCaptureDateTime(data)
        result['location'] = (self.lon, self.lat, self.alt)
        return result
示例#6
0
    def pnext_rec(self, timeout=100):
        pdata = ''
        ldata = ''

        self.handle.timeout=timeout         # Allow pySerial to handle timeout
        startChar = self.handle.read()
        if startChar == None: return None   # Sense timeout case and return

        # Listens for serial message of general format R!<data>;
        if startChar == "R":                # Get packet data
            if self.handle.read() == "!":
                x = self.handle.read()
                while (x != ";"):
                    pdata += x
                    x = self.handle.read()
        if startChar == "L":                # Get location data
            if self.handle.read() == "!":
                x = self.handle.read()
                while (x != ";"):
                    ldata += x
                    x = self.handle.read()
        if startChar == "[":                # Sense when done reading from EEPROM
            if self.handle.read(40) == "{[ DONE READING BACK ALL LOGGED DATA ]}]":
                raise StopIteration("All Data Read")

        # If location received, update our local variables:
        if ldata != None and ldata != '':
            self.processLocationUpdate(ldata)

        if pdata == None or pdata == '':
            return None

        # Parse received data as <rssi>!<time>!<packtlen>!<frame>
        data = pdata.split("!", 3)
        try:
            rssi = ord(data[0])
            frame = data[3]
            if frame[-2:] == makeFCS(frame[:-2]): validcrc = True
            else: validcrc = False
        except:
            print "Error parsing stream received from device:", pdata, data
            return None
        #Return in a nicer dictionary format, so we don't have to reference by number indicies.
        #Note that 0,1,2 indicies inserted twice for backwards compatibility.
        result = {0:frame, 1:validcrc, 2:rssi, 'bytes':frame, 'validcrc':validcrc, 'rssi':rssi}
        result['dbm'] = None #TODO calculate dBm antenna signal based on RSSI formula
        result['datetime'] = self.getCaptureDateTime(data)
        result['location'] = (self.lon, self.lat, self.alt)
        return result
示例#7
0
 def __parse_zep_v2(data):
     '''
     Parse the packet from the ZigBee encapsulation protocol version 2/3 and 
     return the fields desired for usage by pnext().
     There is support here for some oddities specific to the Sewio 
     implementation of ZEP and the packet, such as CC24xx format FCS 
     headers being expected.
     
     The ZEP protocol parsing is mainly based on Wireshark source at:
     http://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors/packet-zep.c
     * ZEP v2 Header will have the following format (if type=1/Data):
     *  |Preamble|Version| Type |Channel ID|Device ID|CRC/LQI Mode|LQI Val|NTP Timestamp|Sequence#|Reserved|Length|
     *  |2 bytes |1 byte |1 byte|  1 byte  | 2 bytes |   1 byte   |1 byte |   8 bytes   | 4 bytes |10 bytes|1 byte|
     * ZEP v2 Header will have the following format (if type=2/Ack):
     *  |Preamble|Version| Type |Sequence#|
     *  |2 bytes |1 byte |1 byte| 4 bytes |
     #define ZEP_PREAMBLE        "EX"
     #define ZEP_V2_HEADER_LEN   32
     #define ZEP_V2_ACK_LEN      8
     #define ZEP_V2_TYPE_DATA    1
     #define ZEP_V2_TYPE_ACK     2
     #define ZEP_LENGTH_MASK     0x7F
     '''
     # Unpack constant part of ZEPv2
     (preamble, version, zeptype) = unpack('<HBB', data[:4])
     if preamble != 22597 or version < 2:  # 'EX'==22597, and v3 is compat with v2 (I think??)
         raise Exception(
             "Can not parse provided data as ZEP due to incorrect preamble or unsupported version."
         )
     if zeptype == 1:  #data
         (ch, devid, crcmode, lqival, ntpsec, ntpnsec, seqnum,
          length) = unpack(">BHBBIII10xB", data[4:32])
         #print "Data ZEP:", ch, devid, crcmode, lqival, ntpsec, ntpnsec, seqnum, length
         #We could convert the NTP timestamp received to system time, but the
         # Sewio firmware uses "relative timestamping" where it begins at 0 each time
         # the sniffer is started. Thus, it isn't that useful to us, so we just add the
         # time the packet is received at the host instead.
         #print "\tConverted time:", ntp_to_system_time(ntpsec, ntpnsec)
         recdtime = datetime.utcnow()
         #The LQI comes in ZEP, but the RSSI comes in the first byte of the FCS,
         # if the FCS was correct. If the byte is 0xb1, Wireshark appears to do 0xb1-256 = -79 dBm.
         # It appears that if CRC/LQI Mode field == 1, then checksum was bad, so the RSSI isn't
         # available, as the CRC is left in the packet. If it == 0, then the first byte of FCS is the RSSI.
         # From Wireshark:
         #define IEEE802154_CC24xx_CRC_OK            0x8000
         #define IEEE802154_CC24xx_RSSI              0x00FF
         frame = data[32:]
         # A length vs len(frame) check is not used here but is an
         #  additional way to verify that all is good (length == len(frame)).
         if crcmode == 0:
             validcrc = ((ord(data[-1]) & 0x80) == 0x80)
             rssi = ord(data[-2])
             # We have to trust the sniffer that the FCS was OK, so we compute
             #  what a good FCS should be and patch it back into the packet.
             frame = frame[:-2] + makeFCS(frame[:-2])
         else:
             validcrc = False
             rssi = None
         return (frame, ch, validcrc, rssi, lqival, recdtime)
     elif zeptype == 2:  #ack
         frame = data[8:]
         (seqnum) = unpack(">I", data[4:8])
         recdtime = datetime.utcnow()
         validcrc = (frame[-2:] == makeFCS(frame[:-2]))
         return (frame, None, validcrc, None, None, recdtime)
     return None
示例#8
0
    def pnext(self, timeout=100):
        """
        Returns a dictionary containing packet data, else None.
        @type timeout: Integer
        @param timeout: Timeout to wait for packet reception in usec
        @rtype: List
        @return: Returns None is timeout expires and no packet received.  When a packet is received, a dictionary is returned with the keys bytes (string of packet bytes), validcrc (boolean if a vaid CRC), rssi (unscaled RSSI), and location (may be set to None). For backwards compatibility, keys for 0,1,2 are provided such that it can be treated as if a list is returned, in the form [ String: packet contents | Bool: Valid CRC | Int: Unscaled RSSI ]
        """
        if self.__stream_open == False:
            self.sniffer_on() #start sniffing

        ret = None
        framedata = []
        explen = 0 # expected remaining packet length
        while True:
            pdata = None
            try:

                pdata = self.dev.read(self._data_ep, self._maxPacketSize, timeout=timeout)
            except usb.core.USBError as e:
                if e.errno != 110: #Operation timed out
                    print("Error args: {}".format(e.args))
                    raise e
                    #TODO error handling enhancements for USB 1.0
                else:
                    return None

            # Accumulate in 'framedata' until we have an entire frame
            for byteval in pdata:
                framedata.append(struct.pack("B", byteval))

            if len(pdata) < 64:

                if len(pdata) < 2:
                    #print "ERROR: Very short frame"
                    return None

                framelen = ord(framedata[1])
                if len(framedata) - 3 != framelen:
                    #print "ERROR: Bad frame length: expected {0}, got {1}".format(framelen, len(framedata))
                    return None

                if framedata[0] != '\x00':
                    #print "Not a capture frame:", framedata
                    return None

                payloadlen = ord(framedata[7]) # Includes TI format FCS
                payload = framedata[8:]

                if len(payload) != payloadlen:
                    # TODO: Log "ERROR: Bad payload length"
                    return None

                # See TI Smart RF User Guide for usage of 'CC24XX' format FCS fields
                # in last two bytes of framedata. Note that we remove these before return of the frame.

                # RSSI is signed value, offset by 73 (see CC2530 data sheet for offset)
                rssi = struct.unpack("b", framedata[-2])[0] - 73

                fcsx = ord(framedata[-1])
                # validcrc is the bit 7 in fcsx
                validcrc  = (fcsx & 0x80) == 0x80
                # correlation value is bits 0-6 in fcsx
                correlation = fcsx & 0x7f

                ret = {1:validcrc, 2:rssi,
                        'validcrc':validcrc, 'rssi':rssi, 'lqi':correlation,
                        'dbm':rssi,'datetime':datetime.utcnow()}

                # Convert the framedata to a string for the return value, and replace the TI FCS with a real FCS
                # if the radio told us that the FCS had passed validation.
                if validcrc:
                    ret[0] = ''.join(payload[:-2]) + makeFCS(payload[:-2])
                else:
                    ret[0] = ''.join(payload)
                ret['bytes'] = ret[0]
                return ret
 def __parse_zep_v2(data):
     '''
     Parse the packet from the ZigBee encapsulation protocol version 2/3 and 
     return the fields desired for usage by pnext().
     There is support here for some oddities specific to the Sewio 
     implementation of ZEP and the packet, such as CC24xx format FCS 
     headers being expected.
     
     The ZEP protocol parsing is mainly based on Wireshark source at:
     http://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors/packet-zep.c
     * ZEP v2 Header will have the following format (if type=1/Data):
     *  |Preamble|Version| Type |Channel ID|Device ID|CRC/LQI Mode|LQI Val|NTP Timestamp|Sequence#|Reserved|Length|
     *  |2 bytes |1 byte |1 byte|  1 byte  | 2 bytes |   1 byte   |1 byte |   8 bytes   | 4 bytes |10 bytes|1 byte|
     * ZEP v2 Header will have the following format (if type=2/Ack):
     *  |Preamble|Version| Type |Sequence#|
     *  |2 bytes |1 byte |1 byte| 4 bytes |
     #define ZEP_PREAMBLE        "EX"
     #define ZEP_V2_HEADER_LEN   32
     #define ZEP_V2_ACK_LEN      8
     #define ZEP_V2_TYPE_DATA    1
     #define ZEP_V2_TYPE_ACK     2
     #define ZEP_LENGTH_MASK     0x7F
     '''
     # Unpack constant part of ZEPv2
     (preamble, version, zeptype) = unpack('<HBB', data[:4])
     if preamble != 22597 or version < 2: # 'EX'==22597, and v3 is compat with v2 (I think??)
         raise Exception("Can not parse provided data as ZEP due to incorrect preamble or unsupported version.")
     if zeptype == 1: #data
         (ch, devid, crcmode, lqival, ntpsec, ntpnsec, seqnum, length) = unpack(">BHBBIII10xB", data[4:32])
         #print "Data ZEP:", ch, devid, crcmode, lqival, ntpsec, ntpnsec, seqnum, length
         #We could convert the NTP timestamp received to system time, but the
         # Sewio firmware uses "relative timestamping" where it begins at 0 each time
         # the sniffer is started. Thus, it isn't that useful to us, so we just add the
         # time the packet is received at the host instead.
         #print "\tConverted time:", ntp_to_system_time(ntpsec, ntpnsec)
         recdtime = datetime.utcnow()
         #The LQI comes in ZEP, but the RSSI comes in the first byte of the FCS,
         # if the FCS was correct. If the byte is 0xb1, Wireshark appears to do 0xb1-256 = -79 dBm.
         # It appears that if CRC/LQI Mode field == 1, then checksum was bad, so the RSSI isn't
         # available, as the CRC is left in the packet. If it == 0, then the first byte of FCS is the RSSI.
         # From Wireshark:
         #define IEEE802154_CC24xx_CRC_OK            0x8000
         #define IEEE802154_CC24xx_RSSI              0x00FF
         frame = data[32:]
         # A length vs len(frame) check is not used here but is an 
         #  additional way to verify that all is good (length == len(frame)).
         if crcmode == 0:
             validcrc = ((ord(data[-1]) & 0x80) == 0x80)
             rssi = ord(data[-2])
             # We have to trust the sniffer that the FCS was OK, so we compute
             #  what a good FCS should be and patch it back into the packet.
             frame = frame[:-2] + makeFCS(frame[:-2])
         else:
             validcrc = False
             rssi = None
         return (frame, ch, validcrc, rssi, lqival, recdtime)
     elif zeptype == 2: #ack
         frame = data[8:]
         (seqnum) = unpack(">I", data[4:8])
         recdtime = datetime.utcnow()
         validcrc = (frame[-2:] == makeFCS(frame[:-2]))
         return (frame, None, validcrc, None, None, recdtime)
     return None