def run(self): try: p =[] while self.goOn: # wait for data p = os.read(self.tunIf,self.ETHERNET_MTU) # convert input from a string to a byte list p = [ord(b) for b in p] # debug info if log.isEnabledFor(logging.DEBUG): log.debug('packet captured on tun interface: {0}'.format(u.formatBuf(p))) # remove tun ID octets p = p[4:] # make sure it's an IPv6 packet (i.e., starts with 0x6x) if (p[0]&0xf0) != 0x60: log.info('this is not an IPv6 packet') continue # because of the nature of tun for Windows, p contains ETHERNET_MTU # bytes. Cut at length of IPv6 packet. p = p[:self.IPv6_HEADER_LENGTH+256*p[4]+p[5]] # call the callback self.callback(p) except Exception as err: errMsg=u.formatCrashMessage(self.name,err) print errMsg log.critical(errMsg) sys.exit(1)
def _format_IPv6(self, ipv6, ipv6_bytes): output = [] output += [''] output += [''] output += [ '============================= IPv6 packet =====================================' ] output += [''] output += ['Version: {0}'.format(ipv6['version'])] output += ['Traffic class: {0}'.format(ipv6['traffic_class'])] output += ['Flow label: {0}'.format(ipv6['flow_label'])] output += ['Payload length: {0}'.format(ipv6['payload_length'])] output += ['Hop Limit: {0}'.format(ipv6['hop_limit'])] output += ['Next header: {0}'.format(ipv6['next_header'])] output += [ 'Source Addr.: {0}'.format(u.formatIPv6Addr(ipv6['src_addr'])) ] output += [ 'Destination Addr.: {0}'.format(u.formatIPv6Addr(ipv6['dst_addr'])) ] output += [ 'Payload: {0}'.format(u.formatBuf(ipv6['payload'])) ] output += [''] output += [self._formatWireshark(ipv6_bytes)] output += [''] return '\n'.join(output)
def run(self): try: p = [] while self.goOn: # wait for data p = os.read(self.tunIf, self.ETHERNET_MTU) # convert input from a string to a byte list p = [ord(b) for b in p] # debug info if log.isEnabledFor(logging.DEBUG): log.debug('packet captured on tun interface: {0}'.format( u.formatBuf(p))) # make sure it's an IPv6 packet (i.e., starts with 0x6x) if (p[0] & 0xf0) != 0x60: log.info('this is not an IPv6 packet') continue # because of the nature of tun for Windows, p contains ETHERNET_MTU # bytes. Cut at length of IPv6 packet. p = p[:self.IPv6_HEADER_LENGTH + 256 * p[4] + p[5]] # call the callback self.callback(p) except Exception as err: errMsg = u.formatCrashMessage(self.name, err) print errMsg log.critical(errMsg) sys.exit(1)
def parseInput(self,input): # log if log.isEnabledFor(logging.DEBUG): log.debug("received input={0}".format(input)) # ensure input not short longer than header self._checkLength(input) headerBytes = input[:3] # extract moteId and statusElem try: (moteId,statusElem) = struct.unpack('<HB',''.join([chr(c) for c in headerBytes])) except struct.error: raise ParserException(ParserException.DESERIALIZE,"could not extract moteId and statusElem from {0}".format(headerBytes)) # log if log.isEnabledFor(logging.DEBUG): log.debug("moteId={0} statusElem={1}".format(moteId,statusElem)) # jump the header bytes input = input[3:] # call the next header parser for key in self.fieldsParsingKeys: if statusElem==key.val: # log if log.isEnabledFor(logging.DEBUG): log.debug("parsing {0}, ({1} bytes) as {2}".format(input,len(input),key.name)) # parse byte array try: fields = struct.unpack(key.structure,''.join([chr(c) for c in input])) except struct.error as err: raise ParserException( ParserException.DESERIALIZE, "could not extract tuple {0} by applying {1} to {2}; error: {3}".format( key.name, key.structure, u.formatBuf(input), str(err) ) ) # map to name tuple returnTuple = self.named_tuple[key.name](*fields) # log if log.isEnabledFor(logging.DEBUG): log.debug("parsed into {0}".format(returnTuple)) # map to name tuple return 'status', returnTuple # if you get here, no key was found raise ParserException(ParserException.NO_KEY, "type={0} (\"{1}\")".format( input[0], chr(input[0])))
def parseInput(self,input): # log if log.isEnabledFor(logging.DEBUG): log.debug("received input={0}".format(input)) # ensure input not short longer than header self._checkLength(input) headerBytes = input[:3] # extract moteId and statusElem try: (moteId,statusElem) = struct.unpack('<HB',''.join([chr(c) for c in headerBytes])) except struct.error: raise ParserException(ParserException.DESERIALIZE,"could not extract moteId and statusElem from {0}".format(headerBytes)) # log if log.isEnabledFor(logging.DEBUG): log.debug("moteId={0} statusElem={1}".format(moteId,statusElem)) # jump the header bytes input = input[3:] # call the next header parser for key in self.fieldsParsingKeys: if statusElem==key.val: # log if log.isEnabledFor(logging.DEBUG): log.debug("parsing {0}, ({1} bytes) as {2}".format(input,len(input),key.name)) # parse byte array try: fields = struct.unpack(key.structure,''.join([chr(c) for c in input])) except struct.error as err: raise ParserException( ParserException.DESERIALIZE, "could not extract tuple {0} by applying {1} to {2}; error: {3}".format( key.name, key.structure, u.formatBuf(input), str(err) ) ) # map to name tuple returnTuple = self.named_tuple[key.name](*fields) # log if log.isEnabledFor(logging.DEBUG): log.debug("parsed into {0}".format(returnTuple)) # map to name tuple return ('status',returnTuple) # if you get here, no key was found raise ParserException(ParserException.NO_KEY, "type={0} (\"{1}\")".format( input[0], chr(input[0])))
def _format_lowpan(self, lowpan, lowpan_bytes): output = [] output += [''] output += [''] output += [ '============================= lowpan packet ===================================' ] output += [''] output += ['tf: {0}'.format(u.formatBuf(lowpan['tf']))] output += ['nh: {0}'.format(u.formatBuf(lowpan['nh']))] output += [ 'hlim: {0}'.format(u.formatBuf(lowpan['hlim'])) ] output += ['cid: {0}'.format(u.formatBuf(lowpan['cid']))] output += [ 'src_addr: {0}'.format(u.formatBuf(lowpan['src_addr'])) ] output += [ 'dst_addr: {0}'.format(u.formatBuf(lowpan['dst_addr'])) ] if 'route' in lowpan: output += ['source route:'] for hop in lowpan['route']: output += [' - {0}'.format(u.formatAddr(hop))] output += [ 'payload: {0}'.format(u.formatBuf(lowpan['payload'])) ] output += [''] output += [self._formatWireshark(lowpan_bytes)] output += [''] return '\n'.join(output)
def _format_IPv6(self,ipv6,ipv6_bytes): output = [] output += [''] output += [''] output += ['============================= IPv6 packet ====================================='] output += [''] output += ['Version: {0}'.format(ipv6['version'])] output += ['Traffic class: {0}'.format(ipv6['traffic_class'])] output += ['Flow label: {0}'.format(ipv6['flow_label'])] output += ['Payload length: {0}'.format(ipv6['payload_length'])] output += ['Hop Limit: {0}'.format(ipv6['hop_limit'])] output += ['Next header: {0}'.format(ipv6['next_header'])] output += ['Source Addr.: {0}'.format(u.formatIPv6Addr(ipv6['src_addr']))] output += ['Destination Addr.: {0}'.format(u.formatIPv6Addr(ipv6['dst_addr']))] output += ['Payload: {0}'.format(u.formatBuf(ipv6['payload']))] output += [''] output += [self._formatWireshark(ipv6_bytes)] output += [''] return '\n'.join(output)
def _format_lowpan(self,lowpan,lowpan_bytes): output = [] output += [''] output += [''] output += ['============================= lowpan packet ==================================='] output += [''] output += ['tf: {0}'.format(u.formatBuf(lowpan['tf']))] output += ['nh: {0}'.format(u.formatBuf(lowpan['nh']))] output += ['hlim: {0}'.format(u.formatBuf(lowpan['hlim']))] output += ['cid: {0}'.format(u.formatBuf(lowpan['cid']))] output += ['src_addr: {0}'.format(u.formatBuf(lowpan['src_addr']))] output += ['dst_addr: {0}'.format(u.formatBuf(lowpan['dst_addr']))] if 'route' in lowpan: output += ['source route:'] for hop in lowpan['route']: output += [' - {0}'.format(u.formatAddr(hop))] output += ['payload: {0}'.format(u.formatBuf(lowpan['payload']))] output += [''] output += [self._formatWireshark(lowpan_bytes)] output += [''] return '\n'.join(output)
def _eventBusNotification(self,signal,sender,data): ''' Adds the signal to stats log and performs signal-specific handling ''' with self.dataLock: key = (sender,signal) if key not in self.stats: self.stats[key] = 0 self.stats[key] += 1 if signal=='infoDagRoot' and data['isDAGroot']==1: self.dagRootEui64 = data['eui64'][:] if signal=='wirelessTxStart': # this signal only exists is simulation mode self.simMode = True if self.wiresharkDebugEnabled: if self.simMode: # simulation mode if signal=='wirelessTxStart': # Forwards a copy of the packet exchanged between simulated motes # to the tun interface for debugging. (moteId,frame,frequency) = data if log.isEnabledFor(logging.DEBUG): output = [] output += [''] output += ['- moteId: {0}'.format(moteId)] output += ['- frame: {0}'.format(u.formatBuf(frame))] output += ['- frequency: {0}'.format(frequency)] output = '\n'.join(output) log.debug(output) print output # poipoi assert len(frame)>=1+2 # 1 for length byte, 2 for CRC # cut frame in pieces length = frame[0] body = frame[1:-2] crc = frame[-2:] # wrap with zep header zep = self._wrapZepCrc(body,frequency) self._dispatchMeshDebugPacket(zep) else: # non-simulation mode if signal=='fromMote.data': # Forwards a copy of the data received from a mode # to the Internet interface for debugging. (previousHop,lowpan) = data zep = self._wrapMacAndZep( previousHop = previousHop, nextHop = self.dagRootEui64, lowpan = lowpan, ) self._dispatchMeshDebugPacket(zep) if signal=='fromMote.sniffedPacket': body = data[0:-3] crc = data[-3:-1] frequency = data[-1] # wrap with zep header zep = self._wrapZepCrc(body,frequency) self._dispatchMeshDebugPacket(zep) if signal=='bytesToMesh': # Forwards a copy of the 6LoWPAN packet destined for the mesh # to the tun interface for debugging. (nextHop,lowpan) = data zep = self._wrapMacAndZep( previousHop = self.dagRootEui64, nextHop = nextHop, lowpan = lowpan, ) self._dispatchMeshDebugPacket(zep)
def _indicateDAO(self,tup): ''' Indicate a new DAO was received. This function parses the received packet, and if valid, updates the information needed to compute source routes. ''' # retrieve source and destination try: source = tup[0] if len(source)>8: source=source[len(source)-8:] dao = tup[1] except IndexError: log.warning("DAO too short ({0} bytes), no space for destination and source".format(len(dao))) return # log if log.isEnabledFor(logging.DEBUG): output = [] output += ['received DAO:'] output += ['- source : {0}'.format(u.formatAddr(source))] output += ['- dao : {0}'.format(u.formatBuf(dao))] output = '\n'.join(output) log.debug(output) # retrieve DAO header dao_header = {} dao_transit_information = {} dao_target_information = {} try: # RPL header dao_header['RPL_InstanceID'] = dao[0] dao_header['RPL_flags'] = dao[1] dao_header['RPL_Reserved'] = dao[2] dao_header['RPL_DAO_Sequence'] = dao[3] # DODAGID dao_header['DODAGID'] = dao[4:20] dao = dao[20:] # retrieve transit information header and parents parents = [] children = [] while len(dao)>0: if dao[0]==self._TRANSIT_INFORMATION_TYPE: # transit information option dao_transit_information['Transit_information_type'] = dao[0] dao_transit_information['Transit_information_length'] = dao[1] dao_transit_information['Transit_information_flags'] = dao[2] dao_transit_information['Transit_information_path_control'] = dao[3] dao_transit_information['Transit_information_path_sequence'] = dao[4] dao_transit_information['Transit_information_path_lifetime'] = dao[5] # address of the parent prefix = dao[6:14] parents += [dao[14:22]] dao = dao[22:] elif dao[0]==self._TARGET_INFORMATION_TYPE: dao_target_information['Target_information_type'] = dao[0] dao_target_information['Target_information_length'] = dao[1] dao_target_information['Target_information_flags'] = dao[2] dao_target_information['Target_information_prefix_length'] = dao[3] # address of the child prefix = dao[4:12] children += [dao[12:20]] dao = dao[20:] else: log.warning("DAO with wrong Option {0}. Neither Transit nor Target.".format(dao[0])) return except IndexError: log.warning("DAO too short ({0} bytes), no space for DAO header".format(len(dao))) return # log output = [] output += [''] output += ['received RPL DAO from {0}:{1}'.format(u.formatIPv6Addr(self.networkPrefix),u.formatIPv6Addr(source))] output += ['- parents:'] for p in parents: output += [' {0}:{1}'.format(u.formatIPv6Addr(self.networkPrefix),u.formatIPv6Addr(p))] output += ['- children:'] for p in children: output += [' {0}:{1}'.format(u.formatIPv6Addr(self.networkPrefix),u.formatIPv6Addr(p))] output = '\n'.join(output) if log.isEnabledFor(logging.DEBUG): log.debug(output) print output node = u.formatIPv6Addr(source) if not (node in self.parentsDaoSeq.keys()): self.parentsDaoSeq[node] = [dao_header['RPL_DAO_Sequence']] else: self.parentsDaoSeq[node].append(dao_header['RPL_DAO_Sequence']) with open('dao_sequence.txt','a') as f: f.write(str(self.parentsDaoSeq)+'\n') # if you get here, the DAO was parsed correctly # update parents information with parents collected -- calls topology module. self.dispatch( signal = 'updateParents', data = (tuple(source),parents) )
def _indicateDAO(self,tup): ''' Indicate a new DAO was received. This function parses the received packet, and if valid, updates the information needed to compute source routes. ''' # retrieve source and destination try: source = tup[0] if len(source)>8: source=source[len(source)-8:] dao = tup[1] except IndexError: log.warning("DAO too short ({0} bytes), no space for destination and source".format(len(dao))) return # log if log.isEnabledFor(logging.DEBUG): output = [] output += ['received DAO:'] output += ['- source : {0}'.format(u.formatAddr(source))] output += ['- dao : {0}'.format(u.formatBuf(dao))] output = '\n'.join(output) log.debug(output) # retrieve DAO header dao_header = {} dao_transit_information = {} dao_target_information = {} try: # RPL header dao_header['RPL_InstanceID'] = dao[0] dao_header['RPL_flags'] = dao[1] dao_header['RPL_Reserved'] = dao[2] dao_header['RPL_DAO_Sequence'] = dao[3] # DODAGID dao_header['DODAGID'] = dao[4:20] dao = dao[20:] # retrieve transit information header and parents parents = [] children = [] while len(dao)>0: if dao[0]==self._TRANSIT_INFORMATION_TYPE: # transit information option dao_transit_information['Transit_information_type'] = dao[0] dao_transit_information['Transit_information_length'] = dao[1] dao_transit_information['Transit_information_flags'] = dao[2] dao_transit_information['Transit_information_path_control'] = dao[3] dao_transit_information['Transit_information_path_sequence'] = dao[4] dao_transit_information['Transit_information_path_lifetime'] = dao[5] # address of the parent prefix = dao[6:14] parents += [dao[14:22]] dao = dao[22:] elif dao[0]==self._TARGET_INFORMATION_TYPE: dao_target_information['Target_information_type'] = dao[0] dao_target_information['Target_information_length'] = dao[1] dao_target_information['Target_information_flags'] = dao[2] dao_target_information['Target_information_prefix_length'] = dao[3] # address of the child prefix = dao[4:12] children += [dao[12:20]] dao = dao[20:] else: log.warning("DAO with wrong Option {0}. Neither Transit nor Target.".format(dao[0])) return except IndexError: log.warning("DAO too short ({0} bytes), no space for DAO header".format(len(dao))) return # log output = [] output += [''] output += ['received RPL DAO from {0}:{1}'.format(u.formatIPv6Addr(self.networkPrefix),u.formatIPv6Addr(source))] output += ['- parents:'] for p in parents: output += [' {0}:{1}'.format(u.formatIPv6Addr(self.networkPrefix),u.formatIPv6Addr(p))] output += ['- children:'] for p in children: output += [' {0}:{1}'.format(u.formatIPv6Addr(self.networkPrefix),u.formatIPv6Addr(p))] output = '\n'.join(output) if log.isEnabledFor(logging.DEBUG): log.debug(output) print output # if you get here, the DAO was parsed correctly # update parents information with parents collected -- calls topology module. self.dispatch( signal = 'updateParents', data = (tuple(source),parents) )
def _sendDIO(self): """ Send a DIO. """ # don't send DIO if I didn't discover the DAGroot's EUI64 if not self.dagRootEui64: return # the list of bytes to be sent to the DAGroot. # - [8B] destination MAC address # - [variable] IPHC+ header dio = [] # next hop: broadcast address nextHop = [0xFF] * 8 # IPHC header dio += [0x78] # dispatch byte dio += [0x33] # dam sam idxNH = len(dio) dio += [0x3A] # next header (0x3A=ICMPv6) dio += [0x00] # HLIM # ICMPv6 header idxICMPv6 = len(dio) # remember where ICMPv6 starts dio += [155] # ICMPv6 type (155=RPL) dio += [0x01] # ICMPv6 CODE (for RPL 0x01=DIO) idxICMPv6CS = len(dio) # remember where ICMPv6 checksum starts dio += [0x00, 0x00] # placeholder for checksum (filled out later) # DIO header dio += [0x00] # instance ID dio += [0x00] # version number dio += [0x00, 0x00] # rank dio += [self.DIO_OPT_GROUNDED | self.MOP_DIO_A | self.MOP_DIO_B | self.MOP_DIO_C] # options: G | 0 | MOP | Prf dio += [0x00] # DTSN dio += [0x00] # flags dio += [0x00] # reserved # DODAGID with self.stateLock: idxSrc = len(dio) # this is a little hack as the source is the dodag dio += self.networkPrefix dio += self.dagRootEui64 # wireshark calculates the IPv6 source and destination from the # 6LoWPAN header. It is lot aware of the network prefix and uses # link-local addresses. We do the same in this implementation to avoid # checksum errors in Wireshark. wiresharkSrc = [0xFE, 0x80] + [0x00] * 6 + [dio[idxSrc + 8] | 0x02] + dio[idxSrc + 9 : idxSrc + 16] wiresharkDst = [0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0xFF, 0xFF] # calculate ICMPv6 checksum over ICMPv6header+ (RFC4443) checksum = u.calculatePseudoHeaderCRC( src=wiresharkSrc, dst=wiresharkDst, length=[0x00, 0x00, 0x00, len(dio[idxICMPv6:])], nh=[0x00] + dio[idxNH : idxNH + 1], payload=dio[idxICMPv6:], ) dio[idxICMPv6CS] = checksum[0] dio[idxICMPv6CS + 1] = checksum[1] # log if log.isEnabledFor(logging.DEBUG): log.debug("sending DIO {0}".format(u.formatBuf(dio))) # dispatch self.dispatch(signal="bytesToMesh", data=(nextHop, dio))
def _eventBusNotification(self, signal, sender, data): ''' Adds the signal to stats log and performs signal-specific handling ''' with self.dataLock: key = (sender, signal) if key not in self.stats: self.stats[key] = 0 self.stats[key] += 1 if signal == 'infoDagRoot' and data['isDAGroot'] == 1: self.dagRootEui64 = data['eui64'][:] if signal == 'wirelessTxStart': # this signal only exists is simulation mode self.simMode = True if self.wiresharkDebugEnabled: if self.simMode: # simulation mode if signal == 'wirelessTxStart': # Forwards a copy of the packet exchanged between simulated motes # to the tun interface for debugging. (moteId, frame, frequency) = data if log.isEnabledFor(logging.DEBUG): output = [] output += [''] output += ['- moteId: {0}'.format(moteId)] output += [ '- frame: {0}'.format(u.formatBuf(frame)) ] output += ['- frequency: {0}'.format(frequency)] output = '\n'.join(output) log.debug(output) print output # poipoi assert len(frame) >= 1 + 2 # 1 for length byte, 2 for CRC # cut frame in pieces length = frame[0] body = frame[1:-2] crc = frame[-2:] # wrap with zep header zep = self._wrapZepCrc(body, frequency) self._dispatchMeshDebugPacket(zep) else: # non-simulation mode if signal == 'fromMote.data': # Forwards a copy of the data received from a mode # to the Internet interface for debugging. (previousHop, lowpan) = data zep = self._wrapMacAndZep( previousHop=previousHop, nextHop=self.dagRootEui64, lowpan=lowpan, ) self._dispatchMeshDebugPacket(zep) if signal == 'bytesToMesh': # Forwards a copy of the 6LoWPAN packet destined for the mesh # to the tun interface for debugging. (nextHop, lowpan) = data zep = self._wrapMacAndZep( previousHop=self.dagRootEui64, nextHop=nextHop, lowpan=lowpan, ) self._dispatchMeshDebugPacket(zep)
def _sendDIO(self): ''' Send a DIO. ''' # don't send DIO if I didn't discover the DAGroot's EUI64 if not self.dagRootEui64: return # the list of bytes to be sent to the DAGroot. # - [8B] destination MAC address # - [variable] IPHC+ header dio = [] # next hop: broadcast address nextHop = [0xff] * 8 # IPHC header dio += [0x78] # dispatch byte dio += [0x33] # dam sam idxNH = len(dio) dio += [0x3A] # next header (0x3A=ICMPv6) dio += [0x00] # HLIM # ICMPv6 header idxICMPv6 = len(dio) # remember where ICMPv6 starts dio += [155] # ICMPv6 type (155=RPL) dio += [0x01] # ICMPv6 CODE (for RPL 0x01=DIO) idxICMPv6CS = len(dio) # remember where ICMPv6 checksum starts dio += [0x00, 0x00] # placeholder for checksum (filled out later) # DIO header dio += [0x00] # instance ID dio += [0x00] # version number dio += [0x00, 0x00] # rank dio += [ self.DIO_OPT_GROUNDED | self.MOP_DIO_A | self.MOP_DIO_B | self.MOP_DIO_C ] # options: G | 0 | MOP | Prf dio += [0x00] # DTSN dio += [0x00] # flags dio += [0x00] # reserved # DODAGID with self.stateLock: idxSrc = len( dio) # this is a little hack as the source is the dodag dio += self.networkPrefix dio += self.dagRootEui64 # wireshark calculates the IPv6 source and destination from the # 6LoWPAN header. It is lot aware of the network prefix and uses # link-local addresses. We do the same in this implementation to avoid # checksum errors in Wireshark. wiresharkSrc = [0xfe, 0x80] + [0x00] * 6 + [ dio[idxSrc + 8] | 0x02 ] + dio[idxSrc + 9:idxSrc + 16] wiresharkDst = [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0xff, 0xff, ] # calculate ICMPv6 checksum over ICMPv6header+ (RFC4443) checksum = u.calculatePseudoHeaderCRC( src=wiresharkSrc, dst=wiresharkDst, length=[0x00, 0x00, 0x00, len(dio[idxICMPv6:])], nh=[0x00] + dio[idxNH:idxNH + 1], payload=dio[idxICMPv6:], ) dio[idxICMPv6CS] = checksum[0] dio[idxICMPv6CS + 1] = checksum[1] # log if log.isEnabledFor(logging.DEBUG): log.debug('sending DIO {0}'.format(u.formatBuf(dio))) # dispatch self.dispatch(signal='bytesToMesh', data=(nextHop, dio))