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
24
25 DEFAULT_REVISION = 2
26
27 CC2420_REG_SYNC = 0x14
28
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
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
54
55 self.__stream_open = False
56 self.capabilities = KBCapabilities()
57 self.__set_capabilities()
58
60 self.handle.serClose()
61 self.handle = None
62
64 return self.capabilities.check(capab)
66 return self.capabilities.getlist()
80
81
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
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
110
111 self.__stream_open = True
112
113
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
122 self.__stream_open = False
123
124
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
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:
159 raise Exception('Packet too long')
160
161 if channel != None:
162 self.set_channel(channel)
163
164 self.handle.RF_autocrc(1)
165 for pnum in range(0, count):
166 gfready = [ord(x) for x in packet]
167 gfready.insert(0, len(gfready)+2)
168 self.handle.RF_txpacket(gfready)
169 time.sleep(1)
170
171
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()
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()
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
197
198 result = {0:frame, 1:validcrc, 2:rssi, 'bytes':frame, 'validcrc':validcrc, 'rssi':rssi, 'location':None}
199 result['dbm'] = rssi - 45
200 result['datetime'] = datetime.combine(date.today(), (datetime.now()).time())
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
226
227
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
236 '''
237 Not yet implemented.
238 @return: None
239 @rtype: None
240 '''
241
242 raise Exception('Not yet implemented')
243