Merge pull request #8509 from volta-networks/pathd_ls_client

pathd: Traffic Engineering Database support
This commit is contained in:
Olivier Dugeon 2021-05-12 09:42:56 +02:00 committed by GitHub
commit 31db7fc227
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 3139 additions and 140 deletions

View File

@ -28,25 +28,8 @@ documented elsewhere.
PCEP Support
============
To build the PCC for pathd, the externall library `pceplib 1.2 <https://github.com/volta-networks/pceplib/tree/devel-1.2>`_ is required.
A pceplib is included in the frr source tree and build by default.
To build FRR with support for PCEP the following steps must be followed:
- Checkout and build pceplib:
```
$ git clone https://github.com/volta-networks/pceplib
$ cd pceplib
$ make
$ make install
$ export PCEPLIB_ROOT=$PWD
```
- Configure FRR with the extra parameters:
```
--enable-pcep LDFLAGS="-L${PCEPLIB_ROOT}/install/lib" CPPFLAGS="-I${PCEPLIB_ROOT}/install/include"
```
To start pathd with pcep support the extra parameter `-M pathd_pcep` should be
passed to the pathd daemon.
@ -62,10 +45,18 @@ Example:
debug pathd pcep basic
segment-routing
traffic-eng
mpls-te on
mpls-te import ospfv2
segment-list SL1
index 10 mpls label 16010
index 20 mpls label 16030
!
segment-list SL2
index 10 nai prefix 10.1.2.1/32 iface 1
index 20 nai adjacency 10.1.20.1 10.1.20.2
index 30 nai prefix 10.10.10.5/32 algorithm 0
index 40 mpls label 18001
!
policy color 1 endpoint 1.1.1.1
name default
binding-sid 4000
@ -113,11 +104,22 @@ Configuration Commands
Configure segment routing traffic engineering.
.. clicmd:: [no] mpls-te <on|off>
Activate/Deactivate use of internal Traffic Engineering Database
.. clicmd:: [no] mpls-te import <ospfv2|ospfv3|isis>
Load data from the selected igp
.. clicmd:: segment-list NAME
Delete or start a segment list definition.
.. clicmd:: index INDEX mpls label LABEL [nai node ADDRESS]
.. clicmd:: index INDEX mpls label LABEL
.. clicmd:: index INDEX nai adjacency A.B.C.D A.B.C.D
.. clicmd:: index INDEX nai prefix A.B.C.D/M algorithm <0|1>
.. clicmd:: index INDEX nai prefix A.B.C.D/M iface (0-65535)
Delete or specify a segment in a segment list definition.

View File

@ -34,6 +34,7 @@
#ifndef VTYSH_EXTRACT_PL
#include "pathd/path_cli_clippy.c"
#endif
#include "pathd/path_ted.h"
#define XPATH_MAXATTRSIZE 64
#define XPATH_MAXKEYSIZE 42
@ -47,6 +48,18 @@ static int config_write_segment_routing(struct vty *vty);
static int config_write_traffic_eng(struct vty *vty);
static int config_write_segment_lists(struct vty *vty);
static int config_write_sr_policies(struct vty *vty);
static int segment_list_has_src_dst(
struct vty *vty, char *xpath, long index, const char *index_str,
struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4,
struct in6_addr adj_src_ipv6, struct in6_addr adj_dst_ipv6,
const char *adj_src_ipv4_str, const char *adj_dst_ipv4_str,
const char *adj_src_ipv6_str, const char *adj_dst_ipv6_str);
static int segment_list_has_prefix(
struct vty *vty, char *xpath, long index, const char *index_str,
const struct prefix_ipv4 *prefix_ipv4, const char *prefix_ipv4_str,
const struct prefix_ipv6 *prefix_ipv6, const char *prefix_ipv6_str,
const char *has_algo, long algo, const char *algo_str,
const char *has_iface_id, long iface_id, const char *iface_id_str);
DEFINE_MTYPE_STATIC(PATHD, PATH_CLI, "Client");
@ -144,6 +157,7 @@ DEFPY(show_srte_policy,
return CMD_SUCCESS;
}
/*
* Show detailed SR-TE info
*/
@ -295,56 +309,227 @@ void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(dnode, "./name"));
}
static int segment_list_has_src_dst(
struct vty *vty, char *xpath, long index, const char *index_str,
struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4,
struct in6_addr adj_src_ipv6, struct in6_addr adj_dst_ipv6,
const char *adj_src_ipv4_str, const char *adj_dst_ipv4_str,
const char *adj_src_ipv6_str, const char *adj_dst_ipv6_str)
{
const char *node_src_id;
uint32_t ted_sid = MPLS_LABEL_NONE;
struct ipaddr ip_src = {};
struct ipaddr ip_dst = {};
if (adj_src_ipv4_str != NULL) {
ip_src.ipa_type = IPADDR_V4;
ip_src.ip._v4_addr = adj_src_ipv4;
ip_dst.ipa_type = IPADDR_V4;
ip_dst.ip._v4_addr = adj_dst_ipv4;
} else if (adj_src_ipv6_str != NULL) {
ip_src.ipa_type = IPADDR_V6;
ip_src.ip._v6_addr = adj_src_ipv6;
ip_dst.ipa_type = IPADDR_V6;
ip_dst.ip._v6_addr = adj_dst_ipv6;
} else {
return CMD_ERR_NO_MATCH;
}
ted_sid = path_ted_query_type_f(&ip_src, &ip_dst);
if (ted_sid == MPLS_LABEL_NONE) {
zlog_warn(
"%s: [rcv ted] CLI NOT FOUND Continue query_type_f SRC (%pIA) DST (%pIA)!",
__func__, &ip_src, &ip_dst);
}
/* type */
snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/type",
index_str);
if (adj_src_ipv4_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv4_adjacency");
node_src_id = adj_src_ipv4_str;
} else if (adj_src_ipv6_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv6_adjacency");
node_src_id = adj_src_ipv6_str;
} else {
return CMD_ERR_NO_MATCH;
}
/* addresses */
snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address",
index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_src_id);
snprintf(xpath, XPATH_MAXLEN,
"./segment[index='%s']/nai/remote-address", index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
adj_dst_ipv4_str ? adj_dst_ipv4_str
: adj_dst_ipv6_str);
return CMD_SUCCESS;
}
int segment_list_has_prefix(
struct vty *vty, char *xpath, long index, const char *index_str,
const struct prefix_ipv4 *prefix_ipv4, const char *prefix_ipv4_str,
const struct prefix_ipv6 *prefix_ipv6, const char *prefix_ipv6_str,
const char *has_algo, long algo, const char *algo_str,
const char *has_iface_id, long iface_id, const char *iface_id_str)
{
char buf_prefix[INET6_ADDRSTRLEN];
uint32_t ted_sid = MPLS_LABEL_NONE;
struct prefix prefix_cli = {};
struct ipaddr pre_ipaddr = {};
/* prefix with algorithm or local interface id */
/* Type */
snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/type",
index_str);
if (has_iface_id != NULL) {
if (prefix_ipv4_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv4_local_iface");
} else if (prefix_ipv6_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv6_local_iface");
} else {
return CMD_ERR_NO_MATCH;
}
} else {
if (prefix_ipv4_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv4_algo");
} else if (prefix_ipv6_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv6_algo");
} else {
return CMD_ERR_NO_MATCH;
}
}
/* Prefix */
if (prefix_ipv4_str != NULL) {
if (!str2prefix(prefix_ipv4_str, &prefix_cli)) {
vty_out(vty, "%% Malformed prefix\n");
return CMD_WARNING_CONFIG_FAILED;
}
inet_ntop(AF_INET, &prefix_cli.u.prefix4, buf_prefix,
sizeof(buf_prefix));
pre_ipaddr.ipa_type = IPADDR_V4;
pre_ipaddr.ip._v4_addr = prefix_cli.u.prefix4;
} else if (prefix_ipv6_str != NULL) {
if (!str2prefix(prefix_ipv6_str, &prefix_cli)) {
vty_out(vty, "%% Malformed prefix\n");
return CMD_WARNING_CONFIG_FAILED;
}
inet_ntop(AF_INET6, &prefix_cli.u.prefix6, buf_prefix,
sizeof(buf_prefix));
pre_ipaddr.ipa_type = IPADDR_V6;
pre_ipaddr.ip._v6_addr = prefix_cli.u.prefix6;
} else {
return CMD_ERR_NO_MATCH;
}
snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address",
index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, buf_prefix);
snprintf(xpath, XPATH_MAXLEN,
"./segment[index='%s']/nai/local-prefix-len", index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
prefix_ipv4_str
? strchr(prefix_ipv4_str, '/') + 1
: strchr(prefix_ipv6_str, '/') + 1);
/* Alg / Iface */
if (has_algo != NULL) {
snprintf(xpath, XPATH_MAXLEN,
"./segment[index='%s']/nai/algorithm", index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, algo_str);
} else {
if (has_iface_id != NULL) {
snprintf(xpath, XPATH_MAXLEN,
"./segment[index='%s']/nai/local-interface",
index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
iface_id_str);
}
}
if (has_algo != NULL) {
ted_sid = path_ted_query_type_c(&prefix_cli, algo);
if (ted_sid == MPLS_LABEL_NONE) {
zlog_err(
"%s: [rcv ted] CLI NOT FOUND Continue query_type_c PREFIX (%pIA/%d) ALGO (%ld) sid:(%d)!",
__func__, &pre_ipaddr, prefix_cli.prefixlen,
algo, ted_sid);
}
}
if (has_iface_id != NULL) {
ted_sid = path_ted_query_type_e(&prefix_cli, iface_id);
if (ted_sid == MPLS_LABEL_NONE) {
zlog_err(
"%s: [rcv ted] CLI NOT FOUND Continue query_type_e PREFIX (%pIA/%d) IFACE (%ld) sid:(%d)!",
__func__, &pre_ipaddr, prefix_cli.prefixlen,
iface_id, ted_sid);
}
}
return CMD_SUCCESS;
}
/*
* XPath: /frr-pathd:pathd/srte/segment-list/segment
*/
DEFPY(srte_segment_list_segment,
srte_segment_list_segment_cmd,
"index (0-4294967295)$index mpls label (16-1048575)$label "
/* clang-format off */
DEFPY(srte_segment_list_segment, srte_segment_list_segment_cmd,
"index (0-4294967295)$index <[mpls$has_mpls_label label (16-1048575)$label] "
"|"
"[nai$has_nai <"
"node <A.B.C.D$node_ipv4|X:X::X:X$node_ipv6>"
">]",
"prefix <A.B.C.D/M$prefix_ipv4|X:X::X:X/M$prefix_ipv6>"
"<algorithm$has_algo (0-1)$algo| iface$has_iface_id (0-4294967295)$iface_id>"
"| adjacency$has_adj "
"<A.B.C.D$adj_src_ipv4 A.B.C.D$adj_dst_ipv4|X:X::X:X$adj_src_ipv6 X:X::X:X$adj_dst_ipv6>"
">]"
">",
"Index\n"
"Index Value\n"
"MPLS or IP Label\n"
"Label\n"
"Label Value\n"
"Segment NAI\n"
"NAI node identifier\n"
"NAI IPv4 node identifier\n"
"NAI IPv6 node identifier\n")
"NAI prefix identifier\n"
"NAI IPv4 prefix identifier\n"
"NAI IPv6 prefix identifier\n"
"IGP Algorithm\n"
"Algorithm Value SPF or Strict-SPF\n"
"Interface Id\n"
"Interface Value\n"
"ADJ identifier\n"
"ADJ IPv4 src identifier\n"
"ADJ IPv4 dst identifier\n"
"ADJ IPv6 src identifier\n"
"ADJ IPv6 dst identifier\n")
/* clang-format on */
{
char xpath[XPATH_MAXLEN];
const char *node_id;
int status = CMD_SUCCESS;
snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
snprintf(xpath, sizeof(xpath), "./segment[index='%s']/sid-value",
index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str);
if (has_nai != NULL) {
snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai/type",
index_str);
if (node_ipv4_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv4_node");
node_id = node_ipv4_str;
} else if (node_ipv6_str != NULL) {
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv6_node");
node_id = node_ipv6_str;
} else {
return CMD_ERR_NO_MATCH;
}
if (has_mpls_label != NULL) {
snprintf(xpath, sizeof(xpath),
"./segment[index='%s']/nai/local-address", index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_id);
"./segment[index='%s']/sid-value", index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str);
return nb_cli_apply_changes(vty, NULL);
}
if (has_adj != NULL) {
status = segment_list_has_src_dst(vty, xpath, index, index_str,
adj_src_ipv4, adj_dst_ipv4,
adj_src_ipv6, adj_dst_ipv6,
adj_src_ipv4_str, adj_dst_ipv4_str,
adj_dst_ipv6_str, adj_src_ipv6_str);
if (status != CMD_SUCCESS)
return status;
} else {
snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai",
index_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
segment_list_has_prefix(
vty, xpath, index, index_str, prefix_ipv4,
prefix_ipv4_str, prefix_ipv6, prefix_ipv6_str, has_algo,
algo, algo_str, has_iface_id, iface_id, iface_id_str);
if (status != CMD_SUCCESS)
return status;
}
return nb_cli_apply_changes(vty, NULL);
@ -369,23 +554,60 @@ void cli_show_srte_segment_list_segment(struct vty *vty,
struct lyd_node *dnode,
bool show_defaults)
{
vty_out(vty, " index %s mpls label %s",
yang_dnode_get_string(dnode, "./index"),
yang_dnode_get_string(dnode, "./sid-value"));
vty_out(vty, " index %s ", yang_dnode_get_string(dnode, "./index"));
if (yang_dnode_exists(dnode, "./sid-value")) {
vty_out(vty, " mpls label %s",
yang_dnode_get_string(dnode, "./sid-value"));
}
if (yang_dnode_exists(dnode, "./nai")) {
struct ipaddr addr;
struct ipaddr addr_rmt;
switch (yang_dnode_get_enum(dnode, "./nai/type")) {
case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
vty_out(vty, " nai node %pI4", &addr.ipaddr_v4);
vty_out(vty, " nai prefix %pI4", &addr.ipaddr_v4);
break;
case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
vty_out(vty, " nai node %pI6", &addr.ipaddr_v6);
vty_out(vty, " nai prefix %pI6", &addr.ipaddr_v6);
break;
case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
yang_dnode_get_ip(&addr_rmt, dnode,
"./nai/remote-address");
vty_out(vty, " nai adjacency %pI4", &addr.ipaddr_v4);
vty_out(vty, " %pI4", &addr_rmt.ipaddr_v4);
break;
case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
yang_dnode_get_ip(&addr_rmt, dnode,
"./nai/remote-address");
vty_out(vty, " nai adjacency %pI6", &addr.ipaddr_v6);
vty_out(vty, " %pI6", &addr_rmt.ipaddr_v6);
break;
default:
break;
}
if (yang_dnode_exists(dnode, "./nai/local-prefix-len")) {
vty_out(vty, "/%s",
yang_dnode_get_string(
dnode, "./nai/local-prefix-len"));
}
if (yang_dnode_exists(dnode, "./nai/local-interface")) {
vty_out(vty, " iface %s",
yang_dnode_get_string(dnode,
"./nai/local-interface"));
}
if (yang_dnode_exists(dnode, "./nai/algorithm")) {
vty_out(vty, " algorithm %s",
yang_dnode_get_string(dnode,
"./nai/algorithm"));
}
}
vty_out(vty, "\n");
}
@ -1038,6 +1260,7 @@ int config_write_segment_routing(struct vty *vty)
int config_write_traffic_eng(struct vty *vty)
{
vty_out(vty, " traffic-eng\n");
path_ted_config_write(vty);
return 1;
}

