Exemplo n.º 1
0
    def __init__(self, mote):

        # store params
        self.mote = mote

        # singletons (quicker access, instead of recreating every time)
        self.engine = SimEngine.SimEngine.SimEngine()
        self.settings = SimEngine.SimSettings.SimSettings()
        self.log = SimEngine.SimLog.SimLog().log

        # local variables
        self.dodagId = None
        self.of = RplOF0(self)

        if self.settings.rpl_timer_type == "trickle":
            self.trickle_timer = TrickleTimer(
                i_min=pow(2, self.DEFAULT_DIO_INTERVAL_MIN),
                i_max=self.DEFAULT_DIO_INTERVAL_DOUBLINGS,
                k=self.DEFAULT_DIO_REDUNDANCY_CONSTANT,
                callback=self._send_DIO)
        elif self.settings.rpl_timer_type == "constant":
            self.trickle_timer = TrickleTimer(
                i_min=pow(2, 10),
                i_max=1,
                k=self.DEFAULT_DIO_REDUNDANCY_CONSTANT,
                callback=self._send_DIO)
        self.parentChildfromDAOs = {
        }  # dictionary containing parents of each node
        self._tx_stat = {}  # indexed by mote_id
        self.dis_mode = self._get_dis_mode()
Exemplo n.º 2
0
    def __init__(self, mote):

        # store params
        self.mote = mote

        # singletons (quicker access, instead of recreating every time)
        self.engine = SimEngine.SimEngine.SimEngine()
        self.settings = SimEngine.SimSettings.SimSettings()
        self.log = SimEngine.SimLog.SimLog().log

        # local variables
        self.dodagId = None
        self.of = getattr(sys.modules[__name__], self.settings.rpl_of)(self)
        self.trickle_timer = TrickleTimer(
            i_min=pow(2, self.DEFAULT_DIO_INTERVAL_MIN),
            i_max=self.DEFAULT_DIO_INTERVAL_DOUBLINGS,
            k=self.DEFAULT_DIO_REDUNDANCY_CONSTANT,
            callback=self._send_DIO)
        self.parentChildfromDAOs = {
        }  # dictionary containing parents of each node
        self._tx_stat = {}  # indexed by mote_id
        self.dis_mode = self._get_dis_mode()

        self.last_rate_update = 0  # time terms
        self.second_last_rate_update = 0
        self.last_rate = 0

        self.transmission_count = 0  # transmitted pkt from the beggining
