diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c index 0fe771bfabcd..9475aab6bde0 100644 --- a/pathd/path_pcep.c +++ b/pathd/path_pcep.c @@ -417,7 +417,7 @@ DEFUN(pcep_cli_no_pcc, pcep_cli_no_pcc_cmd, "no pcc", } DEFUN(pcep_cli_pce, pcep_cli_pce_cmd, - "pce [port (1024-65535)] [sr-draft07]", + "pce [port (1024-65535)] [sr-draft07] [precedence (0-255)]", "PCE configuration\n" "PCE IPv4 address\n" "Remote PCE server IPv4 address\n" @@ -425,14 +425,17 @@ DEFUN(pcep_cli_pce, pcep_cli_pce_cmd, "Remote PCE server IPv6 address\n" "Remote PCE server port\n" "Remote PCE server port value\n" - "Use the draft 07 of PCEP segemnt routing\n") + "Use the draft 07 of PCEP segment routing\n" + "Priority when multiple pce, the lower the more precedence\n" + "Priority value , (default 255)\n") { /* TODO: Add support for multiple PCE */ struct ipaddr pce_addr; uint32_t pce_port = PCEP_DEFAULT_PORT; - struct pce_opts *pce_opts, *pce_opts_copy; + struct pce_opts *pce_opts; bool draft07 = false; + uint8_t pce_precedence = PCE_DEFAULT_PRECEDENCE; int i = 1; /* Get the first argument, should be either ip or ipv6 */ @@ -476,6 +479,14 @@ DEFUN(pcep_cli_pce, pcep_cli_pce_cmd, i++; continue; } + if (strcmp("precedence", argv[i]->arg) == 0) { + i++; + if (i >= argc) + return CMD_ERR_NO_MATCH; + pce_precedence = atoi(argv[i]->arg); + i++; + continue; + } return CMD_ERR_NO_MATCH; } @@ -483,15 +494,13 @@ DEFUN(pcep_cli_pce, pcep_cli_pce_cmd, IPADDR_COPY(&pce_opts->addr, &pce_addr); pce_opts->port = pce_port; pce_opts->draft07 = draft07; + pce_opts->precedence = pce_precedence; - if (pcep_ctrl_update_pce_options(pcep_g->fpt, 1, pce_opts)) +#define NO_USE 0 + if (pcep_ctrl_update_pce_options(pcep_g->fpt, NO_USE /*current_pcc_id*/, + pce_opts)) return CMD_WARNING; - if (pcep_g->pce_opts[0] != NULL) - XFREE(MTYPE_PCEP, pcep_g->pce_opts[0]); - pce_opts_copy = XCALLOC(MTYPE_PCEP, sizeof(*pce_opts)); - pce_opts_copy = memcpy(pce_opts_copy, pce_opts, sizeof(*pce_opts)); - pcep_g->pce_opts[0] = pce_opts_copy; return CMD_SUCCESS; } @@ -508,11 +517,39 @@ DEFUN(pcep_cli_no_pce, pcep_cli_no_pce_cmd, "Remote PCE server port value\n") { /* TODO: Add support for multiple PCE */ + int i = 2; + struct ipaddr pce_addr; + pce_addr.ipa_type = IPADDR_V4; + SET_IPADDR_V4(&pce_addr); + if (strcmp("ipv6", argv[i]->arg) == 0) { + SET_IPADDR_V6(&pce_addr); + } else if (strcmp("ip", argv[i]->arg) != 0) { + return CMD_ERR_NO_MATCH; + } - pcep_ctrl_remove_pcc(pcep_g->fpt, 1); - if (pcep_g->pce_opts[0] != NULL) { - XFREE(MTYPE_PCEP, pcep_g->pce_opts[0]); - pcep_g->pce_opts[0] = NULL; + /* Get the first argument value */ + i++; + if (i >= argc) { + return CMD_ERR_NO_MATCH; + } + if (IS_IPADDR_V6(&pce_addr)) { + if (!inet_pton(AF_INET6, argv[i]->arg, &pce_addr.ipaddr_v6)) { + return CMD_ERR_INCOMPLETE; + } + } else { + if (!inet_pton(AF_INET, argv[i]->arg, &pce_addr.ipaddr_v4)) { + return CMD_ERR_INCOMPLETE; + } + } + + + int current_pcc_id = get_pcc_id_by_ip(pcep_g->fpt, &pce_addr); + if (current_pcc_id) { + pcep_ctrl_remove_pcc(pcep_g->fpt, current_pcc_id); + if (pcep_g->pce_opts[current_pcc_id - 1] != NULL) { + XFREE(MTYPE_PCEP, pcep_g->pce_opts[current_pcc_id - 1]); + pcep_g->pce_opts[current_pcc_id - 1] = NULL; + } } return CMD_SUCCESS; } @@ -622,6 +659,12 @@ int pcep_cli_pcc_config_write(struct vty *vty) csnprintfrr(buff, sizeof(buff), " sr-draft07"); } + if (pce_opts->precedence + != PCE_DEFAULT_PRECEDENCE) { + csnprintfrr(buff, sizeof(buff), + " precedence %d", + pce_opts->precedence); + } if (IS_IPADDR_V6(&pce_opts->addr)) { vty_out(vty, " pce ipv6 %pI6%s\n", &pce_opts->addr.ipaddr_v6, diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h index 7fc1e9400d44..0677671253d1 100644 --- a/pathd/path_pcep.h +++ b/pathd/path_pcep.h @@ -28,9 +28,10 @@ #include "pathd/pathd.h" #include "pathd/path_pcep_memory.h" +#define PCE_DEFAULT_PRECEDENCE 255 #define PCC_DEFAULT_MSD 4 #define PCEP_DEFAULT_PORT 4189 -#define MAX_PCC 1 +#define MAX_PCC 3 #define MAX_TAG_SIZE 50 #define PCEP_DEBUG_MODE_BASIC 0x01 #define PCEP_DEBUG_MODE_PATH 0x02 @@ -84,6 +85,9 @@ struct pce_opts { struct ipaddr addr; short port; bool draft07; + uint8_t precedence; + bool is_best; + bool previous_best; }; struct pcc_opts { diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c index 0522db932acc..b5e0652d5c38 100644 --- a/pathd/path_pcep_controller.c +++ b/pathd/path_pcep_controller.c @@ -45,6 +45,8 @@ _a <= _b ? _a : _b; \ }) +#define MAX_RETRIES_BEFORE_LOST_PRECEDENCE 5 + /* Event handling data structures */ enum pcep_ctrl_event_type { @@ -191,6 +193,7 @@ int pcep_ctrl_initialize(struct thread_master *main_thread, ctrl_state->pcc_count = 0; ctrl_state->pcc_opts = XCALLOC(MTYPE_PCEP, sizeof(*ctrl_state->pcc_opts)); + memset(ctrl_state->pcc, 0, sizeof(ctrl_state->pcc[0]) * MAX_PCC); /* Default to no PCC address defined */ UNSET_FLAG(ctrl_state->pcc_opts->flags, F_PCC_OPTS_IPV4); UNSET_FLAG(ctrl_state->pcc_opts->flags, F_PCC_OPTS_IPV6); @@ -452,6 +455,32 @@ int pcep_thread_timer_handler(struct thread *thread) int ret = 0; struct pcc_state *pcc_state = NULL; + pcc_state = get_pcc_state(ctrl_state, pcc_id); + assert(pcc_state != NULL); + if (pcc_state->retry_count < MAX_RETRIES_BEFORE_LOST_PRECEDENCE) { + PCEP_DEBUG("%s wait and keep pce precedence ", pcc_state->tag); + } else { + PCEP_DEBUG("%s recalculating pce precedence ", pcc_state->tag); + int best = calculate_best_pce(ctrl_state); + struct pcc_state *best_pcc_state = + get_pcc_state(ctrl_state, best); + if (best != pcc_state->id + && !best_pcc_state->pce_opts->previous_best) { + PCEP_DEBUG("%s disable-enable pce (%i)-(%i) && !(%i)", + pcc_state->tag, best, pcc_state->id, + !best_pcc_state->pce_opts->previous_best); + pcep_pcc_disable(ctrl_state, best_pcc_state); + best_pcc_state->retry_count++; + pcep_thread_schedule_reconnect( + ctrl_state, best, best_pcc_state->retry_count, + &best_pcc_state->t_reconnect); + } else { + PCEP_DEBUG( + "%s NO disable-enable pce (%i)-(%i) && !(%i)", + pcc_state->tag, best, pcc_state->id, + !best_pcc_state->pce_opts->previous_best); + } + } switch (type) { case TM_RECONNECT_PCC: @@ -654,11 +683,27 @@ int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state; struct pcc_opts *pcc_opts; + struct pce_opts *pce_opts_copy; + int current_pcc_id = get_pcc_id_by_ip(pcep_g->fpt, &pce_opts->addr); + if (!current_pcc_id) { + current_pcc_id = pcep_ctrl_get_free_pcc_id(pcep_g->fpt); + } + if (pcep_g->pce_opts[current_pcc_id - 1] != NULL) + XFREE(MTYPE_PCEP, pcep_g->pce_opts[0]); + pce_opts_copy = XCALLOC(MTYPE_PCEP, sizeof(*pce_opts)); + pce_opts_copy = memcpy(pce_opts_copy, pce_opts, sizeof(*pce_opts)); + pcep_g->pce_opts[current_pcc_id - 1] = pce_opts_copy; + pcc_id = current_pcc_id; + if (pcc_id > ctrl_state->pcc_count) { pcc_state = pcep_pcc_initialize(ctrl_state, pcc_id); set_pcc_state(ctrl_state, pcc_state); } else { pcc_state = get_pcc_state(ctrl_state, pcc_id); + if (!pcc_state) { + pcc_state = pcep_pcc_initialize(ctrl_state, pcc_id); + set_pcc_state(ctrl_state, pcc_state); + } } /* Copy the pcc options to delegate it to the update function */ @@ -670,6 +715,7 @@ int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state, "failed to update PCC configuration"); } + calculate_best_pce(ctrl_state); return 0; } @@ -677,11 +723,9 @@ int pcep_thread_event_remove_pcc(struct ctrl_state *ctrl_state, int pcc_id) { struct pcc_state *pcc_state; - if (pcc_id <= ctrl_state->pcc_count) { pcc_state = get_pcc_state(ctrl_state, pcc_id); remove_pcc_state(ctrl_state, pcc_state); pcep_pcc_finalize(ctrl_state, pcc_state); - } return 0; } @@ -710,6 +754,7 @@ int pcep_thread_event_pathd_event(struct ctrl_state *ctrl_state, for (i = 0; i < ctrl_state->pcc_count; i++) { struct pcc_state *pcc_state = ctrl_state->pcc[i]; + // path->is_delegated = is_best_pce(ctrl_state, i); pcep_pcc_pathd_event_handler(ctrl_state, pcc_state, type, path); } @@ -753,7 +798,120 @@ int pcep_main_event_handler(struct thread *thread) /* ------------ Helper functions ------------ */ +bool is_best_pce(struct ctrl_state *ctrl_state, int pce) +{ + if (ctrl_state && ctrl_state->pcc[pce]) { + return ctrl_state->pcc[pce]->pce_opts->is_best; + } else { + return false; + } +} +bool get_previous_best_pce(struct ctrl_state *ctrl_state) +{ + if (!ctrl_state || !ctrl_state->pcc_count) + return 0; + int previous_best_pce = -1; + struct pcc_state **pcc = &ctrl_state->pcc[0]; + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts + && pcc[i]->pce_opts->previous_best == true) { + previous_best_pce = i; + break; + } + } + return previous_best_pce != -1 ? previous_best_pce + 1 : -1; +} +int calculate_best_pce(struct ctrl_state *ctrl_state) +{ + bool is_best = false; + int best_precedence = PCE_DEFAULT_PRECEDENCE; + int best_pce = -1; + int default_pce = -1; + int previous_best_pce = -1; + + if (!ctrl_state || !ctrl_state->pcc_count) + return 0; + + struct pcc_state **pcc = &ctrl_state->pcc[0]; + + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts) { + if (pcc[i]->pce_opts->is_best == true) { + previous_best_pce = i; + pcc[i]->pce_opts->is_best = false; + } + if (pcc[i]->pce_opts->previous_best == true) { + pcc[i]->pce_opts->previous_best = false; + } + } + } + + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts + && pcc[i]->status != PCEP_PCC_DISCONNECTED) { + default_pce = i; // In case none better + if (pcc[i]->pce_opts->precedence <= best_precedence) { + if (best_pce != -1 + && pcc[best_pce]->pce_opts->precedence + == pcc[i]->pce_opts->precedence + && ipaddr_cmp( + &pcc[i]->pce_opts->addr, + &pcc[best_pce]->pce_opts->addr) + > 0) { + // collide of precedences so compare ip + best_pce = i; + } else { + best_precedence = + pcc[i]->pce_opts->precedence; + best_pce = i; + } + } + } + } + + if (best_pce != -1) { + pcc[best_pce]->pce_opts->is_best = true; + zlog_debug("best pce (%i) ", best_pce + 1); + } else { + if (default_pce != -1) { + pcc[default_pce]->pce_opts->is_best = true; + zlog_debug("best pce (default) (%i) ", default_pce + 1); + } else { + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts) { + pcc[i]->pce_opts->is_best = true; + zlog_debug("best pce (default) (%i) ", + i + 1); + break; + } + } + } + } + + if (previous_best_pce != -1) { + pcc[previous_best_pce]->pce_opts->previous_best = true; + zlog_debug("previous best pce (%i) ", previous_best_pce + 1); + } + + return best_pce + 1; +} +int pcep_ctrl_get_free_pcc_id(struct frr_pthread *fpt) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + if (ctrl_state->pcc_count == 0) { + return 1; + } + + for (int pcc_id = 0; pcc_id < MAX_PCC; pcc_id++) { + if (ctrl_state->pcc[pcc_id] == NULL) { + zlog_debug("new pcc_id (%d)", pcc_id + 1); + return pcc_id + 1; + } + } + + return 0; +} void set_ctrl_state(struct frr_pthread *fpt, struct ctrl_state *ctrl_state) { assert(fpt != NULL); @@ -771,16 +929,32 @@ struct ctrl_state *get_ctrl_state(struct frr_pthread *fpt) return ctrl_state; } +int get_pcc_id_by_ip(struct frr_pthread *fpt, struct ipaddr *pce_ip) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + for (int pcc_id = 0; pcc_id < MAX_PCC; pcc_id++) { + if (ctrl_state->pcc[pcc_id]) { + if (ipaddr_cmp((const struct ipaddr *)&ctrl_state + ->pcc[pcc_id] + ->pce_opts->addr, + (const struct ipaddr *)pce_ip) + == 0) { + zlog_debug("found pcc_id (%d)", pcc_id + 1); + return pcc_id + 1; + } + } + } + return 0; +} + struct pcc_state *get_pcc_state(struct ctrl_state *ctrl_state, int pcc_id) { assert(ctrl_state != NULL); assert(pcc_id >= 0); assert(pcc_id <= MAX_PCC); - assert(pcc_id <= ctrl_state->pcc_count); struct pcc_state *pcc_state; pcc_state = ctrl_state->pcc[pcc_id - 1]; - assert(pcc_state != NULL); return pcc_state; } @@ -789,11 +963,10 @@ void set_pcc_state(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) assert(ctrl_state != NULL); assert(pcc_state->id >= 0); assert(pcc_state->id <= MAX_PCC); - assert(pcc_state->id > ctrl_state->pcc_count); - assert(ctrl_state->pcc[pcc_state->id - 1] == NULL); ctrl_state->pcc[pcc_state->id - 1] = pcc_state; - ctrl_state->pcc_count = pcc_state->id; + ctrl_state->pcc_count++; + zlog_debug("added pce pcc_id (%d)", pcc_state->id); } void remove_pcc_state(struct ctrl_state *ctrl_state, @@ -804,11 +977,11 @@ void remove_pcc_state(struct ctrl_state *ctrl_state, assert(pcc_state->id <= MAX_PCC); /* FIXME: Can only remove the last PCC for now, * we have only one anyway */ - assert(pcc_state->id == ctrl_state->pcc_count); - assert(ctrl_state->pcc[pcc_state->id - 1] != NULL); + // assert(ctrl_state->pcc[pcc_state->id - 1] != NULL); ctrl_state->pcc[pcc_state->id - 1] = NULL; ctrl_state->pcc_count--; + zlog_debug("removed pce pcc_id (%d)", pcc_state->id); } uint32_t backoff_delay(uint32_t max, uint32_t base, uint32_t retry_count) diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h index 0cf58e37d92c..cb0fc2c124eb 100644 --- a/pathd/path_pcep_controller.h +++ b/pathd/path_pcep_controller.h @@ -75,6 +75,10 @@ struct pcep_ctrl_socket_data { typedef int (*pcep_ctrl_thread_callback)(struct thread *); /* Functions called from the main thread */ +int calculate_best_pce(struct ctrl_state *ctrl_state); +bool is_best_pce(struct ctrl_state *ctrl_state, int pce); +int get_pcc_id_by_ip(struct frr_pthread *fpt, struct ipaddr *pce_ip); +int pcep_ctrl_get_free_pcc_id(struct frr_pthread *fpt); int pcep_ctrl_initialize(struct thread_master *main_thread, struct frr_pthread **fpt, pcep_main_event_handler_t event_handler); diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c index 1583ffcef46b..5878be506755 100644 --- a/pathd/path_pcep_pcc.c +++ b/pathd/path_pcep_pcc.c @@ -226,6 +226,11 @@ int compare_pce_opts(struct pce_opts *lhs, struct pce_opts *rhs) return 1; } + retval = lhs->precedence - rhs->precedence; + if (retval != 0) { + return retval; + } + retval = memcmp(&lhs->addr, &rhs->addr, sizeof(lhs->addr)); if (retval != 0) { return retval; @@ -518,9 +523,11 @@ void pcep_pcc_pcep_event_handler(struct ctrl_state *ctrl_state, PCEP_DEBUG_PCEP("%s PCEP message: %s", pcc_state->tag, format_pcep_message(event->message)); break; + case PCE_DEAD_TIMER_EXPIRED: + PCEP_DEBUG("%s PCE_DEAD_TIMER_EXPIRED %d", pcc_state->tag, + pcc_state->retry_count); case PCE_CLOSED_SOCKET: case PCE_SENT_PCEP_CLOSE: - case PCE_DEAD_TIMER_EXPIRED: case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED: case PCC_PCEP_SESSION_CLOSED: case PCC_RCVD_MAX_INVALID_MSGS: @@ -765,7 +772,8 @@ void specialize_output_path(struct pcc_state *pcc_state, struct path *path) path->sender = pcc_state->pcc_addr_tr; if ((path->originator == NULL) - || (strcmp(path->originator, pcc_state->originator) == 0)) { + || (strcmp(path->originator, pcc_state->originator) == 0) + || pcc_state->pce_opts->is_best) { is_delegated = path->type == SRTE_CANDIDATE_TYPE_DYNAMIC; /* it seems the PCE consider updating an LSP a creation ?!? at least Cisco does... */ @@ -773,8 +781,8 @@ void specialize_output_path(struct pcc_state *pcc_state, struct path *path) } path->pcc_id = pcc_state->id; - path->go_active = is_delegated; - path->is_delegated = is_delegated; + path->go_active = (is_delegated & pcc_state->pce_opts->is_best); + path->is_delegated = (is_delegated & pcc_state->pce_opts->is_best); path->was_created = was_created; }