EV_PCEPLIB_EVENT,
EV_RESET_PCC_SESSION,
EV_SEND_REPORT,
+ EV_SEND_ERROR,
EV_PATH_REFINED
};
}
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_ERROR, 0, error);
+}
+
+
/* ------------ Internal Functions Called from Main Thread ------------ */
int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
path);
}
+void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path)
+{
+ send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
+ path);
+}
+
void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state)
{
struct pcep_refine_path_event_data *refine_data = NULL;
struct path *path_copy = NULL;
+ struct pcep_error *error = NULL;
switch (type) {
case EV_UPDATE_PCC_OPTS:
refine_data = (struct pcep_refine_path_event_data *)payload;
pcep_thread_path_refined_event(ctrl_state, refine_data);
break;
+ case EV_SEND_ERROR:
+ assert(payload != NULL);
+ error = (struct pcep_error *)payload;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_error(ctrl_state, pcc_state, error,
+ (bool)sub_type);
+ break;
default:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unexpected event received in controller thread: %u",
enum pcep_main_event_type {
PCEP_MAIN_EVENT_UNDEFINED = 0,
PCEP_MAIN_EVENT_START_SYNC,
+ PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP,
};
int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
struct path *path, bool is_stable);
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error);
+
/* Functions called from the controller thread */
void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id);
void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id,
struct path *path);
+void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path);
void pcep_thread_cancel_timer(struct thread **thread);
void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
int retry_count, struct thread **thread);
switch (tlv_type) {
case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
return "NO_PATH_VECTOR";
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ return "OBJECTIVE_FUNCTION_LIST";
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ return "VENDOR_INFO";
case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
return "STATEFUL_PCE_CAPABILITY";
case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
return "PATH_SETUP_TYPE";
case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
return "PATH_SETUP_TYPE_CAPABILITY";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ return "SRPOLICY_POL_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ return "SRPOLICY_POL_NAME";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ return "SRPOLICY_CPATH_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ return "SRPOLICY_CPATH_PREFERENCE";
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ return "ARBITRARY";
default:
return "UNKNOWN";
}
#define DEFAULT_LSAP_SETUP_PRIO 4
#define DEFAULT_LSAP_HOLDING_PRIO 4
#define DEFAULT_LSAP_LOCAL_PRETECTION false
+#define MAX_PATH_NAME_SIZE 255
/* pceplib logging callback */
static int pceplib_logging_cb(int level, const char *fmt, va_list args);
static void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp);
static void pcep_lib_parse_lspa(struct path *path,
struct pcep_object_lspa *lspa);
+static void pcep_lib_parse_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv);
static void pcep_lib_parse_metric(struct path *path,
struct pcep_object_metric *obj);
+static void
+pcep_lib_parse_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj);
+static void
+pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj);
+static void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *obj);
static void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero);
static struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next,
struct pcep_ro_subobj_sr *sr);
}
config->support_stateful_pce_lsp_update = true;
- config->support_pce_lsp_instantiation = false;
+ config->support_pce_lsp_instantiation = pcep_options->pce_initiated;
config->support_include_db_version = false;
config->support_lsp_triggered_resync = false;
config->support_lsp_delta_sync = false;
}
}
-struct pcep_message *pcep_lib_format_error(int error_type, int error_value)
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value,
+ struct path *path)
{
- return pcep_msg_create_error(error_type, error_value);
+ double_linked_list *objs, *srp_tlvs;
+ struct pcep_object_srp *srp;
+ struct pcep_object_tlv_header *tlv;
+
+ if ((path == NULL) || (path->srp_id == 0))
+ return pcep_msg_create_error(error_type, error_value);
+
+ objs = dll_initialize();
+ srp_tlvs = dll_initialize();
+ tlv = (struct pcep_object_tlv_header *)pcep_tlv_create_path_setup_type(
+ SR_TE_PST);
+ dll_append(srp_tlvs, tlv);
+ srp = pcep_obj_create_srp(path->do_remove, path->srp_id, srp_tlvs);
+ dll_append(objs, srp);
+ return pcep_msg_create_error_with_objects(error_type, error_value,
+ objs);
}
struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid)
struct pcep_object_metric *metric = NULL;
struct pcep_object_bandwidth *bandwidth = NULL;
struct pcep_object_objective_function *of = NULL;
+ struct pcep_object_endpoints_ipv4 *epv4 = NULL;
+ struct pcep_object_endpoints_ipv6 *epv6 = NULL;
+ struct pcep_object_vendor_info *vendor_info = NULL;
path = pcep_new_path();
path->has_pce_objfun = true;
path->pce_objfun = of->of_code;
break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4):
+ epv4 = (struct pcep_object_endpoints_ipv4 *)obj;
+ pcep_lib_parse_endpoints_ipv4(path, epv4);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6):
+ epv6 = (struct pcep_object_endpoints_ipv6 *)obj;
+ pcep_lib_parse_endpoints_ipv6(path, epv6);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_VENDOR_INFO,
+ PCEP_OBJ_TYPE_VENDOR_INFO):
+ vendor_info = (struct pcep_object_vendor_info *)obj;
+ pcep_lib_parse_vendor_info(path, vendor_info);
+ break;
default:
flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
"Unexpected PCEP object %s (%u) / %s (%u)",
tlv = (struct pcep_object_tlv_header *)
pcep_tlv_create_tlv_arbitrary(
binding_sid_lsp_tlv_data,
- sizeof(binding_sid_lsp_tlv_data), 65505);
+ sizeof(binding_sid_lsp_tlv_data),
+ PCEP_OBJ_TYPE_CISCO_BSID);
assert(tlv != NULL);
dll_append(lsp_tlvs, tlv);
}
double_linked_list *tlvs = lsp->header.tlv_list;
double_linked_list_node *node;
struct pcep_object_tlv_header *tlv;
+ struct pcep_object_tlv_symbolic_path_name *name;
+ struct pcep_object_tlv_arbitrary *arb_tlv;
path->plsp_id = lsp->plsp_id;
path->status = lsp->operational_status;
for (node = tlvs->head; node != NULL; node = node->next_node) {
tlv = (struct pcep_object_tlv_header *)node->data;
switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ name = (struct pcep_object_tlv_symbolic_path_name *)tlv;
+ pcep_lib_parse_lsp_symbolic_name(path, name);
+ break;
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ arb_tlv = (struct pcep_object_tlv_arbitrary *)tlv;
+ memcpy(&path->binding_sid, arb_tlv->data + 2,
+ sizeof(path->binding_sid));
+ path->binding_sid = ntohl(path->binding_sid);
+ path->binding_sid = (path->binding_sid >> 12);
+ break;
default:
flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
"Unexpected LSP TLV %s (%u)",
}
}
+void pcep_lib_parse_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv)
+{
+ uint16_t size = tlv->symbolic_path_name_length;
+ assert(path->name == NULL);
+ size = size > MAX_PATH_NAME_SIZE ? MAX_PATH_NAME_SIZE : size;
+ path->name = XCALLOC(MTYPE_PCEP, size);
+ strlcpy((char *)path->name, tlv->symbolic_path_name, size + 1);
+}
+
void pcep_lib_parse_lspa(struct path *path, struct pcep_object_lspa *lspa)
{
path->has_affinity_filters = true;
path->first_metric = metric;
}
+void pcep_lib_parse_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj)
+{
+ SET_IPADDR_V4(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v4 = obj->src_ipv4;
+ SET_IPADDR_V4(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v4 = obj->dst_ipv4;
+}
+
+void pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj)
+{
+ SET_IPADDR_V6(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v6 = obj->src_ipv6;
+ SET_IPADDR_V6(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v6 = obj->dst_ipv6;
+}
+
+void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *obj)
+{
+ if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO
+ && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO)
+ path->nbkey.color = obj->enterprise_specific_info1;
+ else
+ path->nbkey.color = 0;
+}
+
void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero)
{
struct path_hop *hop = NULL;
struct path *path);
struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid);
-struct pcep_message *pcep_lib_format_error(int error_type, int error_value);
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value,
+ struct path *path);
struct path *pcep_lib_parse_path(struct pcep_message *msg);
void pcep_lib_parse_capabilities(struct pcep_message *msg,
struct pcep_caps *caps);
struct pcep_message *msg);
static void send_pcep_error(struct pcc_state *pcc_state,
enum pcep_error_type error_type,
- enum pcep_error_value error_value);
+ enum pcep_error_value error_value,
+ struct path *trigger_path);
static void send_report(struct pcc_state *pcc_state, struct path *path);
static void send_comp_request(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
return;
}
- PCEP_DEBUG("%s Send report for candidate path %s", pcc_state->tag,
- path->name);
+ PCEP_DEBUG("(%s)%s Send report for candidate path %s", __func__,
+ pcc_state->tag, path->name);
/* ODL and Cisco requires the first reported
* LSP to have a DOWN status, the later status changes
/* If no update is expected and the real status wasn't down, we need to
* send a second report with the real status */
if (is_stable && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
+ PCEP_DEBUG("(%s)%s Send report for candidate path (!DOWN) %s",
+ __func__, pcc_state->tag, path->name);
path->srp_id = 0;
path->status = real_status;
send_report(pcc_state, path);
}
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *error,
+ bool sub_type)
+{
+
+ PCEP_DEBUG("(%s) Send error after PcInitiated ", __func__);
+
+
+ send_pcep_error(pcc_state, error->error_type, error->error_value,
+ error->path);
+ pcep_free_path(error->path);
+ XFREE(MTYPE_PCEP, error);
+}
/* ------------ Timeout handler ------------ */
void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
PCEP_DEBUG("%s Candidate path %s removed", pcc_state->tag,
path->name);
path->was_removed = true;
+ /* Removed as response to a PcInitiated 'R'emove*/
+ /* RFC 8281 #5.4 LSP Deletion*/
+ path->do_remove = path->was_removed;
if (pcc_state->caps.is_stateful)
send_report(pcc_state, path);
return;
struct pcc_state *pcc_state,
struct pcep_message *msg)
{
- PCEP_DEBUG("%s Received LSP initiate, not supported yet",
- pcc_state->tag);
+ char err[MAX_ERROR_MSG_SIZE] = "";
+ struct path *path;
+
+ path = pcep_lib_parse_path(msg);
+
+ if (!pcc_state->pce_opts->config_opts.pce_initiated) {
+ /* PCE Initiated is not enabled */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Not allowed PCE initiated path received: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR, path);
+ return;
+ }
- /* TODO when we support both PCC and PCE initiated sessions,
- * we should first check the session type before
- * rejecting this message. */
- send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
- PCEP_ERRV_LSP_NOT_PCE_INITIATED);
+ if (path->do_remove) {
+ // lookup in nbkey sequential as no endpoint
+ struct nbkey_map_data *key;
+ char endpoint[46];
+
+ frr_each (nbkey_map, &pcc_state->nbkey_map, key) {
+ ipaddr2str(&key->nbkey.endpoint, endpoint,
+ sizeof(endpoint));
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint, path->plsp_id);
+ if (path->plsp_id == key->plspid) {
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH MATCH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint,
+ path->plsp_id);
+ path->nbkey = key->nbkey;
+ break;
+ }
+ }
+ } else {
+ if (path->first_hop == NULL /*ero sets first_hop*/) {
+ /* If the PCC receives a PCInitiate message without an
+ * ERO and the R flag in the SRP object != zero, then it
+ * MUST send a PCErr message with Error-type=6
+ * (Mandatory Object missing) and Error-value=9 (ERO
+ * object missing). */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "ERO object missing or incomplete : %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state,
+ PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_INTERNAL_ERROR, path);
+ return;
+ }
+
+ if (path->plsp_id != 0) {
+ /* If the PCC receives a PCInitiate message with a
+ * non-zero PLSP-ID and the R flag in the SRP object set
+ * to zero, then it MUST send a PCErr message with
+ * Error-type=19 (Invalid Operation) and Error-value=8
+ * (Non-zero PLSP-ID in the LSP Initiate Request) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path with non-zero PLSP ID: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID,
+ path);
+ return;
+ }
+
+ if (path->name == NULL) {
+ /* If the PCC receives a PCInitiate message without a
+ * SYMBOLIC-PATH-NAME TLV, then it MUST send a PCErr
+ * message with Error-type=10 (Reception of an invalid
+ * object) and Error-value=8 (SYMBOLIC-PATH-NAME TLV
+ * missing) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path without symbolic name: %s",
+ format_pcep_message(msg));
+ send_pcep_error(
+ pcc_state, PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING, path);
+ return;
+ }
+ }
+
+ /* TODO: If there is a conflict with the symbolic path name of an
+ * existing LSP, the PCC MUST send a PCErr message with Error-type=23
+ * (Bad Parameter value) and Error-value=1 (SYMBOLIC-PATH-NAME in
+ * use) */
+
+ specialize_incoming_path(pcc_state, path);
+ /* TODO: Validate the PCC address received from the PCE is valid */
+ PCEP_DEBUG("%s Received LSP initiate", pcc_state->tag);
+ PCEP_DEBUG_PATH("%s", format_path(path));
+
+ if (validate_incoming_path(pcc_state, path, err, sizeof(err))) {
+ pcep_thread_initiate_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);
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_NOT_PCE_INITIATED, path);
+ }
}
void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
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);
+ PCEP_ERRV_UNASSIGNED, NULL);
return;
}
void send_pcep_error(struct pcc_state *pcc_state,
enum pcep_error_type error_type,
- enum pcep_error_value error_value)
+ enum pcep_error_value error_value,
+ struct path *trigger_path)
{
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);
+ msg = pcep_lib_format_error(error_type, error_value, trigger_path);
send_pcep_message(pcc_state, msg);
}
/* Updates the path for the PCC */
void specialize_incoming_path(struct pcc_state *pcc_state, struct path *path)
{
- set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
+ if (IS_IPADDR_NONE(&path->pcc_addr))
+ set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
path->sender = pcc_state->pce_opts->addr;
path->pcc_id = pcc_state->id;
path->update_origin = SRTE_ORIGIN_PCEP;
}
if (err_type != 0) {
- send_pcep_error(pcc_state, err_type, err_value);
+ send_pcep_error(pcc_state, err_type, err_value, NULL);
return false;
}
if (!pcc_state->is_best) {
return;
}
- /* TODO: Add a timer to retry the computation request ? */
specialize_outgoing_path(pcc_state, req->path);
send_pcep_message(pcc_state, msg);
req->was_sent = true;
- /* TODO: Enable this back when the pcep config changes are merged back
- */
- // timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
- timeout = 30;
+ timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
pcep_thread_schedule_timeout(ctrl_state, pcc_state->id,
TO_COMPUTATION_REQUEST, timeout,
(void *)req, &req->t_retry);
}
}
-
/* ------------ Data Structure Helper Functions ------------ */
void lookup_plspid(struct pcc_state *pcc_state, struct path *path)
void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct path *path,
bool is_stable);
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *path,
+ bool is_stable);
int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
struct pcc_state **pcc_state_list);
int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,