Exemplo n.º 3
0
class Rpl(object):

    DEFAULT_DIO_INTERVAL_MIN = 14
    DEFAULT_DIO_INTERVAL_DOUBLINGS = 9
    DEFAULT_DIO_REDUNDANCY_CONSTANT = 3

    # locally-defined constants
    DEFAULT_DIS_INTERVAL_SECONDS = 60

    def __init__(self, mote):

        # store params
        self.mote = mote

        # singletons (quicker access, instead of recreating every time)
        self.engine = SimEngine.SimEngine.SimEngine()
        self.settings = SimEngine.SimSettings.SimSettings()
        self.log = SimEngine.SimLog.SimLog().log

        # local variables
        self.dodagId = None
        self.of = RplOF0(self)
        self.trickle_timer = TrickleTimer(
            i_min=pow(2, self.DEFAULT_DIO_INTERVAL_MIN),
            i_max=self.DEFAULT_DIO_INTERVAL_DOUBLINGS,
            k=self.DEFAULT_DIO_REDUNDANCY_CONSTANT,
            callback=self._send_DIO)
        self.parentChildfromDAOs = {
        }  # dictionary containing parents of each node
        self._tx_stat = {}  # indexed by mote_id
        self.dis_mode = self._get_dis_mode()

    #======================== public ==========================================

    # getters/setters

    def get_rank(self):
        return self.of.rank

    def getDagRank(self):
        if self.of.rank is None:
            return None
        else:
            return int(self.of.rank / d.RPL_MINHOPRANKINCREASE)

    def addParentChildfromDAOs(self, parent_addr, child_addr):
        self.parentChildfromDAOs[child_addr] = parent_addr

    def getPreferredParent(self):
        # FIXME: when we implement IPv6 address or MAC address, we should
        # define the return type of this method. Currently, this method can
        # return a node ID, a MAC address, or an IPv6 address since they are
        # all the same value for a certain mote.
        return self.of.get_preferred_parent()

    # admin

    def start(self):
        if self.mote.dagRoot:
            self.dodagId = self.mote.get_ipv6_global_addr()
            self.of = RplOFNone(self)
            self.of.set_rank(d.RPL_MINHOPRANKINCREASE)
            self.trickle_timer.start()
            # now start a new RPL instance; reset the timer as per Section 8.3 of
            # RFC 6550
            self.trickle_timer.reset()
        else:
            if self.dis_mode != 'disabled':
                # the destination address of the first DIS is determined based
                # on self.dis_mode
                if self.dis_mode == 'dis_unicast':
                    # join_proxy is a possible parent
                    dstIp = d.IPV6_ALL_RPL_NODES_ADDRESS
                elif self.dis_mode == 'dis_broadcast':
                    dstIp = d.IPV6_ALL_RPL_NODES_ADDRESS
                else:
                    raise NotImplementedError()
                self.send_DIS(dstIp)
                self.start_dis_timer()

    def indicate_tx(self, cell, dstMac, isACKed):
        self.of.update_etx(cell, dstMac, isACKed)

    def indicate_preferred_parent_change(self, old_preferred, new_preferred):
        # log
        self.log(
            SimEngine.SimLog.LOG_RPL_CHURN, {
                "_mote_id": self.mote.id,
                "rank": self.of.rank,
                "preferredParent": new_preferred
            })

        # trigger DAO
        self._schedule_sendDAO(firstDAO=True)

        # use the new parent as our clock source
        self.mote.tsch.clock.sync(new_preferred)

        # trigger 6P ADD if parent changed
        self.mote.sf.indication_parent_change(old_preferred, new_preferred)

        # reset trickle timer to inform new rank quickly
        self.trickle_timer.reset()

    def local_repair(self):
        assert ((self.of.rank is None)
                or (self.of.rank == d.RPL_INFINITE_RANK))
        self.log(SimEngine.SimLog.LOG_RPL_LOCAL_REPAIR,
                 {"_mote_id": self.mote.id})
        self._send_DIO()  # sending a DIO with the infinite rank
        self.dodagId = None
        self.trickle_timer.stop()
        self.mote.tsch.stopSendingEBs()
        # start the DIS timer
        self.start_dis_timer()

    # === DIS

    def action_receiveDIS(self, packet):
        self.log(SimEngine.SimLog.LOG_RPL_DIS_RX, {
            "_mote_id": self.mote.id,
            "packet": packet,
        })
        if self.dodagId is None:
            # ignore DIS
            pass
        else:
            if self.mote.is_my_ipv6_addr(packet['net']['dstIp']):
                # unicast DIS; send unicast DIO back to the source
                self._send_DIO(packet['net']['srcIp'])
            elif packet['net']['dstIp'] == d.IPV6_ALL_RPL_NODES_ADDRESS:
                # broadcast DIS
                self.trickle_timer.reset()
            else:
                # shouldn't happen
                assert False

    def _get_dis_mode(self):
        if 'dis_unicast' in self.settings.rpl_extensions:
            assert 'dis_broadcast' not in self.settings.rpl_extensions
            return 'dis_unicast'
        elif 'dis_broadcast' in self.settings.rpl_extensions:
            assert 'dis_unicast' not in self.settings.rpl_extensions
            return 'dis_broadcast'
        else:
            return 'disabled'

    def start_dis_timer(self):
        self.engine.scheduleIn(delay=self.DEFAULT_DIS_INTERVAL_SECONDS,
                               cb=self.handle_dis_timer,
                               uniqueTag=str(self.mote.id) + 'dis',
                               intraSlotOrder=d.INTRASLOTORDER_STACKTASKS)

    def stop_dis_timer(self):
        self.engine.removeFutureEvent(str(self.mote.id) + 'dis')

    def handle_dis_timer(self):
        self.send_DIS(d.IPV6_ALL_RPL_NODES_ADDRESS)
        self.start_dis_timer()

    def send_DIS(self, dstIp):
        assert dstIp is not None
        dis = {
            'type': d.PKT_TYPE_DIS,
            'net': {
                'srcIp': str(self.mote.get_ipv6_link_local_addr()),
                'dstIp': dstIp,
                'packet_length': d.PKT_LEN_DIS
            },
            'app': {}
        }
        self.log(SimEngine.SimLog.LOG_RPL_DIS_TX, {
            "_mote_id": self.mote.id,
            "packet": dis,
        })
        self.mote.sixlowpan.sendPacket(dis)

    # === DIO

    def _send_DIO(self, dstIp=None):
        if self.dodagId is None:
            # seems we performed local repair
            return

        dio = self._create_DIO(dstIp)

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DIO_TX, {
            "_mote_id": self.mote.id,
            "packet": dio,
        })

        self.mote.sixlowpan.sendPacket(dio)

    def _create_DIO(self, dstIp=None):

        assert self.dodagId is not None

        if dstIp is None:
            dstIp = d.IPV6_ALL_RPL_NODES_ADDRESS

        if self.of.rank is None:
            rank = d.RPL_INFINITE_RANK
        else:
            rank = self.of.rank

        # create
        newDIO = {
            'type': d.PKT_TYPE_DIO,
            'app': {
                'rank': rank,
                'dodagId': self.dodagId,
            },
            'net': {
                'srcIp': self.mote.get_ipv6_link_local_addr(),
                'dstIp': dstIp,
                'packet_length': d.PKT_LEN_DIO
            }
        }

        return newDIO

    def action_receiveDIO(self, packet):

        assert packet['type'] == d.PKT_TYPE_DIO

        # abort if I'm not sync'ed (I cannot decrypt the DIO)
        if not self.mote.tsch.getIsSync():
            return

        # abort if I'm not join'ed (I cannot decrypt the DIO)
        if not self.mote.secjoin.getIsJoined():
            return

        # abort if I'm the DAGroot (I don't need to parse a DIO)
        if self.mote.dagRoot:
            return

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DIO_RX, {
            "_mote_id": self.mote.id,
            "packet": packet,
        })

        # handle the infinite rank
        if packet['app']['rank'] == d.RPL_INFINITE_RANK:
            if self.dodagId is None:
                # ignore this DIO
                return
            else:
                # if the DIO has the infinite rank, reset the Trickle timer
                self.trickle_timer.reset()

        # feed our OF with the received DIO
        self.of.update(packet)

        # record dodagId
        if ((self.dodagId is None)
                and (self.getPreferredParent() is not None)):
            # join the RPL network
            self.dodagId = packet['app']['dodagId']
            self.mote.add_ipv6_prefix(d.IPV6_DEFAULT_PREFIX)
            self.trickle_timer.start()
            self.trickle_timer.reset()
            self.stop_dis_timer()

    # === DAO

    def _schedule_sendDAO(self, firstDAO=False):
        """
        Schedule to send a DAO sometimes in the future.
        """

        assert self.mote.dagRoot is False

        # abort if DAO disabled
        if self.settings.rpl_daoPeriod == 0:
            # secjoin never completes if downward traffic is not supported by
            # DAO
            assert self.settings.secjoin_enabled is False

            # start sending EBs and application packets.
            self.mote.tsch.startSendingEBs()
            self.mote.app.startSendingData()
            return

        asnNow = self.engine.getAsn()

        if firstDAO:
            asnDiff = 1
        else:
            asnDiff = int(
                math.ceil(
                    random.uniform(0.8 * self.settings.rpl_daoPeriod,
                                   1.2 * self.settings.rpl_daoPeriod) /
                    self.settings.tsch_slotDuration))

        # schedule sending a DAO
        self.engine.scheduleAtAsn(
            asn=asnNow + asnDiff,
            cb=self._action_sendDAO,
            uniqueTag=(self.mote.id, '_action_sendDAO'),
            intraSlotOrder=d.INTRASLOTORDER_STACKTASKS,
        )

    def _action_sendDAO(self):
        """
        Enqueue a DAO and schedule next one.
        """

        if self.of.get_preferred_parent() is None:
            # stop sending DAO
            return

        # enqueue
        self._action_enqueueDAO()

        # the root now knows a source route to me
        # I can serve as join proxy: start sending DIOs and EBs
        # I can send data back-and-forth with an app
        self.mote.tsch.startSendingEBs()  # mote
        self.mote.app.startSendingData()  # mote

        # schedule next DAO
        self._schedule_sendDAO()

    def _action_enqueueDAO(self):
        """
        enqueue a DAO into TSCH queue
        """

        assert not self.mote.dagRoot
        assert self.dodagId != None

        # abort if not ready yet
        if self.mote.clear_to_send_EBs_DATA() == False:
            return

        parent_mac_addr = netaddr.EUI(self.of.get_preferred_parent())
        prefix = netaddr.IPAddress(d.IPV6_DEFAULT_PREFIX)
        parent_ipv6_addr = str(parent_mac_addr.ipv6(prefix))

        # create
        newDAO = {
            'type': d.PKT_TYPE_DAO,
            'app': {
                'parent_addr': parent_ipv6_addr,
            },
            'net': {
                'srcIp': self.mote.get_ipv6_global_addr(),
                'dstIp': self.dodagId,  # to DAGroot
                'packet_length': d.PKT_LEN_DAO,
            },
        }

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DAO_TX, {
            "_mote_id": self.mote.id,
            "packet": newDAO,
        })

        # remove other possible DAOs from the queue
        self.mote.tsch.remove_packets_in_tx_queue(type=d.PKT_TYPE_DAO)

        # send
        self.mote.sixlowpan.sendPacket(newDAO)

    def action_receiveDAO(self, packet):
        """
        DAGroot receives DAO, store parent/child relationship for source route calculation.
        """

        assert self.mote.dagRoot

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DAO_RX, {
            "_mote_id": self.mote.id,
            "packet": packet,
        })

        # store parent/child relationship for source route calculation
        self.addParentChildfromDAOs(parent_addr=packet['app']['parent_addr'],
                                    child_addr=packet['net']['srcIp'])

    # source route

    def computeSourceRoute(self, dst_addr):
        assert self.mote.dagRoot
        try:
            sourceRoute = []
            cur_addr = dst_addr
            while self.mote.is_my_ipv6_addr(cur_addr) is False:
                sourceRoute += [cur_addr]
                cur_addr = self.parentChildfromDAOs[cur_addr]
                if cur_addr in sourceRoute:
                    # routing loop is detected; cannot return an effective
                    # source-routing header
                    returnVal = None
                    break
        except KeyError:
            returnVal = None
        else:
            # reverse (so goes from source to destination)
            sourceRoute.reverse()

            returnVal = sourceRoute

        return returnVal
