Ejemplo n.º 1
0
    def handle_revocation(self, cpld, meta):
        pmgt = cpld.union
        rev_info = pmgt.union
        assert isinstance(rev_info, RevocationInfo), type(rev_info)
        logging.debug("Revocation info received: %s", rev_info.short_desc())
        try:
            rev_info.validate()
        except SCIONBaseError as e:
            logging.warning("Failed to validate RevInfo from %s: %s", meta, e)
            return SCIONDRevReplyStatus.INVALID
        # Verify epoch information and on failure return directly
        epoch_status = ConnectedHashTree.verify_epoch(rev_info.p.epoch)
        if epoch_status == ConnectedHashTree.EPOCH_PAST:
            logging.error(
                "Failed to verify epoch: epoch in the past %d,current epoch %d."
                % (rev_info.p.epoch, ConnectedHashTree.get_current_epoch()))
            return SCIONDRevReplyStatus.INVALID

        if epoch_status == ConnectedHashTree.EPOCH_FUTURE:
            logging.warning(
                "Failed to verify epoch: epoch in the future %d,current epoch %d."
                % (rev_info.p.epoch, ConnectedHashTree.get_current_epoch()))
            return SCIONDRevReplyStatus.INVALID

        if epoch_status == ConnectedHashTree.EPOCH_NEAR_PAST:
            logging.info(
                "Failed to verify epoch: epoch in the near past %d, current epoch %d."
                % (rev_info.p.epoch, ConnectedHashTree.get_current_epoch()))
            return SCIONDRevReplyStatus.STALE

        self.peer_revs.add(rev_info)
        # Go through all segment databases and remove affected segments.
        removed_up = self._remove_revoked_pcbs(self.up_segments, rev_info)
        removed_core = self._remove_revoked_pcbs(self.core_segments, rev_info)
        removed_down = self._remove_revoked_pcbs(self.down_segments, rev_info)
        logging.info("Removed %d UP- %d CORE- and %d DOWN-Segments." %
                     (removed_up, removed_core, removed_down))
        total = removed_up + removed_core + removed_down
        if total > 0:
            return SCIONDRevReplyStatus.VALID
        else:
            # FIXME(scrye): UNKNOWN is returned in the following situations:
            #  - No matching segments exist
            #  - Matching segments exist, but the revoked interface is part of
            #  a peering link; new path queries sent to SCIOND won't use the
            #  link, but nothing is immediately revoked
            #  - A hash check failed and prevented the revocation from taking
            #  place
            #
            # This should be fixed in the future to provide clearer meaning
            # behind why the revocation could not be validated.
            #
            # For now, if applications receive an UNKOWN reply to a revocation,
            # they should strongly consider flushing paths containing the
            # interface.
            return SCIONDRevReplyStatus.UNKNOWN
Ejemplo n.º 2
0
 def add_rev_infos(self, rev_infos):  # pragma: no cover
     """
     Appends a list of revocations to the PCB. Replaces existing
     revocations with newer ones.
     """
     if not rev_infos:
         return
     existing = {}
     current_epoch = ConnectedHashTree.get_current_epoch()
     for i in range(len(self.p.exts.revInfos)):
         orphan = self.p.exts.revInfos.disown(i)
         info_p = orphan.get()
         if info_p.epoch >= current_epoch:
             existing[(info_p.isdas, info_p.ifID)] = orphan
     # Remove revocations for which we already have a newer one.
     filtered = []
     for info in rev_infos:
         if (info.p.epoch >= current_epoch and
                 (info.p.isdas, info.p.ifID) not in existing):
             filtered.append(info)
     self.p.exts.init("revInfos", len(existing) + len(filtered))
     for i, orphan in enumerate(existing.values()):
         self.p.exts.revInfos.adopt(i, orphan)
     n_existing = len(existing)
     for i, info in enumerate(filtered):
         self.p.exts.revInfos[n_existing + i] = info.p
Ejemplo n.º 3
0
    def _remove_revoked_pcbs(self, db, rev_info):
        """
        Removes all segments from 'db' that contain an IF token for which
        rev_token is a preimage (within 20 calls).

        :param db: The PathSegmentDB.
        :type db: :class:`lib.path_db.PathSegmentDB`
        :param rev_info: The revocation info
        :type rev_info: RevocationInfo

        :returns: The number of deletions.
        :rtype: int
        """

        if not ConnectedHashTree.verify_epoch(rev_info.p.epoch):
            logging.debug(
                "Failed to verify epoch: rev_info epoch %d,current epoch %d." %
                (rev_info.p.epoch, ConnectedHashTree.get_current_epoch()))
            return 0

        to_remove = []
        for segment in db(full=True):
            for asm in segment.iter_asms():
                if self._verify_revocation_for_asm(rev_info, asm):
                    logging.debug("Removing segment: %s" %
                                  segment.short_desc())
                    to_remove.append(segment.get_hops_hash())
        return db.delete_all(to_remove)