View File

@ -33,6 +33,7 @@
#include "path_nb.h"
#include "path_zebra.h"
#include "path_errors.h"
#include "path_ted.h"
char backup_config_file[256];
@ -70,6 +71,8 @@ static void sighup(void)
static void sigint(void)
{
zlog_notice("Terminating on signal");
zlog_notice("Unregisterfrom opaque,etc ");
pathd_shutdown();
exit(0);
}
@ -146,6 +149,7 @@ int main(int argc, char **argv, char **envp)
path_error_init();
path_zebra_init(master);
path_cli_init();
path_ted_init(master);
frr_config_fork();
frr_run(master);

View File

@ -90,6 +90,7 @@ const struct frr_yang_module_info frr_pathd_info = {
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/sid-value",
.cbs = {
.modify = pathd_srte_segment_list_segment_sid_value_modify,
.destroy = pathd_srte_segment_list_segment_sid_value_destroy,
},
.priority = NB_DFLT_PRIORITY - 1
},
@ -114,6 +115,10 @@ const struct frr_yang_module_info frr_pathd_info = {
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-interface",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-prefix-len",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-address",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
@ -122,6 +127,10 @@ const struct frr_yang_module_info frr_pathd_info = {
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-interface",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/algorithm",
.cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
},
{
.xpath = "/frr-pathd:pathd/srte/policy",
.cbs = {

View File

@ -43,6 +43,8 @@ int pathd_srte_segment_list_segment_nai_destroy(
struct nb_cb_destroy_args *args);
void pathd_srte_segment_list_segment_nai_apply_finish(
struct nb_cb_apply_finish_args *args);
int pathd_srte_segment_list_segment_sid_value_destroy(
struct nb_cb_destroy_args *args);
int pathd_srte_policy_create(struct nb_cb_create_args *args);
int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args);
const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args);

View File

@ -160,6 +160,22 @@ int pathd_srte_segment_list_segment_sid_value_modify(
return NB_OK;
}
int pathd_srte_segment_list_segment_sid_value_destroy(
struct nb_cb_destroy_args *args)
{
struct srte_segment_entry *segment;
if (args->event != NB_EV_APPLY)
return NB_OK;
segment = nb_running_get_entry(args->dnode, NULL, true);
segment->sid_value = MPLS_LABEL_NONE;
SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED);
return NB_OK;
}
int pathd_srte_segment_list_segment_nai_destroy(struct nb_cb_destroy_args *args)
{
struct srte_segment_entry *segment;
@ -184,6 +200,8 @@ void pathd_srte_segment_list_segment_nai_apply_finish(
enum srte_segment_nai_type type;
struct ipaddr local_addr, remote_addr;
uint32_t local_iface = 0, remote_iface = 0;
uint8_t algo = 0, local_prefix_len = 0;
const char *algo_buf, *local_prefix_len_buf;
segment = nb_running_get_entry(args->dnode, NULL, true);
type = yang_dnode_get_enum(args->dnode, "./type");
@ -207,12 +225,31 @@ void pathd_srte_segment_list_segment_nai_apply_finish(
remote_iface = yang_dnode_get_uint32(args->dnode,
"./remote-interface");
break;
case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
algo_buf = yang_dnode_get_string(args->dnode, "./algorithm");
algo = atoi(algo_buf);
local_prefix_len_buf = yang_dnode_get_string(
args->dnode, "./local-prefix-len");
local_prefix_len = atoi(local_prefix_len_buf);
break;
case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
local_iface =
yang_dnode_get_uint32(args->dnode, "./local-interface");
local_prefix_len_buf = yang_dnode_get_string(
args->dnode, "./local-prefix-len");
local_prefix_len = atoi(local_prefix_len_buf);
break;
default:
break;
}
srte_segment_entry_set_nai(segment, type, &local_addr, local_iface,
&remote_addr, remote_iface);
zlog_debug(" Segment list name (%d) index (%s) ", segment->index,
segment->segment_list->name);
if (srte_segment_entry_set_nai(segment, type, &local_addr, local_iface,
&remote_addr, remote_iface, algo,
local_prefix_len))
SET_FLAG(segment->segment_list->flags,
F_SEGMENT_LIST_SID_CONFLICT);
}
/*

View File

@ -246,6 +246,10 @@ path_pcep_config_list_path_hops(struct srte_segment_list *segment_list)
switch (segment->nai_type) {
case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
memcpy(&hop->nai.local_addr, &segment->nai_local_addr,
sizeof(struct ipaddr));
break;
@ -278,6 +282,7 @@ int path_pcep_config_update_path(struct path *path)
assert(path->nbkey.preference != 0);
assert(path->nbkey.endpoint.ipa_type == IPADDR_V4);
int number_of_sid_clashed = 0;
struct path_hop *hop;
struct path_metric *metric;
int index;
@ -297,40 +302,44 @@ int path_pcep_config_update_path(struct path *path)
if (candidate->lsp->segment_list) {
SET_FLAG(candidate->lsp->segment_list->flags,
F_SEGMENT_LIST_DELETED);
srte_segment_list_del(candidate->lsp->segment_list);
candidate->lsp->segment_list = NULL;
}
if (path->first_hop != NULL) {
snprintf(segment_list_name_buff, sizeof(segment_list_name_buff),
"%s-%u", path->name, path->plsp_id);
segment_list_name = segment_list_name_buff;
if (path->first_hop == NULL)
return PATH_NB_ERR;
segment_list = srte_segment_list_add(segment_list_name);
segment_list->protocol_origin = path->update_origin;
strlcpy(segment_list->originator, path->originator,
sizeof(segment_list->originator));
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
snprintf(segment_list_name_buff, sizeof(segment_list_name_buff),
"%s-%u", path->name, path->plsp_id);
segment_list_name = segment_list_name_buff;
for (hop = path->first_hop, index = 10; hop != NULL;
hop = hop->next, index += 10) {
assert(hop->has_sid);
assert(hop->is_mpls);
segment_list = srte_segment_list_add(segment_list_name);
segment_list->protocol_origin = path->update_origin;
strlcpy(segment_list->originator, path->originator,
sizeof(segment_list->originator));
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
segment = srte_segment_entry_add(segment_list, index);
for (hop = path->first_hop, index = 10; hop != NULL;
hop = hop->next, index += 10) {
assert(hop->has_sid);
assert(hop->is_mpls);
segment->sid_value = (mpls_label_t)hop->sid.mpls.label;
SET_FLAG(segment->segment_list->flags,
F_SEGMENT_LIST_MODIFIED);
segment = srte_segment_entry_add(segment_list, index);
if (hop->has_nai)
srte_segment_entry_set_nai(
segment, srte_nai_type(hop->nai.type),
&hop->nai.local_addr,
hop->nai.local_iface,
&hop->nai.remote_addr,
hop->nai.remote_iface);
}
segment->sid_value = (mpls_label_t)hop->sid.mpls.label;
SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED);
if (!hop->has_nai)
continue;
if (srte_segment_entry_set_nai(
segment, srte_nai_type(hop->nai.type),
&hop->nai.local_addr, hop->nai.local_iface,
&hop->nai.remote_addr, hop->nai.remote_iface, 0, 0)
== PATH_SID_ERROR)
/* TED queries don't match PCE */
/* Don't apply srte,zebra changes */
number_of_sid_clashed++;
}
candidate->lsp->segment_list = segment_list;
@ -352,7 +361,11 @@ int path_pcep_config_update_path(struct path *path)
candidate->lsp->objfun = path->pce_objfun;
}
srte_apply_changes();
if (number_of_sid_clashed)
SET_FLAG(segment->segment_list->flags,
F_SEGMENT_LIST_SID_CONFLICT);
else
srte_apply_changes();
return 0;
}
@ -402,6 +415,16 @@ enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type)
return PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY;
case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
return PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY;
case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES:
return PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY;
case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
return PCEP_SR_SUBOBJ_NAI_IPV4_NODE;
case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
return PCEP_SR_SUBOBJ_NAI_IPV6_NODE;
case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
return PCEP_SR_SUBOBJ_NAI_IPV4_NODE;
case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
return PCEP_SR_SUBOBJ_NAI_IPV6_NODE;
default:
return PCEP_SR_SUBOBJ_NAI_UNKNOWN;
}

