From 8a4487cdfdcd30a54a05c1f16df357b0d8206545 Mon Sep 17 00:00:00 2001 From: Sebastien Merle Date: Fri, 19 Jun 2020 11:57:34 +0200 Subject: [PATCH] path: pcep module: change the synchronization workflow. --- pathd/path_errors.c | 6 + pathd/path_errors.h | 1 + pathd/path_pcep.c | 38 ++++- pathd/path_pcep.h | 7 + pathd/path_pcep_debug.c | 32 +++- pathd/path_pcep_debug.h | 1 + pathd/path_pcep_lib.c | 12 +- pathd/path_pcep_lib.h | 2 +- pathd/path_pcep_nb.c | 3 +- pathd/path_pcep_pcc.c | 355 +++++++++++++++++++++++++++------------- pathd/path_pcep_pcc.h | 10 +- 11 files changed, 348 insertions(+), 119 deletions(-) diff --git a/pathd/path_errors.c b/pathd/path_errors.c index fda3745546a0..635f01d7c84f 100644 --- a/pathd/path_errors.c +++ b/pathd/path_errors.c @@ -73,6 +73,12 @@ static struct log_ref ferr_path_warn[] = { .description = "Some recoverable internal error", .suggestion = "Open an Issue with all relevant log files" }, + { + .code = EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + .title = "Unsupported PCEP feature", + .description = "Receved an unsupported PCEP message", + .suggestion = "The PCC and PCE are probably not compatible. Open an Issue with all relevant log files" + }, { .code = EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE, .title = "Unexpected pcep message", diff --git a/pathd/path_errors.h b/pathd/path_errors.h index 3f26364b0946..edd896560479 100644 --- a/pathd/path_errors.h +++ b/pathd/path_errors.h @@ -31,6 +31,7 @@ enum path_log_refs { EC_PATH_PCEP_LIB_CONNECT, EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE, EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT, EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT, diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c index 64d77158c6e0..20d51da8c6b1 100644 --- a/pathd/path_pcep.c +++ b/pathd/path_pcep.c @@ -58,6 +58,10 @@ static int pathd_candidate_created_handler(struct srte_candidate *candidate); static int pathd_candidate_updated_handler(struct srte_candidate *candidate); static int pathd_candidate_removed_handler(struct srte_candidate *candidate); +/* Path manipulation functions */ +static struct path_metric *pcep_copy_metrics(struct path_metric *metric); +static struct path_hop *pcep_copy_hops(struct path_hop *hop); + /* CLI Functions */ static int pcep_cli_debug_config_write(struct vty *vty); static int pcep_cli_debug_set_all(uint32_t flags, bool set); @@ -101,6 +105,38 @@ struct path_metric *pcep_new_metric(void) return metric; } +struct path_metric *pcep_copy_metrics(struct path_metric *metric) +{ + if (metric == NULL) return NULL; + struct path_metric *new_metric = pcep_new_metric(); + *new_metric = *metric; + new_metric->next = pcep_copy_metrics(metric->next); + return new_metric; +} + +struct path_hop *pcep_copy_hops(struct path_hop *hop) +{ + if (hop == NULL) return NULL; + struct path_hop *new_hop = pcep_new_hop(); + *new_hop = *hop; + new_hop->next = pcep_copy_hops(hop->next); + return new_hop; +} + +struct path *pcep_copy_path(struct path *path) +{ + struct path *new_path = pcep_new_path(); + + *new_path = *path; + new_path->first_metric = pcep_copy_metrics(path->first_metric); + new_path->first_hop = pcep_copy_hops(path->first_hop); + if (path->name != NULL) + new_path->name = XSTRDUP(MTYPE_PCEP, path->name); + if (path->originator != NULL) + new_path->originator = XSTRDUP(MTYPE_PCEP, path->originator); + return new_path; +} + void pcep_free_path(struct path *path) { struct path_hop *hop; @@ -174,8 +210,6 @@ int pcep_main_event_start_sync(int pcc_id) int pcep_main_event_start_sync_cb(struct path *path, void *arg) { int *pcc_id = (int *)arg; - path->is_synching = true; - path->go_active = true; pcep_ctrl_sync_path(pcep_g->fpt, *pcc_id, path); return 1; } diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h index 7fc1e9400d44..4b395a3dfa79 100644 --- a/pathd/path_pcep.h +++ b/pathd/path_pcep.h @@ -237,6 +237,11 @@ struct path { /* Indicate the given path was created by the PCE, See draft-ietf-pce-pce-initiated-lsp, section-5.3.1, flag C */ bool was_created; + + /* The following data is defined for comnputation replies */ + + /* Indicate that no path could be computed */ + bool no_path; }; struct pcep_glob { @@ -254,6 +259,8 @@ extern struct pcep_glob *pcep_g; struct path *pcep_new_path(void); struct path_hop *pcep_new_hop(void); struct path_metric *pcep_new_metric(void); +struct path *pcep_copy_path(struct path *path); void pcep_free_path(struct path *path); + #endif // _PATH_PCEP_H_ diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c index 3fa9384004ec..d5beeb7a1c23 100644 --- a/pathd/path_pcep_debug.c +++ b/pathd/path_pcep_debug.c @@ -65,6 +65,7 @@ _format_pcep_object_ipv4_endpoint(int ps, struct pcep_object_endpoints_ipv4 *obj); static void _format_pcep_object_metric(int ps, struct pcep_object_metric *obj); static void _format_pcep_object_bandwidth(int ps, struct pcep_object_bandwidth *obj); +static void _format_pcep_object_nopath(int ps, struct pcep_object_nopath *obj); static void _format_pcep_object_ro(int ps, struct pcep_object_ro *obj); static void _format_pcep_object_ro_details(int ps, struct pcep_object_ro_subobj *ro); @@ -594,6 +595,22 @@ const char *pcep_metric_type_name(enum pcep_metric_types type) } } +const char *pcep_nopath_tlv_err_code_name(enum pcep_nopath_tlv_err_codes type) +{ + switch (type) { + case PCEP_NOPATH_TLV_ERR_NO_TLV: + return "NO_TLV"; + case PCEP_NOPATH_TLV_ERR_PCE_UNAVAILABLE: + return "PCE_UNAVAILABLE"; + case PCEP_NOPATH_TLV_ERR_UNKNOWN_DST: + return "UNKNOWN_DST"; + case PCEP_NOPATH_TLV_ERR_UNKNOWN_SRC: + return "UNKNOWN_SRC"; + default: + return "UNKNOWN"; + } +} + const char *format_pcc_opts(struct pcc_opts *opts) { PCEP_FORMAT_INIT(); @@ -831,7 +848,7 @@ void _format_path(int ps, struct path *path) path->has_bandwidth); if (path->has_bandwidth) { PCEP_FORMAT("%*sbandwidth: %f\n", ps2, "", - path->bandwidth); + path->bandwidth); } if (path->first_hop == NULL) { @@ -1045,6 +1062,10 @@ void _format_pcep_object_details(int ps, struct pcep_object_header *obj) _format_pcep_object_bandwidth(ps, (struct pcep_object_bandwidth *)obj); break; + case TUP(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH): + _format_pcep_object_nopath(ps, + (struct pcep_object_nopath *)obj); + break; default: PCEP_FORMAT("%*s...\n", ps, ""); break; @@ -1120,6 +1141,15 @@ void _format_pcep_object_bandwidth(int ps, struct pcep_object_bandwidth *obj) PCEP_FORMAT("%*sbandwidth: %f\n", ps, "", obj->bandwidth); } +void _format_pcep_object_nopath(int ps, struct pcep_object_nopath *obj) +{ + PCEP_FORMAT("%*sni: %u\n", ps, "", obj->ni); + PCEP_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c); + PCEP_FORMAT("%*serr_code: %s (%u)\n", ps, "", + pcep_nopath_tlv_err_code_name(obj->err_code), + obj->err_code); +} + void _format_pcep_object_ro(int ps, struct pcep_object_ro *obj) { double_linked_list *obj_list = obj->sub_objects; diff --git a/pathd/path_pcep_debug.h b/pathd/path_pcep_debug.h index 6694c22973de..3c4a6d910a2e 100644 --- a/pathd/path_pcep_debug.h +++ b/pathd/path_pcep_debug.h @@ -44,6 +44,7 @@ const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type); const char *pcep_ro_type_name(enum pcep_ro_subobj_types ro_type); const char *pcep_nai_type_name(enum pcep_sr_subobj_nai nai_type); const char *pcep_metric_type_name(enum pcep_metric_types type); +const char *pcep_nopath_tlv_err_code_name(enum pcep_nopath_tlv_err_codes code); const char *format_pcc_opts(struct pcc_opts *ops); const char *format_pcc_state(struct pcc_state *state); diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c index b76758fceb68..f568d20300ee 100644 --- a/pathd/path_pcep_lib.c +++ b/pathd/path_pcep_lib.c @@ -340,18 +340,21 @@ struct pcep_message *pcep_lib_format_request(uint32_t reqid, struct ipaddr *src, dll_append(rp_tlvs, setup_type_tlv); rp = pcep_obj_create_rp(0, false, false, false, reqid, rp_tlvs); + rp->header.flag_p = true; if (IS_IPADDR_V6(src)) { endpoints_ipv6 = pcep_obj_create_endpoint_ipv6(&src->ipaddr_v6, &dst->ipaddr_v6); + endpoints_ipv6->header.flag_p = true; return pcep_msg_create_request_ipv6(rp, endpoints_ipv6, NULL); } else { endpoints_ipv4 = pcep_obj_create_endpoint_ipv4(&src->ipaddr_v4, &dst->ipaddr_v4); + endpoints_ipv4->header.flag_p = true; return pcep_msg_create_request(rp, endpoints_ipv4, NULL); } } -struct pcep_message *pcep_lib_reject_message(int error_type, int error_value) +struct pcep_message *pcep_lib_format_error(int error_type, int error_value) { return pcep_msg_create_error(error_type, error_value); } @@ -409,6 +412,10 @@ struct path *pcep_lib_parse_path(struct pcep_message *msg) path->has_bandwidth = true; path->bandwidth = bandwidth->bandwidth; break; + case CLASS_TYPE(PCEP_OBJ_CLASS_NOPATH, + PCEP_OBJ_TYPE_NOPATH): + path->no_path = true; + break; default: flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT, "Unexpected PCEP object %s (%u) / %s (%u)", @@ -508,6 +515,7 @@ double_linked_list *pcep_lib_format_path(struct path *path) dll_append(srp_tlvs, tlv); srp = pcep_obj_create_srp(path->do_remove, path->srp_id, srp_tlvs); assert(srp != NULL); + srp->header.flag_p = true; dll_append(objs, srp); } @@ -564,6 +572,7 @@ double_linked_list *pcep_lib_format_path(struct path *path) path->is_synching /* S Flag */, path->is_delegated /* D Flag */, lsp_tlvs); assert(lsp != NULL); + lsp->header.flag_p = true; dll_append(objs, lsp); /* ERO object */ ero_objs = dll_initialize(); @@ -660,6 +669,7 @@ double_linked_list *pcep_lib_format_path(struct path *path) } ero = pcep_obj_create_ero(ero_objs); assert(ero != NULL); + ero->header.flag_p = true; dll_append(objs, ero); if (path->plsp_id == 0) { diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h index 6e97626223b5..2c0670fce659 100644 --- a/pathd/path_pcep_lib.h +++ b/pathd/path_pcep_lib.h @@ -34,7 +34,7 @@ void pcep_lib_disconnect(pcep_session *sess); struct pcep_message *pcep_lib_format_report(struct path *path); struct pcep_message *pcep_lib_format_request(uint32_t reqid, struct ipaddr *src, struct ipaddr *dst); -struct pcep_message *pcep_lib_reject_message(int error_type, int error_value); +struct pcep_message *pcep_lib_format_error(int error_type, int error_value); struct path *pcep_lib_parse_path(struct pcep_message *msg); void pcep_lib_parse_capabilities(struct pcep_message *msg, struct pcep_caps *caps); diff --git a/pathd/path_pcep_nb.c b/pathd/path_pcep_nb.c index 096104f25351..b8cdd972c683 100644 --- a/pathd/path_pcep_nb.c +++ b/pathd/path_pcep_nb.c @@ -168,6 +168,7 @@ struct path *candidate_to_path(struct srte_candidate *candidate) segment_list = RB_FIND(srte_segment_list_head, &srte_segment_lists, &key); assert(segment_list != NULL); + assert(segment_list == candidate->segment_list); hop = path_nb_list_path_hops(segment_list); update_origin = segment_list->protocol_origin; originator = XSTRDUP(MTYPE_PCEP, segment_list->originator); @@ -242,7 +243,7 @@ struct path_hop *path_nb_list_path_hops(struct srte_segment_list *segment_list) hop = pcep_new_hop(); *hop = (struct path_hop){ .next = last_hop, - .is_loose = false, + .is_loose = true, .has_sid = true, .is_mpls = true, .has_attribs = false, diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c index 61126cde2d3e..4447579704cb 100644 --- a/pathd/path_pcep_pcc.c +++ b/pathd/path_pcep_pcc.c @@ -51,6 +51,7 @@ /* The number of time we will skip connecting if we are missing the PCC * address for an inet family different from the selected transport one*/ #define OTHER_FAMILY_MAX_RETRIES 4 +#define MAX_ERROR_MSG_SIZE 256 /* PCEP Event Handler */ static void handle_pcep_open(struct ctrl_state *ctrl_state, @@ -78,18 +79,21 @@ static void update_tag(struct pcc_state *pcc_state); static void update_originator(struct pcc_state *pcc_state); static void schedule_reconnect(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state); -static void send_pcep_message(struct ctrl_state *ctrl_state, - struct pcc_state *pcc_state, +static void send_pcep_message(struct pcc_state *pcc_state, struct pcep_message *msg); -static void send_report(struct ctrl_state *ctrl_state, - struct pcc_state *pcc_state, struct path *path); -static void specialize_output_path(struct pcc_state *pcc_state, - struct path *path); -static void specialize_input_path(struct pcc_state *pcc_state, - struct path *path); -static void send_comp_request(struct ctrl_state *ctrl_state, - struct pcc_state *pcc_state, - struct lsp_nb_key *nb_key); +static void send_pcep_error(struct pcc_state *pcc_state, + enum pcep_error_type error_type, + enum pcep_error_value error_value); +static void send_report(struct pcc_state *pcc_state, struct path *path); +static void send_comp_request(struct pcc_state *pcc_state, + struct req_entry *req); +static void specialize_outgoing_path(struct pcc_state *pcc_state, + struct path *path); +static void specialize_incoming_path(struct pcc_state *pcc_state, + struct path *path); +static bool validate_incoming_path(struct pcc_state *pcc_state, + struct path *path, + char* errbuff, size_t buffsize); static void set_pcc_address(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey, struct ipaddr *addr); static int compare_pcc_opts(struct pcc_opts *lhs, struct pcc_opts *rhs); @@ -98,8 +102,10 @@ static int compare_pce_opts(struct pce_opts *lhs, struct pce_opts *rhs); /* Data Structure Helper Functions */ static void lookup_plspid(struct pcc_state *pcc_state, struct path *path); static void lookup_nbkey(struct pcc_state *pcc_state, struct path *path); -static uint32_t push_req(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey); -static bool pop_req(struct pcc_state *pcc_state, struct path *path); +static void free_req_entry(struct req_entry *req); +static struct req_entry* push_req(struct pcc_state *pcc_state, + struct path* path); +static struct req_entry* pop_req(struct pcc_state *pcc_state, uint32_t reqid); /* Data Structure Callbacks */ static int plspid_map_cmp(const struct plspid_map_data *a, @@ -108,16 +114,19 @@ static uint32_t plspid_map_hash(const struct plspid_map_data *e); static int nbkey_map_cmp(const struct nbkey_map_data *a, const struct nbkey_map_data *b); static uint32_t nbkey_map_hash(const struct nbkey_map_data *e); -static int req_map_cmp(const struct req_map_data *a, - const struct req_map_data *b); -static uint32_t req_map_hash(const struct req_map_data *e); /* Data Structure Declarations */ DECLARE_HASH(plspid_map, struct plspid_map_data, mi, plspid_map_cmp, plspid_map_hash) DECLARE_HASH(nbkey_map, struct nbkey_map_data, mi, nbkey_map_cmp, nbkey_map_hash) -DECLARE_HASH(req_map, struct req_map_data, mi, req_map_cmp, req_map_hash) + +static inline int req_entry_compare(const struct req_entry *a, + const struct req_entry *b) +{ + return a->path->req_id - b->path->req_id; +} +RB_GENERATE(req_entry_head, req_entry, entry, req_entry_compare) /* ------------ API Functions ------------ */ @@ -131,6 +140,8 @@ struct pcc_state *pcep_pcc_initialize(struct ctrl_state *ctrl_state, int index) pcc_state->next_reqid = 1; pcc_state->next_plspid = 1; + RB_INIT(req_entry_head, &pcc_state->requests); + update_tag(pcc_state); update_originator(pcc_state); @@ -380,15 +391,27 @@ void pcep_pcc_sync_path(struct ctrl_state *ctrl_state, return; assert(pcc_state->status == PCEP_PCC_SYNCHRONIZING); - assert(path->is_synching); + path->is_synching = true; + path->go_active = true; + + /* Accumulate the dynamic paths without any LSP so computation + * requests can be performed after synchronization */ + if ((path->type == SRTE_CANDIDATE_TYPE_DYNAMIC) + && (path->first_hop == NULL)) { + PCEP_DEBUG("%s Scheduling computation request for path %s", + pcc_state->tag, path->name); + push_req(pcc_state, path); + return; + } + + /* Synchronize the path if the PCE supports LSP updates and the + * endpoint address familly is supported */ if (pcc_state->caps.is_stateful) { - /* PCE supports LSP updates, just sync all the path with - * compatible IP version */ if (filter_path(pcc_state, path)) { PCEP_DEBUG("%s Synchronizing path %s", pcc_state->tag, path->name); - send_report(ctrl_state, pcc_state, path); + send_report(pcc_state, path); } else { PCEP_DEBUG( "%s Skipping %s candidate path %s " @@ -397,18 +420,14 @@ void pcep_pcc_sync_path(struct ctrl_state *ctrl_state, ipaddr_type_name(&path->nbkey.endpoint), path->name); } - } else if (path->is_delegated) { - /* PCE doesn't supports LSP updates, trigger computation - * request instead of synchronizing if the path is to be - * delegated. - */ - send_comp_request(ctrl_state, pcc_state, &path->nbkey); } } void pcep_pcc_sync_done(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) { + struct req_entry *req; + if (pcc_state->status == PCEP_PCC_DISCONNECTED) return; @@ -426,7 +445,7 @@ void pcep_pcc_sync_done(struct ctrl_state *ctrl_state, .is_delegated = false, .first_hop = NULL, .first_metric = NULL}; - send_report(ctrl_state, pcc_state, path); + send_report(pcc_state, path); pcep_free_path(path); } @@ -434,6 +453,11 @@ void pcep_pcc_sync_done(struct ctrl_state *ctrl_state, pcc_state->status = PCEP_PCC_OPERATING; PCEP_DEBUG("%s Synchronization done", pcc_state->tag); + + /* Start the computation request accumulated during synchronization */ + RB_FOREACH (req, req_entry_head, &pcc_state->requests) { + send_comp_request(pcc_state, req); + } } void pcep_pcc_send_report(struct ctrl_state *ctrl_state, @@ -443,7 +467,7 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state, if (pcc_state->caps.is_stateful) { PCEP_DEBUG("%s Send report for candidate path %s", pcc_state->tag, path->name); - send_report(ctrl_state, pcc_state, path); + send_report(pcc_state, path); } } @@ -454,6 +478,8 @@ void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state, enum pcep_pathd_event_type type, struct path *path) { + struct req_entry* req; + if (!pcc_state->synchronized) return; @@ -470,23 +496,25 @@ void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state, case PCEP_PATH_CREATED: PCEP_DEBUG("%s Candidate path %s created", pcc_state->tag, path->name); - if (pcc_state->caps.is_stateful) - send_report(ctrl_state, pcc_state, path); - else if (path->is_delegated) - send_comp_request(ctrl_state, pcc_state, &path->nbkey); + if ((path->first_hop == NULL) + && (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC)) { + req = push_req(pcc_state, path); + send_comp_request(pcc_state, req); + } else if (pcc_state->caps.is_stateful) + send_report(pcc_state, path); return; case PCEP_PATH_UPDATED: PCEP_DEBUG("%s Candidate path %s updated", pcc_state->tag, path->name); if (pcc_state->caps.is_stateful) - send_report(ctrl_state, pcc_state, path); + send_report(pcc_state, path); return; case PCEP_PATH_REMOVED: PCEP_DEBUG("%s Candidate path %s removed", pcc_state->tag, path->name); path->was_removed = true; if (pcc_state->caps.is_stateful) - send_report(ctrl_state, pcc_state, path); + send_report(pcc_state, path); return; default: flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, @@ -580,50 +608,104 @@ void handle_pcep_lsp_update(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, struct pcep_message *msg) { + char err[MAX_ERROR_MSG_SIZE] = ""; struct path *path; path = pcep_lib_parse_path(msg); - specialize_input_path(pcc_state, path); - path->update_origin = SRTE_ORIGIN_PCEP; - path->originator = pcc_state->originator; + lookup_nbkey(pcc_state, path); + /* TODO: Investigate if this is safe to do in the controller thread */ + path_nb_lookup(path); + specialize_incoming_path(pcc_state, path); PCEP_DEBUG("%s Received LSP update", pcc_state->tag); PCEP_DEBUG_PATH("%s", format_path(path)); - pcep_thread_update_path(ctrl_state, pcc_state->id, path); + + if (validate_incoming_path(pcc_state, path, err, sizeof(err))) + pcep_thread_update_path(ctrl_state, pcc_state->id, path); + else { + /* FIXME: Monitor the amount of errors from the PCE and + * possibly disconnect and blacklist */ + flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "Unsupported PCEP protocol feature: %s", err); + pcep_free_path(path); + } } void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, struct pcep_message *msg) { - struct pcep_message *error; - PCEP_DEBUG("%s Received LSP initiate, not supported yet", pcc_state->tag); /* TODO when we support both PCC and PCE initiated sessions, * we should first check the session type before * rejecting this message. */ - error = pcep_lib_reject_message(PCEP_ERRT_INVALID_OPERATION, - PCEP_ERRV_LSP_NOT_PCE_INITIATED); - send_pcep_message(ctrl_state, pcc_state, error); + send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_NOT_PCE_INITIATED); } void handle_pcep_comp_reply(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, struct pcep_message *msg) { + char err[MAX_ERROR_MSG_SIZE] = ""; + struct req_entry *req; struct path *path; path = pcep_lib_parse_path(msg); - if (!pop_req(pcc_state, path)) { + req = pop_req(pcc_state, path->req_id); + if (req == NULL) { /* TODO: check the rate of bad computation reply and close * the connection if more that a given rate. */ + PCEP_DEBUG("%s Received computation reply for unknown request " + "%d", pcc_state->tag, path->req_id); + PCEP_DEBUG_PATH("%s", format_path(path)); + send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF, + PCEP_ERRV_UNASSIGNED); return; } - PCEP_DEBUG("%s Received computation reply", pcc_state->tag); + /* Transfer relevent metadata from the request to the response */ + path->nbkey = req->path->nbkey; + path->plsp_id = req->path->plsp_id; + path->type = req->path->type; + path->name = XSTRDUP(MTYPE_PCEP, req->path->name); + specialize_incoming_path(pcc_state, path); + + PCEP_DEBUG("%s Received computation reply %d (no-path: %s)", + pcc_state->tag, path->req_id, + path->no_path?"true":"false"); PCEP_DEBUG_PATH("%s", format_path(path)); - pcep_thread_update_path(ctrl_state, pcc_state->id, path); + if (path->no_path) { + PCEP_DEBUG("%s Computation for path %s did not find any result", + pcc_state->tag, path->name); + } else if (validate_incoming_path(pcc_state, path, err, sizeof(err))) { + /* Updating a dynamic path will automatically delegate it */ + pcep_thread_update_path(ctrl_state, pcc_state->id, path); + free_req_entry(req); + return; + } else { + /* FIXME: Monitor the amount of errors from the PCE and + * possibly disconnect and blacklist */ + flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "Unsupported PCEP protocol feature: %s", err); + } + + pcep_free_path(path); + + /* Delegate the path regardless of the outcome */ + /* TODO: For now we are using the path from the request, when + * pathd API is thread safe, we could get a new path */ + if (pcc_state->caps.is_stateful) { + PCEP_DEBUG("%s Delegating undefined dynamic path %s to PCE %s", + pcc_state->tag, path->name, pcc_state->originator); + path = pcep_copy_path(req->path); + path->is_delegated = true; + send_report(pcc_state, path); + pcep_free_path(path); + } + + free_req_entry(req); } @@ -734,28 +816,38 @@ void schedule_reconnect(struct ctrl_state *ctrl_state, &pcc_state->t_reconnect); } -void send_pcep_message(struct ctrl_state *ctrl_state, - struct pcc_state *pcc_state, struct pcep_message *msg) +void send_pcep_message(struct pcc_state *pcc_state, struct pcep_message *msg) { PCEP_DEBUG_PCEP("%s Sending PCEP message: %s", pcc_state->tag, format_pcep_message(msg)); send_message(pcc_state->sess, msg, true); } -void send_report(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, - struct path *path) +void send_pcep_error(struct pcc_state *pcc_state, + enum pcep_error_type error_type, + enum pcep_error_value error_value) +{ + struct pcep_message* msg; + PCEP_DEBUG("%s Sending PCEP error type %s (%d) value %s (%d)", + pcc_state->tag, pcep_error_type_name(error_type), error_type, + pcep_error_value_name(error_type, error_value), error_value); + msg = pcep_lib_format_error(error_type, error_value); + send_pcep_message(pcc_state, msg); +} + +void send_report(struct pcc_state *pcc_state, struct path *path) { struct pcep_message *report; - specialize_output_path(pcc_state, path); + specialize_outgoing_path(pcc_state, path); PCEP_DEBUG_PATH("%s Sending path %s: %s", pcc_state->tag, path->name, format_path(path)); report = pcep_lib_format_report(path); - send_pcep_message(ctrl_state, pcc_state, report); + send_pcep_message(pcc_state, report); } /* Updates the path for the PCE, updating the delegation and creation flags */ -void specialize_output_path(struct pcc_state *pcc_state, struct path *path) +void specialize_outgoing_path(struct pcc_state *pcc_state, struct path *path) { bool is_delegated = false; bool was_created = false; @@ -765,13 +857,20 @@ void specialize_output_path(struct pcc_state *pcc_state, struct path *path) set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr); path->sender = pcc_state->pcc_addr_tr; - if ((path->originator == NULL) - || (strcmp(path->originator, pcc_state->originator) == 0)) { - is_delegated = path->type == SRTE_CANDIDATE_TYPE_DYNAMIC; - /* it seems the PCE consider updating an LSP a creation ?!? - at least Cisco does... */ - was_created = path->update_origin == SRTE_ORIGIN_PCEP; - } + /* TODO: When the pathd API have a way to mark a path as + * delegated, use it instead of considering all dynamic path + * delegated. We need to disable the originator check for now, + * because path could be delegated without having any originator yet */ + // if ((path->originator == NULL) + // || (strcmp(path->originator, pcc_state->originator) == 0)) { + // is_delegated = (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC) + // && (path->first_hop != NULL); + // /* it seems the PCE consider updating an LSP a creation ?!? + // at least Cisco does... */ + // was_created = path->update_origin == SRTE_ORIGIN_PCEP; + // } + is_delegated = (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC); + was_created = path->update_origin == SRTE_ORIGIN_PCEP; path->pcc_id = pcc_state->id; path->go_active = is_delegated; @@ -780,35 +879,75 @@ void specialize_output_path(struct pcc_state *pcc_state, struct path *path) } /* Updates the path for the PCC */ -void specialize_input_path(struct pcc_state *pcc_state, struct path *path) +void specialize_incoming_path(struct pcc_state *pcc_state, struct path *path) { - lookup_nbkey(pcc_state, path); - path_nb_lookup(path); - set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr); - path->sender = pcc_state->pcc_addr_tr; + path->sender = pcc_state->pce_opts->addr; path->pcc_id = pcc_state->id; + path->update_origin = SRTE_ORIGIN_PCEP; + path->originator = XSTRDUP(MTYPE_PCEP, pcc_state->originator); +} + +/* Ensure the path can be handled by the PCC and if not, sends an error */ +bool validate_incoming_path(struct pcc_state *pcc_state, struct path *path, + char* errbuff, size_t buffsize) +{ + struct path_hop *hop; + enum pcep_error_type err_type = 0; + enum pcep_error_value err_value = PCEP_ERRV_UNASSIGNED; + + for (hop = path->first_hop; hop != NULL; hop = hop->next) { + /* Hops without SID are not supported */ + if (!hop->has_sid) { + snprintfrr(errbuff, buffsize, "SR segment without SID"); + err_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT; + /* FIXME: Use the constant when added to pceplib */ + /* NAI cannot be resolved to a SID */ + err_value = 15; + break; + } + /* Hops with non-MPLS SID are not supported */ + if (!hop->is_mpls) { + snprintfrr(errbuff, buffsize, "SR segment with non-MPLS SID"); + err_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT; + /* FIXME: Use the constant when added to pceplib */ + /* Unsupported NAI Type in the SR-ERO/SR-RRO subobject */ + err_value = 13; + break; + } + } + + if (err_type != 0) { + send_pcep_error(pcc_state, err_type, err_value); + return false; + } + + return true; } -void send_comp_request(struct ctrl_state *ctrl_state, - struct pcc_state *pcc_state, struct lsp_nb_key *nbkey) +void send_comp_request(struct pcc_state *pcc_state, struct req_entry *req) { + assert(req != NULL); + assert(req->path != NULL); + assert(req->path->req_id > 0); + assert(RB_FIND(req_entry_head, &pcc_state->requests, req) == req); + char buff[40]; - uint32_t reqid; struct pcep_message *msg; - struct ipaddr pcc_addr; - reqid = push_req(pcc_state, nbkey); - /* TODO: Add a timer to retry the computation request */ + /* TODO: Add a timer to retry the computation request ? */ - set_pcc_address(pcc_state, nbkey, &pcc_addr); + specialize_outgoing_path(pcc_state, req->path); - PCEP_DEBUG("%s Sending computation request for path to %s", - pcc_state->tag, - ipaddr2str(&nbkey->endpoint, buff, sizeof(buff))); + PCEP_DEBUG("%s Sending computation request %d for path %s to %s", + pcc_state->tag, req->path->req_id, req->path->name, + ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff))); + PCEP_DEBUG_PATH("%s Computation request path %s: %s", pcc_state->tag, + req->path->name, format_path(req->path)); - msg = pcep_lib_format_request(reqid, &pcc_addr, &nbkey->endpoint); - send_pcep_message(ctrl_state, pcc_state, msg); + msg = pcep_lib_format_request(req->path->req_id, &req->path->pcc_addr, + &req->path->nbkey.endpoint); + send_pcep_message(pcc_state, msg); } void set_pcc_address(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey, @@ -869,42 +1008,46 @@ void lookup_nbkey(struct pcc_state *pcc_state, struct path *path) path->nbkey = mapping->nbkey; } -uint32_t push_req(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey) +void free_req_entry(struct req_entry *req) { - struct req_map_data *req_mapping; - uint32_t reqid = pcc_state->next_reqid; + pcep_free_path(req->path); + XFREE(MTYPE_PCEP, req); +} - req_mapping = XCALLOC(MTYPE_PCEP, sizeof(*req_mapping)); - req_mapping->reqid = reqid; - req_mapping->nbkey = *nbkey; +struct req_entry* push_req(struct pcc_state *pcc_state, struct path* path) +{ + assert(path->req_id == 0); - assert(req_map_find(&pcc_state->req_map, req_mapping) == NULL); - req_map_add(&pcc_state->req_map, req_mapping); + uint32_t reqid = pcc_state->next_reqid; + struct req_entry *req; + void* res; + + req = XCALLOC(MTYPE_PCEP, sizeof(*req)); + req->path = pcep_copy_path(path); + req->path->req_id = reqid; + res = RB_INSERT(req_entry_head, &pcc_state->requests, req); + assert(res == NULL); pcc_state->next_reqid += 1; /* Wrapping is allowed, but 0 is not a valid id */ if (pcc_state->next_reqid == 0) pcc_state->next_reqid = 1; - return reqid; + return req; } -bool pop_req(struct pcc_state *pcc_state, struct path *path) +struct req_entry* pop_req(struct pcc_state *pcc_state, uint32_t reqid) { - struct req_map_data key, *req_mapping; - - key.reqid = path->req_id; - - req_mapping = req_map_find(&pcc_state->req_map, &key); - if (req_mapping == NULL) - return false; + struct path path = {.req_id = reqid}; + struct req_entry key = {.path = &path}; + struct req_entry *req; - req_map_del(&pcc_state->req_map, req_mapping); + req = RB_FIND(req_entry_head, &pcc_state->requests, &key); + if (req == NULL) + return NULL; + RB_REMOVE(req_entry_head, &pcc_state->requests, req); - path->nbkey = req_mapping->nbkey; - - XFREE(MTYPE_PCEP, req_mapping); - return true; + return req; } @@ -952,15 +1095,3 @@ static uint32_t nbkey_map_hash(const struct nbkey_map_data *e) { return e->plspid; } - -static int req_map_cmp(const struct req_map_data *a, - const struct req_map_data *b) -{ - CMP_RETURN(a->reqid, b->reqid); - return 0; -} - -static uint32_t req_map_hash(const struct req_map_data *e) -{ - return e->reqid; -} diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h index 357d46151e35..f8b39edbef9e 100644 --- a/pathd/path_pcep_pcc.h +++ b/pathd/path_pcep_pcc.h @@ -53,6 +53,13 @@ struct req_map_data { struct lsp_nb_key nbkey; }; +struct req_entry { + RB_ENTRY(req_entry) entry; + struct path *path; +}; +RB_HEAD(req_entry_head, req_entry); +RB_PROTOTYPE(req_entry_head, req_entry, entry, req_entry_compare); + struct pcc_state { int id; char tag[MAX_TAG_SIZE]; @@ -64,6 +71,7 @@ struct pcc_state { struct pce_opts *pce_opts; struct in_addr pcc_addr_v4; struct in6_addr pcc_addr_v6; + /* PCC transport source address */ struct ipaddr pcc_addr_tr; char *originator; pcep_session *sess; @@ -75,7 +83,7 @@ struct pcc_state { uint32_t next_plspid; struct plspid_map_head plspid_map; struct nbkey_map_head nbkey_map; - struct req_map_head req_map; + struct req_entry_head requests; struct pcep_caps caps; };