def handle_pcbs_propagation(self): """ Generate a new beacon or gets ready to forward the one received. """ timestamp = int(SCIONTime.get_time()) # Create beacon for downstream ASes. down_iof = InfoOpaqueField.from_values(timestamp, self.addr.isd_as[0]) downstream_pcb = PathSegment.from_values(down_iof) propagated_pcbs = self.propagate_downstream_pcb(downstream_pcb) # Create beacon for core ASes. core_iof = InfoOpaqueField.from_values(timestamp, self.addr.isd_as[0]) core_pcb = PathSegment.from_values(core_iof) propagated = self.propagate_core_pcb(core_pcb) for k, v in propagated.items(): propagated_pcbs[k].extend(v) # Propagate received beacons. A core beacon server can only receive # beacons from other core beacon servers. beacons = [] with self._rev_seg_lock: for ps in self.core_beacons.values(): beacons.extend(ps.get_best_segments()) for pcb in beacons: propagated = self.propagate_core_pcb(pcb) for k, v in propagated.items(): propagated_pcbs[k].extend(v) self._log_propagations(propagated_pcbs)
def _create_one_hop_path(self, egress_if): ts = int(SCIONTime.get_time()) info = InfoOpaqueField.from_values(ts, self.addr.isd_as[0], hops=2) hf1 = HopOpaqueField.from_values(self.HOF_EXP_TIME, 0, egress_if) hf1.set_mac(self.of_gen_key, ts, None) # Return a path where second HF is empty. return SCIONPath.from_values(info, [hf1, HopOpaqueField()])
def _parse_iof(self, data, label): """ Parse a raw :any:`InfoOpaqueField`. :param Raw data: Raw instance. :param str label: OF label. """ iof = InfoOpaqueField(data.pop(InfoOpaqueField.LEN)) self._ofs.set(label, [iof]) return iof
def get_path(self, reverse_direction=False): """ Returns the list of HopOpaqueFields in the path. """ hofs = [] info = InfoOpaqueField(self.p.info) asms = list(self.iter_asms()) if reverse_direction: asms = reversed(asms) info.up_flag ^= True for asm in asms: hofs.append(asm.pcbm(0).hof()) return SCIONPath.from_values(info, hofs)
def handle_pcbs_propagation(self): """ Generate a new beacon or gets ready to forward the one received. """ timestamp = int(SCIONTime.get_time()) # Create beacon for downstream ASes. down_iof = InfoOpaqueField.from_values(timestamp, self.addr.isd_as[0]) downstream_pcb = PathSegment.from_values(down_iof) self.propagate_downstream_pcb(downstream_pcb) # Create beacon for core ASes. core_iof = InfoOpaqueField.from_values(timestamp, self.addr.isd_as[0]) core_pcb = PathSegment.from_values(core_iof) core_count = self.propagate_core_pcb(core_pcb) # Propagate received beacons. A core beacon server can only receive # beacons from other core beacon servers. beacons = [] for ps in self.core_beacons.values(): beacons.extend(ps.get_best_segments()) for pcb in beacons: core_count += self.propagate_core_pcb(pcb) if core_count: logging.info("Propagated %d Core PCBs", core_count)
def test(self): """ Test the main functionalities of the path store. """ path_policy_file = "topology/ISD1/path_policies/ISD1-AD10.json" path_policy = PathPolicy.from_file(path_policy_file) test_segments = PathStore(path_policy) print("Best paths: " + str(len(test_segments.get_best_segments()))) print("Paths in path store: " + str(len(test_segments.candidates))) print("Paths in latest history snapshot: " + str(len(test_segments.get_latest_history_snapshot())) + "\n") path = 1 for _ in range(1, 6): for _ in range(1, 6): pcb = PathSegment() pcb.iof = InfoOpaqueField.from_values(OFT.TDC_XOVR, False, int(time.time()), path) ad_marking = self._create_ad_marking() pcb.add_ad(ad_marking) print("insert path " + str(path) + ", exp time: " + str(pcb.get_expiration_time())) test_segments.add_segment(pcb) path += 1 print("Best paths: " + str(len(test_segments.get_best_segments()))) print("Paths in path store: " + str(len(test_segments.candidates))) print("Paths in latest history snapshot: " + str(len(test_segments.get_latest_history_snapshot()))) print("Time: " + str(int(time.time())) + "\n") time.sleep(5) print("Waiting for some paths to expire...") time.sleep(25) print("Best paths: " + str(len(test_segments.get_best_segments()))) print("Paths in path store: " + str(len(test_segments.candidates))) print("Paths in latest history snapshot: " + str(len(test_segments.get_latest_history_snapshot())))
def _setup(self): self.info = InfoOpaqueField(self.p.info) self._calc_min_exp() self.sibra_ext = None if self.is_sibra(): self.sibra_ext = SibraPCBExt(self.p.exts.sibra)
class PathSegment(SCIONPayloadBaseProto): NAME = "PathSegment" PAYLOAD_CLASS = PayloadClass.PCB P_CLS = P.PathSegment def __init__(self, p): # pragma: no cover super().__init__(p) self._min_exp = float("inf") self._setup() def _setup(self): self.info = InfoOpaqueField(self.p.info) self._calc_min_exp() self.sibra_ext = None if self.is_sibra(): self.sibra_ext = SibraPCBExt(self.p.exts.sibra) @classmethod def from_values(cls, info, rev_infos=None, sibra_ext=None): # pragma: no cover p = cls.P_CLS.new_message(info=info.pack()) if sibra_ext: p.exts.sibra = sibra_ext.p if rev_infos: p.exts.init("revInfos", len(rev_infos)) for i, info in enumerate(rev_infos): p.exts.revInfos[i] = info.copy() return cls(p) def _calc_min_exp(self): # NB: only the expiration time of the first pcbm is considered. for asm in self.iter_asms(): self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time) def asm(self, idx): # pragma: no cover return ASMarking(self.p.asms[idx]) def iter_asms(self, start=0): # pragma: no cover for i in range(start, len(self.p.asms)): yield self.asm(i) def is_sibra(self): # pragma: no cover return bool(self.p.exts.sibra.id) def sig_pack(self, ver=3): b = [] if ver >= 3: b.append(self.p.info) # ifID field is changed on the fly, and so is ignored. for asm in self.iter_asms(): b.append(asm.sig_pack(9)) if self.is_sibra(): b.append(self.sibra_ext.sig_pack(2)) return b"".join(b) def sign(self, key, set_=True): # pragma: no cover assert not self.p.asms[-1].sig sig = sign(self.sig_pack(3), key) if set_: self.p.asms[-1].sig = sig return sig def add_asm(self, asm): # pragma: no cover """ Appends a new ASMarking block. """ d = self.p.to_dict() d.setdefault('asms', []).append(asm.p) self.p.from_dict(d) self._update_info() self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time) def _update_info(self): # pragma: no cover self.info.hops = len(self.p.asms) self.p.info = self.info.pack() def add_sibra_ext(self, ext_p): # pragma: no cover self.p.exts.sibra = ext_p.copy() self.sibra_ext = SibraPCBExt(self.p.exts.sibra) 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 def remove_crypto(self): # pragma: no cover """ Removes the signatures and certificates from each AS block. """ for asm in self.iter_asms(): asm.remove_sig() asm.remove_chain() def get_path(self, reverse_direction=False): """ Returns the list of HopOpaqueFields in the path. """ hofs = [] info = InfoOpaqueField(self.p.info) asms = list(self.iter_asms()) if reverse_direction: asms = reversed(asms) info.up_flag ^= True for asm in asms: hofs.append(asm.pcbm(0).hof()) return SCIONPath.from_values(info, hofs) def first_ia(self): # pragma: no cover return self.asm(0).isd_as() def last_ia(self): # pragma: no cover return self.asm(-1).isd_as() def last_hof(self): # pragma: no cover if self.p.asms: return self.asm(-1).pcbm(0).hof() return None def get_hops_hash(self, hex=False): # pragma: no cover """ Returns the hash over all triples (ISD_AS, IG_IF, EG_IF) included in the path segment. """ h = SHA256.new() for asm in self.iter_asms(): pcbm = asm.pcbm(0) h.update(asm.isd_as().pack() + struct.pack("!QQ", pcbm.p.inIF, pcbm.p.outIF)) if hex: return h.hexdigest() return h.digest() def get_n_peer_links(self): # pragma: no cover """Return the total number of peer links in the PathSegment.""" n = 0 for asm in self.p.asms: n += len(asm.pcbms) - 1 return n def get_n_hops(self): # pragma: no cover """Return the number of hops in the PathSegment.""" return len(self.p.asms) def get_timestamp(self): # pragma: no cover """Returns the creation timestamp of this PathSegment.""" return self.info.timestamp def set_timestamp(self, timestamp): # pragma: no cover """Updates the timestamp in the IOF.""" assert timestamp < 2 ** 32 - 1 self.info.timestamp = timestamp self._update_info() def get_expiration_time(self): # pragma: no cover """ Returns the expiration time of the path segment in real time. If a PCB extension in the last ASMarking supplies an expiration time, use that. Otherwise fall-back to the standard expiration time calculation. """ if self.is_sibra(): return self.sibra_ext.exp_ts() return self.info.timestamp + int(self._min_exp * EXP_TIME_UNIT) def flags(self): # pragma: no cover f = 0 if self.is_sibra(): f |= PSF.SIBRA return f def rev_info(self, idx): return RevocationInfo(self.p.exts.revInfos[idx]) def iter_rev_infos(self, start=0): for i in range(start, len(self.p.exts.revInfos)): yield self.rev_info(i) def get_rev_map(self): """ Returns a dict (ISD_AS, IF) -> RevocationInfo, if there are any revocations in the PCB extensions, otherwise an empty dict. """ result = {} for rev_info in self.iter_rev_infos(): key = (rev_info.isd_as(), rev_info.p.ifID) result[key] = rev_info return result def short_desc(self): # pragma: no cover """ Return a short description string of the PathSegment, consisting of a truncated hash, the IOF timestamp, and the list of hops. """ desc = [] desc.append("%s, %s, " % ( self.get_hops_hash(hex=True)[:12], iso_timestamp(self.get_timestamp()), )) hops = [] for asm in self.iter_asms(): hops.append(str(asm.isd_as())) exts = [] if self.is_sibra(): exts.append(" %s" % self.sibra_ext.short_desc()) for rev_info in self.iter_rev_infos(): exts.append(" %s" % rev_info.short_desc()) desc.append(" > ".join(hops)) if exts: return "%s\n%s" % ("".join(desc), "\n".join(exts)) return "".join(desc) def __str__(self): s = [] s.append("%s:" % self.NAME) s.append(" %s" % self.info) for asm in self.iter_asms(): for line in asm.short_desc().splitlines(): s.append(" %s" % line) if self.sibra_ext: for line in str(self.sibra_ext).splitlines(): s.append(" %s" % line) for rev_info in self.iter_rev_infos(): for line in rev_info.short_desc().splitlines(): s.append(" %s" % line) return "\n".join(s) def __hash__(self): # pragma: no cover return hash(self.get_hops_hash()) # FIMXE(PSz): should add timestamp?
def infoF(self): info = InfoOpaqueField(self.sdata.p.infoF) info.hops = len(self.p.asEntries) return info
class PathSegment(SCIONPayloadBaseProto): NAME = "PathSegment" PAYLOAD_CLASS = PayloadClass.PCB P_CLS = P.PathSegment VER = len(P_CLS.schema.fields) - 1 def __init__(self, p): # pragma: no cover super().__init__(p) self._min_exp = float("inf") self._setup() def _setup(self): self.info = InfoOpaqueField(self.p.info) self._calc_min_exp() self.sibra_ext = None if self.is_sibra(): self.sibra_ext = SibraPCBExt(self.p.exts.sibra) @classmethod def from_values(cls, info, sibra_ext=None): # pragma: no cover p = cls.P_CLS.new_message(info=info.pack()) if sibra_ext: p.exts.sibra = sibra_ext.p return cls(p) def _calc_min_exp(self): # NB: only the expiration time of the first pcbm is considered. for asm in self.iter_asms(): self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time) def asm(self, idx): # pragma: no cover return ASMarking(self.p.asms[idx]) def iter_asms(self, start=0): # pragma: no cover for i in range(start, len(self.p.asms)): yield self.asm(i) def is_sibra(self): # pragma: no cover return bool(self.p.exts.sibra.id) def sig_pack3(self): """ Pack for signing version 3 (defined by highest field number). """ if self.VER != 3: raise SCIONSigVerError("PathSegment.sig_pack3 cannot support version %s", self.VER) b = [] b.append(self.p.info) # ifID field is changed on the fly, and so is ignored. for asm in self.iter_asms(): b.append(asm.sig_pack8()) if self.is_sibra(): b.append(self.sibra_ext.sig_pack3()) return b"".join(b) def sign(self, key, set_=True): # pragma: no cover sig = sign(self.sig_pack3(), key) if set_: self.p.asms[-1].sig = sig return sig def add_asm(self, asm): # pragma: no cover """ Appends a new ASMarking block. """ d = self.p.to_dict() d.setdefault('asms', []).append(asm.p) self.p.from_dict(d) self._update_info() self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time) def _update_info(self): # pragma: no cover self.info.hops = len(self.p.asms) self.p.info = self.info.pack() def add_sibra_ext(self, ext_p): # pragma: no cover self.p.exts.sibra = ext_p.copy() self.sibra_ext = SibraPCBExt(self.p.exts.sibra) def get_trcs_certs(self): """ Returns a dict of all trcs' versions and a dict of all certificates' versions used in this PCB. """ trcs = defaultdict(set) certs = defaultdict(set) for asm in self.iter_asms(): isd_as = asm.isd_as() isd = isd_as[0] trcs[isd].add(asm.p.trcVer) certs[isd_as].add(asm.p.certVer) return trcs, certs def get_path(self, reverse_direction=False): """ Returns the list of HopOpaqueFields in the path. """ hofs = [] info = InfoOpaqueField(self.p.info) asms = list(self.iter_asms()) if reverse_direction: asms = reversed(asms) info.up_flag ^= True for asm in asms: hofs.append(asm.pcbm(0).hof()) return SCIONPath.from_values(info, hofs) def first_ia(self): # pragma: no cover return self.asm(0).isd_as() def last_ia(self): # pragma: no cover return self.asm(-1).isd_as() def last_hof(self): # pragma: no cover if self.p.asms: return self.asm(-1).pcbm(0).hof() return None def get_hops_hash(self, hex=False): # pragma: no cover """ Returns the hash over all triples (ISD_AS, IG_IF, EG_IF) included in the path segment. """ data = [] for asm in self.iter_asms(): pcbm = asm.pcbm(0) data.append(asm.isd_as().pack()) data.append(struct.pack("!QQ", pcbm.p.inIF, pcbm.p.outIF)) data = b"".join(data) if hex: return crypto_hash(data).hex() return crypto_hash(data) def get_n_peer_links(self): # pragma: no cover """Return the total number of peer links in the PathSegment.""" n = 0 for asm in self.p.asms: n += len(asm.pcbms) - 1 return n def get_n_hops(self): # pragma: no cover """Return the number of hops in the PathSegment.""" return len(self.p.asms) def get_timestamp(self): # pragma: no cover """Returns the creation timestamp of this PathSegment.""" return self.info.timestamp def set_timestamp(self, timestamp): # pragma: no cover """Updates the timestamp in the IOF.""" assert timestamp < 2 ** 32 - 1 self.info.timestamp = timestamp self._update_info() def get_expiration_time(self): # pragma: no cover """ Returns the expiration time of the path segment in real time. If a PCB extension in the last ASMarking supplies an expiration time, use that. Otherwise fall-back to the standard expiration time calculation. """ if self.is_sibra(): return self.sibra_ext.exp_ts() return self.info.timestamp + int(self._min_exp * EXP_TIME_UNIT) def flags(self): # pragma: no cover f = 0 if self.is_sibra(): f |= PSF.SIBRA return f def short_id(self): # pragma: no cover """ Return a 12-byte hex ID identifying the PCB (mostly for logging purposes). """ return self.get_hops_hash(hex=True)[:12] def short_desc(self): # pragma: no cover """ Return a short description string of the PathSegment, consisting of a truncated hash, the IOF timestamp, and the list of hops. """ desc = [] desc.append("%s, %s, " % (self.short_id(), iso_timestamp(self.get_timestamp()))) hops = [] for asm in self.iter_asms(): hop = [] hof = asm.pcbm(0).hof() if hof.ingress_if: hop.append("%d " % hof.ingress_if) hop.append("%s" % asm.isd_as()) if hof.egress_if: hop.append(" %d" % hof.egress_if) hops.append("".join(hop)) exts = [] if self.is_sibra(): exts.append(" %s" % self.sibra_ext.short_desc()) desc.append(">".join(hops)) if exts: return "%s\n%s" % ("".join(desc), "\n".join(exts)) return "".join(desc) def __str__(self): s = [] s.append("%s:" % self.NAME) s.append(" %s" % self.info) for asm in self.iter_asms(): for line in asm.short_desc().splitlines(): s.append(" %s" % line) if self.sibra_ext: for line in str(self.sibra_ext).splitlines(): s.append(" %s" % line) return "\n".join(s) def __eq__(self, other): return self.__hash__() == hash(other) def __hash__(self): # pragma: no cover return hash(self.get_hops_hash()) # FIMXE(PSz): should add timestamp?
class PathSegment(SCIONPayloadBaseProto): NAME = "PathSegment" PAYLOAD_CLASS = PayloadClass.PCB PAYLOAD_TYPE = PCBType.SEGMENT P_CLS = P.PathSegment def __init__(self, p): # pragma: no cover super().__init__(p) self._min_exp = float("inf") self._setup() def _setup(self): self.info = InfoOpaqueField(self.p.info) self._calc_min_exp() self.sibra_ext = None if self.is_sibra(): self.sibra_ext = SibraPCBExt(self.p.exts.sibra) @classmethod def from_values(cls, info, sibra_ext=None): # pragma: no cover p = cls.P_CLS.new_message(info=info.pack()) if sibra_ext: p.exts.sibra = sibra_ext.p return cls(p) def _calc_min_exp(self): # NB: only the expiration time of the first pcbm is considered. for asm in self.iter_asms(): self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time) def asm(self, idx): # pragma: no cover return ASMarking(self.p.asms[idx]) def iter_asms(self, start=0): # pragma: no cover for i in range(start, len(self.p.asms)): yield self.asm(i) def is_sibra(self): # pragma: no cover return bool(self.p.exts.sibra.id) def sig_pack(self, ver=3): b = [] if ver >= 3: b.append(self.p.info) # ifID field is changed on the fly, and so is ignored. for asm in self.iter_asms(): b.append(asm.sig_pack(9)) if self.is_sibra(): b.append(self.sibra_ext.sig_pack(2)) return b"".join(b) def sign(self, key, set_=True): # pragma: no cover assert not self.p.asms[-1].sig sig = sign(self.sig_pack(3), key) if set_: self.p.asms[-1].sig = sig return sig def add_asm(self, asm): # pragma: no cover """ Appends a new ASMarking block. """ d = self.p.to_dict() d.setdefault('asms', []).append(asm.p) self.p.from_dict(d) self._update_info() self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time) def _update_info(self): # pragma: no cover self.info.hops = len(self.p.asms) self.p.info = self.info.pack() def add_sibra_ext(self, ext_p): # pragma: no cover self.p.exts.sibra = ext_p.copy() self.sibra_ext = SibraPCBExt(self.p.exts.sibra) def remove_crypto(self): # pragma: no cover """ Remover the signatures and certificates from each AS block. """ for asm in self.iter_asms(): asm.remove_sig() asm.remove_chain() def get_path(self, reverse_direction=False): """ Returns the list of HopOpaqueFields in the path. """ hofs = [] info = InfoOpaqueField(self.p.info) asms = list(self.iter_asms()) if reverse_direction: asms = reversed(asms) info.up_flag ^= True for asm in asms: hofs.append(asm.pcbm(0).hof()) return SCIONPath.from_values(info, hofs) def first_ia(self): # pragma: no cover return self.asm(0).isd_as() def last_ia(self): # pragma: no cover return self.asm(-1).isd_as() def last_hof(self): # pragma: no cover if self.p.asms: return self.asm(-1).pcbm(0).hof() return None def get_hops_hash(self, hex=False): """ Returns the hash over all the interface revocation tokens included in the path segment. """ h = SHA256.new() for token in self.get_all_iftokens(): h.update(token) if hex: return h.hexdigest() return h.digest() def get_n_peer_links(self): # pragma: no cover """Return the total number of peer links in the PathSegment.""" n = 0 for asm in self.p.asms: n += len(asm.pcbms) - 1 return n def get_n_hops(self): # pragma: no cover """Return the number of hops in the PathSegment.""" return len(self.p.asms) def get_timestamp(self): # pragma: no cover """Returns the creation timestamp of this PathSegment.""" return self.info.timestamp def set_timestamp(self, timestamp): # pragma: no cover """Updates the timestamp in the IOF.""" assert timestamp < 2**32 - 1 self.info.timestamp = timestamp self._update_info() def get_expiration_time(self): # pragma: no cover """ Returns the expiration time of the path segment in real time. If a PCB extension in the last ASMarking supplies an expiration time, use that. Otherwise fall-back to the standard expiration time calculation. """ if self.is_sibra(): return self.sibra_ext.exp_ts() return self.info.timestamp + int(self._min_exp * EXP_TIME_UNIT) def get_all_iftokens(self): """ Returns all interface revocation tokens included in the path segment. """ tokens = [] for asm in self.p.asms: for pcbm in asm.pcbms: tokens.append(pcbm.igRevToken) tokens.append(asm.egRevToken) return tokens def flags(self): # pragma: no cover f = 0 if self.is_sibra(): f |= PSF.SIBRA return f def short_desc(self): # pragma: no cover """ Return a short description string of the PathSegment, consisting of a truncated hash, the IOF timestamp, and the list of hops. """ desc = [] desc.append("%s, %s, " % ( self.get_hops_hash(hex=True)[:12], iso_timestamp(self.get_timestamp()), )) hops = [] for asm in self.iter_asms(): hops.append(str(asm.isd_as())) exts = [] if self.is_sibra(): exts.append(" %s" % self.sibra_ext.short_desc()) desc.append(" > ".join(hops)) if exts: return "%s\n%s" % ("".join(desc), "\n".join(exts)) return "".join(desc) def __str__(self): s = [] s.append("%s:" % self.NAME) s.append(" %s" % self.info) for asm in self.iter_asms(): for line in asm.short_desc().splitlines(): s.append(" %s" % line) if self.sibra_ext: for line in str(self.sibra_ext).splitlines(): s.append(" %s" % line) return "\n".join(s) def __hash__(self): # pragma: no cover return hash(self.get_hops_hash()) # FIMXE(PSz): should add timestamp?