726
pathd/path_ted.c Normal file
View File

@ -0,0 +1,726 @@
/*
* Copyright (C) 2020 Volta Networks, 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 Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stdlib.h"
#include <zebra.h>
#include "memory.h"
#include "log.h"
#include "command.h"
#include "prefix.h"
#include <lib/json.h>
#include "pathd.h"
#include "pathd/path_errors.h"
#include "pathd/path_ted.h"
#ifndef VTYSH_EXTRACT_PL
#include "pathd/path_ted_clippy.c"
#endif
static struct ls_ted *path_ted_create_ted(void);
static void path_ted_register_vty(void);
static void path_ted_unregister_vty(void);
static uint32_t path_ted_start_importing_igp(const char *daemon_str);
static uint32_t path_ted_stop_importing_igp(void);
static enum zclient_send_status path_ted_link_state_sync(void);
static int path_ted_timer_handler_sync(struct thread *thread);
static int path_ted_timer_handler_refresh(struct thread *thread);
static int path_ted_cli_debug_config_write(struct vty *vty);
static int path_ted_cli_debug_set_all(uint32_t flags, bool set);
extern struct zclient *zclient;
struct ted_state ted_state_g = {};
/*
* path_path_ted public API function implementations
*/
void path_ted_init(struct thread_master *master)
{
ted_state_g.main = master;
ted_state_g.link_state_delay_interval = TIMER_RETRY_DELAY;
ted_state_g.segment_list_refresh_interval = TIMER_RETRY_DELAY;
path_ted_register_vty();
path_ted_segment_list_refresh();
}
uint32_t path_ted_teardown(void)
{
PATH_TED_DEBUG("%s : TED [%p]", __func__, ted_state_g.ted);
path_ted_unregister_vty();
path_ted_stop_importing_igp();
ls_ted_del_all(ted_state_g.ted);
path_ted_timer_sync_cancel();
path_ted_timer_refresh_cancel();
return 0;
}
/**
* Set all needed to receive igp data.
*
* @return true if ok
*
*/
uint32_t path_ted_start_importing_igp(const char *daemon_str)
{
uint32_t status = 0;
if (strcmp(daemon_str, "ospfv2") == 0)
ted_state_g.import = IMPORT_OSPFv2;
else if (strcmp(daemon_str, "ospfv3") == 0) {
ted_state_g.import = IMPORT_UNKNOWN;
return 1;
} else if (strcmp(daemon_str, "isis") == 0)
ted_state_g.import = IMPORT_ISIS;
else {
ted_state_g.import = IMPORT_UNKNOWN;
return 1;
}
if (ls_register(zclient, false /*client*/) != 0) {
PATH_TED_ERROR("%s: PATHD-TED: Unable to register Link State",
__func__);
ted_state_g.import = IMPORT_UNKNOWN;
status = 1;
} else {
if (path_ted_link_state_sync() != -1) {
PATH_TED_DEBUG("%s: PATHD-TED: Importing %s data ON",
__func__,
PATH_TED_IGP_PRINT(ted_state_g.import));
} else {
PATH_TED_WARN("%s: PATHD-TED: Importing %s data OFF",
__func__,
PATH_TED_IGP_PRINT(ted_state_g.import));
ted_state_g.import = IMPORT_UNKNOWN;
}
}
return status;
}
/**
* Unset all needed to receive igp data.
*
* @return true if ok
*
*/
uint32_t path_ted_stop_importing_igp(void)
{
uint32_t status = 0;
if (ted_state_g.import != IMPORT_UNKNOWN) {
if (ls_unregister(zclient, false /*client*/) != 0) {
PATH_TED_ERROR(
"%s: PATHD-TED: Unable to unregister Link State",
__func__);
status = 1;
} else {
ted_state_g.import = IMPORT_UNKNOWN;
PATH_TED_DEBUG("%s: PATHD-TED: Importing igp data OFF",
__func__);
}
path_ted_timer_sync_cancel();
}
return status;
}
/**
* Check for ted status
*
* @return true if ok
*
*/
bool path_ted_is_initialized(void)
{
if (ted_state_g.ted == NULL) {
PATH_TED_WARN("PATHD TED ls_ted not initialized");
return false;
}
return true;
}
/**
* Creates an empty ted
*
* @param void
*
* @return Ptr to ted or NULL
*/
struct ls_ted *path_ted_create_ted()
{
struct ls_ted *ted = ls_ted_new(TED_KEY, TED_NAME, TED_ASN);
if (ted == NULL) {
PATH_TED_ERROR("%s Unable to initialize TED Key [%d] ASN [%d] Name [%s]",
__func__, TED_KEY, TED_ASN, TED_NAME);
} else {
PATH_TED_INFO("%s Initialize TED Key [%d] ASN [%d] Name [%s]",
__func__, TED_KEY, TED_ASN, TED_NAME);
}
return ted;
}
uint32_t path_ted_rcvd_message(struct ls_message *msg)
{
if (!path_ted_is_initialized())
return 1;
if (msg == NULL) {
PATH_TED_ERROR("%s: [rcv ted] TED received NULL message ",
__func__);
return 1;
}
if (path_ted_get_current_igp(msg->data.node->adv.origin))
return 1;
switch (msg->type) {
case LS_MSG_TYPE_NODE:
ls_msg2vertex(ted_state_g.ted, msg, true /*hard delete*/);
break;
case LS_MSG_TYPE_ATTRIBUTES:
ls_msg2edge(ted_state_g.ted, msg, true /*ĥard delete*/);
break;
case LS_MSG_TYPE_PREFIX:
ls_msg2subnet(ted_state_g.ted, msg, true /*hard delete*/);
break;
default:
PATH_TED_DEBUG(
"%s: [rcv ted] TED received unknown message type [%d]",
__func__, msg->type);
break;
}
return 0;
}
uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote)
{
uint32_t sid = MPLS_LABEL_NONE;
struct ls_edge *edge;
uint64_t key;
if (!path_ted_is_initialized())
return MPLS_LABEL_NONE;
if (!local || !remote)
return MPLS_LABEL_NONE;
switch (local->ipa_type) {
case IPADDR_V4:
/* We have local and remote ip */
/* so check all attributes in ted */
key = ((uint64_t)ntohl(local->ip._v4_addr.s_addr)) & 0xffffffff;
edge = ls_find_edge_by_key(ted_state_g.ted, key);
if (edge) {
if (edge->attributes->standard.remote.s_addr
== remote->ip._v4_addr.s_addr
&& CHECK_FLAG(edge->attributes->flags,
LS_ATTR_ADJ_SID)) {
sid = edge->attributes->adj_sid[0]
.sid; /* from primary */
break;
}
}
break;
case IPADDR_V6:
key = (uint64_t)(local->ip._v6_addr.s6_addr32[0] & 0xffffffff)
| ((uint64_t)local->ip._v6_addr.s6_addr32[1] << 32);
edge = ls_find_edge_by_key(ted_state_g.ted, key);
if (edge) {
if ((memcmp(&edge->attributes->standard.remote6,
&remote->ip._v6_addr,
sizeof(remote->ip._v6_addr))
&& CHECK_FLAG(edge->attributes->flags,
LS_ATTR_ADJ_SID))) {
sid = edge->attributes->adj_sid[0]
.sid; /* from primary */
break;
}
}
break;
case IPADDR_NONE:
break;
}
return sid;
}
uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo)
{
uint32_t sid = MPLS_LABEL_NONE;
struct ls_subnet *subnet;
if (!path_ted_is_initialized())
return MPLS_LABEL_NONE;
if (!prefix)
return MPLS_LABEL_NONE;
switch (prefix->family) {
case AF_INET:
case AF_INET6:
subnet = ls_find_subnet(ted_state_g.ted, *prefix);
if (subnet) {
if ((CHECK_FLAG(subnet->ls_pref->flags, LS_PREF_SR))
&& (subnet->ls_pref->sr.algo == algo))
sid = subnet->ls_pref->sr.sid;
}
break;
default:
break;
}
return sid;
}
uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id)
{
uint32_t sid = MPLS_LABEL_NONE;
struct ls_subnet *subnet;
struct listnode *lst_node;
struct ls_edge *edge;
if (!path_ted_is_initialized())
return MPLS_LABEL_NONE;
if (!prefix)
return MPLS_LABEL_NONE;
switch (prefix->family) {
case AF_INET:
case AF_INET6:
subnet = ls_find_subnet(ted_state_g.ted, *prefix);
if (subnet && subnet->vertex
&& subnet->vertex->outgoing_edges) {
/* from the vertex linked in subnet */
/* loop over outgoing edges */
for (ALL_LIST_ELEMENTS_RO(
subnet->vertex->outgoing_edges, lst_node,
edge)) {
/* and look for ifaceid */
/* so get sid of attribute */
if (CHECK_FLAG(edge->attributes->flags,
LS_ATTR_LOCAL_ID)
&& edge->attributes->standard.local_id
== iface_id) {
sid = subnet->ls_pref->sr.sid;
break;
}
}
}
break;
default:
break;
}
return sid;
}
DEFPY (debug_path_ted,
debug_path_ted_cmd,
"[no] debug pathd mpls-te",
NO_STR
DEBUG_STR
"path debugging\n"
"ted debugging\n")
{
uint32_t mode = DEBUG_NODE2MODE(vty->node);
bool no_debug = (no != NULL);
DEBUG_MODE_SET(&ted_state_g.dbg, mode, !no);
DEBUG_FLAGS_SET(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC, !no_debug);
return CMD_SUCCESS;
}
/*
* Followings are vty command functions.
*/
/* clang-format off */
DEFUN (path_ted_on,
path_ted_on_cmd,
"mpls-te on",
NO_STR
"Enable the TE database (TED) functionality\n")
/* clang-format on */
{
if (ted_state_g.enabled) {
PATH_TED_DEBUG("%s: PATHD-TED: Enabled ON -> ON.", __func__);
return CMD_SUCCESS;
}
ted_state_g.ted = path_ted_create_ted();
ted_state_g.enabled = true;
PATH_TED_DEBUG("%s: PATHD-TED: Enabled OFF -> ON.", __func__);
return CMD_SUCCESS;
}
/* clang-format off */
DEFUN (no_path_ted,
no_path_ted_cmd,
"no mpls-te [on]",
NO_STR
NO_STR
"Disable the TE Database functionality\n")
/* clang-format on */
{
if (ted_state_g.enabled) {
PATH_TED_DEBUG("%s: PATHD-TED: OFF -> OFF", __func__);
return CMD_SUCCESS;
}
/* Remove TED */
ls_ted_del_all(ted_state_g.ted);
ted_state_g.enabled = false;
PATH_TED_DEBUG("%s: PATHD-TED: ON -> OFF", __func__);
ted_state_g.import = IMPORT_UNKNOWN;
if (ls_unregister(zclient, false /*client*/) != 0) {
vty_out(vty, "Unable to unregister Link State\n");
return CMD_WARNING;
}
return CMD_SUCCESS;
}
/* clang-format off */
DEFPY(path_ted_import,
path_ted_import_cmd,
"mpls-te import <ospfv2|ospfv3|isis>$import_daemon",
"Enable the TE database (TED) fill with remote igp data\n"
"import\n"
"Origin ospfv2\n"
"Origin ospfv3\n"
"Origin isis\n")
/* clang-format on */
{
if (ted_state_g.enabled)
if (path_ted_start_importing_igp(import_daemon)) {
vty_out(vty, "Unable to start importing\n");
return CMD_WARNING;
}
return CMD_SUCCESS;
}
/* clang-format off */
DEFUN (no_path_ted_import,
no_path_ted_import_cmd,
"no mpls-te import",
NO_STR
NO_STR
"Disable the TE Database fill with remote igp data\n")
/* clang-format on */
{
if (ted_state_g.import) {
if (path_ted_stop_importing_igp()) {
vty_out(vty, "Unable to stop importing\n");
return CMD_WARNING;
} else {
PATH_TED_DEBUG(
"%s: PATHD-TED: Importing igp data already OFF",
__func__);
}
}
return CMD_SUCCESS;
}
/* clang-format off */
DEFPY (show_pahtd_ted_db,
show_pathd_ted_db_cmd,
"show pathd ted database <verbose|json>$ver_json ",
"show command\n"
"pathd daemon\n"
"traffic eng\n"
"database\n"
"verbose output\n"
"Show complete received TED database\n")
/* clang-format on */
{
bool st_json = false;
json_object *json = NULL;
if (!ted_state_g.enabled) {
vty_out(vty, "PATHD TED database is not enabled\n");
return CMD_WARNING;
}
if (strcmp(ver_json, "json") == 0) {
st_json = true;
json = json_object_new_object();
}
/* Show the complete TED */
ls_show_ted(ted_state_g.ted, vty, json, !st_json);
if (st_json) {
vty_out(vty, "%s\n",
json_object_to_json_string_ext(
json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
return CMD_SUCCESS;
}
/*
* Config Write functions
*/
int path_ted_cli_debug_config_write(struct vty *vty)
{
if (DEBUG_MODE_CHECK(&ted_state_g.dbg, DEBUG_MODE_CONF)) {
if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC))
vty_out(vty, "debug pathd mpls-te\n");
return 1;
}
return 0;
}
int path_ted_cli_debug_set_all(uint32_t flags, bool set)
{
DEBUG_FLAGS_SET(&ted_state_g.dbg, flags, set);
/* If all modes have been turned off, don't preserve options. */
if (!DEBUG_MODE_CHECK(&ted_state_g.dbg, DEBUG_MODE_ALL))
DEBUG_CLEAR(&ted_state_g.dbg);
return 0;
}
/**
* Help fn to show ted related configuration
*
* @param vty
*
* @return Status
*/
uint32_t path_ted_config_write(struct vty *vty)
{
if (ted_state_g.enabled) {
vty_out(vty, " mpls-te on\n");
switch (ted_state_g.import) {
case IMPORT_ISIS:
vty_out(vty, " mpls-te import isis\n");
break;
case IMPORT_OSPFv2:
vty_out(vty, " mpls-te import ospfv2\n");
break;
case IMPORT_OSPFv3:
vty_out(vty, " mpls-te import ospfv3\n");
break;
default:
break;
}
}
return 0;
}
/**
* Register the fn's for CLI and hook for config show
*
* @param void
*
*/
static void path_ted_register_vty(void)
{
install_element(VIEW_NODE, &show_pathd_ted_db_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &path_ted_on_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &path_ted_import_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_import_cmd);
install_element(CONFIG_NODE, &debug_path_ted_cmd);
install_element(ENABLE_NODE, &debug_path_ted_cmd);
hook_register(nb_client_debug_config_write,
path_ted_cli_debug_config_write);
hook_register(nb_client_debug_set_all, path_ted_cli_debug_set_all);
}
/**
* UnRegister the fn's for CLI and hook for config show
*
* @param void
*
*/
static void path_ted_unregister_vty(void)
{
uninstall_element(VIEW_NODE, &show_pathd_ted_db_cmd);
uninstall_element(SR_TRAFFIC_ENG_NODE, &path_ted_on_cmd);
uninstall_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_cmd);
uninstall_element(SR_TRAFFIC_ENG_NODE, &path_ted_import_cmd);
uninstall_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_import_cmd);
}
/**
* Ask igp for a complete TED so far
*
* @param void
*
* @return zclient status
*/
enum zclient_send_status path_ted_link_state_sync(void)
{
enum zclient_send_status status;
status = ls_request_sync(zclient);
if (status == -1) {
PATH_TED_ERROR(
"%s: PATHD-TED: Opaque error asking for TED sync ",
__func__);
return status;
} else {
PATH_TED_DEBUG("%s: PATHD-TED: Opaque asked for TED sync ",
__func__);
}
thread_add_timer(ted_state_g.main, path_ted_timer_handler_sync,
&ted_state_g, ted_state_g.link_state_delay_interval,
&ted_state_g.t_link_state_sync);
return status;
}
/**
* Timer cb for check link state sync
*
* @param thread Current thread
*
* @return status
*/
int path_ted_timer_handler_sync(struct thread *thread)
{
/* data unpacking */
struct ted_state *data = THREAD_ARG(thread);
assert(data != NULL);
/* Retry the sync */
return path_ted_link_state_sync();
}
/**
* refresg segment list and create timer to keep up updated
*
* @param void
*
* @return status
*/
int path_ted_segment_list_refresh(void)
{
int status = 0;
path_ted_timer_refresh_cancel();
thread_add_timer(ted_state_g.main, path_ted_timer_handler_refresh,
&ted_state_g,
ted_state_g.segment_list_refresh_interval,
&ted_state_g.t_segment_list_refresh);
return status;
}
/**
* Timer cb for refreshing sid in segment lists
*
* @param void
*
* @return status
*/
int path_ted_timer_handler_refresh(struct thread *thread)
{
if (!path_ted_is_initialized())
return MPLS_LABEL_NONE;
PATH_TED_DEBUG("%s: PATHD-TED: Refresh sid from current TED", __func__);
/* data unpacking */
struct ted_state *data = THREAD_ARG(thread);
assert(data != NULL);
srte_policy_update_ted_sid();
return 0;
}
/**
* Cancel sync timer
*
* @param void
*
* @return void status
*/
void path_ted_timer_sync_cancel(void)
{
if (ted_state_g.t_link_state_sync != NULL) {
thread_cancel(&ted_state_g.t_link_state_sync);
ted_state_g.t_link_state_sync = NULL;
}
}
/**
* Cancel refresh timer
*
* @param void
*
* @return void status
*/
void path_ted_timer_refresh_cancel(void)
{
if (ted_state_g.t_segment_list_refresh != NULL) {
thread_cancel(&ted_state_g.t_segment_list_refresh);
ted_state_g.t_segment_list_refresh = NULL;
}
}
/**
* Check which igp is configured
*
* @param igp who want to check against config-
*
* @return status
*/
uint32_t path_ted_get_current_igp(uint32_t igp)
{
switch (igp) {
case ISIS_L1:
case ISIS_L2:
if (ted_state_g.import != IMPORT_ISIS) {
PATH_TED_ERROR(
"%s: [rcv ted] Incorrect igp origin wait (%s) got (%s) ",
__func__,
PATH_TED_IGP_PRINT(ted_state_g.import),
LS_IGP_PRINT(igp));
return 1;
}
break;
case OSPFv2:
if (ted_state_g.import != IMPORT_OSPFv2) {
PATH_TED_ERROR(
"%s: [rcv ted] Incorrect igp origin wait (%s) got (%s) ",
__func__,
PATH_TED_IGP_PRINT(ted_state_g.import),
LS_IGP_PRINT(igp));
return 1;
}
break;
case STATIC:
break;
}
return 0;
}

