def handleDAO(interfaces, message): route_updated = False if not Address(message.dst).is_RPL_all_nodes( ) and not gv.address_cache.is_assigned(message.dst): logger.debug("DAO message is for a different node, dropping it") return is_multicast = Address(message.dst).is_RPL_all_nodes() dao = DAO() payload = dao.parse(message.msg) if gv.dodag_cache.is_empty() or dao.instanceID != gv.global_instanceID: logger.debug( "Currently not participating in any DODAG for this instanceID, cannot process the DAO message" ) return if is_multicast and dao.K: logger.debug( "Multicast DAO message can not request an acknowledgment (K=1)") return dodag = gv.dodag_cache.get_active_dodag() # when D flag is set in the DAO, check that the DODAGID matches the current DODAG if dao.D and dodag.dodagID != dao.DODAGID: logger.debug("DAO indicates a DODAG ID (%s) that does not match the active DODAG (%s), dropping it" % \ (repr(Address(dao.DODAGID)), repr(Address(dodag.dodagID)))) return options = getAllOption(payload) targets = [] last_opt_is_transit_info = False logger.debug("DAO message contains the following (%d) options:" % len(options)) for opt in options: logger.debug("- " + opt.__class__.__name__) if isinstance(opt, RPL_Option_RPL_Target): if last_opt_is_transit_info: targets = [] last_opt_is_transit_info = False targets.append( Route(repr(Address(opt.target_prefix)) + "/" + str(opt.prefix_len), message.src, message.iface, onehop=is_multicast)) if isinstance(opt, RPL_Option_Transit_Information): last_opt_is_transit_info = True if opt.E: logger.debug( "E flag is not supported for the RPL Transit Information Option, dropping DAO message" ) return if not opt.path_control == 0: logger.debug( "Path control different than 0 is not supported, dropping DAO message" ) return if opt.path_lifetime == 0: # this is a No-Path DAO route_updated += gv.route_cache.remove_routes(targets) for target in targets: try: dodag.downward_route_del(target) except KeyError: pass elif opt.path_lifetime == 0xff: # infinite lifetime for target in targets: dodag.downward_route_add(target) else: logger.debug( "Path lifetime that is not null or infinite is not supported" ) return (removed_routes, new_routes) = dodag.get_filtered_downward_routes() logger.debug("routes to be removed (%d):\n" % len(removed_routes) + repr(removed_routes)) logger.debug("routes to be added (%d):\n" % len(new_routes) + repr(new_routes)) route_updated += gv.route_cache.remove_routes(removed_routes) route_updated += gv.route_cache.add_routes(new_routes) if dao.K: dodag.sendDAO_ACK(message.iface, message.src, dao.DAOsequence, dao.DODAGID) if route_updated: dodag.last_PathSequence += 1 if not dodag.is_dodagRoot: logger.debug( "Downward routes have been updated, scheduling a DAO message transmission" ) dodag.setDAOtimer()
def handleDAO(interfaces, message): route_updated = False if not Address(message.dst).is_RPL_all_nodes() and not gv.address_cache.is_assigned(message.dst): logger.debug("DAO message is for a different node, dropping it") return is_multicast = Address(message.dst).is_RPL_all_nodes() dao = DAO() payload = dao.parse(message.msg) if gv.dodag_cache.is_empty() or dao.instanceID != gv.global_instanceID: logger.debug("Currently not participating in any DODAG for this instanceID, cannot process the DAO message") return if is_multicast and dao.K: logger.debug("Multicast DAO message can not request an acknowledgment (K=1)") return dodag = gv.dodag_cache.get_active_dodag() # when D flag is set in the DAO, check that the DODAGID matches the current DODAG if dao.D and dodag.dodagID != dao.DODAGID: logger.debug("DAO indicates a DODAG ID (%s) that does not match the active DODAG (%s), dropping it" % \ (repr(Address(dao.DODAGID)), repr(Address(dodag.dodagID)))) return options = getAllOption(payload) targets = [] last_opt_is_transit_info = False logger.debug("DAO message contains the following (%d) options:" % len(options)) for opt in options: logger.debug("- " + opt.__class__.__name__) if isinstance(opt, RPL_Option_RPL_Target): if last_opt_is_transit_info: targets = [] last_opt_is_transit_info = False targets.append(Route(repr(Address(opt.target_prefix)) + "/" + str(opt.prefix_len), message.src, message.iface, onehop=is_multicast)) if isinstance(opt, RPL_Option_Transit_Information): last_opt_is_transit_info = True if opt.E: logger.debug("E flag is not supported for the RPL Transit Information Option, dropping DAO message") return if not opt.path_control == 0: logger.debug("Path control different than 0 is not supported, dropping DAO message") return if opt.path_lifetime == 0: # this is a No-Path DAO route_updated += gv.route_cache.remove_routes(targets) for target in targets: try: dodag.downward_route_del(target) except KeyError: pass elif opt.path_lifetime == 0xff: # infinite lifetime for target in targets: dodag.downward_route_add(target) else: logger.debug("Path lifetime that is not null or infinite is not supported") return (removed_routes, new_routes) = dodag.get_filtered_downward_routes() logger.debug("routes to be removed (%d):\n" % len(removed_routes) + repr(removed_routes)) logger.debug("routes to be added (%d):\n" % len(new_routes) + repr(new_routes)) route_updated += gv.route_cache.remove_routes(removed_routes) route_updated += gv.route_cache.add_routes(new_routes) if dao.K: dodag.sendDAO_ACK(message.iface, message.src, dao.DAOsequence, dao.DODAGID) if route_updated: dodag.last_PathSequence += 1 if not dodag.is_dodagRoot: logger.debug("Downward routes have been updated, scheduling a DAO message transmission") dodag.setDAOtimer()
def handleDIO(interfaces, message): """Handler for DIO messages""" dio = DIO() payload = dio.parse(message.msg) consistent = True # attach to the very first RPL Instance we see if gv.global_instanceID == 0: gv.global_instanceID = dio.instanceID if dio.instanceID != gv.global_instanceID: logger.debug( "ignoring DIO message address targeting a different RPL instance") return (None, None) try: dodag = gv.dodag_cache.get_dodag(dio.DODAGID, dio.version, dio.instanceID)[0] except IndexError: dodag = None if dodag and dodag.is_dodagRoot: logger.debug( "This node is the DODAG Root for this DODAG, dropping DIO") return # we should not process this DIO message any further elif dodag: # this is a DIO, from the same RPL Instance, DODAG ID, Version logger.debug("Updating information on an existing DODAG") dodag.last_dio = time() if dio.MOP != dodag.MOP: raise NotImplementedError( "Change MOP on an existing DODAG is not implemented") if dio.Prf != dodag.Prf: dodag.Prf = dio.Prf consistent = False # if the rank is INFINITE_RANK, remove the source node from the parent # list (and from the neighbor set) node = gv.neigh_cache.get_node(message.iface, message.src, dodag) if dio.rank == INFINITE_RANK and node: logger.debug( "Node %s advertises a DIO message with infinite rank" % repr(Address(message.src))) updated = gv.neigh_cache.remove_node_by_address(dodag, message.src) if updated: consistent = False node = None # check if the node is a DIO parent and request its sub-DODAG to send DAO messages if node and node.dtsn < dio.DTSN and node in gv.neigh_cache.get_parent_list( ): logger.info( "Parent %s has increased its DTSN field, scheduling a DAO message" % repr(Address(message.src))) dodag.downward_routes_reset() dodag.setDAOtimer() consistent = False dodag.DTSN.set_val(dio.DTSN) else: # we might have multiple older or newer versions of this DODAG still in # our cache old_dodags = gv.dodag_cache.get_dodag(dio.DODAGID, instanceID=dio.instanceID) try: if old_dodags[0].is_dodagRoot: logger.debug( "This node is the DODAG Root for this DODAG, dropping DIO") return # we should not process this DIO message any further least_recent = old_dodags[0].version most_recent = old_dodags[0].version least_recent_dodag = old_dodags[0] most_recent_dodag = old_dodags[0] for old_dodag in old_dodags: dodag_version = old_dodag.version if dodag_version < least_recent: least_recent = dodag_version least_recent_dodag = old_dodag if dodag_version > most_recent: most_recent = dodag_version most_recent_dodag = old_dodag if dio.version > most_recent: logger.debug("Receiving a DIO from a new version of the DODAG %s," \ " version %d < version %d" % (repr(Address(most_recent_dodag.dodagID)), most_recent_dodag.version.get_val(), dio.version )) # this should trigger global repair downward consistent = False if dio.version < least_recent: logger.debug("Receiving a DIO from an old version of the DODAG %s," \ " version %d < version %d" % (repr(Address(least_recent_dodag.dodagID)), dio.version, least_recent_dodag.version.get_val())) logger.debug("DIO dropped") return except IndexError: # there was not older version of this DODAG pass # this is the first time we receive a DIO from this DODAG version # make sure that the DODAG is grounded, and that we support this # mode of operation. if dio.rank != INFINITE_RANK and dio.G == 1 and dio.MOP == 2: logger.info( "Receiving a DIO from a new DODAG, adding it to our cache") dodag = DODAG(instanceID=dio.instanceID, version=dio.version, G=dio.G, MOP=dio.MOP, Prf=dio.Prf, DTSN=dio.DTSN, dodagID=dio.DODAGID, interfaces=interfaces) try: # if this is a newer version of a DODAG, some information needs to be passed down dodag.last_DAOSequence = deepcopy( most_recent_dodag.last_DAOSequence) dodag.last_PathSequence = deepcopy( most_recent_dodag.last_PathSequence) except NameError: pass gv.dodag_cache.add(dodag) consistent = False else: logger.debug("DIO dropped") if dio.rank == INFINITE_RANK: logger.debug("Rank is INFINITE_RANK") if not dio.G: logger.debug("floating DODAG are not supported") if dio.MOP != 2: logger.debug("incompatible Mode of Operation (MOP)") return options = getAllOption(payload) logger.debug("DIO message contains the following options:") for opt in options: logger.debug("- " + opt.__class__.__name__) if isinstance(opt, RPL_Option_DODAG_Configuration): dodag.authenticated = opt.A dodag.PCS = opt.PCS dodag.DIOIntDoublings = opt.DIOIntDoubl dodag.DIOIntMin = opt.DIOIntMin dodag.DIORedundancyConst = opt.DIORedun dodag.MaxRankIncrease = opt.MaxRankIncrease dodag.MinHopRankIncrease = opt.MinHopRankIncrease dodag.OCP = opt.OCP dodag.DftLft = opt.DefLifetime dodag.LftUnit = opt.LifetimeUnit if isinstance(opt, RPL_Option_Prefix_Information): if opt.L: # TODO: prefix field contains an address that can be used for # on-link determination # (It means that one can send a packet directly to the parent, # meaning that the node can probably have a direct route) pass if opt.R: # TODO: process the R flags (only seems useful if the RPL target # option is required somehow) # - if L=O and R=1, the parent provides its own address in the # PIO, then the parent must advertise that address as a DAO # target pass if opt.A: if opt.prefix_len != 64: logger.debug( "PIO option: cannot derive an address from a prefix whose length is not 64 bits" ) continue # take only the 64 first bits of the prefix prefix = opt.prefix[:8] # Compute an IID for each interface/physical address # and build an IPv6 address with this prefix for each interfaces addresses = [] for iface in interfaces: address = derive_address(iface, prefix) if address: addresses.append((address, iface)) # assigns the new addresses for (address, iface) in addresses: gv.address_cache.add(repr(address), iface, 64, opt.valid_lifetime, opt.preferred_lifetime) # make sure we record this prefix as one of the prefix we # advertise if prefix not in dodag.advertised_prefixes: dodag.advertised_prefixes.append(prefix) if isinstance(opt, RPL_Option_DAG_Metric_Container): # TODO pass if dio.rank != INFINITE_RANK: gv.neigh_cache.register_node(message.iface, message.src, dodag, dio.rank, dio.DTSN) # update the DIO parent (the new parent could be from a different DODAG) updated = gv.neigh_cache.update_DIO_parent() if updated: consistent = False # if there is no DIO parent for this node, it must advertises an # INFINITE_RANK, so that it is not selected by its children parent = dodag.preferred_parent # there is no parent left for the node if not parent and dodag.rank != INFINITE_RANK: dodag.rank = INFINITE_RANK consistent = False # if we moved to a new DODAG version, now is a good time to clean up old # versions gv.dodag_cache.purge_old_versions() # happen when all neighboring nodes send a poison DIO message if gv.dodag_cache.is_empty(): return if dodag.rank < dodag.lowest_rank_advertized: dodag.lowest_rank_advertized = dodag.rank # if not consistent, reset the trickle timer try: if not consistent: dodag.DIOtimer.hear_inconsistent() dodag.setDAOtimer() else: dodag.DIOtimer.hear_consistent() except AttributeError: pass return
def handleDIO(interfaces, message): """Handler for DIO messages""" dio = DIO() payload = dio.parse(message.msg) consistent = True # attach to the very first RPL Instance we see if gv.global_instanceID == 0: gv.global_instanceID = dio.instanceID if dio.instanceID != gv.global_instanceID: logger.debug("ignoring DIO message address targeting a different RPL instance") return (None, None) try: dodag = gv.dodag_cache.get_dodag(dio.DODAGID, dio.version, dio.instanceID)[0] except IndexError: dodag = None if dodag and dodag.is_dodagRoot: logger.debug("This node is the DODAG Root for this DODAG, dropping DIO") return # we should not process this DIO message any further elif dodag: # this is a DIO, from the same RPL Instance, DODAG ID, Version logger.debug("Updating information on an existing DODAG") dodag.last_dio = time() if dio.MOP != dodag.MOP: raise NotImplementedError("Change MOP on an existing DODAG is not implemented") if dio.Prf != dodag.Prf: dodag.Prf = dio.Prf consistent = False # if the rank is INFINITE_RANK, remove the source node from the parent # list (and from the neighbor set) node = gv.neigh_cache.get_node(message.iface, message.src, dodag) if dio.rank == INFINITE_RANK and node: logger.debug("Node %s advertises a DIO message with infinite rank" % repr(Address(message.src))) updated = gv.neigh_cache.remove_node_by_address(dodag, message.src) if updated: consistent = False node = None # check if the node is a DIO parent and request its sub-DODAG to send DAO messages if node and node.dtsn < dio.DTSN and node in gv.neigh_cache.get_parent_list(): logger.info("Parent %s has increased its DTSN field, scheduling a DAO message" % repr(Address(message.src))) dodag.downward_routes_reset() dodag.setDAOtimer() consistent = False dodag.DTSN.set_val(dio.DTSN) else: # we might have multiple older or newer versions of this DODAG still in # our cache old_dodags = gv.dodag_cache.get_dodag(dio.DODAGID, instanceID=dio.instanceID) try: if old_dodags[0].is_dodagRoot: logger.debug("This node is the DODAG Root for this DODAG, dropping DIO") return # we should not process this DIO message any further least_recent = old_dodags[0].version most_recent = old_dodags[0].version least_recent_dodag = old_dodags[0] most_recent_dodag = old_dodags[0] for old_dodag in old_dodags: dodag_version = old_dodag.version if dodag_version < least_recent: least_recent = dodag_version least_recent_dodag = old_dodag if dodag_version > most_recent: most_recent = dodag_version most_recent_dodag = old_dodag if dio.version > most_recent: logger.debug("Receiving a DIO from a new version of the DODAG %s," \ " version %d < version %d" % (repr(Address(most_recent_dodag.dodagID)), most_recent_dodag.version.get_val(), dio.version )) # this should trigger global repair downward consistent = False if dio.version < least_recent: logger.debug("Receiving a DIO from an old version of the DODAG %s," \ " version %d < version %d" % (repr(Address(least_recent_dodag.dodagID)), dio.version, least_recent_dodag.version.get_val())) logger.debug("DIO dropped") return except IndexError: # there was not older version of this DODAG pass # this is the first time we receive a DIO from this DODAG version # make sure that the DODAG is grounded, and that we support this # mode of operation. if dio.rank != INFINITE_RANK and dio.G == 1 and dio.MOP == 2: logger.info("Receiving a DIO from a new DODAG, adding it to our cache") dodag = DODAG(instanceID=dio.instanceID, version=dio.version, G=dio.G, MOP=dio.MOP, Prf=dio.Prf, DTSN=dio.DTSN, dodagID=dio.DODAGID, interfaces=interfaces ) try: # if this is a newer version of a DODAG, some information needs to be passed down dodag.last_DAOSequence = deepcopy(most_recent_dodag.last_DAOSequence) dodag.last_PathSequence = deepcopy(most_recent_dodag.last_PathSequence) except NameError: pass gv.dodag_cache.add(dodag) consistent = False else: logger.debug("DIO dropped") if dio.rank == INFINITE_RANK: logger.debug("Rank is INFINITE_RANK") if not dio.G: logger.debug("floating DODAG are not supported") if dio.MOP != 2: logger.debug("incompatible Mode of Operation (MOP)") return options = getAllOption(payload) logger.debug("DIO message contains the following options:") for opt in options: logger.debug("- " + opt.__class__.__name__) if isinstance(opt, RPL_Option_DODAG_Configuration): dodag.authenticated = opt.A dodag.PCS = opt.PCS dodag.DIOIntDoublings = opt.DIOIntDoubl dodag.DIOIntMin = opt.DIOIntMin dodag.DIORedundancyConst = opt.DIORedun dodag.MaxRankIncrease = opt.MaxRankIncrease dodag.MinHopRankIncrease = opt.MinHopRankIncrease dodag.OCP = opt.OCP dodag.DftLft = opt.DefLifetime dodag.LftUnit = opt.LifetimeUnit if isinstance(opt, RPL_Option_Prefix_Information): if opt.L: # TODO: prefix field contains an address that can be used for # on-link determination # (It means that one can send a packet directly to the parent, # meaning that the node can probably have a direct route) pass if opt.R: # TODO: process the R flags (only seems useful if the RPL target # option is required somehow) # - if L=O and R=1, the parent provides its own address in the # PIO, then the parent must advertise that address as a DAO # target pass if opt.A: if opt.prefix_len != 64: logger.debug("PIO option: cannot derive an address from a prefix whose length is not 64 bits") continue # take only the 64 first bits of the prefix prefix = opt.prefix[:8] # Compute an IID for each interface/physical address # and build an IPv6 address with this prefix for each interfaces addresses = [] for iface in interfaces: address = derive_address(iface, prefix) if address: addresses.append((address, iface)) # assigns the new addresses for (address, iface) in addresses: gv.address_cache.add(repr(address), iface, 64, opt.valid_lifetime, opt.preferred_lifetime) # make sure we record this prefix as one of the prefix we # advertise if prefix not in dodag.advertised_prefixes: dodag.advertised_prefixes.append(prefix) if isinstance(opt, RPL_Option_DAG_Metric_Container): # TODO pass if dio.rank != INFINITE_RANK: gv.neigh_cache.register_node(message.iface, message.src, dodag, dio.rank, dio.DTSN) # update the DIO parent (the new parent could be from a different DODAG) updated = gv.neigh_cache.update_DIO_parent() if updated: consistent = False # if there is no DIO parent for this node, it must advertises an # INFINITE_RANK, so that it is not selected by its children parent = dodag.preferred_parent # there is no parent left for the node if not parent and dodag.rank != INFINITE_RANK: dodag.rank = INFINITE_RANK consistent = False # if we moved to a new DODAG version, now is a good time to clean up old # versions gv.dodag_cache.purge_old_versions() # happen when all neighboring nodes send a poison DIO message if gv.dodag_cache.is_empty(): return if dodag.rank < dodag.lowest_rank_advertized: dodag.lowest_rank_advertized = dodag.rank # if not consistent, reset the trickle timer try: if not consistent: dodag.DIOtimer.hear_inconsistent() dodag.setDAOtimer() else: dodag.DIOtimer.hear_consistent() except AttributeError: pass return