def _dispatchMeshDebugPacket(self, zep): ''' Wraps ZEP-based debug packet, for outgoing mesh 6LoWPAN message, with UDP and IPv6 headers. Then forwards as an event to the Internet interface. ''' # UDP udplen = len(zep)+8 udp = [0x00,0x00] # src port (unused) udp += [0x45,0x5a] # dest port (17754) udp += [udplen >> 8, udplen & 0xff] # length udp += [0x00,0x00] # checksum udp += zep # Common address for source and destination addr = [] addr += openTun.IPV6PREFIX addr += openTun.IPV6HOST # CRC See https://tools.ietf.org/html/rfc2460. # not sure if the payload contains the udp header in this case. udp[6:8] = u.calculatePseudoHeaderCRC( src = addr, dst = addr, length = [0x00,0x00]+udp[4:6], nh = [0x00,0x00,0x00,17], payload = zep, ) # IPv6 ip = [6<<4] # v6 + traffic class (upper nybble) ip += [0x00,0x00,0x00] # traffic class (lower nibble) + flow label ip += udp[4:6] # payload length ip += [17] # next header (protocol) ip += [8] # hop limit (pick a safe value) ip += addr # source ip += addr # destination ip += udp dispatcher.send( sender = self.name, signal = 'v6ToInternet', data = ip, )
def _dispatchMeshDebugPacket(self, zep): ''' Wraps ZEP-based debug packet, for outgoing mesh 6LoWPAN message, with UDP and IPv6 headers. Then forwards as an event to the Internet interface. ''' # UDP udplen = len(zep) + 8 udp = [0x00, 0x00] # src port (unused) udp += [0x45, 0x5a] # dest port (17754) udp += [udplen >> 8, udplen & 0xff] # length udp += [0x00, 0x00] # checksum udp += zep # Common address for source and destination addr = [] addr += openTun.IPV6PREFIX addr += openTun.IPV6HOST # CRC See https://tools.ietf.org/html/rfc2460. # not sure if the payload contains the udp header in this case. udp[6:8] = u.calculatePseudoHeaderCRC( src=addr, dst=addr, length=[0x00, 0x00] + udp[4:6], nh=[0x00, 0x00, 0x00, 17], payload=zep, ) # IPv6 ip = [6 << 4] # v6 + traffic class (upper nybble) ip += [0x00, 0x00, 0x00] # traffic class (lower nibble) + flow label ip += udp[4:6] # payload length ip += [17] # next header (protocol) ip += [8] # hop limit (pick a safe value) ip += addr # source ip += addr # destination ip += udp dispatcher.send( sender=self.name, signal='v6ToInternet', data=ip, )
def _meshToV6_notif(self, sender, signal, data): ''' Converts a 6LowPAN packet into a IPv6 packet. This function dispatches the IPv6 packet with signal 'according to the destination address, protocol_type and port'. ''' try: ipv6dic = {} #build lowpan dictionary from the data ipv6dic = self.lowpan_to_ipv6(data) success = True dispatchSignal = None #read next header if ipv6dic['next_header'] == self.IANA_IPv6HOPHEADER: #hop by hop header present, check flags and parse if (ipv6dic['hop_flags'] & self.O_FLAG) == self.O_FLAG: #error -- this packet has gone downstream somewhere. log.error( "detected possible downstream link on upstream route from {0}" .format(",".join(str(c) for c in ipv6dic['src_addr']))) if (ipv6dic['hop_flags'] & self.R_FLAG) == self.R_FLAG: #error -- loop in the route log.error( "detected possible loop on upstream route from {0}". format(",".join(str(c) for c in ipv6dic['src_addr']))) #skip the header and process the rest of the message. ipv6dic['next_header'] = ipv6dic['hop_next_header'] #=================================================================== if ipv6dic['next_header'] == self.IPV6_HEADER: #ipv6 header (inner) ipv6dic_inner = {} # prasing the iphc inner header and get the next_header ipv6dic_inner = self.lowpan_to_ipv6( [ipv6dic['pre_hop'], ipv6dic['payload']]) ipv6dic['next_header'] = ipv6dic_inner['next_header'] ipv6dic['payload'] = ipv6dic_inner['payload'] ipv6dic['payload_length'] = ipv6dic_inner['payload_length'] ipv6dic['src_addr'] = ipv6dic_inner['src_addr'] if not ipv6dic.has_key('hop_limit'): ipv6dic['hop_limit'] = ipv6dic_inner['hop_limit'] ipv6dic['dst_addr'] = ipv6dic_inner['dst_addr'] ipv6dic['flow_label'] = ipv6dic_inner['flow_label'] if ipv6dic['next_header'] == self.IANA_ICMPv6: #icmp header if len(ipv6dic['payload']) < 5: log.critical( "wrong payload lenght on ICMPv6 packet {0}".format( ",".join(str(c) for c in data))) print "wrong payload lenght on ICMPv6 packet {0}".format( ",".join(str(c) for c in data)) return ipv6dic['icmpv6_type'] = ipv6dic['payload'][0] ipv6dic['icmpv6_code'] = ipv6dic['payload'][1] ipv6dic['icmpv6_checksum'] = ipv6dic['payload'][2:4] ipv6dic['app_payload'] = ipv6dic['payload'][4:] #this function does the job dispatchSignal = (tuple(ipv6dic['dst_addr']), self.PROTO_ICMPv6, ipv6dic['icmpv6_type']) elif ipv6dic['next_header'] == self.IANA_UDP: #udp header -- can be compressed.. assume first it is not compressed. if len(ipv6dic['payload']) < 5: log.critical( "wrong payload lenght on UDP packet {0}".format( ",".join(str(c) for c in data))) print "wrong payload lenght on UDP packet {0}".format( ",".join(str(c) for c in data)) return if ipv6dic['payload'][0] & self.NHC_UDP_MASK == self.NHC_UDP_ID: lowpan_nhc = ipv6dic['payload'][0] udp_header_length = 0 newUdp = [] newUdp_header_length = 0 if lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_INLINE: src_port = u.buf2int(ipv6dic['payload'][1:3]) dest_port = u.buf2int(ipv6dic['payload'][3:5]) udp_header_length = 5 elif lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_16S_8D: src_port = u.buf2int(ipv6dic['payload'][1:3]) dest_port = 0xf000 + ipv6dic['payload'][3] udp_header_length = 4 elif lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_8S_16D: src_port = 0xf000 + ipv6dic['payload'][1] dest_port = u.buf2int(ipv6dic['payload'][2:4]) udp_header_length = 4 elif lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_4S_4D: src_port = 0xf0b0 + ( (ipv6dic['payload'][1] >> 4) & 0x0f) dest_port = 0xf0b0 + ( (ipv6dic['payload'][1] >> 0) & 0x0f) udp_header_length = 2 newUdp = [src_port >> 8, src_port & 0x00ff] newUdp += [dest_port >> 8, dest_port & 0x00ff] idxLen = len(newUdp) # remember index of payload length newUdp += [0x00, 0x00] # length (placeholder) newUdp_header_length += 2 udp_header_length += 2 # skip two bytes checksum following idxCS = len(newUdp) # remember index of checksum newUdp += [0x00, 0x00] # Checksum (placeholder) newUdp_header_length += 2 #append payload to compute crc again newUdp += ipv6dic['payload'][ udp_header_length:] # data octets udp_len = [ 0, len(ipv6dic['payload'][udp_header_length:]) + 8 ] newUdp[idxLen] = udp_len[0] newUdp[idxLen + 1] = udp_len[1] checksum = u.calculatePseudoHeaderCRC( ipv6dic['src_addr'], ipv6dic['dst_addr'], udp_len, [0, ipv6dic['next_header']], newUdp) #fill crc with the right value. newUdp[idxCS] = checksum[0] newUdp[idxCS + 1] = checksum[1] #keep fields for later processing if needed ipv6dic['udp_src_port'] = u.buf2int(newUdp[0:2]) ipv6dic['udp_dest_port'] = u.buf2int(newUdp[2:4]) ipv6dic['udp_length'] = newUdp[4:6] ipv6dic['udp_checksum'] = newUdp[6:8] ipv6dic['app_payload'] = newUdp[8:] #substitute udp header by the uncompressed header. ipv6dic['payload'] = newUdp ipv6dic['payload_length'] = len(newUdp) else: #No UDP header compressed ipv6dic['udp_src_port'] = u.buf2int(ipv6dic['payload'][:2]) ipv6dic['udp_dest_port'] = u.buf2int( ipv6dic['payload'][2:4]) ipv6dic['udp_length'] = ipv6dic['payload'][4:6] ipv6dic['udp_checksum'] = ipv6dic['payload'][6:8] ipv6dic['app_payload'] = ipv6dic['payload'][8:] dispatchSignal = (tuple(ipv6dic['dst_addr']), self.PROTO_UDP, ipv6dic['udp_dest_port']) #keep payload and app_payload in case we want to assemble the message later. #as source address is being retrieved from the IPHC header, the signal includes it in case #receiver such as RPL DAO processing needs to know the source. success = self._dispatchProtocol( dispatchSignal, (ipv6dic['src_addr'], ipv6dic['app_payload'])) if success: return # assemble the packet and dispatch it again as nobody answer ipv6pkt = self.reassemble_ipv6_packet(ipv6dic) self.dispatch('v6ToInternet', ipv6pkt) except (ValueError, NotImplementedError) as err: log.error(err) pass
def _meshToV6_notif(self,sender,signal,data): ''' Converts a 6LowPAN packet into a IPv6 packet. This function dispatches the IPv6 packet with signal 'according to the destination address, protocol_type and port'. ''' try: ipv6dic={} #build lowpan dictionary from the data ipv6dic = self.lowpan_to_ipv6(data) success = True dispatchSignal = None hopbyhop_header_present = False #read next header if ipv6dic['next_header']==self.IANA_IPv6HOPHEADER: # mark hop by hop header present, check hop_flags after obtaining src_addr in IPV6 header. hopbyhop_header_present = True #skip the header and process the rest of the message. ipv6dic['next_header'] = ipv6dic['hop_next_header'] #=================================================================== if ipv6dic['next_header']==self.IPV6_HEADER: #ipv6 header (inner) ipv6dic_inner = {} # parsing the iphc inner header and get the next_header ipv6dic_inner = self.lowpan_to_ipv6([ipv6dic['pre_hop'],ipv6dic['payload']]) ipv6dic['next_header'] = ipv6dic_inner['next_header'] ipv6dic['payload'] = ipv6dic_inner['payload'] ipv6dic['payload_length'] = ipv6dic_inner['payload_length'] ipv6dic['src_addr'] = ipv6dic_inner['src_addr'] if not ipv6dic.has_key('hop_limit'): ipv6dic['hop_limit'] = ipv6dic_inner['hop_limit'] ipv6dic['dst_addr'] = ipv6dic_inner['dst_addr'] ipv6dic['flow_label'] = ipv6dic_inner['flow_label'] if hopbyhop_header_present: #hop by hop header present, check hop_flags if (ipv6dic['hop_flags'] & self.O_FLAG) == self.O_FLAG: #error -- this packet has gone downstream somewhere. log.error("detected possible downstream link on upstream route from {0}".format(",".join(str(c) for c in ipv6dic['src_addr']))) if (ipv6dic['hop_flags'] & self.R_FLAG) == self.R_FLAG: #error -- loop in the route log.error("detected possible loop on upstream route from {0}".format(",".join(str(c) for c in ipv6dic['src_addr']))) if ipv6dic['next_header']==self.IANA_ICMPv6: #icmp header if len(ipv6dic['payload'])<5: log.critical("wrong payload lenght on ICMPv6 packet {0}".format(",".join(str(c) for c in data))) print "wrong payload lenght on ICMPv6 packet {0}".format(",".join(str(c) for c in data)) return ipv6dic['icmpv6_type']=ipv6dic['payload'][0] ipv6dic['icmpv6_code']=ipv6dic['payload'][1] ipv6dic['icmpv6_checksum']=ipv6dic['payload'][2:4] ipv6dic['app_payload']=ipv6dic['payload'][4:] #this function does the job dispatchSignal=(tuple(ipv6dic['dst_addr']),self.PROTO_ICMPv6,ipv6dic['icmpv6_type']) elif ipv6dic['next_header']==self.IANA_UDP: #udp header -- can be compressed.. assume first it is not compressed. if len(ipv6dic['payload'])<5: log.critical("wrong payload lenght on UDP packet {0}".format(",".join(str(c) for c in data))) print "wrong payload lenght on UDP packet {0}".format(",".join(str(c) for c in data)) return if ipv6dic['payload'][0] & self.NHC_UDP_MASK==self.NHC_UDP_ID: lowpan_nhc = ipv6dic['payload'][0] udp_header_length = 0 newUdp = [] newUdp_header_length = 0 if lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_INLINE: src_port = u.buf2int(ipv6dic['payload'][1:3]) dest_port = u.buf2int(ipv6dic['payload'][3:5]) udp_header_length = 5 elif lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_16S_8D: src_port = u.buf2int(ipv6dic['payload'][1:3]) dest_port = 0xf000 + ipv6dic['payload'][3] udp_header_length = 4 elif lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_8S_16D: src_port = 0xf000 + ipv6dic['payload'][1] dest_port = u.buf2int(ipv6dic['payload'][2:4]) udp_header_length = 4 elif lowpan_nhc & self.NHC_UDP_PORTS_MASK == self.NHC_UDP_PORTS_4S_4D: src_port = 0xf0b0 +((ipv6dic['payload'][1] >> 4) & 0x0f) dest_port = 0xf0b0 +((ipv6dic['payload'][1] >> 0) & 0x0f) udp_header_length = 2 newUdp = [src_port >>8 , src_port & 0x00ff] newUdp += [dest_port >>8 , dest_port & 0x00ff] idxLen = len(newUdp) # remember index of payload length newUdp += [0x00,0x00] # length (placeholder) newUdp_header_length += 2 udp_header_length += 2 # skip two bytes checksum following idxCS = len(newUdp) # remember index of checksum newUdp += [0x00,0x00] # Checksum (placeholder) newUdp_header_length += 2 #append payload to compute crc again newUdp += ipv6dic['payload'][udp_header_length:] # data octets udp_len = [0, len(ipv6dic['payload'][udp_header_length:])+8] newUdp[idxLen] = udp_len[0] newUdp[idxLen+1] = udp_len[1] checksum = u.calculatePseudoHeaderCRC(ipv6dic['src_addr'],ipv6dic['dst_addr'],udp_len,[0,ipv6dic['next_header']],newUdp) #fill crc with the right value. newUdp[idxCS] = checksum[0] newUdp[idxCS+1] = checksum[1] #keep fields for later processing if needed ipv6dic['udp_src_port'] = u.buf2int(newUdp[0:2]) ipv6dic['udp_dest_port'] = u.buf2int(newUdp[2:4]) ipv6dic['udp_length'] = newUdp[4:6] ipv6dic['udp_checksum'] = newUdp[6:8] ipv6dic['app_payload'] = newUdp[8:] #substitute udp header by the uncompressed header. ipv6dic['payload'] = newUdp ipv6dic['payload_length'] = len(newUdp) else: #No UDP header compressed ipv6dic['udp_src_port']=u.buf2int(ipv6dic['payload'][:2]) ipv6dic['udp_dest_port']=u.buf2int(ipv6dic['payload'][2:4]) ipv6dic['udp_length']=ipv6dic['payload'][4:6] ipv6dic['udp_checksum']=ipv6dic['payload'][6:8] ipv6dic['app_payload']=ipv6dic['payload'][8:] dispatchSignal=(tuple(ipv6dic['dst_addr']),self.PROTO_UDP,ipv6dic['udp_dest_port']) #keep payload and app_payload in case we want to assemble the message later. #as source address is being retrieved from the IPHC header, the signal includes it in case #receiver such as RPL DAO processing needs to know the source. success = self._dispatchProtocol(dispatchSignal,(ipv6dic['src_addr'],ipv6dic['app_payload'])) if success: return # assemble the packet and dispatch it again as nobody answer ipv6pkt=self.reassemble_ipv6_packet(ipv6dic) self.dispatch('v6ToInternet',ipv6pkt) except (ValueError,IndexError,NotImplementedError) as err: log.error(err) pass
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 _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))