178
pathd/path_ted.h Normal file
View File

@ -0,0 +1,178 @@
/*
* Copyright (C) 2020 Volta Networks, 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 Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#ifndef _PATH_TED_H
#define _PATH_TED_H
#ifdef __cplusplus
extern "C" {
#endif
#include <zebra.h>
#include <stdbool.h>
#include <debug.h>
#include "linklist.h"
#include "log.h"
#include "command.h"
#include "stream.h"
#include "prefix.h"
#include "zclient.h"
#include "link_state.h"
extern struct ted_state ted_state_g;
#define TIMER_RETRY_DELAY 5 /* Timeout in seconds between ls sync request */
#define TED_KEY 1
#define TED_ASN 1
#define TED_NAME "PATHD TED"
enum igp_import {
IMPORT_UNKNOWN = 0,
IMPORT_ISIS,
IMPORT_OSPFv2,
IMPORT_OSPFv3
};
struct ted_state {
struct thread_master *main;
/* Status of TED: enable or disable */
bool enabled;
/* From which igp is going to receive data */
enum igp_import import;
/* The TED itself as in link_state.h */
struct ls_ted *ted;
/* Timer for ted sync */
struct thread *t_link_state_sync;
/* Timer for refresh sid in segment list */
struct thread *t_segment_list_refresh;
/* delay interval in seconds */
uint32_t link_state_delay_interval;
/* delay interval refresh in seconds */
uint32_t segment_list_refresh_interval;
struct debug dbg;
};
/* Debug flags. */
#define PATH_TED_DEBUG_BASIC 0x01
#define PATH_TED_DEBUG(fmt, ...) \
do { \
if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
DEBUGD(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
} while (0)
#define PATH_TED_ERROR(fmt, ...) \
do { \
if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
DEBUGE(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
} while (0)
#define PATH_TED_WARN(fmt, ...) \
do { \
if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
DEBUGW(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
} while (0)
#define PATH_TED_INFO(fmt, ...) \
do { \
if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
DEBUGI(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
} while (0)
/* TED management functions */
bool path_ted_is_initialized(void);
void path_ted_init(struct thread_master *master);
uint32_t path_ted_teardown(void);
void path_ted_timer_sync_cancel(void);
void path_ted_timer_refresh_cancel(void);
int path_ted_segment_list_refresh(void);
/* TED configuration functions */
uint32_t path_ted_config_write(struct vty *vty);
/* TED util functions */
/* clang-format off */
#define LS_MSG_EVENT_PRINT(event) event == LS_MSG_EVENT_ADD?"add"\
: event == LS_MSG_EVENT_DELETE?"del"\
: event == LS_MSG_EVENT_UPDATE?"upd"\
: event == LS_MSG_EVENT_SYNC?"syn"\
: event == LS_MSG_EVENT_SYNC?"und" : "none"
#define LS_MSG_TYPE_PRINT(type) type == LS_MSG_TYPE_NODE?"node"\
: type == LS_MSG_TYPE_ATTRIBUTES?"att"\
: type == LS_MSG_TYPE_PREFIX?"pre" : "none"
#define LS_IGP_PRINT(type) type == ISIS_L1?"ISIS_L1"\
: type == ISIS_L2?"ISIS_L2"\
: type == DIRECT?"DIRECT"\
: type == STATIC?"STATIC"\
: type == OSPFv2?"OSPFv2" : "none"
#define PATH_TED_IGP_PRINT(type) type == IMPORT_OSPFv2?"OSPFv2"\
: type == IMPORT_OSPFv3?"OSPFv3"\
: type == IMPORT_ISIS?"ISIS" : "none"
/* clang-format on */
uint32_t path_ted_get_current_igp(uint32_t);
/* TED Query functions */
/*
* Type of queries from draft-ietf-spring-segment-routing-policy-07 for types
* f,c,e
*/
/**
* Search for sid based in prefix and optional algo
*
* @param prefix Net prefix to resolv
* @param algo Algorithm for link state
*
* @return sid of attribute
*/
uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo);
/**
* Search for sid based in prefix and interface id
*
* @param prefix Net prefix to resolv
* @param iface_id The interface id
*
* @return sid of attribute
*/
uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id);
/**
* Search for sid based in local, remote pair
*
* @param local local ip of attribute
* @param remote remote ip of attribute
*
* @return sid of attribute
*/
uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote);
/**
* Handle the received opaque msg
*
* @param msg Holds the ted data
*
* @return sid of attribute
*/
uint32_t path_ted_rcvd_message(struct ls_message *msg);
#ifdef __cplusplus
}
#endif
#endif /* _PATH_TED_H */

