Exemple #1
0
 def test_same_epoch(self, time):
     # Setup
     time.return_value = HASHTREE_EPOCH_TIME + HASHTREE_EPOCH_TOLERANCE + 1
     # Call and tests
     ntools.eq_(ConnectedHashTree.verify_epoch(1),
                ConnectedHashTree.EPOCH_OK)
     ntools.eq_(ConnectedHashTree.verify_epoch(2),
                ConnectedHashTree.EPOCH_FUTURE)
Exemple #2
0
 def test_different_epoch(self, time):
     # Setup
     time.return_value = HASHTREE_EPOCH_TIME + 1
     # Call and test
     ntools.eq_(ConnectedHashTree.verify_epoch(0),
                ConnectedHashTree.EPOCH_OK)
     ntools.eq_(ConnectedHashTree.verify_epoch(1),
                ConnectedHashTree.EPOCH_OK)
Exemple #3
0
    def _remove_revoked_segments(self, rev_info):
        """
        Try the previous and next hashes as possible astokens,
        and delete any segment that matches

        :param rev_info: The revocation info
        :type rev_info: RevocationInfo
        """
        if ConnectedHashTree.verify_epoch(
                rev_info.p.epoch) != ConnectedHashTree.EPOCH_OK:
            return
        (hash01, hash12) = ConnectedHashTree.get_possible_hashes(rev_info)
        if_id = rev_info.p.ifID

        with self.htroot_if2seglock:
            down_segs_removed = 0
            core_segs_removed = 0
            up_segs_removed = 0
            for h in (hash01, hash12):
                for sid in self.htroot_if2seg.pop((h, if_id), []):
                    if self.down_segments.delete(
                            sid) == DBResult.ENTRY_DELETED:
                        down_segs_removed += 1
                    if self.core_segments.delete(
                            sid) == DBResult.ENTRY_DELETED:
                        core_segs_removed += 1
                    if not self.topology.is_core_as:
                        if (self.up_segments.delete(sid) ==
                                DBResult.ENTRY_DELETED):
                            up_segs_removed += 1
            logging.debug(
                "Removed segments revoked by [%s]: UP: %d DOWN: %d CORE: %d" %
                (rev_info.short_desc(), up_segs_removed, down_segs_removed,
                 core_segs_removed))
Exemple #4
0
 def add(self, rev_info):
     """
     Adds rev_info to the cache and returns True if the operation succeeds.
     """
     if not ConnectedHashTree.verify_epoch(rev_info.p.epoch):
         return False
     with self._lock:
         key = _mk_key(rev_info)
         stored_info = self.get(key)
         if not stored_info:
             # Try to free up space in case the cache reaches the cap limit.
             if len(self._cache) >= self._capacity:
                 for info in list(self._cache.values()):
                     self._validate_entry(info)
             # Couldn't free up enough space...
             if len(self._cache) >= self._capacity:
                 logging.error("Revocation cache full!.")
                 return False
             self._cache[key] = rev_info
             if self._labels:
                 REVS_ADDED.labels(**self._labels).inc()
                 REVS_TOTAL.labels(**self._labels).inc()
                 REVS_BYTES.labels(**self._labels).inc(len(rev_info))
             return True
         if rev_info.p.epoch > stored_info.p.epoch:
             self._cache[key] = rev_info
             if self._labels:
                 REVS_ADDED.labels(**self._labels).inc()
                 REVS_REMOVED.labels(**self._labels).inc()
                 REVS_BYTES.labels(**self._labels).inc(len(rev_info) - len(stored_info))
             return True
         return False
Exemple #5
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)
Exemple #6
0
    def _pcb_list_to_remove(self, candidates, rev_info):
        """
        Calculates the list of PCBs to remove.
        Called by _remove_revoked_pcbs.

        :param candidates: Candidate PCBs.
        :type candidates: List
        :param rev_info: The RevocationInfo object.
        :type rev_info: RevocationInfo
        """
        to_remove = []
        processed = set()
        for cand in candidates:
            if cand.id in processed:
                continue
            processed.add(cand.id)
            if not ConnectedHashTree.verify_epoch(rev_info.p.epoch):
                continue

            # If the interface on which we received the PCB is
            # revoked, then the corresponding pcb needs to be removed.
            root_verify = ConnectedHashTree.verify(rev_info,
                                                   self._get_ht_root())
            if (self.addr.isd_as == rev_info.isd_as()
                    and cand.pcb.p.ifID == rev_info.p.ifID and root_verify):
                to_remove.append(cand.id)

            for asm in cand.pcb.iter_asms():
                if self._verify_revocation_for_asm(rev_info, asm, False):
                    to_remove.append(cand.id)

        return to_remove
Exemple #7
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
Exemple #8
0
 def _validate_entry(self, rev_info, cur_epoch=None):  # pragma: no cover
     """Removes an expired revocation from the cache."""
     if not ConnectedHashTree.verify_epoch(rev_info.p.epoch, cur_epoch):
         del self._cache[_mk_key(rev_info)]
         if self._labels:
             REVS_REMOVED.labels(**self._labels).inc()
             REVS_TOTAL.labels(**self._labels).dec()
             REVS_BYTES.labels(**self._labels).dec(len(rev_info))
         return False
     return True
Exemple #9
0
    def _validate_segment(self, seg):
        """
        Check segment for revoked upstream/downstream interfaces.

        :param seg: The PathSegment object.
        :return: False, if the path segment contains a revoked upstream/
            downstream interface (not peer). True otherwise.
        """
        for rev_info in list(self.revocations):
            if not ConnectedHashTree.verify_epoch(rev_info.p.epoch):
                self.revocations.pop(rev_info)
                continue
            for asm in seg.iter_asms():
                pcbm = asm.pcbm(0)
                if (rev_info.isd_as() == asm.isd_as()
                        and rev_info.p.ifID in [pcbm.p.inIF, pcbm.p.outIF]):
                    logging.debug("Found revoked interface (%d) in segment "
                                  "%s." % (rev_info.p.ifID, seg.short_desc()))
                    return False
        return True
Exemple #10
0
 def _skip_peer(cls, peer_rev, ht_root):  # pragma: no cover
     if not peer_rev:
         return False
     return (ConnectedHashTree.verify_epoch(peer_rev.p.epoch)
             and ConnectedHashTree.verify(peer_rev, ht_root))
Exemple #11
0
def _skip_peer(peer_rev, ht_root):  # pragma: no cover
    if not peer_rev:
        return False
    rev_status = ConnectedHashTree.verify_epoch(peer_rev.p.epoch)
    return (rev_status == ConnectedHashTree.EPOCH_OK and
            ConnectedHashTree.verify(peer_rev, ht_root))
Exemple #12
0
 def _validate_entry(self, rev_info, cur_epoch=None):  # pragma: no cover
     """Removes an expired revocation from the cache."""
     if not ConnectedHashTree.verify_epoch(rev_info.p.epoch, cur_epoch):
         del self._cache[_mk_key(rev_info)]
         return False
     return True