def _send_ifstate_update(self, state_infos, border_metas, server_metas=None): server_metas = server_metas or [] payload = CtrlPayload(PathMgmt( IFStatePayload.from_values(state_infos))) for meta in border_metas: self.send_meta(payload.copy(), meta, (meta.host, meta.port)) for meta in server_metas: self.send_meta(payload.copy(), meta)
def _reply_cc(self, key, req_info): isd_as, ver = key ver = None if ver == CertChainRequest.NEWEST_VERSION else ver meta = req_info[0] cert_chain = self.trust_store.get_cert(isd_as, ver) self.send_meta(CtrlPayload(CertMgmt(CertChainReply.from_values(cert_chain))), meta) logging.info("Cert chain for %sv%s sent to %s", isd_as, ver, meta)
def _resolve_not_core_failed(self, seg_req, req_id, meta, dst_ia, flags, logger): """ Execute after _resolve_not_core() cannot resolve a new request, due to lack of corresponding down segment(s). This must not be executed for a pending request. """ sibra = PATH_FLAG_SIBRA in flags with self.pen_req_lock: self.pending_req[(dst_ia, sibra)][str(meta)] = (seg_req, req_id, meta, logger) if dst_ia[0] == self.addr.isd_as[0]: # Master may know down segment as dst is in local ISD. self._query_master(dst_ia, logger, flags=flags) return # Dst is in a remote ISD, ask any core AS from there. Don't use a SIBRA # segment, even if the request has the SIBRA flag set, as this is just # for basic internal communication. csegs = self.core_segments( first_isd=dst_ia[0], last_ia=self.addr.isd_as) if csegs: cseg = csegs[0] path = cseg.get_path(reverse_direction=True) dst_ia = cseg.first_ia() logger.info("Down-Segment request for different ISD, " "forwarding request to CPS in %s via %s" % (dst_ia, cseg.short_desc())) meta = self._build_meta(ia=dst_ia, path=path, host=SVCType.PS_A, reuse=True) self.send_meta(CtrlPayload(PathMgmt(seg_req)), meta) else: # If no core segment was available, add request to waiting targets. logger.info("Waiting for core segment to ISD %s", dst_ia[0]) self.waiting_targets[dst_ia[0]].append((seg_req, logger)) # Ask for any segment to dst_isd self._query_master(dst_ia.any_as(), logger)
def _api_handle_rev_notification(self, pld, meta): request = pld.union assert isinstance(request, SCIONDRevNotification), type(request) status = self.handle_revocation( CtrlPayload(PathMgmt(request.rev_info())), meta) rev_reply = SCIONDMsg(SCIONDRevReply.from_values(status), pld.id) self.send_meta(rev_reply.pack(), meta)
def _send_path_segments(self, req, req_id, meta, logger, up=None, core=None, down=None): """ Sends path-segments to requester (depending on Path Server's location). """ up = up or set() core = core or set() down = down or set() all_segs = up | core | down if not all_segs: logger.warning("No segments to send for request: %s from: %s" % (req.short_desc(), meta)) return revs_to_add = self._peer_revs_for_segs(all_segs) recs = PathSegmentRecords.from_values( { PST.UP: up, PST.CORE: core, PST.DOWN: down }, revs_to_add) pld = PathSegmentReply.from_values(req.copy(), recs) self.send_meta(CtrlPayload(PathMgmt(pld), req_id=req_id), meta) logger.info("Sending PATH_REPLY with %d segment(s).", len(all_segs))
def process_drkey_request(self, cpld, meta): """ Process first order DRKey requests from other ASes. :param DRKeyRequest req: the DRKey request :param UDPMetadata meta: the metadata """ dpld = cpld.union req = dpld.union assert isinstance(req, DRKeyRequest), type(req) logging.info("DRKeyRequest received from %s: %s [id: %s]", meta, req.short_desc(), cpld.req_id_str()) REQS_TOTAL.labels(**self._labels, type="drkey").inc() try: cert = self._verify_drkey_request(req, meta) except SCIONVerificationError as e: logging.warning("Invalid DRKeyRequest from %s. Reason %s: %s", meta, e, req.short_desc()) return sv = self._get_drkey_secret(get_drkey_exp_time(req.p.flags.prefetch)) cert_version = self.trust_store.get_cert( self.addr.isd_as).certs[0].version trc_version = self.trust_store.get_trc(self.addr.isd_as[0]).version rep = get_drkey_reply(sv, self.addr.isd_as, meta.ia, self.private_key, self.signing_key, cert_version, cert, trc_version) self.send_meta(CtrlPayload(DRKeyMgmt(rep), req_id=cpld.req_id), meta) logging.info("DRKeyReply sent to %s: %s [id: %s]", meta, req.short_desc(), cpld.req_id_str())
def process_trc_reply(self, cpld, meta): """ Process the TRC reply. :param rep: TRC reply. :type rep: TRCReply. """ meta.close() cmgt = cpld.union rep = cmgt.union assert isinstance(rep, TRCReply), type(rep) isd, ver = rep.trc.get_isd_ver() logging.info("TRC reply received for %sv%s from %s [id: %s]", isd, ver, meta, cpld.req_id_str()) self.trust_store.add_trc(rep.trc, True) # Update core ases for isd this trc belongs to max_local_ver = self.trust_store.get_trc(rep.trc.isd) if max_local_ver.version == rep.trc.version: self._update_core_ases(rep.trc) with self.req_trcs_lock: self.requested_trcs.pop((isd, ver), None) if self._labels: PENDING_TRC_REQS_TOTAL.labels(**self._labels).set(len(self.requested_trcs)) # Send trc to CS if meta.get_addr().isd_as != self.addr.isd_as: cs_meta = self._get_cs() self.send_meta(CtrlPayload(CertMgmt(rep)), cs_meta) cs_meta.close() # Remove received TRC from map self._check_segs_with_rec_trc(isd, ver)
def _reply_trc(self, key, req_info): isd, ver = key ver = None if ver == TRCRequest.NEWEST_VERSION else ver meta = req_info[0] trc = self.trust_store.get_trc(isd, ver) self.send_meta(CtrlPayload(CertMgmt(TRCReply.from_values(trc))), meta) logging.info("TRC for %sv%s sent to %s", isd, ver, meta)
def _fetch_drkey(self, drkey, _): """ Fetch missing first order DRKey with the same (SrcIA, DstIA, expTime). :param FirstOrderDRKey drkey: The missing DRKey. """ cert = self.trust_store.get_cert(self.addr.isd_as) trc = self.trust_store.get_trc(self.addr.isd_as[0]) if not cert or not trc: logging.warning( "DRKeyRequest for %s not sent. Own CertChain/TRC not present.", drkey.src_ia) return req = get_drkey_request(drkey.src_ia, False, self.signing_key, cert.certs[0].version, trc.version) path_meta = self._get_path_via_sciond(drkey.src_ia) if path_meta: meta = self._build_meta(drkey.src_ia, host=SVCType.CS_A, path=path_meta.fwd_path()) req_id = mk_ctrl_req_id() self.send_meta(CtrlPayload(DRKeyMgmt(req)), meta) logging.info("DRKeyRequest (%s) sent to %s via %s [id: %016x]", req.short_desc(), meta, path_meta, req_id) else: logging.warning("DRKeyRequest (for %s) not sent", req.short_desc())
def _create_hdrs(self): """ Create headers for a SCION packet """ dest = SCIONAddr.from_values(self.remote, SVCType.SB_A) cmn_hdr, addr_hdr = build_base_hdrs(dest, self.addr) payload = CtrlPayload(SIBRAPayload.from_values()) udp_hdr = SCIONUDPHeader.from_values(self.addr, self._port, dest, 0) return cmn_hdr, addr_hdr, udp_hdr, payload
def _handle_scmp_revocation(self, pld, meta): rev_info = RevocationInfo.from_raw(pld.info.rev_info) try: rev_info.validate() except SCIONBaseError as e: logging.warning("Failed to validate SCMP RevInfo from %s: %s\n%s", meta, e, rev_info.short_desc()) return self._handle_revocation(CtrlPayload(PathMgmt(rev_info)), meta)
def _cached_trcs_handler(self, raw_entries): """ Handles cached (through ZK) TRCs, passed as a list. """ for raw in raw_entries: trc = TRC.from_raw(raw.decode('utf-8')) rep = CtrlPayload(CertMgmt(TRCReply.from_values(trc))) self.process_trc_reply(rep, None, from_zk=True) if len(raw_entries) > 0: logging.debug("Processed %s trcs from ZK", len(raw_entries))
def _cached_certs_handler(self, raw_entries): """ Handles cached (through ZK) chains, passed as a list. """ for raw in raw_entries: cert = CertificateChain.from_raw(raw.decode('utf-8')) rep = CtrlPayload(CertMgmt(CertChainReply.from_values(cert))) self.process_cert_chain_reply(rep, None, from_zk=True) if len(raw_entries) > 0: logging.debug("Processed %s certs from ZK", len(raw_entries))
def _reply_cc(self, key, req_info): isd_as, ver = key meta = req_info[0] req_id = req_info[2] cert_chain = self.trust_store.get_cert(isd_as, ver) self.send_meta( CtrlPayload(CertMgmt(CertChainReply.from_values(cert_chain)), req_id=req_id), meta) logging.info("Cert chain for %sv%s sent to %s [id: %016x]", isd_as, ver, meta, req_id)
def register_down_segment(self, pcb): """ Send down-segment to Core Path Server """ pcb.sign(self.signing_key) core_path = pcb.get_path(reverse_direction=True) records = PathRecordsReg.from_values({PST.DOWN: [pcb]}) dst_ia = pcb.asm(0).isd_as() meta = self._build_meta(ia=dst_ia, host=SVCType.PS_A, path=core_path, reuse=True) self.send_meta(CtrlPayload(PathMgmt(records)), meta) return meta
def _send_trc_request(self, isd, ver): trc_req = TRCRequest.from_values(isd, ver, cache_only=True) path_meta = self._get_path_via_sciond(trc_req.isd_as()) if path_meta: meta = self._build_meta( path_meta.dst_ia(), host=SVCType.CS_A, path=path_meta.fwd_path()) self.send_meta(CtrlPayload(CertMgmt(trc_req)), meta) logging.info("TRC request sent to %s via [%s]: %s", meta, path_meta.short_desc(), trc_req.short_desc()) else: logging.warning("TRC request not sent for %s: no path found.", trc_req.short_desc())
def _send_cc_request(self, isd_as, ver): req = CertChainRequest.from_values(isd_as, ver, cache_only=True) path_meta = self._get_path_via_sciond(isd_as) if path_meta: meta = self._build_meta(isd_as, host=SVCType.CS_A, path=path_meta.fwd_path()) self.send_meta(CtrlPayload(CertMgmt(req)), meta) logging.info("Cert chain request sent to %s via [%s]: %s", meta, path_meta.short_desc(), req.short_desc()) else: logging.warning("Cert chain request (for %s) not sent: " "no path found", req.short_desc())
def sign(self, pld: CtrlPayload) -> SignedCtrlPayload: """ Creates a signed version of the supplied control payload. :param CtrlPayload pld: The control payload to be signed :returns: the signed control payload :rtype: SignedCtrlPayload :raises: ProtoSignError """ sig_pld = SignedCtrlPayload.from_values(pld.proto().to_bytes_packed(), self._sign.copy()) sig_pld.sign(self._key) return sig_pld
def _fetch_segments(self, req): """ Called to fetch the requested path. """ try: addr, port = self.dns_query_topo(PATH_SERVICE)[0] except SCIONServiceLookupError: log_exception("Error querying path service:") return logging.debug("Sending path request (%s) to [%s]:%s", req.short_desc(), addr, port) meta = self._build_meta(host=addr, port=port) self.send_meta(CtrlPayload(PathMgmt(req)), meta)
def handle_ifstate_infos(self, cpld, meta): """ Handles IFStateInfos. :param IFStatePayload infos: The state info objects. """ pmgt = cpld.union infos = pmgt.union assert isinstance(infos, IFStatePayload), type(infos) for info in infos.iter_infos(): if not info.p.active and info.p.sRevInfo: self._handle_revocation( CtrlPayload(PathMgmt(info.srev_info())), meta)
def register_up_segment(self, pcb, svc_type): """ Send up-segment to Local Path Servers and Sibra Servers :raises: SCIONServiceLookupError: service type lookup failure """ pcb.sign(self.signing_key) records = PathRecordsReg.from_values({PST.UP: [pcb]}) addr, port = self.dns_query_topo(svc_type)[0] meta = self._build_meta(host=addr, port=port) self.send_meta(CtrlPayload(PathMgmt(records)), meta) return meta
def _handle_pcbs_from_zk(self, pcbs): """ Handles cached pcbs through ZK, passed as a list. """ for pcb in pcbs: try: pcb = PCB.from_raw(pcb) except SCIONParseError as e: logging.error("Unable to parse raw pcb: %s", e) continue self.handle_pcb(CtrlPayload(pcb)) if pcbs: logging.debug("Processed %s PCBs from ZK", len(pcbs))
def _send_ifstate_update(self, border_metas, server_metas=None): server_metas = server_metas or [] with self.ifid_state_lock: infos = [] for (ifid, state) in self.ifid_state.items(): # Don't include inactive interfaces in update. if state.is_inactive(): continue rev_info = self._get_ht_proof( ifid) if state.is_revoked() else None info = IFStateInfo.from_values(ifid, state.is_active(), rev_info) infos.append(info) if not infos and not self._quiet_startup(): logging.warning( "No IF state info to put in IFState update for %s.", ", ".join([str(m) for m in border_metas + server_metas])) return payload = CtrlPayload(PathMgmt(IFStatePayload.from_values(infos))) for meta in border_metas: self.send_meta(payload.copy(), meta, (meta.host, meta.port)) for meta in server_metas: self.send_meta(payload.copy(), meta)
def register_core_segment(self, pcb, svc_type): """ Send core-segment to Local Path Servers and Sibra Servers :raises: SCIONServiceLookupError: service type lookup failure """ pcb.sign(self.signing_key) # Register core path with local core path server. addr, port = self.dns_query_topo(svc_type)[0] records = PathRecordsReg.from_values({PST.CORE: [pcb]}) meta = self._build_meta(host=addr, port=port, reuse=True) self.send_meta(CtrlPayload(PathMgmt(records.copy())), meta) return meta
def _fetch_segments(self, req): """ Called to fetch the requested path. """ try: addr, port = self.dns_query_topo(ServiceType.PS)[0] except SCIONServiceLookupError: log_exception("Error querying path service:") return req_id = mk_ctrl_req_id() logging.debug("Sending path request (%s) to [%s]:%s [id: %016x]", req.short_desc(), addr, port, req_id) meta = self._build_meta(host=addr, port=port) self.send_meta(CtrlPayload(PathMgmt(req), req_id=req_id), meta)
def _send_waiting_queries(self, dst_isd, pcb): targets = self.waiting_targets[dst_isd] if not targets: return path = pcb.get_path(reverse_direction=True) src_ia = pcb.first_ia() while targets: (seg_req, logger) = targets.pop(0) meta = self._build_meta(ia=src_ia, path=path, host=SVCType.PS_A, reuse=True) self.send_meta(CtrlPayload(PathMgmt(seg_req)), meta) logger.info("Waiting request (%s) sent to %s via %s", seg_req.short_desc(), meta, pcb.short_desc())
def process_trc_request(self, cpld, meta): """Process a TRC request.""" cmgt = cpld.union req = cmgt.union assert isinstance(req, TRCRequest), type(req) isd, ver = req.isd_as()[0], req.p.version logging.info("TRC request received for %sv%s from %s [id: %s]" % (isd, ver, meta, cpld.req_id_str())) trc = self.trust_store.get_trc(isd, ver) if trc: self.send_meta( CtrlPayload(CertMgmt(TRCReply.from_values(trc)), req_id=cpld.req_id), meta) else: logging.warning("Could not find requested TRC %sv%s [id: %s]" % (isd, ver, cpld.req_id_str()))
def process_cert_chain_request(self, cpld, meta): """Process a certificate chain request.""" cmgt = cpld.union req = cmgt.union assert isinstance(req, CertChainRequest), type(req) isd_as, ver = req.isd_as(), req.p.version logging.info("Cert chain request received for %sv%s from %s [id: %s]" % (isd_as, ver, meta, cpld.req_id_str())) cert = self.trust_store.get_cert(isd_as, ver) if cert: self.send_meta( CtrlPayload(CertMgmt(CertChainReply.from_values(cert)), req_id=cpld.req_id), meta) else: logging.warning("Could not find requested certificate %sv%s [id: %s]" % (isd_as, ver, cpld.req_id_str()))
def _handle_pending_requests(self): rem_keys = [] # Serve pending requests. with self.pen_req_lock: for key in self.pending_req: for req_id, (req, meta, logger) in self.pending_req[key].items(): if self.path_resolution(CtrlPayload(PathMgmt(req)), meta, new_request=False, logger=logger): meta.close() del self.pending_req[key][req_id] if not self.pending_req[key]: rem_keys.append(key) for key in rem_keys: del self.pending_req[key]
def _check_local_cert(self): while self.run_flag.is_set(): chain = self._get_my_cert() exp = min(chain.as_cert.expiration_time, chain.core_as_cert.expiration_time) diff = exp - int(time.time()) if diff > self.config.segment_ttl: time.sleep(diff - self.config.segment_ttl) continue cs_meta = self._get_cs() req = CertChainRequest.from_values(self.addr.isd_as, chain.as_cert.version + 1, cache_only=True) logging.info("Request new certificate chain. Req: %s", req) self.send_meta(CtrlPayload(CertMgmt(req)), cs_meta) cs_meta.close() time.sleep(self.CERT_REQ_RATE)