Ejemplo n.º 4
0
Archivo: base.py Proyecto: fjacky/scion
 def _handle_if_timeouts(self):
     """
     Periodically checks each interface state and issues an if revocation, if
     no keep-alive message was received for IFID_TOUT.
     """
     if_id_last_revoked = defaultdict(int)
     while self.run_flag.is_set():
         start_time = time.time()
         with self.ifid_state_lock:
             to_revoke = []
             for (if_id, if_state) in self.ifid_state.items():
                 cur_epoch = ConnectedHashTree.get_current_epoch()
                 if not if_state.is_expired() or (
                         if_state.is_revoked() and if_id_last_revoked[if_id] == cur_epoch):
                     # Either the interface hasn't timed out, or it's already revoked for this
                     # epoch
                     continue
                 if_id_last_revoked[if_id] = cur_epoch
                 if not if_state.is_revoked():
                     logging.info("IF %d went down.", if_id)
                 to_revoke.append(if_id)
                 if_state.revoke_if_expired()
             self._issue_revocations(to_revoke)
         sleep_interval(start_time, self.IF_TIMEOUT_INTERVAL,
                        "Handle IF timeouts")
Ejemplo n.º 5
0
    def _add_peer_revs(self, segments):
        """
        Adds revocations to revoked peering interfaces in segments.

        :returns: Set with modified segments. Elements of the original set
            stay untouched.
        """
        # TODO(shitz): This could be optimized, by keeping a map of (ISD_AS, IF)
        # -> RevocationInfo for peer revocations.
        modified_segs = set()
        current_epoch = ConnectedHashTree.get_current_epoch()
        for segment in segments:
            seg_id = segment.get_hops_hash()
            with self.pcb_cache_lock:
                if seg_id in self.pcb_cache:
                    cached_seg = self.pcb_cache[seg_id]
                    logging.debug("Adding segment from PCB cache to response:"
                                  " %s" % cached_seg.short_desc())
                    modified_segs.add(cached_seg)
                    continue
                revs_to_add = set()
                for rev_info in list(self.revocations):
                    if rev_info.p.epoch < current_epoch:
                        self.revocations.pop(rev_info)
                        continue
                    for asm in segment.iter_asms():
                        if asm.isd_as() != rev_info.isd_as():
                            continue
                        for pcbm in asm.iter_pcbms(1):
                            hof = pcbm.hof()
                            if rev_info.p.ifID in [
                                    hof.ingress_if, hof.egress_if
                            ]:
                                revs_to_add.add(rev_info.copy())
                if revs_to_add:
                    new_seg = segment.copy()
                    new_seg.add_rev_infos(list(revs_to_add))
                    logging.debug("Adding revocations to PCB: %s" %
                                  new_seg.short_desc())
                    self.pcb_cache[seg_id] = new_seg
                    modified_segs.add(new_seg)
                else:
                    modified_segs.add(segment)

        return modified_segs
Ejemplo n.º 6
0
 def _handle_if_timeouts(self):
     """
     Periodically checks each interface state and issues an if revocation, if
     no keep-alive message was received for IFID_TOUT.
     """
     if_id_last_revoked = defaultdict(int)
     while self.run_flag.is_set():
         start_time = time.time()
         with self.ifid_state_lock:
             for (if_id, if_state) in self.ifid_state.items():
                 cur_epoch = ConnectedHashTree.get_current_epoch()
                 # Check if interface has timed-out.
                 if ((if_state.is_expired() or if_state.is_revoked())
                         and (if_id_last_revoked[if_id] != cur_epoch)):
                     if_id_last_revoked[if_id] = cur_epoch
                     if not if_state.is_revoked():
                         logging.info("IF %d appears to be down.", if_id)
                     self._issue_revocation(if_id)
                     if_state.revoke_if_expired()
         sleep_interval(start_time, self.IF_TIMEOUT_INTERVAL,
                        "Handle IF timeouts")