Exemplo n.º 4
0
class Rpl(object):

    DEFAULT_DIO_INTERVAL_MIN = 14
    DEFAULT_DIO_INTERVAL_DOUBLINGS = 9
    DEFAULT_DIO_REDUNDANCY_CONSTANT = 3

    # locally-defined constants
    DEFAULT_DIS_INTERVAL_SECONDS = 60

    def __init__(self, mote):

        # store params
        self.mote = mote

        # singletons (quicker access, instead of recreating every time)
        self.engine = SimEngine.SimEngine.SimEngine()
        self.settings = SimEngine.SimSettings.SimSettings()
        self.log = SimEngine.SimLog.SimLog().log

        # local variables
        self.of = RplOF0(self)
        self.trickle_timer = TrickleTimer(
            i_min=pow(2, self.DEFAULT_DIO_INTERVAL_MIN),
            i_max=self.DEFAULT_DIO_INTERVAL_DOUBLINGS,
            k=self.DEFAULT_DIO_REDUNDANCY_CONSTANT,
            callback=self._send_DIO)
        self.parentChildfromDAOs = {
        }  # dictionary containing parents of each node
        self._tx_stat = {}  # indexed by mote_id
        self.dis_mode = self._get_dis_mode()

    #======================== public ==========================================

    # getters/setters

    def get_rank(self):
        return self.of.get_rank()

    def getDagRank(self):
        return int(self.of.get_rank() / d.RPL_MINHOPRANKINCREASE)

    def addParentChildfromDAOs(self, parent_id, child_id):
        assert type(parent_id) is int
        assert type(child_id) is int
        self.parentChildfromDAOs[child_id] = parent_id

    def getPreferredParent(self):
        # FIXME: when we implement IPv6 address or MAC address, we should
        # define the return type of this method. Currently, this method can
        # return a node ID, a MAC address, or an IPv6 address since they are
        # all the same value for a certain mote.
        return self.of.get_preferred_parent()

    # admin

    def start(self):
        if self.mote.dagRoot:
            self.of = RplOFNone(self)
            self.of.set_rank(d.RPL_MINHOPRANKINCREASE)
            self.trickle_timer.start()
            # now start a new RPL instance; reset the timer as per Section 8.3 of
            # RFC 6550
            self.trickle_timer.reset()
        else:
            # start sending DIS
            self._send_DIS()

    def indicate_tx(self, cell, dstMac, isACKed):
        self.of.update_etx(cell, dstMac, isACKed)

    def indicate_preferred_parent_change(self, old_preferred, new_preferred):
        # log
        self.log(
            SimEngine.SimLog.LOG_RPL_CHURN, {
                "_mote_id": self.mote.id,
                "rank": self.of.get_rank(),
                "preferredParent": new_preferred
            })

        # print('-------- change parent for mote:', self.mote.id)

        # trigger DAO
        self._schedule_sendDAO(firstDAO=True)

        # use the new parent as our clock source
        self.mote.tsch.clock.sync(new_preferred)

        # trigger 6P ADD if parent changed
        self.mote.sf.indication_parent_change(old_preferred, new_preferred)

        # added Fadoua: I need reset the counters of DATA packets
        # at this level after changing the preferred parent
        # in this way you remove old statistics and make sure that
        # they are not taken into consideration for the next running time

        src = self.mote.id
        dst = old_preferred

        if ((src != None) and (dst != None)):
            couple = str(
                (dst, src)
            )  # I have done this to be conform to the format of tuple in the settings.count dictionary that I used at the reception side of (sixlowpan file)

            if (couple in self.settings.count.keys()):

                if (self.settings.count[couple] >
                        d.MAX_DATA_PKTS_THROUGHT_SHARED_CELL):
                    del self.settings.count[couple]

        # reset trickle timer to inform new rank quickly
        self.trickle_timer.reset()

    def update_preferred_parent(self, reason):  # added Fadoua

        self.of._update_preferred_parent(reason)

    def get_list_of_old_parents(self):  # added Fadoua
        return self.of.get_list_of_old_parents()

    # === DIS

    def action_receiveDIS(self, packet):
        if packet['net']['dstIp'] == self.mote.id:
            # unicast DIS; send unicast DIO back to the source
            self._send_DIO(packet['net']['srcIp'])
        elif packet['net']['dstIp'] == d.BROADCAST_ADDRESS:
            # broadcast DIS
            self.trickle_timer.reset()
        else:
            # shouldn't happen
            assert False

    def _get_dis_mode(self):
        if 'dis_unicast' in self.settings.rpl_extensions:
            assert 'dis_broadcast' not in self.settings.rpl_extensions
            return 'dis_unicast'
        elif 'dis_broadcast' in self.settings.rpl_extensions:
            assert 'dis_unicast' not in self.settings.rpl_extensions
            return 'dis_broadcast'
        else:
            return 'disabled'

    def _start_dis_timer(self):
        self.engine.scheduleIn(delay=self.DEFAULT_DIS_INTERVAL_SECONDS,
                               cb=self._send_DIS,
                               uniqueTag=str(self.mote.id) + 'dis',
                               intraSlotOrder=d.INTRASLOTORDER_STACKTASKS)

    def _stop_dis_timer(self):
        self.engine.removeFutureEvent(str(self.mote.id) + 'dis')

    def _send_DIS(self):

        if self.dis_mode == 'dis_unicast':
            dstIp = self.mote.tsch.join_proxy  # possible parent
        elif self.dis_mode == 'dis_broadcast':
            dstIp = d.BROADCAST_ADDRESS
        elif self.dis_mode == 'disabled':
            return

        dis = {
            'type': d.PKT_TYPE_DIS,
            'net': {
                'srcIp': self.mote.id,
                'dstIp': dstIp,
                'packet_length': d.PKT_LEN_DIS
            },
            'app': {}
        }

        self.mote.sixlowpan.sendPacket(dis, link_local=True)
        self._start_dis_timer()

    # === DIO

    def _send_DIO(self, dstIp=None):
        dio = self._create_DIO(dstIp)

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DIO_TX, {
            "_mote_id": self.mote.id,
            "packet": dio,
        })

        self.mote.sixlowpan.sendPacket(dio, link_local=True)

    def _create_DIO(self, dstIp=None):

        assert self.mote.dodagId is not None

        if dstIp is None:
            dstIp = d.BROADCAST_ADDRESS

        # create
        newDIO = {
            'type': d.PKT_TYPE_DIO,
            'app': {
                'rank': self.of.get_rank(),
                'dodagId': self.mote.dodagId,
            },
            'net': {
                'srcIp': self.mote.id,  # from mote
                'dstIp': dstIp,  # broadcast (in reality "all RPL routers")
                'packet_length': d.PKT_LEN_DIO
            }
        }

        return newDIO

    def action_receiveDIO(self, packet):

        assert packet['type'] == d.PKT_TYPE_DIO

        # abort if I'm not sync'ed (I cannot decrypt the DIO)
        if not self.mote.tsch.getIsSync():
            return

        # abort if I'm not join'ed (I cannot decrypt the DIO)
        if not self.mote.secjoin.getIsJoined():
            return

        # abort if I'm the DAGroot (I don't need to parse a DIO)
        if self.mote.dagRoot:
            return

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DIO_RX, {
            "_mote_id": self.mote.id,
            "packet": packet,
        })

        # record dodagId
        if self.mote.dodagId is None:
            # join the RPL network
            self.mote.dodagId = packet['app']['dodagId']
            self.trickle_timer.start()
            self.trickle_timer.reset()
            self._stop_dis_timer()

        # feed our OF with the received DIO
        self.of.update(packet)

    # === DAO

    def _schedule_sendDAO(self, firstDAO=False):
        """
        Schedule to send a DAO sometimes in the future.
        """

        assert self.mote.dagRoot is False

        # abort if DAO disabled
        if self.settings.rpl_daoPeriod == 0:
            return

        asnNow = self.engine.getAsn()

        if firstDAO:
            asnDiff = 1
        else:
            asnDiff = int(
                math.ceil(
                    random.uniform(0.8 * self.settings.rpl_daoPeriod,
                                   1.2 * self.settings.rpl_daoPeriod) /
                    self.settings.tsch_slotDuration))

        # schedule sending a DAO
        self.engine.scheduleAtAsn(
            asn=asnNow + asnDiff,
            cb=self._action_sendDAO,
            uniqueTag=(self.mote.id, '_action_sendDAO'),
            intraSlotOrder=d.INTRASLOTORDER_STACKTASKS,
        )

    def _action_sendDAO(self):
        """
        Enqueue a DAO and schedule next one.
        """

        # enqueue
        self._action_enqueueDAO()

        # the root now knows a source route to me
        # I can serve as join proxy: start sending DIOs and EBs
        # I can send data back-and-forth with an app
        self.mote.tsch.startSendingEBs()  # mote
        self.mote.app.startSendingData()  # mote

        # schedule next DAO
        self._schedule_sendDAO()

    def _action_enqueueDAO(self):
        """
        enqueue a DAO into TSCH queue
        """

        assert not self.mote.dagRoot
        assert self.mote.dodagId != None

        # abort if not ready yet
        if self.mote.clear_to_send_EBs_DATA() == False:
            return

        # create
        newDAO = {
            'type': d.PKT_TYPE_DAO,
            'app': {
                'child_id': self.mote.id,
                'parent_id': self.of.get_preferred_parent()
            },
            'net': {
                'srcIp': self.mote.id,  # from mote
                'dstIp': self.mote.dodagId,  # to DAGroot
                'packet_length': d.PKT_LEN_DAO,
            },
        }

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DAO_TX, {
            "_mote_id": self.mote.id,
            "packet": newDAO,
        })

        # remove other possible DAOs from the queue
        self.mote.tsch.remove_frame_from_tx_queue(type=d.PKT_TYPE_DAO)

        # send
        self.mote.sixlowpan.sendPacket(newDAO)

    def action_receiveDAO(self, packet):
        """
        DAGroot receives DAO, store parent/child relationship for source route calculation.
        """

        assert self.mote.dagRoot

        # log
        self.log(SimEngine.SimLog.LOG_RPL_DAO_RX, {
            "_mote_id": self.mote.id,
            "packet": packet,
        })

        # store parent/child relationship for source route calculation
        self.addParentChildfromDAOs(
            parent_id=packet['app']['parent_id'],
            child_id=packet['app']['child_id'],
        )

    # source route

    def computeSourceRoute(self, dest_id):
        """
        Compute the source route to a given mote.

        :param destAddr: [in] The EUI64 address of the final destination.

        :returns: The source route, a list of EUI64 address, ordered from
            destination to source, or None
        """
        assert type(dest_id) is int

        try:
            sourceRoute = []
            cur_id = dest_id
            while cur_id != 0:
                sourceRoute += [cur_id]
                cur_id = self.parentChildfromDAOs[cur_id]
        except KeyError:
            returnVal = None
        else:
            # reverse (so goes from source to destination)
            sourceRoute.reverse()

            returnVal = sourceRoute

        return returnVal