View File

@ -32,9 +32,14 @@
#include "typesafe.h"
#include "pathd/pathd.h"
#include "pathd/path_ted.h"
#include "pathd/path_zebra.h"
#include "lib/command.h"
#include "lib/link_state.h"
static struct zclient *zclient;
static int path_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS);
struct zclient *zclient;
static struct zclient *zclient_sync;
/* Global Variables */
@ -267,6 +272,54 @@ static void path_zebra_label_manager_connect(void)
}
}
static int path_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
{
int ret = 0;
struct stream *s;
struct zapi_opaque_msg info;
s = zclient->ibuf;
if (zclient_opaque_decode(s, &info) != 0)
return -1;
switch (info.type) {
case LINK_STATE_UPDATE:
case LINK_STATE_SYNC:
/* Start receiving ls data so cancel request sync timer */
path_ted_timer_sync_cancel();
struct ls_message *msg = ls_parse_msg(s);
if (msg) {
zlog_debug("%s: [rcv ted] ls (%s) msg (%s)-(%s) !",
__func__,
info.type == LINK_STATE_UPDATE
? "LINK_STATE_UPDATE"
: "LINK_STATE_SYNC",
LS_MSG_TYPE_PRINT(msg->type),
LS_MSG_EVENT_PRINT(msg->event));
} else {
zlog_err(
"%s: [rcv ted] Could not parse LinkState stream message.",
__func__);
return -1;
}
ret = path_ted_rcvd_message(msg);
ls_delete_msg(msg);
/* Update local configuration after process update. */
path_ted_segment_list_refresh();
break;
default:
zlog_debug("%s: [rcv ted] unknown opaque event (%d) !",
__func__, info.type);
break;
}
return ret;
}
/**
* Initializes Zebra asynchronous connection.
*
@ -283,6 +336,7 @@ void path_zebra_init(struct thread_master *master)
zclient->zebra_connected = path_zebra_connected;
zclient->sr_policy_notify_status = path_zebra_sr_policy_notify_status;
zclient->router_id_update = path_zebra_router_id_update;
zclient->opaque_msg_handler = path_zebra_opaque_msg_handler;
/* Initialize special zclient for synchronous message exchanges. */
zclient_sync = zclient_new(master, &options);

View File

