diff options
Diffstat (limited to '')
-rw-r--r-- | pathd/pathd.c | 1419 |
1 files changed, 1419 insertions, 0 deletions
diff --git a/pathd/pathd.c b/pathd/pathd.c new file mode 100644 index 0000000..e9d7cc6 --- /dev/null +++ b/pathd/pathd.c @@ -0,0 +1,1419 @@ +/* + * 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 "memory.h" +#include "log.h" +#include "lib_errors.h" +#include "network.h" +#include "libfrr.h" + +#include "pathd/pathd.h" +#include "pathd/path_zebra.h" +#include "pathd/path_debug.h" +#include "pathd/path_ted.h" + +#define HOOK_DELAY 3 + +DEFINE_MGROUP(PATHD, "pathd"); + +DEFINE_MTYPE_STATIC(PATHD, PATH_SEGMENT_LIST, "Segment List"); +DEFINE_MTYPE_STATIC(PATHD, PATH_SR_POLICY, "SR Policy"); +DEFINE_MTYPE_STATIC(PATHD, PATH_SR_CANDIDATE, "SR Policy candidate path"); + +DEFINE_HOOK(pathd_candidate_created, (struct srte_candidate * candidate), + (candidate)); +DEFINE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate), + (candidate)); +DEFINE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate), + (candidate)); + +static void trigger_pathd_candidate_created(struct srte_candidate *candidate); +static void trigger_pathd_candidate_created_timer(struct thread *thread); +static void trigger_pathd_candidate_updated(struct srte_candidate *candidate); +static void trigger_pathd_candidate_updated_timer(struct thread *thread); +static void trigger_pathd_candidate_removed(struct srte_candidate *candidate); +static const char * +srte_candidate_metric_name(enum srte_candidate_metric_type type); + +static void srte_set_metric(struct srte_metric *metric, float value, + bool required, bool is_bound, bool is_computed); +static void srte_unset_metric(struct srte_metric *metric); + + +/* Generate rb-tree of Segment List Segment instances. */ +static inline int srte_segment_entry_compare(const struct srte_segment_entry *a, + const struct srte_segment_entry *b) +{ + return a->index - b->index; +} +RB_GENERATE(srte_segment_entry_head, srte_segment_entry, entry, + srte_segment_entry_compare) + +/* Generate rb-tree of Segment List instances. */ +static inline int srte_segment_list_compare(const struct srte_segment_list *a, + const struct srte_segment_list *b) +{ + return strcmp(a->name, b->name); +} +RB_GENERATE(srte_segment_list_head, srte_segment_list, entry, + srte_segment_list_compare) + +struct srte_segment_list_head srte_segment_lists = + RB_INITIALIZER(&srte_segment_lists); + +/* Generate rb-tree of Candidate Path instances. */ +static inline int srte_candidate_compare(const struct srte_candidate *a, + const struct srte_candidate *b) +{ + return a->preference - b->preference; +} +RB_GENERATE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare) + +/* Generate rb-tree of SR Policy instances. */ +static inline int srte_policy_compare(const struct srte_policy *a, + const struct srte_policy *b) +{ + return sr_policy_compare(&a->endpoint, &b->endpoint, a->color, + b->color); +} +RB_GENERATE(srte_policy_head, srte_policy, entry, srte_policy_compare) + +struct srte_policy_head srte_policies = RB_INITIALIZER(&srte_policies); + +/** + * Adds a segment list to pathd. + * + * @param name The name of the segment list to add + * @return The added segment list + */ +struct srte_segment_list *srte_segment_list_add(const char *name) +{ + struct srte_segment_list *segment_list; + + segment_list = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment_list)); + strlcpy(segment_list->name, name, sizeof(segment_list->name)); + RB_INIT(srte_segment_entry_head, &segment_list->segments); + RB_INSERT(srte_segment_list_head, &srte_segment_lists, segment_list); + + return segment_list; +} + +/** + * Deletes a segment list from pathd. + * + * The given segment list structure will be freed and should not be used anymore + * after calling this function. + * + * @param segment_list the segment list to remove from pathd. + */ +void srte_segment_list_del(struct srte_segment_list *segment_list) +{ + struct srte_segment_entry *segment, *safe_seg; + RB_FOREACH_SAFE (segment, srte_segment_entry_head, + &segment_list->segments, safe_seg) { + srte_segment_entry_del(segment); + } + RB_REMOVE(srte_segment_list_head, &srte_segment_lists, segment_list); + XFREE(MTYPE_PATH_SEGMENT_LIST, segment_list); +} + +/** + * Search for a segment list by name. + * + * @param name The name of the segment list to look for + * @return The segment list if found, NULL otherwise + */ +struct srte_segment_list *srte_segment_list_find(const char *name) +{ + struct srte_segment_list search; + + strlcpy(search.name, name, sizeof(search.name)); + return RB_FIND(srte_segment_list_head, &srte_segment_lists, &search); +} + +/** + * Adds a segment to a segment list. + * + * @param segment_list The segment list the segment should be added to + * @param index The index of the added segment in the segment list + * @return The added segment + */ +struct srte_segment_entry * +srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index) +{ + struct srte_segment_entry *segment; + + segment = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment)); + segment->segment_list = segment_list; + segment->index = index; + RB_INSERT(srte_segment_entry_head, &segment_list->segments, segment); + + return segment; +} + +/** + * Deletes a segment from a segment list. + * + * @param segment The segment to be removed + */ +void srte_segment_entry_del(struct srte_segment_entry *segment) +{ + RB_REMOVE(srte_segment_entry_head, &segment->segment_list->segments, + segment); + XFREE(MTYPE_PATH_SEGMENT_LIST, segment); +} + +/** + * Set the node or adjacency identifier of a segment. + * + * @param segment The segment for which the NAI should be set + * @param type The type of the NAI + * @param type The address of the node or the local address of the adjacency + * @param type The local interface index of the unumbered adjacency + * @param type The remote address of the adjacency + * @param type The remote interface index of the unumbered adjacency + */ +int srte_segment_entry_set_nai(struct srte_segment_entry *segment, + enum srte_segment_nai_type type, + struct ipaddr *local_ip, uint32_t local_iface, + struct ipaddr *remote_ip, uint32_t remote_iface, + uint8_t algo, uint8_t pref_len) +{ + + int32_t status = 0; + struct prefix pre = {0}; + + if (!segment || !local_ip || !remote_ip) + return 1; + + segment->nai_type = type; + memcpy(&segment->nai_local_addr, local_ip, sizeof(struct ipaddr)); + + switch (type) { + case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: + case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: + case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: + memcpy(&segment->nai_remote_addr, remote_ip, + sizeof(struct ipaddr)); + status = srte_ted_do_query_type_f(segment, local_ip, remote_ip); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY: + memcpy(&segment->nai_remote_addr, remote_ip, + sizeof(struct ipaddr)); + segment->nai_local_iface = local_iface; + segment->nai_remote_iface = remote_iface; + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: + pre.family = AF_INET6; + pre.prefixlen = pref_len; + pre.u.prefix6 = local_ip->ip._v6_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_algorithm = algo; + status = srte_ted_do_query_type_c(segment, &pre, algo); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + pre.family = AF_INET; + pre.prefixlen = pref_len; + pre.u.prefix4 = local_ip->ip._v4_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_algorithm = algo; + status = srte_ted_do_query_type_c(segment, &pre, algo); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + pre.family = AF_INET6; + pre.prefixlen = pref_len; + pre.u.prefix6 = local_ip->ip._v6_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_local_iface = local_iface; + status = srte_ted_do_query_type_e(segment, &pre, local_iface); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + pre.family = AF_INET; + pre.prefixlen = pref_len; + pre.u.prefix4 = local_ip->ip._v4_addr; + segment->nai_local_prefix_len = pref_len; + segment->nai_local_iface = local_iface; + status = srte_ted_do_query_type_e(segment, &pre, local_iface); + break; + default: + segment->nai_local_addr.ipa_type = IPADDR_NONE; + segment->nai_local_iface = 0; + segment->nai_remote_addr.ipa_type = IPADDR_NONE; + segment->nai_remote_iface = 0; + } + return status; +} + +/** + * Mark segment as modified depending in protocol and sid conditions + * + * @param protocol_origin Origin of the segment list + * @param s_list Ptr to segment list with flags,sid to modidy + * @param s_entry Ptr to segment entry with sid to modidy + * @param ted_sid The sid from ted query + * @return void + */ +void srte_segment_set_local_modification(struct srte_segment_list *s_list, + struct srte_segment_entry *s_entry, + uint32_t ted_sid) +{ + if (!s_list || !s_entry) + return; + + if (s_list->protocol_origin == SRTE_ORIGIN_LOCAL + && s_entry->sid_value != ted_sid) { + s_entry->sid_value = ted_sid; + SET_FLAG(s_list->flags, F_SEGMENT_LIST_MODIFIED); + } +} + +/** + * Add a policy to pathd. + * + * WARNING: The color 0 is a special case as it is the no-color. + * + * @param color The color of the policy. + * @param endpoint The IP address of the policy endpoint + * @return The created policy + */ +struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint, + enum srte_protocol_origin origin, + const char *originator) +{ + struct srte_policy *policy; + + policy = XCALLOC(MTYPE_PATH_SR_POLICY, sizeof(*policy)); + policy->color = color; + policy->endpoint = *endpoint; + policy->binding_sid = MPLS_LABEL_NONE; + policy->protocol_origin = origin; + if (originator != NULL) + strlcpy(policy->originator, originator, + sizeof(policy->originator)); + + RB_INIT(srte_candidate_head, &policy->candidate_paths); + RB_INSERT(srte_policy_head, &srte_policies, policy); + + return policy; +} + +/** + * Delete a policy from pathd. + * + * The given policy structure will be freed and should never be used again + * after calling this function. + * + * @param policy The policy to be removed + */ +void srte_policy_del(struct srte_policy *policy) +{ + struct srte_candidate *candidate; + + path_zebra_delete_sr_policy(policy); + + path_zebra_release_label(policy->binding_sid); + + while (!RB_EMPTY(srte_candidate_head, &policy->candidate_paths)) { + candidate = + RB_ROOT(srte_candidate_head, &policy->candidate_paths); + trigger_pathd_candidate_removed(candidate); + srte_candidate_del(candidate); + } + + RB_REMOVE(srte_policy_head, &srte_policies, policy); + XFREE(MTYPE_PATH_SR_POLICY, policy); +} + +/** + * Search for a policy by color and endpoint. + * + * WARNING: The color 0 is a special case as it is the no-color. + * + * @param color The color of the policy to look for + * @param endpoint The endpoint of the policy to look for + * @return The policy if found, NULL otherwise + */ +struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint) +{ + struct srte_policy search; + + search.color = color; + search.endpoint = *endpoint; + return RB_FIND(srte_policy_head, &srte_policies, &search); +} + +/* + * After new data from igp,local and pce the segment list : + * Mark as invalid for origin pce if cannot be validated + * Updated for origin local + */ +int srte_policy_update_ted_sid(void) +{ + + int number_of_sid_clashed = 0; + struct srte_segment_list *s_list; + struct srte_segment_entry *s_entry; + + if (!path_ted_is_initialized()) + return 0; + if (RB_EMPTY(srte_segment_list_head, &srte_segment_lists)) + return 0; + + RB_FOREACH (s_list, srte_segment_list_head, &srte_segment_lists) { + if (CHECK_FLAG(s_list->flags, F_SEGMENT_LIST_DELETED)) + continue; + RB_FOREACH (s_entry, srte_segment_entry_head, + &s_list->segments) { + PATH_TED_DEBUG( + "%s:PATHD-TED: SL: Name: %s index:(%d) sid:(%d) prefix_len:(%d) local iface:(%d) algorithm:(%d)", + __func__, s_list->name, s_entry->index, + s_entry->sid_value, + s_entry->nai_local_prefix_len, + s_entry->nai_local_iface, + s_entry->nai_algorithm); + struct prefix prefix_cli = {0}; + + switch (s_entry->nai_type) { + case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: + case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: + number_of_sid_clashed += + srte_ted_do_query_type_f( + s_entry, + &s_entry->nai_local_addr, + &s_entry->nai_remote_addr); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE: + prefix_cli.family = AF_INET6; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix6 = + s_entry->nai_local_addr.ip._v6_addr; + number_of_sid_clashed += + srte_ted_do_query_type_e( + s_entry, &prefix_cli, + s_entry->nai_local_iface); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE: + prefix_cli.family = AF_INET; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix4 = + s_entry->nai_local_addr.ip._v4_addr; + number_of_sid_clashed += + srte_ted_do_query_type_e( + s_entry, &prefix_cli, + s_entry->nai_local_iface); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM: + prefix_cli.family = AF_INET6; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix6 = + s_entry->nai_local_addr.ip._v6_addr; + number_of_sid_clashed += + srte_ted_do_query_type_c( + s_entry, &prefix_cli, + s_entry->nai_algorithm); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM: + prefix_cli.family = AF_INET; + prefix_cli.prefixlen = + s_entry->nai_local_prefix_len; + prefix_cli.u.prefix4 = + s_entry->nai_local_addr.ip._v4_addr; + number_of_sid_clashed += + srte_ted_do_query_type_c( + s_entry, &prefix_cli, + s_entry->nai_algorithm); + break; + default: + break; + } + } + if (number_of_sid_clashed) { + SET_FLAG(s_list->flags, F_SEGMENT_LIST_SID_CONFLICT); + number_of_sid_clashed = 0; + } else + UNSET_FLAG(s_list->flags, F_SEGMENT_LIST_SID_CONFLICT); + } + srte_apply_changes(); + + return 0; +} + +/** + * Update a policy binding SID. + * + * @param policy The policy for which the SID should be updated + * @param binding_sid The new binding SID for the given policy + */ +void srte_policy_update_binding_sid(struct srte_policy *policy, + uint32_t binding_sid) +{ + if (policy->binding_sid != MPLS_LABEL_NONE) + path_zebra_release_label(policy->binding_sid); + + policy->binding_sid = binding_sid; + + /* Reinstall the Binding-SID if necessary. */ + if (policy->best_candidate) + path_zebra_add_sr_policy( + policy, policy->best_candidate->lsp->segment_list); +} + +/** + * Gives the policy best candidate path. + * + * @param policy The policy we want the best candidate path from + * @return The best candidate path + */ +static struct srte_candidate * +srte_policy_best_candidate(const struct srte_policy *policy) +{ + struct srte_candidate *candidate; + + RB_FOREACH_REVERSE (candidate, srte_candidate_head, + &policy->candidate_paths) { + /* search for highest preference with existing segment list */ + if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED) + && candidate->lsp->segment_list + && (!CHECK_FLAG(candidate->lsp->segment_list->flags, + F_SEGMENT_LIST_SID_CONFLICT))) + return candidate; + } + + return NULL; +} + +void srte_clean_zebra(void) +{ + struct srte_policy *policy, *safe_pol; + + RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol) + srte_policy_del(policy); + + path_zebra_stop(); +} + +/** + * Apply changes defined by setting the policies, candidate paths + * and segment lists modification flags NEW, MODIFIED and DELETED. + * + * This allows the northbound code to delay all the side effects of adding + * modifying and deleting them to the end. + * + * Example of marking an object as modified: + * `SET_FLAG(obj->flags, F_XXX_MODIFIED)` + */ +void srte_apply_changes(void) +{ + struct srte_policy *policy, *safe_pol; + struct srte_segment_list *segment_list, *safe_sl; + + RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol) { + if (CHECK_FLAG(policy->flags, F_POLICY_DELETED)) { + srte_policy_del(policy); + continue; + } + srte_policy_apply_changes(policy); + UNSET_FLAG(policy->flags, F_POLICY_NEW); + UNSET_FLAG(policy->flags, F_POLICY_MODIFIED); + } + + RB_FOREACH_SAFE (segment_list, srte_segment_list_head, + &srte_segment_lists, safe_sl) { + if (CHECK_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED)) { + srte_segment_list_del(segment_list); + continue; + } + UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW); + UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); + } +} + +/** + * Apply changes defined by setting the given policy and its candidate paths + * modification flags NEW, MODIFIED and DELETED. + * + * In moste cases `void srte_apply_changes(void)` should be used instead, + * this function will not handle the changes of segment lists used by the + * policy. + * + * @param policy The policy changes has to be applied to. + */ +void srte_policy_apply_changes(struct srte_policy *policy) +{ + struct srte_candidate *candidate, *safe; + struct srte_candidate *old_best_candidate; + struct srte_candidate *new_best_candidate; + char endpoint[46]; + + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + + /* Get old and new best candidate path. */ + old_best_candidate = policy->best_candidate; + new_best_candidate = srte_policy_best_candidate(policy); + + if (new_best_candidate != old_best_candidate) { + /* TODO: add debug guard. */ + zlog_debug( + "SR-TE(%s, %u): best candidate changed from %s to %s", + endpoint, policy->color, + old_best_candidate ? old_best_candidate->name : "none", + new_best_candidate ? new_best_candidate->name : "none"); + + if (old_best_candidate) { + policy->best_candidate = NULL; + UNSET_FLAG(old_best_candidate->flags, F_CANDIDATE_BEST); + SET_FLAG(old_best_candidate->flags, + F_CANDIDATE_MODIFIED); + + /* + * Rely on replace semantics if there's a new best + * candidate. + */ + if (!new_best_candidate) + path_zebra_delete_sr_policy(policy); + } + if (new_best_candidate) { + policy->best_candidate = new_best_candidate; + SET_FLAG(new_best_candidate->flags, F_CANDIDATE_BEST); + SET_FLAG(new_best_candidate->flags, + F_CANDIDATE_MODIFIED); + + path_zebra_add_sr_policy( + policy, new_best_candidate->lsp->segment_list); + } + } else if (new_best_candidate) { + /* The best candidate path did not change, but some of its + * attributes or its segment list may have changed. + */ + + bool candidate_changed = CHECK_FLAG(new_best_candidate->flags, + F_CANDIDATE_MODIFIED); + bool segment_list_changed = + new_best_candidate->lsp->segment_list + && CHECK_FLAG( + new_best_candidate->lsp->segment_list->flags, + F_SEGMENT_LIST_MODIFIED); + + if (candidate_changed || segment_list_changed) { + /* TODO: add debug guard. */ + zlog_debug("SR-TE(%s, %u): best candidate %s changed", + endpoint, policy->color, + new_best_candidate->name); + + path_zebra_add_sr_policy( + policy, new_best_candidate->lsp->segment_list); + } + } + + RB_FOREACH_SAFE (candidate, srte_candidate_head, + &policy->candidate_paths, safe) { + if (CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED)) { + trigger_pathd_candidate_removed(candidate); + srte_candidate_del(candidate); + continue; + } else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_NEW)) { + trigger_pathd_candidate_created(candidate); + } else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_MODIFIED)) { + trigger_pathd_candidate_updated(candidate); + } else if (candidate->lsp->segment_list + && CHECK_FLAG(candidate->lsp->segment_list->flags, + F_SEGMENT_LIST_MODIFIED)) { + trigger_pathd_candidate_updated(candidate); + } + + UNSET_FLAG(candidate->flags, F_CANDIDATE_NEW); + UNSET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + } +} + +/** + * Adds a candidate path to a policy. + * + * @param policy The policy the candidate path should be added to + * @param preference The preference of the candidate path to be added + * @return The added candidate path + */ +struct srte_candidate *srte_candidate_add(struct srte_policy *policy, + uint32_t preference, + enum srte_protocol_origin origin, + const char *originator) +{ + struct srte_candidate *candidate; + struct srte_lsp *lsp; + + candidate = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*candidate)); + lsp = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*lsp)); + + candidate->preference = preference; + candidate->policy = policy; + candidate->type = SRTE_CANDIDATE_TYPE_UNDEFINED; + candidate->discriminator = frr_weak_random(); + candidate->protocol_origin = origin; + if (originator != NULL) { + strlcpy(candidate->originator, originator, + sizeof(candidate->originator)); + lsp->protocol_origin = origin; + } + + if (candidate->protocol_origin == SRTE_ORIGIN_PCEP + || candidate->protocol_origin == SRTE_ORIGIN_BGP) { + candidate->type = SRTE_CANDIDATE_TYPE_DYNAMIC; + } + lsp->candidate = candidate; + candidate->lsp = lsp; + + RB_INSERT(srte_candidate_head, &policy->candidate_paths, candidate); + + return candidate; +} + +/** + * Deletes a candidate. + * + * The corresponding LSP will be removed alongside the candidate path. + * The given candidate will be freed and shouldn't be used anymore after the + * calling this function. + * + * @param candidate The candidate path to delete + */ +void srte_candidate_del(struct srte_candidate *candidate) +{ + struct srte_policy *srte_policy = candidate->policy; + + RB_REMOVE(srte_candidate_head, &srte_policy->candidate_paths, + candidate); + + XFREE(MTYPE_PATH_SR_CANDIDATE, candidate->lsp); + XFREE(MTYPE_PATH_SR_CANDIDATE, candidate); +} + +/** + * Sets the bandwidth constraint of given candidate path. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path of which the bandwidth should be changed + * @param bandwidth The Bandwidth constraint to set to the candidate path + * @param required If the constraint is required (true) or only desired (false) + */ +void srte_candidate_set_bandwidth(struct srte_candidate *candidate, + float bandwidth, bool required) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug( + "SR-TE(%s, %u): candidate %s %sconfig bandwidth set to %f B/s", + endpoint, policy->color, candidate->name, + required ? "required " : "", bandwidth); + SET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH); + COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required); + candidate->bandwidth = bandwidth; + + srte_lsp_set_bandwidth(candidate->lsp, bandwidth, required); +} + +/** + * Sets the bandwidth constraint of the given LSP. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The lsp of which the bandwidth should be changed + * @param bandwidth The Bandwidth constraint to set to the candidate path + * @param required If the constraint is required (true) or only desired (false) + */ +void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth, + bool required) +{ + struct srte_candidate *candidate = lsp->candidate; + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("SR-TE(%s, %u): candidate %s %slsp bandwidth set to %f B/s", + endpoint, policy->color, candidate->name, + required ? "required" : "", bandwidth); + SET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH); + COND_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required); + lsp->bandwidth = bandwidth; +} + +/** + * Remove a candidate path bandwidth constraint. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path of which the bandwidth should be removed + */ +void srte_candidate_unset_bandwidth(struct srte_candidate *candidate) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("SR-TE(%s, %u): candidate %s config bandwidth unset", + endpoint, policy->color, candidate->name); + UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH); + UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH); + candidate->bandwidth = 0; + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + srte_lsp_unset_bandwidth(candidate->lsp); +} + +/** + * Remove an LSP bandwidth constraint. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The lsp of which the bandwidth should be changed + */ +void srte_lsp_unset_bandwidth(struct srte_lsp *lsp) +{ + struct srte_candidate *candidate = lsp->candidate; + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("SR-TE(%s, %u): candidate %s lsp bandwidth unset", endpoint, + policy->color, candidate->name); + UNSET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH); + UNSET_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH); + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + lsp->bandwidth = 0; +} + +/** + * Sets a candidate path metric constraint. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path of which the metric should be changed + * @param type The metric type + * @param value The metric value + * @param required If the constraint is required (true) or only desired (false) + * @param is_bound If the metric is an indicative value or a strict upper bound + * @param is_computed If the metric was computed or configured + */ +void srte_candidate_set_metric(struct srte_candidate *candidate, + enum srte_candidate_metric_type type, + float value, bool required, bool is_bound, + bool is_computed) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug( + "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f (is-bound: %s; is_computed: %s)", + endpoint, policy->color, candidate->name, + required ? "required " : "", srte_candidate_metric_name(type), + type, value, is_bound ? "true" : "false", + is_computed ? "true" : "false"); + assert((type > 0) && (type <= MAX_METRIC_TYPE)); + srte_set_metric(&candidate->metrics[type - 1], value, required, + is_bound, is_computed); + srte_lsp_set_metric(candidate->lsp, type, value, required, is_bound, + is_computed); + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +} + +/** + * Sets an LSP metric constraint. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The LSP of which the metric should be changed + * @param type The metric type + * @param value The metric value + * @param required If the constraint is required (true) or only desired (false) + * @param is_bound If the metric is an indicative value or a strict upper bound + * @param is_computed If the metric was computed or configured + */ +void srte_lsp_set_metric(struct srte_lsp *lsp, + enum srte_candidate_metric_type type, float value, + bool required, bool is_bound, bool is_computed) +{ + struct srte_candidate *candidate = lsp->candidate; + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug( + "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f (is-bound: %s; is_computed: %s)", + endpoint, policy->color, candidate->name, + required ? "required " : "", srte_candidate_metric_name(type), + type, value, is_bound ? "true" : "false", + is_computed ? "true" : "false"); + assert((type > 0) && (type <= MAX_METRIC_TYPE)); + srte_set_metric(&lsp->metrics[type - 1], value, required, is_bound, + is_computed); +} + +void srte_set_metric(struct srte_metric *metric, float value, bool required, + bool is_bound, bool is_computed) +{ + SET_FLAG(metric->flags, F_METRIC_IS_DEFINED); + COND_FLAG(metric->flags, F_METRIC_IS_REQUIRED, required); + COND_FLAG(metric->flags, F_METRIC_IS_BOUND, is_bound); + COND_FLAG(metric->flags, F_METRIC_IS_COMPUTED, is_computed); + metric->value = value; +} + +/** + * Removes a candidate path metric constraint. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path from which the metric should be removed + * @param type The metric type + */ +void srte_candidate_unset_metric(struct srte_candidate *candidate, + enum srte_candidate_metric_type type) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("SR-TE(%s, %u): candidate %s config metric %s (%u) unset", + endpoint, policy->color, candidate->name, + srte_candidate_metric_name(type), type); + assert((type > 0) && (type <= MAX_METRIC_TYPE)); + srte_unset_metric(&candidate->metrics[type - 1]); + srte_lsp_unset_metric(candidate->lsp, type); + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +} + +/** + * Removes a candidate path metric constraint. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The LSP from which the metric should be removed + * @param type The metric type + */ +void srte_lsp_unset_metric(struct srte_lsp *lsp, + enum srte_candidate_metric_type type) +{ + struct srte_candidate *candidate = lsp->candidate; + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("SR-TE(%s, %u): candidate %s lsp metric %s (%u) unset", + endpoint, policy->color, candidate->name, + srte_candidate_metric_name(type), type); + assert((type > 0) && (type <= MAX_METRIC_TYPE)); + srte_unset_metric(&lsp->metrics[type - 1]); +} + +void srte_unset_metric(struct srte_metric *metric) +{ + UNSET_FLAG(metric->flags, F_METRIC_IS_DEFINED); + UNSET_FLAG(metric->flags, F_METRIC_IS_BOUND); + UNSET_FLAG(metric->flags, F_METRIC_IS_COMPUTED); + metric->value = 0; +} + +/** + * Sets a candidate path objective function. + * + * @param candidate The candidate path of which the OF should be changed + * @param required If the constraint is required (true) or only desired (false) + * @param type The objective function type + */ +void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required, + enum objfun_type type) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + + candidate->objfun = type; + SET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN); + COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN, required); + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + zlog_debug("SR-TE(%s, %u): candidate %s %sobjective function set to %s", + endpoint, policy->color, candidate->name, + required ? "required " : "", objfun_type_name(type)); +} + +/** + * Removed the objective function constraint from a candidate path. + * + * @param candidate The candidate path from which the OF should be removed + */ +void srte_candidate_unset_objfun(struct srte_candidate *candidate) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + + UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN); + UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN); + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + candidate->objfun = OBJFUN_UNDEFINED; + zlog_debug( + "SR-TE(%s, %u): candidate %s objective functions preferences unset", + endpoint, policy->color, candidate->name); +} + +static uint32_t filter_type_to_flag(enum affinity_filter_type type) +{ + switch (type) { + case AFFINITY_FILTER_EXCLUDE_ANY: + return F_CANDIDATE_HAS_EXCLUDE_ANY; + case AFFINITY_FILTER_INCLUDE_ANY: + return F_CANDIDATE_HAS_INCLUDE_ANY; + case AFFINITY_FILTER_INCLUDE_ALL: + return F_CANDIDATE_HAS_INCLUDE_ALL; + default: + return 0; + } +} + +static const char *filter_type_name(enum affinity_filter_type type) +{ + switch (type) { + case AFFINITY_FILTER_EXCLUDE_ANY: + return "exclude-any"; + case AFFINITY_FILTER_INCLUDE_ANY: + return "include-any"; + case AFFINITY_FILTER_INCLUDE_ALL: + return "include-all"; + default: + return "unknown"; + } +} + +/** + * Sets a candidate path affinity filter constraint. + * + * @param candidate The candidate path of which the constraint should be changed + * @param type The affinity constraint type to set + * @param filter The bitmask filter of the constraint + */ +void srte_candidate_set_affinity_filter(struct srte_candidate *candidate, + enum affinity_filter_type type, + uint32_t filter) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + + assert(type > AFFINITY_FILTER_UNDEFINED); + assert(type <= MAX_AFFINITY_FILTER_TYPE); + SET_FLAG(candidate->flags, filter_type_to_flag(type)); + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + candidate->affinity_filters[type - 1] = filter; + zlog_debug( + "SR-TE(%s, %u): candidate %s affinity filter %s set to 0x%08x", + endpoint, policy->color, candidate->name, + filter_type_name(type), filter); +} + +/** + * Removes a candidate path affinity filter constraint. + * + * @param candidate The candidate path from which the constraint should be + * removed + * @param type The affinity constraint type to remove + */ +void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate, + enum affinity_filter_type type) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + + assert(type > AFFINITY_FILTER_UNDEFINED); + assert(type <= MAX_AFFINITY_FILTER_TYPE); + UNSET_FLAG(candidate->flags, filter_type_to_flag(type)); + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + candidate->affinity_filters[type - 1] = 0; + zlog_debug("SR-TE(%s, %u): candidate %s affinity filter %s unset", + endpoint, policy->color, candidate->name, + filter_type_name(type)); +} + +/** + * Searches for a candidate path of the given policy. + * + * @param policy The policy to search for candidate path + * @param preference The preference of the candidate path you are looking for + * @return The candidate path if found, NULL otherwise + */ +struct srte_candidate *srte_candidate_find(struct srte_policy *policy, + uint32_t preference) +{ + struct srte_candidate search; + + search.preference = preference; + return RB_FIND(srte_candidate_head, &policy->candidate_paths, &search); +} + +/** + * Searches for a an entry of a given segment list. + * + * @param segment_list The segment list to search for the entry + * @param index The index of the entry you are looking for + * @return The segment list entry if found, NULL otherwise. + */ +struct srte_segment_entry * +srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index) +{ + struct srte_segment_entry search; + + search.index = index; + return RB_FIND(srte_segment_entry_head, &segment_list->segments, + &search); +} + +/** + * Updates a candidate status. + * + * @param candidate The candidate of which the status should be updated + * @param status The new candidate path status + */ +void srte_candidate_status_update(struct srte_candidate *candidate, int status) +{ + struct srte_policy *policy = candidate->policy; + char endpoint[46]; + ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + zlog_debug("SR-TE(%s, %u): zebra updated status to %d", endpoint, + policy->color, status); + switch (status) { + case ZEBRA_SR_POLICY_DOWN: + switch (policy->status) { + /* If the policy is GOING_UP, and zebra faild + to install it, we wait for zebra to retry */ + /* TODO: Add some timeout after which we would + get is back to DOWN and remove the + policy */ + case SRTE_POLICY_STATUS_GOING_UP: + case SRTE_POLICY_STATUS_DOWN: + return; + default: + zlog_debug("SR-TE(%s, %u): policy is DOWN", endpoint, + policy->color); + policy->status = SRTE_POLICY_STATUS_DOWN; + break; + } + break; + case ZEBRA_SR_POLICY_UP: + switch (policy->status) { + case SRTE_POLICY_STATUS_UP: + return; + default: + zlog_debug("SR-TE(%s, %u): policy is UP", endpoint, + policy->color); + policy->status = SRTE_POLICY_STATUS_UP; + break; + } + break; + } + + trigger_pathd_candidate_updated(candidate); +} + +/** + * Flags the segment lists from give originator for removal. + * + * The function srte_apply_changes must be called afterward for + * the segment list to be removed. + * + * @param originator The originator tag of the segment list to be marked + * @param force If the unset should be forced regardless of the originator + */ +void srte_candidate_unset_segment_list(const char *originator, bool force) +{ + if (originator == NULL) { + zlog_warn( + "Cannot unset segment list because originator is NULL"); + return; + } + + zlog_debug("Unset segment lists for originator %s", originator); + + /* Iterate the policies, then iterate each policy's candidate path + * to check the candidate path's segment list originator */ + struct srte_policy *policy; + RB_FOREACH (policy, srte_policy_head, &srte_policies) { + zlog_debug("Unset segment lists checking policy %s", + policy->name); + struct srte_candidate *candidate; + RB_FOREACH (candidate, srte_candidate_head, + &policy->candidate_paths) { + zlog_debug("Unset segment lists checking candidate %s", + candidate->name); + if (candidate->lsp == NULL) { + continue; + } + + /* The candidate->lsp->segment_list is operational data, + * configured by the PCE. We dont want to modify the + * candidate->segment_list, + * which is configuration data. */ + struct srte_segment_list *segment_list = + candidate->lsp->segment_list; + if (segment_list == NULL) { + continue; + } + + if (segment_list->protocol_origin + == SRTE_ORIGIN_LOCAL) { + zlog_warn( + "Cannot unset segment list %s because it was created locally", + segment_list->name); + continue; + } + + /* In the case of last pce,we force the unset + * because we don't have pce by prefix (TODO) is all + * 'global' */ + if (strncmp(segment_list->originator, originator, + sizeof(segment_list->originator)) + == 0 + || force) { + zlog_debug("Unset segment list %s", + segment_list->name); + SET_FLAG(segment_list->flags, + F_SEGMENT_LIST_DELETED); + SET_FLAG(candidate->flags, + F_CANDIDATE_MODIFIED); + candidate->lsp->segment_list = NULL; + } + } + } +} + +/** + * Gives a string representation of given protocol origin enum. + * + * @param origin The enum you want a string representation of + * @return The string representation of given enum + */ +const char *srte_origin2str(enum srte_protocol_origin origin) +{ + switch (origin) { + case SRTE_ORIGIN_PCEP: + return "PCEP"; + case SRTE_ORIGIN_BGP: + return "BGP"; + case SRTE_ORIGIN_LOCAL: + return "Local"; + default: + return "Unknown"; + } +} + +void pathd_shutdown(void) +{ + path_ted_teardown(); + srte_clean_zebra(); + frr_fini(); +} + +void trigger_pathd_candidate_created(struct srte_candidate *candidate) +{ + /* The hook is called asynchronously to let the PCEP module + time to send a response to the PCE before receiving any updates from + pathd. In addition, a minimum amount of time need to pass before + the hook is called to prevent the hook to be called multiple times + from changing the candidate by hand with the console */ + if (candidate->hook_timer != NULL) + return; + thread_add_timer(master, trigger_pathd_candidate_created_timer, + (void *)candidate, HOOK_DELAY, &candidate->hook_timer); +} + +void trigger_pathd_candidate_created_timer(struct thread *thread) +{ + struct srte_candidate *candidate = THREAD_ARG(thread); + candidate->hook_timer = NULL; + hook_call(pathd_candidate_created, candidate); +} + +void trigger_pathd_candidate_updated(struct srte_candidate *candidate) +{ + /* The hook is called asynchronously to let the PCEP module + time to send a response to the PCE before receiving any updates from + pathd. In addition, a minimum amount of time need to pass before + the hook is called to prevent the hook to be called multiple times + from changing the candidate by hand with the console */ + if (candidate->hook_timer != NULL) + return; + thread_add_timer(master, trigger_pathd_candidate_updated_timer, + (void *)candidate, HOOK_DELAY, &candidate->hook_timer); +} + +void trigger_pathd_candidate_updated_timer(struct thread *thread) +{ + struct srte_candidate *candidate = THREAD_ARG(thread); + candidate->hook_timer = NULL; + hook_call(pathd_candidate_updated, candidate); +} + +void trigger_pathd_candidate_removed(struct srte_candidate *candidate) +{ + /* The hook needs to be call synchronously, otherwise the candidate + path will be already deleted when the handler is called */ + if (candidate->hook_timer != NULL) { + thread_cancel(&candidate->hook_timer); + candidate->hook_timer = NULL; + } + hook_call(pathd_candidate_removed, candidate); +} + +const char *srte_candidate_metric_name(enum srte_candidate_metric_type type) +{ + switch (type) { + case SRTE_CANDIDATE_METRIC_TYPE_IGP: + return "IGP"; + case SRTE_CANDIDATE_METRIC_TYPE_TE: + return "TE"; + case SRTE_CANDIDATE_METRIC_TYPE_HC: + return "HC"; + case SRTE_CANDIDATE_METRIC_TYPE_ABC: + return "ABC"; + case SRTE_CANDIDATE_METRIC_TYPE_LMLL: + return "LMLL"; + case SRTE_CANDIDATE_METRIC_TYPE_CIGP: + return "CIGP"; + case SRTE_CANDIDATE_METRIC_TYPE_CTE: + return "CTE"; + case SRTE_CANDIDATE_METRIC_TYPE_PIGP: + return "PIGP"; + case SRTE_CANDIDATE_METRIC_TYPE_PTE: + return "PTE"; + case SRTE_CANDIDATE_METRIC_TYPE_PHC: + return "PHC"; + case SRTE_CANDIDATE_METRIC_TYPE_MSD: + return "MSD"; + case SRTE_CANDIDATE_METRIC_TYPE_PD: + return "PD"; + case SRTE_CANDIDATE_METRIC_TYPE_PDV: + return "PDV"; + case SRTE_CANDIDATE_METRIC_TYPE_PL: + return "PL"; + case SRTE_CANDIDATE_METRIC_TYPE_PPD: + return "PPD"; + case SRTE_CANDIDATE_METRIC_TYPE_PPDV: + return "PPDV"; + case SRTE_CANDIDATE_METRIC_TYPE_PPL: + return "PPL"; + case SRTE_CANDIDATE_METRIC_TYPE_NAP: + return "NAP"; + case SRTE_CANDIDATE_METRIC_TYPE_NLP: + return "NLP"; + case SRTE_CANDIDATE_METRIC_TYPE_DC: + return "DC"; + case SRTE_CANDIDATE_METRIC_TYPE_BNC: + return "BNC"; + default: + return "UNKNOWN"; + } +} + +int32_t srte_ted_do_query_type_c(struct srte_segment_entry *entry, + struct prefix *prefix_cli, uint32_t algo) +{ + int32_t status = 0; + uint32_t ted_sid = MPLS_LABEL_NONE; + + if (!entry || !prefix_cli) + return 0; + + if (!path_ted_is_initialized()) + return 0; + + ted_sid = path_ted_query_type_c(prefix_cli, algo); + if (ted_sid == MPLS_LABEL_NONE) { + zlog_warn(" %s: PATHD-TED: SL: ERROR query C : ted-sid (%d)", + __func__, ted_sid); + } else { + zlog_debug("%s: PATHD-TED: SL: Success query C : ted-sid (%d)", + __func__, ted_sid); + } + if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, + entry->sid_value)) { + status = PATH_SID_ERROR; + } else + srte_segment_set_local_modification(entry->segment_list, entry, + ted_sid); + return status; +} + +int32_t srte_ted_do_query_type_e(struct srte_segment_entry *entry, + struct prefix *prefix_cli, + uint32_t local_iface) +{ + int32_t status = 0; + uint32_t ted_sid = MPLS_LABEL_NONE; + + if (!entry || !prefix_cli) + return 0; + + if (!path_ted_is_initialized()) + return 0; + + ted_sid = path_ted_query_type_e(prefix_cli, local_iface); + if (ted_sid == MPLS_LABEL_NONE) { + zlog_warn(" %s: PATHD-TED: SL: ERROR query E : ted-sid (%d)", + __func__, ted_sid); + } else { + zlog_debug("%s: PATHD-TED: SL: Success query E : ted-sid (%d)", + __func__, ted_sid); + } + if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, + entry->sid_value)) { + status = PATH_SID_ERROR; + } else + srte_segment_set_local_modification(entry->segment_list, entry, + ted_sid); + return status; +} + +int32_t srte_ted_do_query_type_f(struct srte_segment_entry *entry, + struct ipaddr *local, struct ipaddr *remote) +{ + int32_t status = 0; + uint32_t ted_sid = MPLS_LABEL_NONE; + + if (!entry || !local || !remote) + return 0; + + if (!path_ted_is_initialized()) + return status; + + ted_sid = path_ted_query_type_f(local, remote); + if (ted_sid == MPLS_LABEL_NONE) { + zlog_warn("%s:SL: ERROR query F : ted-sid (%d)", __func__, + ted_sid); + } else { + zlog_debug("%s:SL: Success query F : ted-sid (%d)", __func__, + ted_sid); + } + if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid, + entry->sid_value)) { + status = PATH_SID_ERROR; + } else + srte_segment_set_local_modification(entry->segment_list, entry, + ted_sid); + return status; +} |