Package killerbee :: Module dev_apimote
[hide private]
[frames] | no frames]

Source Code for Module killerbee.dev_apimote

  1  ''' 
  2  GoodFET Chipcon RF Radio Client for ApiMote Hardware 
  3   
  4  (C) 2013 Ryan Speers <ryan at riverloopsecurity.com> 
  5   
  6  The ApiMote product is a work in progress. 
  7  This code is being rewritten and refactored. 
  8   
  9  TODO list (help is welcomed): 
 10    - RF testing and calibration for RSSI/dBm 
 11    - Testing carrier jamming and implementing jammer_off() 
 12    - Platform recognition (ApiMote versons) 
 13  ''' 
 14   
 15  import os 
 16  import time 
 17  import struct 
 18  import time 
 19  from datetime import datetime, date, timedelta 
 20  from kbutils import KBCapabilities, makeFCS 
 21  from GoodFETCCSPI import GoodFETCCSPI 
 22   
 23  # Default revision of the ApiMote. This is liable to change at any time 
 24  # as new ApiMote versions are released. Automatic recognition would be nice. 
 25  DEFAULT_REVISION = 2 
 26   
 27  CC2420_REG_SYNC = 0x14 
 28   
29 -class APIMOTE:
30 - def __init__(self, dev, revision=DEFAULT_REVISION):
31 ''' 32 Instantiates the KillerBee class for the ApiMote platform running GoodFET firmware. 33 @type dev: String 34 @param dev: Serial device identifier (ex /dev/ttyUSB0) 35 @type revision: Integer 36 @param revision: The revision number for the ApiMote, which is used by 37 the called GoodFET libraries to properly communicate with 38 and configure the hardware. 39 @return: None 40 @rtype: None 41 ''' 42 self._channel = None 43 self.handle = None 44 self.dev = dev 45 46 self.__revision_num = revision 47 # Set enviroment variables for GoodFET code to use 48 os.environ["platform"] = "apimote%d".format(self.__revision_num) 49 os.environ["board"] = "apimote%d".format(self.__revision_num) 50 self.handle = GoodFETCCSPI() 51 self.handle.serInit(port=self.dev) 52 self.handle.setup() 53 # TODO can we verify here the revision number that was sent is correct? 54 55 self.__stream_open = False 56 self.capabilities = KBCapabilities() 57 self.__set_capabilities()
58
59 - def close(self):
60 self.handle.serClose() 61 self.handle = None
62
63 - def check_capability(self, capab):
64 return self.capabilities.check(capab)
65 - def get_capabilities(self):
66 return self.capabilities.getlist()
67 - def __set_capabilities(self):
68 ''' 69 Sets the capability information appropriate for GoodFETCCSPI client and firmware. 70 @rtype: None 71 @return: None 72 ''' 73 self.capabilities.setcapab(KBCapabilities.FREQ_2400, True) 74 self.capabilities.setcapab(KBCapabilities.SNIFF, True) 75 self.capabilities.setcapab(KBCapabilities.SETCHAN, True) 76 self.capabilities.setcapab(KBCapabilities.INJECT, True) 77 self.capabilities.setcapab(KBCapabilities.PHYJAM_REFLEX, True) 78 self.capabilities.setcapab(KBCapabilities.SET_SYNC, True) 79 return
80 81 # KillerBee expects the driver to implement this function
82 - def get_dev_info(self):
83 ''' 84 Returns device information in a list identifying the device. 85 @rtype: List 86 @return: List of 3 strings identifying device. 87 ''' 88 return [self.dev, "GoodFET Apimote v%d".format(self.__revision_num), ""]
89 90 # KillerBee expects the driver to implement this function
91 - def sniffer_on(self, channel=None):
92 ''' 93 Turns the sniffer on such that pnext() will start returning observed 94 data. Will set the command mode to Air Capture if it is not already 95 set. 96 @type channel: Integer 97 @param channel: Sets the channel, optional 98 @rtype: None 99 ''' 100 self.capabilities.require(KBCapabilities.SNIFF) 101 102 self.handle.RF_promiscuity(1); 103 self.handle.RF_autocrc(0); 104 105 if channel != None: 106 self.set_channel(channel) 107 108 self.handle.CC_RFST_RX(); 109 #print "Sniffer started (listening as %010x on %i MHz)" % (self.handle.RF_getsmac(), self.handle.RF_getfreq()/10**6); 110 111 self.__stream_open = True
112 113 # KillerBee expects the driver to implement this function
114 - def sniffer_off(self):
115 ''' 116 Turns the sniffer off, freeing the hardware for other functions. It is 117 not necessary to call this function before closing the interface with 118 close(). 119 @rtype: None 120 ''' 121 #TODO actually have firmware stop sending us packets! 122 self.__stream_open = False
123 124 # KillerBee expects the driver to implement this function
125 - def set_channel(self, channel):
126 ''' 127 Sets the radio interface to the specifid channel (limited to 2.4 GHz channels 11-26) 128 @type channel: Integer 129 @param channel: Sets the channel, optional 130 @rtype: None 131 ''' 132 self.capabilities.require(KBCapabilities.SETCHAN) 133 134 if channel >= 11 or channel <= 26: 135 self._channel = channel 136 self.handle.RF_setchan(channel) 137 else: 138 raise Exception('Invalid channel')
139 140 # KillerBee expects the driver to implement this function
141 - def inject(self, packet, channel=None, count=1, delay=0):
142 ''' 143 Injects the specified packet contents. 144 @type packet: String 145 @param packet: Packet contents to transmit, without FCS. 146 @type channel: Integer 147 @param channel: Sets the channel, optional 148 @type count: Integer 149 @param count: Transmits a specified number of frames, def=1 150 @type delay: Float 151 @param delay: Delay between each frame, def=1 152 @rtype: None 153 ''' 154 self.capabilities.require(KBCapabilities.INJECT) 155 156 if len(packet) < 1: 157 raise Exception('Empty packet') 158 if len(packet) > 125: # 127 - 2 to accommodate FCS 159 raise Exception('Packet too long') 160 161 if channel != None: 162 self.set_channel(channel) 163 164 self.handle.RF_autocrc(1) #let radio add the CRC 165 for pnum in range(0, count): 166 gfready = [ord(x) for x in packet] #convert packet string to GoodFET expected integer format 167 gfready.insert(0, len(gfready)+2) #add a length that leaves room for CRC 168 self.handle.RF_txpacket(gfready) 169 time.sleep(1)
170 171 # KillerBee expects the driver to implement this function
172 - def pnext(self, timeout=100):
173 ''' 174 Returns a dictionary containing packet data, else None. 175 @type timeout: Integer 176 @param timeout: Timeout to wait for packet reception in usec 177 @rtype: List 178 @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 ] 179 ''' 180 if self.__stream_open == False: 181 self.sniffer_on() #start sniffing 182 183 packet = None; 184 start = datetime.now() 185 186 while (packet is None and (start + timedelta(microseconds=timeout) > datetime.now())): 187 packet = self.handle.RF_rxpacket() 188 rssi = self.handle.RF_getrssi() #TODO calibrate 189 190 if packet is None: 191 return None 192 193 frame = packet[1:] 194 if frame[-2:] == makeFCS(frame[:-2]): validcrc = True 195 else: validcrc = False 196 #Return in a nicer dictionary format, so we don't have to reference by number indicies. 197 #Note that 0,1,2 indicies inserted twice for backwards compatibility. 198 result = {0:frame, 1:validcrc, 2:rssi, 'bytes':frame, 'validcrc':validcrc, 'rssi':rssi, 'location':None} 199 result['dbm'] = rssi - 45 #TODO tune specifically to the Apimote platform (does ext antenna need to different?) 200 result['datetime'] = datetime.combine(date.today(), (datetime.now()).time()) #TODO address timezones by going to UTC everywhere 201 return result
202
203 - def ping(self, da, panid, sa, channel=None):
204 ''' 205 Not yet implemented. 206 @return: None 207 @rtype: None 208 ''' 209 raise Exception('Not yet implemented')
210
211 - def jammer_on(self, channel=None):
212 ''' 213 Not yet implemented. 214 @type channel: Integer 215 @param channel: Sets the channel, optional 216 @rtype: None 217 ''' 218 self.capabilities.require(KBCapabilities.PHYJAM_REFLEX) 219 220 self.handle.RF_promiscuity(1) 221 self.handle.RF_autocrc(0) 222 if channel != None: 223 self.set_channel(channel) 224 self.handle.CC_RFST_RX() 225 self.handle.RF_carrier() #constant carrier wave jamming
226 #self.handle.RF_reflexjam() #reflexive jamming (advanced) 227
228 - def set_sync(self, sync=0xA70F):
229 '''Set the register controlling the 802.15.4 PHY sync byte.''' 230 self.capabilities.require(KBCapabilities.SET_SYNC) 231 if (sync >> 16) > 0: 232 raise Exception("Sync word (%x) must be 2-bytes or less." % sync) 233 return self.handle.poke(CC2420_REG_SYNC, sync)
234
235 - def jammer_off(self, channel=None):
236 ''' 237 Not yet implemented. 238 @return: None 239 @rtype: None 240 ''' 241 #TODO implement 242 raise Exception('Not yet implemented')
243