@ -26,6 +26,7 @@
#include "pathd/pathd.h"
#include "pathd/path_zebra.h"
#include "pathd/path_debug.h"
#include "pathd/path_ted.h"
#define HOOK_DELAY 3
@ -188,14 +189,20 @@ void srte_segment_entry_del(struct srte_segment_entry *segment)
* @param type The remote address of the adjacency
* @param type The remote interface index of the unumbered adjacency
*/
void 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)
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};
segment->nai_type = type;
memcpy(&segment->nai_local_addr, local_ip, sizeof(struct ipaddr));
if (!segment || !local_ip || !remote_ip)
return 1;
switch (type) {
case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
@ -204,6 +211,7 @@ void srte_segment_entry_set_nai(struct srte_segment_entry *segment,
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,
@ -211,12 +219,68 @@ void srte_segment_entry_set_nai(struct srte_segment_entry *segment,
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);
}
}
/**
@ -287,6 +351,105 @@ struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *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.
*
@ -322,13 +485,23 @@ srte_policy_best_candidate(const struct srte_policy *policy)
&policy->candidate_paths) {
/* search for highest preference with existing segment list */
if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED)
&& candidate->lsp->segment_list)
&& 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);
}
/**
* Apply changes defined by setting the policies, candidate paths
* and segment lists modification flags NEW, MODIFIED and DELETED.
@ -526,6 +699,7 @@ void srte_candidate_set_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 %sconfig bandwidth set to %f B/s",
@ -1024,6 +1198,12 @@ const char *srte_origin2str(enum srte_protocol_origin origin)
}
}
void pathd_shutdown(void)
{
path_ted_teardown();
srte_clean_zebra();
}
void trigger_pathd_candidate_created(struct srte_candidate *candidate)
{
/* The hook is called asynchronously to let the PCEP module
@ -1124,3 +1304,91 @@ const char *srte_candidate_metric_name(enum srte_candidate_metric_type type)
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: Sucess 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: Sucess 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: Sucess 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;
}

View File

@ -24,6 +24,13 @@
#include "lib/ipaddr.h"
#include "lib/srte.h"
#include "lib/hook.h"
#include "lib/prefix.h"
#define PATH_SID_ERROR 1
#define PATH_SID_NO_ERROR 0
#define CHECK_SID(or, ts, es) \
((or == SRTE_ORIGIN_PCEP && (ts == MPLS_LABEL_NONE || es != ts)) \
|| (or == SRTE_ORIGIN_LOCAL && ts == MPLS_LABEL_NONE))
DECLARE_MGROUP(PATHD);
@ -100,7 +107,12 @@ enum srte_segment_nai_type {
SRTE_SEGMENT_NAI_TYPE_IPV6_NODE = 2,
SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY = 3,
SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY = 4,
SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY = 5
SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY = 5,
SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES = 6,
SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE = 7,
SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE = 8,
SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM = 9,
SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM = 10
};
enum objfun_type {
@ -175,6 +187,9 @@ struct srte_segment_entry {
/* NAI remote interface when nai type is not IPv4 unnumbered adjacency
*/
uint32_t nai_remote_iface;
/* Support draft-ietf-spring-segment-routing-policy sl types queries*/
uint8_t nai_local_prefix_len;
uint8_t nai_algorithm;
};
RB_HEAD(srte_segment_entry_head, srte_segment_entry);
RB_PROTOTYPE(srte_segment_entry_head, srte_segment_entry, entry,
@ -200,6 +215,7 @@ struct srte_segment_list {
#define F_SEGMENT_LIST_NEW 0x0002
#define F_SEGMENT_LIST_MODIFIED 0x0004
#define F_SEGMENT_LIST_DELETED 0x0008
#define F_SEGMENT_LIST_SID_CONFLICT 0x0010
};
RB_HEAD(srte_segment_list_head, srte_segment_list);
RB_PROTOTYPE(srte_segment_list_head, srte_segment_list, entry,
@ -361,17 +377,22 @@ struct srte_segment_list *srte_segment_list_find(const char *name);
struct srte_segment_entry *
srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index);
void srte_segment_entry_del(struct srte_segment_entry *segment);
void 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);
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);
void srte_segment_set_local_modification(struct srte_segment_list *s_list,
struct srte_segment_entry *s_entry,
uint32_t ted_sid);
struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint);
void srte_policy_del(struct srte_policy *policy);
struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint);
int srte_policy_update_ted_sid(void);
void srte_policy_update_binding_sid(struct srte_policy *policy,
uint32_t binding_sid);
void srte_apply_changes(void);
void srte_clean_zebra(void);
void srte_policy_apply_changes(struct srte_policy *policy);
struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
uint32_t preference);
@ -408,8 +429,45 @@ srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index);
void srte_candidate_status_update(struct srte_candidate *candidate, int status);
void srte_candidate_unset_segment_list(const char *originator, bool force);
const char *srte_origin2str(enum srte_protocol_origin origin);
void pathd_shutdown(void);
/* path_cli.c */
void path_cli_init(void);
/**
* Search for sid based in prefix and algorithm
*
* @param Prefix The prefix to use
* @param algo Algorithm we want to query for
* @param ted_sid Sid to query
*
* @return void
*/
int32_t srte_ted_do_query_type_c(struct srte_segment_entry *entry,
struct prefix *prefix_cli, uint32_t algo);
/**
* Search for sid based in prefix and interface id
*
* @param Prefix The prefix to use
* @param local_iface The id of interface
* @param ted_sid Sid to query
*
* @return void
*/
int32_t srte_ted_do_query_type_e(struct srte_segment_entry *entry,
struct prefix *prefix_cli,
uint32_t local_iface);
/**
* Search for sid based in local and remote ip
*
* @param entry entry to update
* @param local Local addr for query
* @param remote Local addr for query
*
* @return void
*/
int32_t srte_ted_do_query_type_f(struct srte_segment_entry *entry,
struct ipaddr *local, struct ipaddr *remote);
#endif /* _FRR_PATHD_H_ */

View File

@ -5,7 +5,10 @@
if PATHD
noinst_LIBRARIES += pathd/libpath.a
sbin_PROGRAMS += pathd/pathd
vtysh_scan += pathd/path_cli.c
vtysh_scan += \
pathd/path_cli.c \
pathd/path_ted.c \
#end
vtysh_daemons += pathd
# TODO add man page
#man8 += $(MANBUILD)/pathd.8
@ -24,6 +27,7 @@ pathd_libpath_a_SOURCES = \
pathd/path_nb.c \
pathd/path_nb_config.c \
pathd/path_nb_state.c \
pathd/path_ted.c \
pathd/path_zebra.c \
pathd/pathd.c \
# end
@ -31,6 +35,7 @@ pathd_libpath_a_SOURCES = \
clippy_scan += \
pathd/path_cli.c \
pathd/path_pcep_cli.c \
pathd/path_ted.c \
# end
noinst_HEADERS += \
@ -44,6 +49,7 @@ noinst_HEADERS += \
pathd/path_pcep_lib.h \
pathd/path_pcep_config.h \
pathd/path_pcep_pcc.h \
pathd/path_ted.h \
pathd/path_zebra.h \
pathd/pathd.h \
# end

View File

@ -0,0 +1,23 @@
log file zebra.log
!
hostname dst
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 9.9.9.2/32
ipv6 address 2001:db8:1066::2/128
!
interface eth-rt6
ip address 10.0.11.2/24
link-params
enable
exit-link-params
!
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,16 @@
log file bgpd.log
!
router bgp 1
bgp router-id 1.1.1.1
neighbor 6.6.6.6 remote-as 1
neighbor 6.6.6.6 update-source lo
!
address-family ipv4 unicast
redistribute static
neighbor 6.6.6.6 next-hop-self
neighbor 6.6.6.6 route-map SET_SR_POLICY in
exit-address-family
!
route-map SET_SR_POLICY permit 10
set sr-te color 1
!

View File

@ -0,0 +1,36 @@
password 1
hostname rt1
log file ospfd.log
!
debug ospf sr
debug ospf te
debug ospf event
debug ospf lsa
debug ospf zebra
!
interface lo
ip ospf area 0.0.0.0
!
interface eth-sw1
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
router ospf
ospf router-id 1.1.1.1
network 1.1.1.1/32 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
capability opaque
!ospf opaque-lsa
mpls-te on
mpls-te export
mpls-te router-address 1.1.1.1
router-info area 0.0.0.0
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
!segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 1.1.1.1/32 index 10
!

View File

@ -0,0 +1,27 @@
log file pathd.log
!
hostname rt1
!
segment-routing
traffic-eng
mpls-te on
mpls-te import ospfv2
segment-list default
index 10 nai adjacency 10.0.1.1 10.0.1.2
index 20 nai adjacency 10.0.2.2 10.0.2.4
index 30 nai adjacency 10.0.7.4 10.0.7.6
!
segment-list test
index 10 nai adjacency 10.0.1.1 10.0.1.2
index 20 nai adjacency 10.0.2.2 10.0.2.4
index 30 nai adjacency 10.0.6.4 10.0.6.5
index 40 nai adjacency 10.0.8.5 10.0.8.6
!
policy color 1 endpoint 6.6.6.6
name default
binding-sid 1111
!
!
!

View File

@ -0,0 +1,13 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": false
}
]
}
}
}

View File

@ -0,0 +1,20 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,20 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,25 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "6.6.6.6",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"discriminator": "*",
"is-best-candidate-path": false
},
{
"preference": 200,
"discriminator": "*",
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,21 @@
log file zebra.log
!
hostname rt1
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 1.1.1.1/32
!
interface eth-sw1
ip address 10.0.1.1/24
link-params
enable
exit-link-params
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,47 @@
hostname rt2
log file ospfd.log
!
debug ospf sr
debug ospf te
debug ospf event
debug ospf lsa
debug ospf zebra
!
interface lo
ip ospf area 0.0.0.0
!
interface eth-sw1
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt4-1
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt4-2
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
router ospf
ospf router-id 2.2.2.2
network 2.2.2.2/32 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
capability opaque
!ospf opaque-lsa
mpls-te on
!mpls-te export
mpls-te router-address 2.2.2.2
router-info area 0.0.0.0
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
!segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 2.2.2.2/32 index 20
!

View File

@ -0,0 +1,35 @@
log file zebra.log
!
hostname rt2
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 2.2.2.2/32
!
interface eth-sw1
ip address 10.0.1.2/24
link-params
enable
exit-link-params
!
interface eth-rt4-1
ip address 10.0.2.2/24
link-params
enable
exit-link-params
!
!
interface eth-rt4-2
ip address 10.0.3.2/24
link-params
enable
exit-link-params
!
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,46 @@
hostname rt3
log file ospfd.log
!
debug ospf sr
debug ospf te
debug ospf event
debug ospf lsa
debug ospf zebra
!
interface lo
ip ospf area 0.0.0.0
!
interface eth-sw1
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt5-1
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt5-2
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
router ospf
ospf router-id 3.3.3.3
network 3.3.3.3/32 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
capability opaque
!ospf opaque-lsa
mpls-te on
!mpls-te export
mpls-te router-address 3.3.3.3
router-info area 0.0.0.0
segment-routing on
segment-routing global-block 16000 23999
!segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 3.3.3.3/32 index 30
!

View File

@ -0,0 +1,33 @@
log file zebra.log
!
hostname rt3
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 3.3.3.3/32
!
interface eth-sw1
ip address 10.0.1.3/24
link-params
enable
exit-link-params
!!
interface eth-rt5-1
ip address 10.0.4.3/24
link-params
enable
exit-link-params
!!
interface eth-rt5-2
ip address 10.0.5.3/24
link-params
enable
exit-link-params
!!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,53 @@
hostname rt4
log file ospfd.log
!
debug ospf sr
debug ospf te
debug ospf event
debug ospf lsa
debug ospf zebra
!
interface lo
ip ospf area 0.0.0.0
!
interface eth-rt2-1
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt2-2
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt5
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt6
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
router ospf
ospf router-id 4.4.4.4
network 4.4.4.4/32 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
capability opaque
!ospf opaque-lsa
mpls-te on
!mpls-te export
mpls-te router-address 4.4.4.4
router-info area 0.0.0.0
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
!segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 4.4.4.4/32 index 40
!

View File

@ -0,0 +1,43 @@
log file zebra.log
!
hostname rt4
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 4.4.4.4/32
!
interface eth-rt2-1
ip address 10.0.2.4/24
link-params
enable
exit-link-params
!!
!
interface eth-rt2-2
ip address 10.0.3.4/24
link-params
enable
exit-link-params
!!
!
interface eth-rt5
ip address 10.0.6.4/24
link-params
enable
exit-link-params
!!
!
interface eth-rt6
ip address 10.0.7.4/24
link-params
enable
exit-link-params
!!
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,53 @@
hostname rt5
log file ospfd.log
!
debug ospf sr
debug ospf te
debug ospf event
debug ospf lsa
debug ospf zebra
!
interface lo
ip ospf area 0.0.0.0
!
interface eth-rt3-1
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt3-2
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt4
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt6
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
router ospf
ospf router-id 5.5.5.5
network 5.5.5.5/32 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
capability opaque
! ospf opaque-lsa
mpls-te on
! mpls-te export
mpls-te router-address 5.5.5.5
router-info area 0.0.0.0
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
! segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 5.5.5.5/32 index 50
!

View File

@ -0,0 +1,43 @@
log file zebra.log
!
hostname rt5
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 5.5.5.5/32
!
interface eth-rt3-1
ip address 10.0.4.5/24
link-params
enable
exit-link-params
!!
!
interface eth-rt3-2
ip address 10.0.5.5/24
link-params
enable
exit-link-params
!!
!
interface eth-rt4
ip address 10.0.6.5/24
link-params
enable
exit-link-params
!!
!
interface eth-rt6
ip address 10.0.8.5/24
link-params
enable
exit-link-params
!!
!
ip forwarding
!
line vty
!

View File

@ -0,0 +1,12 @@
log file bgpd.log
!
router bgp 1
bgp router-id 6.6.6.6
neighbor 1.1.1.1 remote-as 1
neighbor 1.1.1.1 update-source lo
!
address-family ipv4 unicast
redistribute static
neighbor 1.1.1.1 next-hop-self
exit-address-family
!

View File

@ -0,0 +1,41 @@
hostname rt6
log file ospfd.log
!
debug ospf sr
debug ospf te
debug ospf event
debug ospf lsa
debug ospf zebra
!
interface lo
ip ospf area 0.0.0.0
!
interface eth-rt4
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
interface eth-rt5
ip ospf network point-to-point
ip ospf hello-interval 2
ip ospf dead-interval 10
ip ospf area 0.0.0.0
!
router ospf
ospf router-id 6.6.6.6
network 6.6.6.6/32 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
capability opaque
! ospf opaque-lsa
mpls-te on
mpls-te export
mpls-te router-address 6.6.6.6
router-info area 0.0.0.0
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
! segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 6.6.6.6/32 index 60
!

View File

@ -0,0 +1,25 @@
log file pathd.log
!
hostname rt6
!
segment-routing
traffic-eng
mpls-te on
mpls-te import ospfv2
segment-list default
index 10 nai adjacency 10.0.7.6 10.0.7.4
index 20 nai adjacency 10.0.2.4 10.0.2.2
index 30 nai adjacency 10.0.1.2 10.0.1.1
!
segment-list test
index 10 nai adjacency 10.0.8.6 10.0.8.5
index 20 nai adjacency 10.0.6.5 10.0.6.4
index 30 nai adjacency 10.0.2.4 10.0.2.2
index 40 nai adjacency 10.0.1.2 10.0.1.1
!
policy color 1 endpoint 1.1.1.1
name default
binding-sid 6666
!
!
!

View File

@ -0,0 +1,13 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": false
}
]
}
}
}

