diff options
Diffstat (limited to 'pathd/path_pcep.c')
-rw-r--r-- | pathd/path_pcep.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c new file mode 100644 index 0000000..ce631eb --- /dev/null +++ b/pathd/path_pcep.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "pceplib/pcep_utils_counters.h" + +#include "memory.h" +#include "log.h" +#include "command.h" +#include "libfrr.h" +#include "printfrr.h" +#include "lib/version.h" +#include "northbound.h" +#include "frr_pthread.h" +#include "jhash.h" +#include "termtable.h" + +#include "pathd/pathd.h" +#include "pathd/path_errors.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_cli.h" +#include "pathd/path_pcep_controller.h" +#include "pathd/path_pcep_lib.h" +#include "pathd/path_pcep_config.h" +#include "pathd/path_pcep_debug.h" + +DEFINE_MTYPE(PATHD, PCEP, "PCEP module"); + +/* + * Globals. + */ +static struct pcep_glob pcep_glob_space = {.dbg = {0, "pathd module: pcep"}}; +struct pcep_glob *pcep_g = &pcep_glob_space; + +/* Main Thread Even Handler */ +static int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id, + void *payload); +static int pcep_main_event_start_sync(int pcc_id); +static int pcep_main_event_start_sync_cb(struct path *path, void *arg); +static int pcep_main_event_initiate_candidate(struct path *path); +static int pcep_main_event_update_candidate(struct path *path); +static int pcep_main_event_remove_candidate_segments(const char *originator, + bool force); + +/* Hook Handlers called from the Main Thread */ +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); + +/* Other static functions */ +static void notify_status(struct path *path, bool not_changed); + +/* Module Functions */ +static int pcep_module_finish(void); +static int pcep_module_late_init(struct thread_master *tm); +static int pcep_module_init(void); + +/* ------------ Path Helper Functions ------------ */ + +struct path *pcep_new_path(void) +{ + struct path *path; + path = XCALLOC(MTYPE_PCEP, sizeof(*path)); + path->binding_sid = MPLS_LABEL_NONE; + path->enforce_bandwidth = true; + return path; +} + +struct path_hop *pcep_new_hop(void) +{ + struct path_hop *hop; + hop = XCALLOC(MTYPE_PCEP, sizeof(*hop)); + return hop; +} + +struct path_metric *pcep_new_metric(void) +{ + struct path_metric *metric; + metric = XCALLOC(MTYPE_PCEP, sizeof(*metric)); + 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; + struct path_metric *metric; + char *tmp; + + metric = path->first_metric; + while (metric != NULL) { + struct path_metric *next = metric->next; + XFREE(MTYPE_PCEP, metric); + metric = next; + } + hop = path->first_hop; + while (hop != NULL) { + struct path_hop *next = hop->next; + XFREE(MTYPE_PCEP, hop); + hop = next; + } + if (path->originator != NULL) { + /* The path own the memory, it is const so it is clear it + shouldn't be modified. XFREE macro do not support type casting + so we need a temporary variable */ + tmp = (char *)path->originator; + XFREE(MTYPE_PCEP, tmp); + path->originator = NULL; + } + if (path->name != NULL) { + /* The path own the memory, it is const so it is clear it + shouldn't be modified. XFREE macro do not support type casting + so we need a temporary variable */ + tmp = (char *)path->name; + XFREE(MTYPE_PCEP, tmp); + path->name = NULL; + } + XFREE(MTYPE_PCEP, path); +} + +/* ------------ Other Static Functions ------------ */ + +void notify_status(struct path *path, bool not_changed) +{ + struct path *resp = NULL; + + if ((resp = path_pcep_config_get_path(&path->nbkey))) { + resp->srp_id = path->srp_id; + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "(%s) Send report for candidate path %s", __func__, + path->name); + pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp, + not_changed); + } +} + +/* ------------ Main Thread Even Handler ------------ */ + +int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id, + void *payload) +{ + int ret = 0; + + switch (type) { + case PCEP_MAIN_EVENT_START_SYNC: + ret = pcep_main_event_start_sync(pcc_id); + break; + case PCEP_MAIN_EVENT_INITIATE_CANDIDATE: + assert(payload != NULL); + ret = pcep_main_event_initiate_candidate( + (struct path *)payload); + break; + case PCEP_MAIN_EVENT_UPDATE_CANDIDATE: + assert(payload != NULL); + ret = pcep_main_event_update_candidate((struct path *)payload); + break; + case PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP: + ret = pcep_main_event_remove_candidate_segments( + (const char *)payload, true); + break; + default: + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "Unexpected event received in the main thread: %u", + type); + break; + } + + return ret; +} + +int pcep_main_event_start_sync(int pcc_id) +{ + path_pcep_config_list_path(pcep_main_event_start_sync_cb, &pcc_id); + pcep_ctrl_sync_done(pcep_g->fpt, pcc_id); + return 0; +} + +int pcep_main_event_start_sync_cb(struct path *path, void *arg) +{ + int *pcc_id = (int *)arg; + pcep_ctrl_sync_path(pcep_g->fpt, *pcc_id, path); + return 1; +} + +int pcep_main_event_initiate_candidate(struct path *path) +{ + int ret = 0; + + ret = path_pcep_config_initiate_path(path); + if (path->do_remove) { + struct pcep_error *error; + error = XCALLOC(MTYPE_PCEP, sizeof(*error)); + error->path = path; + error->error_type = PCEP_ERRT_INVALID_OPERATION; + switch (ret) { + case ERROR_19_1: + error->error_value = + PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP; + break; + case ERROR_19_3: + error->error_value = + PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID; + break; + case ERROR_19_9: + error->error_value = PCEP_ERRV_LSP_NOT_PCE_INITIATED; + break; + default: + zlog_warn("(%s)PCE tried to REMOVE unknown error!", + __func__); + XFREE(MTYPE_PCEP, error); + pcep_free_path(path); + return ret; + break; + } + pcep_ctrl_send_error(pcep_g->fpt, path->pcc_id, error); + } else if (ret != PATH_NB_ERR && path->srp_id != 0) + notify_status(path, ret == PATH_NB_NO_CHANGE); + return ret; +} + +int pcep_main_event_update_candidate(struct path *path) +{ + int ret = 0; + + ret = path_pcep_config_update_path(path); + if (ret != PATH_NB_ERR && path->srp_id != 0) + notify_status(path, ret == PATH_NB_NO_CHANGE); + return ret; +} + +int pcep_main_event_remove_candidate_segments(const char *originator, + bool force) +{ + srte_candidate_unset_segment_list(originator, force); + /* Avoid compiler warnings about const char* */ + void *free_ptr = (void *)originator; + XFREE(MTYPE_PCEP, free_ptr); + + srte_apply_changes(); + + return 0; +} + +/* ------------ Hook Handlers Functions Called From Main Thread ------------ */ + +int pathd_candidate_created_handler(struct srte_candidate *candidate) +{ + struct path *path = candidate_to_path(candidate); + int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_CREATED, path); + return ret; +} + +int pathd_candidate_updated_handler(struct srte_candidate *candidate) +{ + struct path *path = candidate_to_path(candidate); + int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_UPDATED, path); + return ret; +} + +int pathd_candidate_removed_handler(struct srte_candidate *candidate) +{ + struct path *path = candidate_to_path(candidate); + int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_REMOVED, path); + return ret; +} + + +/* ------------ Module Functions ------------ */ + +/* this creates threads, therefore must run after fork(). but it must also + * run before config load, so the CLI commands don't try to touch things that + * aren't set up yet... + */ +static int pcep_module_config_pre(struct thread_master *tm) +{ + assert(pcep_g->fpt == NULL); + assert(pcep_g->master == NULL); + + struct frr_pthread *fpt; + + if (pcep_ctrl_initialize(tm, &fpt, pcep_main_event_handler)) + return 1; + + if (pcep_lib_initialize(fpt)) + return 1; + + pcep_g->master = tm; + pcep_g->fpt = fpt; + + return 0; +} + +static int pcep_module_late_init(struct thread_master *tm) +{ + hook_register(pathd_candidate_created, pathd_candidate_created_handler); + hook_register(pathd_candidate_updated, pathd_candidate_updated_handler); + hook_register(pathd_candidate_removed, pathd_candidate_removed_handler); + + hook_register(frr_config_pre, pcep_module_config_pre); + hook_register(frr_fini, pcep_module_finish); + + pcep_cli_init(); + + return 0; +} + +int pcep_module_finish(void) +{ + pcep_ctrl_finalize(&pcep_g->fpt); + pcep_lib_finalize(); + + for (int i = 0; i < MAX_PCC; i++) + if (pcep_g->pce_opts_cli[i] != NULL) + XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]); + + return 0; +} + +int pcep_module_init(void) +{ + pcep_g->num_pce_opts_cli = 0; + for (int i = 0; i < MAX_PCE; i++) + pcep_g->pce_opts_cli[i] = NULL; + pcep_g->num_config_group_opts = 0; + for (int i = 0; i < MAX_PCE; i++) + pcep_g->config_group_opts[i] = NULL; + + hook_register(frr_late_init, pcep_module_late_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "frr_pathd_pcep", .version = FRR_VERSION, + .description = "FRR pathd PCEP module", + .init = pcep_module_init, +); |