View File

@ -0,0 +1,19 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,19 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,23 @@
{
"frr-pathd:pathd": {
"srte": {
"policy": [
{
"color": 1,
"endpoint": "1.1.1.1",
"is-operational": true,
"candidate-path": [
{
"preference": 100,
"is-best-candidate-path": false
},
{
"preference": 200,
"is-best-candidate-path": true
}
]
}
]
}
}
}

View File

@ -0,0 +1,38 @@
log file zebra.log
!
hostname rt6
!
debug zebra kernel
debug zebra packet
debug zebra mpls
!
interface lo
ip address 6.6.6.6/32
!
interface eth-rt4
ip address 10.0.7.6/24
link-params
enable
exit-link-params
!!
!
interface eth-rt5
ip address 10.0.8.6/24
link-params
enable
exit-link-params
!!
!
interface eth-dst
ip address 10.0.11.1/24
link-params
enable
exit-link-params
!!
!
ip forwarding
!
ip route 9.9.9.2/32 10.0.11.2
!
line vty
!

View File

@ -0,0 +1,640 @@
#!/usr/bin/env python
#
# test_ospf_sr_te_topo1.py
#
# Copyright (c) 2021 by
# Volta Networks
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE.
#
"""
test_ospf_sr_te_topo1.py:
+---------+
| |
| RT1 |
| 1.1.1.1 |
| |
+---------+
|eth-sw1
|
|
|
+---------+ | +---------+
| | | | |
| RT2 |eth-sw1 | eth-sw1| RT3 |
| 2.2.2.2 +----------+ + 3.3.3.3 |
| | 10.0.1.0/24 | |
+---------+ +---------+
eth-rt4-1| eth-rt5-1| |eth-rt5-2
| | |
10.0.2.0/24| 10.0.4.0/24| |10.0.5.0/24
| | |
eth-rt2-1| eth-rt3-1| |eth-rt3-2
+---------+ +---------+
| | | |
| RT4 | 10.0.6.0/24 | RT5 |
| 4.4.4.4 +---------------------+ 5.5.5.5 |
| |eth-rt5 eth-rt4| |
+---------+ +---------+
eth-rt6| |eth-rt6
| |
10.0.7.0/24| |10.0.8.0/24
| +---------+ |
| | | |
| | RT6 | |
+----------+ 6.6.6.6 +-----------+
eth-rt4| |eth-rt5
+---------+
|eth-dst (.1)
|
|10.0.11.0/24
|
|eth-rt6 (.2)
+---------+
| |
| DST |
| 9.9.9.2 |
| |
+---------+
"""
import os
import sys
import pytest
import json
import re
from time import sleep
from functools import partial
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pathd]
class TemplateTopo(Topo):
"Test topology builder"
def build(self, *_args, **_opts):
"Build function"
tgen = get_topogen(self)
#
# Define FRR Routers
#
for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]:
tgen.add_router(router)
#
# Define connections
#
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
#switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
#switch = tgen.add_switch("s3")
#switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
#switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
switch = tgen.add_switch("s4")
switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
switch = tgen.add_switch("s5")
switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
switch = tgen.add_switch("s6")
switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
switch = tgen.add_switch("s7")
switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
switch = tgen.add_switch("s8")
switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
switch = tgen.add_switch("s9")
switch.add_link(tgen.gears["rt6"], nodeif="eth-dst")
switch.add_link(tgen.gears["dst"], nodeif="eth-rt6")
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
if not os.path.isfile(os.path.join(frrdir, "pathd")):
pytest.skip("pathd daemon wasn't built in:"+frrdir)
tgen.start_topology()
router_list = tgen.routers()
# For all registered routers, load the zebra configuration file
for rname, router in router_list.items():
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
router.load_config(
TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
)
router.load_config(
TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname))
)
router.load_config(
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
"Teardown the pytest environment"
tgen = get_topogen()
# This function tears down the whole topology.
tgen.stop_topology()
def setup_testcase(msg):
logger.info(msg)
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
return tgen
def print_cmd_result(rname, command):
print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
def compare_json_test(router, command, reference, exact):
output = router.vtysh_cmd(command, isjson=True)
result = topotest.json_cmp(output, reference)
# Note: topotest.json_cmp() just checks on inclusion of keys.
# For exact matching also compare the other way around.
if not result and exact:
return topotest.json_cmp(reference, output)
else:
return result
def cmp_json_output(rname, command, reference, exact=False):
"Compare router JSON output"
logger.info('Comparing router "%s" "%s" output', rname, command)
tgen = get_topogen()
filename = "{}/{}/{}".format(CWD, rname, reference)
expected = json.loads(open(filename).read())
# Run test function until we get an result. Wait at most 60 seconds.
test_func = partial(compare_json_test, tgen.gears[rname], command, expected, exact)
_, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
assert diff is None, assertmsg
def cmp_json_output_exact(rname, command, reference):
return cmp_json_output(rname, command, reference, True)
def add_candidate_path(rname, endpoint, pref, name, segment_list="default"):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "policy color 1 endpoint """
+ endpoint
+ """" \
-c "candidate-path preference """
+ str(pref)
+ """ name """
+ name
+ """ explicit segment-list """
+ segment_list
+ '''"'''
)
def delete_candidate_path(rname, endpoint, pref):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "policy color 1 endpoint """
+ endpoint
+ """" \
-c "no candidate-path preference """
+ str(pref)
+ '''"'''
)
def add_segment(rname, name, index, label):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "segment-list """
+ name
+ """" \
-c "index """
+ str(index)
+ """ mpls label """
+ str(label)
+ '''"'''
)
def delete_segment(rname, name, index):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "segment-list """
+ name
+ """" \
-c "no index """
+ str(index)
+ '''"'''
)
def add_segment_adj(rname, name, index, src, dst):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "segment-list """
+ name
+ """" \
-c "index """
+ str(index)
+ """ nai adjacency """
+ str(src)
+ """ """
+ str(dst)
+ '''"'''
)
def create_sr_policy(rname, endpoint, bsid):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "policy color 1 endpoint """
+ endpoint
+ """" \
-c "name default" \
-c "binding-sid """
+ str(bsid)
+ '''"'''
)
def delete_sr_policy(rname, endpoint):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "segment-routing" \
-c "traffic-eng" \
-c "no policy color 1 endpoint """
+ endpoint
+ '''"'''
)
def create_prefix_sid(rname, prefix, sid):
get_topogen().net[rname].cmd(
""" \
vtysh -c "conf t" \
-c "router ospf " \
-c "segment-routing prefix """
+ prefix
+ " index "
+ str(sid)
+ '''"'''
)
def delete_prefix_sid(rname, prefix):
get_topogen().net[rname].cmd(
''' \
vtysh -c "conf t" \
-c "router ospf " \
-c "no segment-routing prefix "'''
+ prefix
)
def check_bsid(rt, bsid, fn_name, positive):
"""
Search for a bsid in rt1 and rt6
Positive means that check is true is bsid is found
Positive="False" means that check is true is bsid is NOT found
"""
logger.info('Checking "%s" bsid "%s" for router "%s" ', positive, bsid, rt)
count = 0
candidate_key = bsid
candidate_output = ""
# First wait for convergence
tgen = get_topogen()
while count < 30:
matched = False
matched_key = False
sleep(1)
count += 1
router = tgen.gears[rt]
candidate_output = router.vtysh_cmd("show mpls table json")
candidate_output_json = json.loads(candidate_output)
for item in candidate_output_json.items():
# logger.info('item "%s"', item)
if item[0] == candidate_key:
matched_key = True
if positive:
break
if positive:
if matched_key:
matched = True
assertmsg = "{} don't has entry {} but is was expected".format(
router.name, candidate_key)
else:
if not matched_key:
matched = True
assertmsg = "{} has entry {} but is wans't expected".format(
router.name, candidate_key)
if matched:
logger.info('Success "%s" in "%s"', router.name, fn_name)
return
assert matched, assertmsg
#
# Step 1
#
# Checking the MPLS table using a single SR Policy and a single Candidate Path
# Segment list are base in adjacency that query TED
#
def test_srte_init_step1():
setup_testcase("Test (step 1): wait OSPF convergence / label distribution")
check_bsid("rt1", "1111", test_srte_init_step1.__name__, False)
check_bsid("rt6", "6666", test_srte_init_step1.__name__, False)
def test_srte_add_candidate_check_mpls_table_step1():
setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path")
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
add_candidate_path(rname, endpoint, 100, "default")
check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True)
delete_candidate_path(rname, endpoint, 100)
def test_srte_reinstall_sr_policy_check_mpls_table_step1():
setup_testcase(
"Test (step 1): check MPLS table after the SR Policy was removed and reinstalled"
)
for rname, endpoint, bsid in [("rt1", "6.6.6.6", 1111), ("rt6", "1.1.1.1", 6666)]:
add_candidate_path(rname, endpoint, 100, "default")
delete_sr_policy(rname, endpoint)
check_bsid(rname, bsid, test_srte_init_step1.__name__, False)
create_sr_policy(rname, endpoint, bsid)
add_candidate_path(rname, endpoint, 100, "default")
check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True)
delete_candidate_path(rname, endpoint, 100)
#
# Step 2
#
# Checking pathd operational data using a single SR Policy and a single Candidate Path
# Segment list are base in adjacency that query TED
#
def test_srte_bare_policy_step2():
setup_testcase("Test (step 2): bare SR Policy should not be operational")
for rname in ["rt1", "rt6"]:
cmp_json_output_exact(
rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step2/show_operational_data.ref",
)
def test_srte_add_candidate_check_operational_data_step2():
setup_testcase(
"Test (step 2): add single Candidate Path, SR Policy should be operational"
)
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
add_candidate_path(rname, endpoint, 100, "default")
cmp_json_output(
rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step2/show_operational_data_with_candidate.ref",
)
def test_srte_config_remove_candidate_check_operational_data_step2():
setup_testcase(
"Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore"
)
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
delete_candidate_path(rname, endpoint, 100)
cmp_json_output_exact(
rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step2/show_operational_data.ref",
)
#
# Step 3
#
# Testing the Candidate Path selection
# Segment list are based in adjacencies resolved by query TED
#
def test_srte_add_two_candidates_step3():
setup_testcase("Test (step 3): second Candidate Path has higher Priority")
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
for pref, cand_name in [("100", "first"), ("200", "second")]:
add_candidate_path(rname, endpoint, pref, cand_name)
cmp_json_output(
rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step3/show_operational_data_with_two_candidates.ref",
)
# cleanup
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
for pref in ["100", "200"]:
delete_candidate_path(rname, endpoint, pref)
def test_srte_add_two_candidates_with_reverse_priority_step3():
setup_testcase("Test (step 3): second Candidate Path has lower Priority")
# Use reversed priorities here
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
for pref, cand_name in [("200", "first"), ("100", "second")]:
add_candidate_path(rname, endpoint, pref, cand_name)
cmp_json_output(
rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step3/show_operational_data_with_two_candidates.ref",
)
# cleanup
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
for pref in ["100", "200"]:
delete_candidate_path(rname, endpoint, pref)
def test_srte_remove_best_candidate_step3():
setup_testcase("Test (step 3): delete the Candidate Path with higher priority")
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
for pref, cand_name in [("100", "first"), ("200", "second")]:
add_candidate_path(rname, endpoint, pref, cand_name)
# Delete candidate with higher priority
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
delete_candidate_path(rname, endpoint, 200)
# Candidate with lower priority should get active now
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
cmp_json_output(
rname,
"show yang operational-data /frr-pathd:pathd pathd",
"step3/show_operational_data_with_single_candidate.ref",
)
# cleanup
delete_candidate_path(rname, endpoint, 100)
#
# Step 4
#
# Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications
# Segment list are base in adjacency that query TED
#
def test_srte_change_segment_list_check_mpls_table_step4():
setup_testcase("Test (step 4): check MPLS table for changed Segment List")
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
add_candidate_path(rname, endpoint, 100, "default")
# now change the segment list name
add_candidate_path(rname, endpoint, 100, "default", "test")
check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True)
delete_segment(rname, "test", 10)
delete_segment(rname, "test", 20)
delete_segment(rname, "test", 30)
delete_segment(rname, "test", 40)
if rname == "rt1":
add_segment_adj(rname, "test", 10, "10.0.1.1", "10.0.1.2")
add_segment_adj(rname, "test", 20, "10.0.2.2", "10.0.2.4")
add_segment_adj(rname, "test", 30, "10.0.6.4", "10.0.6.5")
add_segment_adj(rname, "test", 40, "10.0.8.5", "10.0.8.6")
else:
add_segment_adj(rname, "test", 10, "10.0.8.6", "10.0.8.5")
add_segment_adj(rname, "test", 20, "10.0.6.5", "10.0.6.4")
add_segment_adj(rname, "test", 30, "10.0.2.4", "10.0.2.2")
add_segment_adj(rname, "test", 40, "10.0.1.2", "10.0.1.1")
check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True)
delete_candidate_path(rname, endpoint, 100)
def test_srte_change_sl_priority_error_ted_check_mpls_table_step4():
setup_testcase("Test (step 4): check MPLS table keeps low prio sl")
for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
add_candidate_path(rname, endpoint, 100, "default")
# now change the segment list name
add_candidate_path(rname, endpoint, 200, "test", "test")
check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True)
delete_segment(rname, "test", 10)
delete_segment(rname, "test", 20)
delete_segment(rname, "test", 30)
delete_segment(rname, "test", 40)
# These won't resolv
if rname == "rt1":
add_segment_adj(rname, "test", 10, "10.0.1.99", "10.0.1.99")
add_segment_adj(rname, "test", 20, "10.0.2.99", "10.0.2.99")
add_segment_adj(rname, "test", 30, "10.0.6.99", "10.0.6.99")
add_segment_adj(rname, "test", 40, "10.0.8.99", "10.0.8.99")
else:
add_segment_adj(rname, "test", 10, "10.0.8.99", "10.0.8.99")
add_segment_adj(rname, "test", 20, "10.0.6.99", "10.0.6.99")
add_segment_adj(rname, "test", 30, "10.0.2.99", "10.0.2.99")
add_segment_adj(rname, "test", 40, "10.0.1.99", "10.0.1.99")
# So policy sticks with default sl even higher prio
check_bsid(rname, "1111" if rname == "rt1" else "6666", test_srte_init_step1.__name__, True)
delete_candidate_path(rname, endpoint, 100)
# Memory leak test template
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
pytest.skip("Memory leak test/report is disabled")
tgen.report_memory_leaks()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View File

@ -84,51 +84,71 @@ module frr-pathd {
leaf index {
type uint32;
description "Segment index";
}
leaf sid-value {
type rt-types:mpls-label;
}
leaf sid-value {
type rt-types:mpls-label;
description "MPLS label value";
}
container nai {
presence "The segment has a Node or Adjacency Identifier";
leaf type {
description "NAI type";
mandatory true;
description "MPLS label value";
}
container nai {
presence "The segement has a Node or Adjacency Identifier";
leaf type {
description "NAI type";
mandatory true;
type enumeration {
enum ipv4_node {
value 1;
description "IPv4 node identifier";
}
enum ipv6_node {
value 2;
description "IPv6 node identifier";
}
enum ipv4_adjacency {
value 3;
description "IPv4 adjacency";
}
enum ipv6_adjacency {
value 4;
description "IPv6 adjacency";
}
enum ipv4_unnumbered_adjacency {
value 5;
description "IPv4 unnumbered adjacency";
}
type enumeration {
enum ipv4_node {
value 1;
description "IPv4 node identifier";
}
enum ipv6_node {
value 2;
description "IPv6 node identifier";
}
enum ipv4_adjacency {
value 3;
description "IPv4 adjacency";
}
enum ipv6_adjacency {
value 4;
description "IPv6 adjacency";
}
enum ipv4_unnumbered_adjacency {
value 5;
description "IPv4 unnumbered adjacency";
}
enum ipv4_local_iface {
value 7;
description "IPv4 prefix with local interface id";
}
enum ipv6_local_iface {
value 8;
description "IPv6 prefix with local interface id";
}
enum ipv4_algo {
value 9;
description "IPv4 prefix with optional algorithm";
}
enum ipv6_algo {
value 10;
description "IPv6 prefix with optional algorithm";
}
}
leaf local-address {
type inet:ip-address;
mandatory true;
}
leaf local-interface {
type uint32;
mandatory true;
when "../type = 'ipv4_unnumbered_adjacency'";
}
leaf remote-address {
type inet:ip-address;
}
leaf local-address {
type inet:ip-address;
mandatory true;
}
leaf local-prefix-len {
type uint8;
mandatory true;
when "../type = 'ipv4_local_iface' or ../type = 'ipv6_local_iface' or ../type = 'ipv4_algo' or ../type = 'ipv6_algo'";
}
leaf local-interface {
type uint32;
mandatory true;
when "../type = 'ipv4_local_iface' or ../type = 'ipv6_local_iface' or ../type = 'ipv4_unnumbered_adjacency'";
}
leaf remote-address {
type inet:ip-address;
mandatory true;
when "../type = 'ipv4_adjacency' or ../type = 'ipv6_adjacency' or ../type = 'ipv4_unnumbered_adjacency'";
}
@ -137,6 +157,11 @@ module frr-pathd {
mandatory true;
when "../type = 'ipv4_unnumbered_adjacency'";
}
leaf algorithm {
type uint8;
mandatory true;
when "../type = 'ipv4_algo' or ../type = 'ipv6_algo'";
}
}
}
}