summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.c24
-rw-r--r--lib/command.h9
-rw-r--r--lib/ferr.h2
-rw-r--r--lib/filter.h54
-rw-r--r--lib/filter_cli.c218
-rw-r--r--lib/filter_nb.c386
-rw-r--r--lib/hash.h4
-rw-r--r--lib/ldp_sync.h9
-rw-r--r--lib/lib_vty.c4
-rw-r--r--lib/link_state.c1284
-rw-r--r--lib/link_state.h780
-rw-r--r--lib/log.c3
-rw-r--r--lib/northbound.h3
-rw-r--r--lib/routemap.c53
-rw-r--r--lib/sockunion.c17
-rw-r--r--lib/sockunion.h1
-rw-r--r--lib/subdir.am2
-rw-r--r--lib/yang.c3
-rw-r--r--lib/zclient.c36
-rw-r--r--lib/zclient.h79
20 files changed, 2921 insertions, 50 deletions
diff --git a/lib/command.c b/lib/command.c
index 87110157f6..f40fe6e2c5 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -863,6 +863,30 @@ enum node_type node_parent(enum node_type node)
case BFD_PROFILE_NODE:
ret = BFD_NODE;
break;
+ case SR_TRAFFIC_ENG_NODE:
+ ret = SEGMENT_ROUTING_NODE;
+ break;
+ case SR_SEGMENT_LIST_NODE:
+ ret = SR_TRAFFIC_ENG_NODE;
+ break;
+ case SR_POLICY_NODE:
+ ret = SR_TRAFFIC_ENG_NODE;
+ break;
+ case SR_CANDIDATE_DYN_NODE:
+ ret = SR_POLICY_NODE;
+ break;
+ case PCEP_NODE:
+ ret = SR_TRAFFIC_ENG_NODE;
+ break;
+ case PCEP_PCE_CONFIG_NODE:
+ ret = PCEP_NODE;
+ break;
+ case PCEP_PCE_NODE:
+ ret = PCEP_NODE;
+ break;
+ case PCEP_PCC_NODE:
+ ret = PCEP_NODE;
+ break;
default:
ret = CONFIG_NODE;
break;
diff --git a/lib/command.h b/lib/command.h
index 1b0504101c..bfe64a7235 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -145,6 +145,15 @@ enum node_type {
PROTOCOL_NODE, /* protocol filtering node */
MPLS_NODE, /* MPLS config node */
PW_NODE, /* Pseudowire config node */
+ SEGMENT_ROUTING_NODE, /* Segment routing root node */
+ SR_TRAFFIC_ENG_NODE, /* SR Traffic Engineering node */
+ SR_SEGMENT_LIST_NODE, /* SR segment list config node */
+ SR_POLICY_NODE, /* SR policy config node */
+ SR_CANDIDATE_DYN_NODE, /* SR dynamic candidate path config node */
+ PCEP_NODE, /* PCEP node */
+ PCEP_PCE_CONFIG_NODE, /* PCE shared configuration node */
+ PCEP_PCE_NODE, /* PCE configuration node */
+ PCEP_PCC_NODE, /* PCC configuration node */
VTY_NODE, /* Vty node. */
FPM_NODE, /* Dataplane FPM node. */
LINK_PARAMS_NODE, /* Link-parameters node */
diff --git a/lib/ferr.h b/lib/ferr.h
index a89b595e87..4e95431cea 100644
--- a/lib/ferr.h
+++ b/lib/ferr.h
@@ -132,6 +132,8 @@ struct ferr {
#define VTYSH_FRR_END 0x0FFFFFFF
#define WATCHFRR_FERR_START 0x10000001
#define WATCHFRR_FERR_END 0x10FFFFFF
+#define PATH_FERR_START 0x11000001
+#define PATH_FERR_END 0x11FFFFFF
#define ZEBRA_FERR_START 0xF1000001
#define ZEBRA_FERR_END 0xF1FFFFFF
#define END_FERR 0xFFFFFFFF
diff --git a/lib/filter.h b/lib/filter.h
index 623fb94527..091a5197f6 100644
--- a/lib/filter.h
+++ b/lib/filter.h
@@ -176,6 +176,60 @@ enum yang_prefix_list_action {
YPLA_PERMIT = 1,
};
+struct acl_dup_args {
+ /** Access list type ("ipv4", "ipv6" or "mac"). */
+ const char *ada_type;
+ /** Access list name. */
+ const char *ada_name;
+
+#define ADA_MAX_VALUES 4
+ /** Entry XPath for value. */
+ const char *ada_xpath[ADA_MAX_VALUES];
+ /** Entry value to match. */
+ const char *ada_value[ADA_MAX_VALUES];
+
+ /** Duplicated entry found in list? */
+ bool ada_found;
+
+ /** (Optional) Already existing `dnode`. */
+ const struct lyd_node *ada_entry_dnode;
+};
+
+/**
+ * Check for duplicated entries using the candidate configuration.
+ *
+ * \param vty so we can get the candidate config.
+ * \param ada the arguments to check.
+ */
+bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada);
+
+struct plist_dup_args {
+ /** Access list type ("ipv4" or "ipv6"). */
+ const char *pda_type;
+ /** Access list name. */
+ const char *pda_name;
+
+#define PDA_MAX_VALUES 4
+ /** Entry XPath for value. */
+ const char *pda_xpath[PDA_MAX_VALUES];
+ /** Entry value to match. */
+ const char *pda_value[PDA_MAX_VALUES];
+
+ /** Duplicated entry found in list? */
+ bool pda_found;
+
+ /** (Optional) Already existing `dnode`. */
+ const struct lyd_node *pda_entry_dnode;
+};
+
+/**
+ * Check for duplicated entries using the candidate configuration.
+ *
+ * \param vty so we can get the candidate config.
+ * \param pda the arguments to check.
+ */
+bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda);
+
/* filter_cli.c */
struct lyd_node;
struct vty;
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
index a8230f3a9a..54b6cda9a5 100644
--- a/lib/filter_cli.c
+++ b/lib/filter_cli.c
@@ -162,10 +162,36 @@ DEFPY_YANG(
"Wildcard bits\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv4";
+ ada.ada_name = name;
+ if (host_str && mask_str == NULL) {
+ ada.ada_xpath[0] = "./host";
+ ada.ada_value[0] = host_str;
+ } else if (host_str && mask_str) {
+ ada.ada_xpath[0] = "./network/address";
+ ada.ada_value[0] = host_str;
+ ada.ada_xpath[1] = "./network/mask";
+ ada.ada_value[1] = mask_str;
+ } else {
+ ada.ada_xpath[0] = "./source-any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -270,11 +296,59 @@ DEFPY_YANG(
"Destination address to match\n"
"Any destination host\n")
{
+ int idx = 0;
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv4";
+ ada.ada_name = name;
+ if (src_str && src_mask_str == NULL) {
+ ada.ada_xpath[idx] = "./host";
+ ada.ada_value[idx] = src_str;
+ idx++;
+ } else if (src_str && src_mask_str) {
+ ada.ada_xpath[idx] = "./network/address";
+ ada.ada_value[idx] = src_str;
+ idx++;
+ ada.ada_xpath[idx] = "./network/mask";
+ ada.ada_value[idx] = src_mask_str;
+ idx++;
+ } else {
+ ada.ada_xpath[idx] = "./source-any";
+ ada.ada_value[idx] = "true";
+ idx++;
+ }
+
+ if (dst_str && dst_mask_str == NULL) {
+ ada.ada_xpath[idx] = "./destination-host";
+ ada.ada_value[idx] = dst_str;
+ idx++;
+ } else if (dst_str && dst_mask_str) {
+ ada.ada_xpath[idx] = "./destination-network/address";
+ ada.ada_value[idx] = dst_str;
+ idx++;
+ ada.ada_xpath[idx] = "./destination-network/mask";
+ ada.ada_value[idx] = dst_mask_str;
+ idx++;
+ } else {
+ ada.ada_xpath[idx] = "./destination-any";
+ ada.ada_value[idx] = "true";
+ idx++;
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -419,10 +493,36 @@ DEFPY_YANG(
"Match any IPv4\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv4";
+ ada.ada_name = name;
+
+ if (prefix_str) {
+ ada.ada_xpath[0] = "./ipv4-prefix";
+ ada.ada_value[0] = prefix_str;
+ if (exact) {
+ ada.ada_xpath[1] = "./ipv4-exact-match";
+ ada.ada_value[1] = "true";
+ }
+ } else {
+ ada.ada_xpath[0] = "./any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -590,10 +690,36 @@ DEFPY_YANG(
"Match any IPv6\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "ipv6";
+ ada.ada_name = name;
+
+ if (prefix_str) {
+ ada.ada_xpath[0] = "./ipv6-prefix";
+ ada.ada_value[0] = prefix_str;
+ if (exact) {
+ ada.ada_xpath[1] = "./ipv6-exact-match";
+ ada.ada_value[1] = "true";
+ }
+ } else {
+ ada.ada_xpath[0] = "./any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -765,10 +891,32 @@ DEFPY_YANG(
"Match any MAC address\n")
{
int64_t sseq;
+ struct acl_dup_args ada = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ ada.ada_type = "mac";
+ ada.ada_name = name;
+
+ if (mac_str) {
+ ada.ada_xpath[0] = "./mac";
+ ada.ada_value[0] = mac_str;
+ } else {
+ ada.ada_xpath[0] = "./any";
+ ada.ada_value[0] = "true";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (acl_is_dup(vty->candidate_config->dnode, &ada))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the access-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -1171,10 +1319,45 @@ DEFPY_YANG(
"Maximum prefix length\n")
{
int64_t sseq;
+ int arg_idx = 0;
+ struct plist_dup_args pda = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ pda.pda_type = "ipv4";
+ pda.pda_name = name;
+ if (prefix_str) {
+ pda.pda_xpath[arg_idx] = "./ipv4-prefix";
+ pda.pda_value[arg_idx] = prefix_str;
+ arg_idx++;
+ if (ge_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv4-prefix-length-greater-or-equal";
+ pda.pda_value[arg_idx] = ge_str;
+ arg_idx++;
+ }
+ if (le_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv4-prefix-length-lesser-or-equal";
+ pda.pda_value[arg_idx] = le_str;
+ arg_idx++;
+ }
+ } else {
+ pda.pda_xpath[0] = "./any";
+ pda.pda_value[0] = "";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (plist_is_dup(vty->candidate_config->dnode, &pda))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the prefix-list first, so we can generate sequence if
* none given (backward compatibility).
*/
@@ -1331,10 +1514,45 @@ DEFPY_YANG(
"Minimum prefix length\n")
{
int64_t sseq;
+ int arg_idx = 0;
+ struct plist_dup_args pda = {};
char xpath[XPATH_MAXLEN];
char xpath_entry[XPATH_MAXLEN + 128];
/*
+ * Backward compatibility: don't complain about duplicated values,
+ * just silently accept.
+ */
+ if (seq_str == NULL) {
+ pda.pda_type = "ipv6";
+ pda.pda_name = name;
+ if (prefix_str) {
+ pda.pda_xpath[arg_idx] = "./ipv6-prefix";
+ pda.pda_value[arg_idx] = prefix_str;
+ arg_idx++;
+ if (ge_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv6-prefix-length-greater-or-equal";
+ pda.pda_value[arg_idx] = ge_str;
+ arg_idx++;
+ }
+ if (le_str) {
+ pda.pda_xpath[arg_idx] =
+ "./ipv6-prefix-length-lesser-or-equal";
+ pda.pda_value[arg_idx] = le_str;
+ arg_idx++;
+ }
+ } else {
+ pda.pda_xpath[0] = "./any";
+ pda.pda_value[0] = "";
+ }
+
+ /* Duplicated entry without sequence, just quit. */
+ if (plist_is_dup(vty->candidate_config->dnode, &pda))
+ return CMD_SUCCESS;
+ }
+
+ /*
* Create the prefix-list first, so we can generate sequence if
* none given (backward compatibility).
*/
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
index 1d522bdbec..2007b37cdf 100644
--- a/lib/filter_nb.c
+++ b/lib/filter_nb.c
@@ -133,6 +133,220 @@ static void cisco_unset_addr_mask(struct in_addr *addr, struct in_addr *mask)
mask->s_addr = CISCO_BIN_HOST_WILDCARD_MASK;
}
+static int _acl_is_dup(const struct lyd_node *dnode, void *arg)
+{
+ struct acl_dup_args *ada = arg;
+ int idx;
+
+ /* This entry is the caller, so skip it. */
+ if (ada->ada_entry_dnode
+ && ada->ada_entry_dnode == dnode)
+ return YANG_ITER_CONTINUE;
+
+ /* Check if all values match. */
+ for (idx = 0; idx < ADA_MAX_VALUES; idx++) {
+ /* No more values. */
+ if (ada->ada_xpath[idx] == NULL)
+ break;
+
+ /* Not same type, just skip it. */
+ if (!yang_dnode_exists(dnode, ada->ada_xpath[idx]))
+ return YANG_ITER_CONTINUE;
+
+ /* Check if different value. */
+ if (strcmp(yang_dnode_get_string(dnode, ada->ada_xpath[idx]),
+ ada->ada_value[idx]))
+ return YANG_ITER_CONTINUE;
+ }
+
+ ada->ada_found = true;
+
+ return YANG_ITER_STOP;
+}
+
+bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada)
+{
+ ada->ada_found = false;
+
+ yang_dnode_iterate(
+ _acl_is_dup, ada, dnode,
+ "/frr-filter:lib/access-list[type='%s'][name='%s']/entry",
+ ada->ada_type, ada->ada_name);
+
+ return ada->ada_found;
+}
+
+static bool acl_cisco_is_dup(const struct lyd_node *dnode)
+{
+ const struct lyd_node *entry_dnode =
+ yang_dnode_get_parent(dnode, "entry");
+ struct acl_dup_args ada = {};
+ int idx = 0, arg_idx = 0;
+ static const char *cisco_entries[] = {
+ "./host",
+ "./network/address",
+ "./network/mask",
+ "./source-any",
+ "./destination-host",
+ "./destination-network/address",
+ "./destination-network/mask",
+ "./destination-any",
+ NULL
+ };
+
+ /* Initialize. */
+ ada.ada_type = "ipv4";
+ ada.ada_name = yang_dnode_get_string(entry_dnode, "../name");
+ ada.ada_entry_dnode = entry_dnode;
+
+ /* Load all values/XPaths. */
+ while (cisco_entries[idx] != NULL) {
+ if (!yang_dnode_exists(entry_dnode, cisco_entries[idx])) {
+ idx++;
+ continue;
+ }
+
+ ada.ada_xpath[arg_idx] = cisco_entries[idx];
+ ada.ada_value[arg_idx] =
+ yang_dnode_get_string(entry_dnode, cisco_entries[idx]);
+ arg_idx++;
+ idx++;
+ }
+
+ return acl_is_dup(entry_dnode, &ada);
+}
+
+static bool acl_zebra_is_dup(const struct lyd_node *dnode,
+ enum yang_access_list_type type)
+{
+ const struct lyd_node *entry_dnode =
+ yang_dnode_get_parent(dnode, "entry");
+ struct acl_dup_args ada = {};
+ int idx = 0, arg_idx = 0;
+ static const char *zebra_entries[] = {
+ "./ipv4-prefix",
+ "./ipv4-exact-match",
+ "./ipv6-prefix",
+ "./ipv6-exact-match",
+ "./mac",
+ "./any",
+ NULL
+ };
+
+ /* Initialize. */
+ switch (type) {
+ case YALT_IPV4:
+ ada.ada_type = "ipv4";
+ break;
+ case YALT_IPV6:
+ ada.ada_type = "ipv6";
+ break;
+ case YALT_MAC:
+ ada.ada_type = "mac";
+ break;
+ }
+ ada.ada_name = yang_dnode_get_string(entry_dnode, "../name");
+ ada.ada_entry_dnode = entry_dnode;
+
+ /* Load all values/XPaths. */
+ while (zebra_entries[idx] != NULL) {
+ if (!yang_dnode_exists(entry_dnode, zebra_entries[idx])) {
+ idx++;
+ continue;
+ }
+
+ ada.ada_xpath[arg_idx] = zebra_entries[idx];
+ ada.ada_value[arg_idx] =
+ yang_dnode_get_string(entry_dnode, zebra_entries[idx]);
+ arg_idx++;
+ idx++;
+ }
+
+ return acl_is_dup(entry_dnode, &ada);
+}
+
+static int _plist_is_dup(const struct lyd_node *dnode, void *arg)
+{
+ struct plist_dup_args *pda = arg;
+ int idx;
+
+ /* This entry is the caller, so skip it. */
+ if (pda->pda_entry_dnode
+ && pda->pda_entry_dnode == dnode)
+ return YANG_ITER_CONTINUE;
+
+ /* Check if all values match. */
+ for (idx = 0; idx < PDA_MAX_VALUES; idx++) {
+ /* No more values. */
+ if (pda->pda_xpath[idx] == NULL)
+ break;
+
+ /* Not same type, just skip it. */
+ if (!yang_dnode_exists(dnode, pda->pda_xpath[idx]))
+ return YANG_ITER_CONTINUE;
+
+ /* Check if different value. */
+ if (strcmp(yang_dnode_get_string(dnode, pda->pda_xpath[idx]),
+ pda->pda_value[idx]))
+ return YANG_ITER_CONTINUE;
+ }
+
+ pda->pda_found = true;
+
+ return YANG_ITER_STOP;
+}
+
+bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda)
+{
+ pda->pda_found = false;
+
+ yang_dnode_iterate(
+ _plist_is_dup, pda, dnode,
+ "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry",
+ pda->pda_type, pda->pda_name);
+
+ return pda->pda_found;
+}
+
+static bool plist_is_dup_nb(const struct lyd_node *dnode)
+{
+ const struct lyd_node *entry_dnode =
+ yang_dnode_get_parent(dnode, "entry");
+ struct plist_dup_args pda = {};
+ int idx = 0, arg_idx = 0;
+ static const char *entries[] = {
+ "./ipv4-prefix",
+ "./ipv4-prefix-length-greater-or-equal",
+ "./ipv4-prefix-length-lesser-or-equal",
+ "./ipv6-prefix",
+ "./ipv6-prefix-length-greater-or-equal",
+ "./ipv6-prefix-length-lesser-or-equal",
+ "./any",
+ NULL
+ };
+
+ /* Initialize. */
+ pda.pda_type = yang_dnode_get_string(entry_dnode, "../type");
+ pda.pda_name = yang_dnode_get_string(entry_dnode, "../name");
+ pda.pda_entry_dnode = entry_dnode;
+
+ /* Load all values/XPaths. */
+ while (entries[idx] != NULL) {
+ if (!yang_dnode_exists(entry_dnode, entries[idx])) {
+ idx++;
+ continue;
+ }
+
+ pda.pda_xpath[arg_idx] = entries[idx];
+ pda.pda_value[arg_idx] =
+ yang_dnode_get_string(entry_dnode, entries[idx]);
+ arg_idx++;
+ idx++;
+ }
+
+ return plist_is_dup(entry_dnode, &pda);
+}
+
/*
* XPath: /frr-filter:lib/access-list
*/
@@ -290,6 +504,19 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
struct filter_zebra *fz;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_zebra_is_dup(
+ args->dnode,
+ yang_dnode_get_enum(args->dnode, "../../type"))) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -330,6 +557,19 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
struct filter_zebra *fz;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_zebra_is_dup(
+ args->dnode,
+ yang_dnode_get_enum(args->dnode, "../../type"))) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -369,6 +609,17 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -410,6 +661,17 @@ lib_access_list_entry_network_address_modify(struct nb_cb_modify_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -432,6 +694,17 @@ lib_access_list_entry_network_mask_modify(struct nb_cb_modify_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -454,6 +727,17 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args)
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -495,6 +779,17 @@ static int lib_access_list_entry_destination_host_modify(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -537,6 +832,17 @@ static int lib_access_list_entry_destination_network_address_modify(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -559,6 +865,17 @@ static int lib_access_list_entry_destination_network_mask_modify(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -581,6 +898,17 @@ static int lib_access_list_entry_destination_any_create(
struct filter_cisco *fc;
struct filter *f;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_cisco_is_dup(args->dnode)) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -623,6 +951,19 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args)
struct filter *f;
int type;
+ /* Don't allow duplicated values. */
+ if (args->event == NB_EV_VALIDATE) {
+ if (acl_zebra_is_dup(
+ args->dnode,
+ yang_dnode_get_enum(args->dnode, "../../type"))) {
+ snprintfrr(args->errmsg, args->errmsg_len,
+ "duplicated access list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -817,15 +1158,12 @@ lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
struct prefix p;
if (args->event == NB_EV_VALIDATE) {
- /*
- * TODO: validate prefix_entry_dup_check() passes.
- *
- * This needs to be implemented using YANG lyd_node
- * navigation, because the `priv` data structures are not
- * available at `NB_EV_VALIDATE` phase. An easier
- * alternative would be mark `ipvx-prefix` as unique
- * (see RFC 7950, Section 7.8.3. The list "unique" Statement).
- */
+ if (plist_is_dup_nb(args->dnode)) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "duplicated prefix list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
return NB_OK;
}
@@ -888,6 +1226,16 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
+ if (args->event == NB_EV_VALIDATE) {
+ if (plist_is_dup_nb(args->dnode)) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "duplicated prefix list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -937,6 +1285,16 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
+ if (args->event == NB_EV_VALIDATE) {
+ if (plist_is_dup_nb(args->dnode)) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "duplicated prefix list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -982,6 +1340,16 @@ static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args)
struct prefix_list_entry *ple;
int type;
+ if (args->event == NB_EV_VALIDATE) {
+ if (plist_is_dup_nb(args->dnode)) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "duplicated prefix list value: %s",
+ yang_dnode_get_string(args->dnode, NULL));
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+ }
+
if (args->event != NB_EV_APPLY)
return NB_OK;
diff --git a/lib/hash.h b/lib/hash.h
index 00953ff3b3..23e93b6d7d 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -103,7 +103,7 @@ struct hash {
*
* hash_cmp
* comparison function used for resolving collisions; when called with two
- * data items, should return nonzero if the two items are equal and 0
+ * data items, should return true if the two items are equal and false
* otherwise
*
* name
@@ -137,7 +137,7 @@ extern struct hash *hash_create(unsigned int (*hash_key)(const void *),
*
* hash_cmp
* comparison function used for resolving collisions; when called with two
- * data items, should return nonzero if the two items are equal and 0
+ * data items, should return true if the two items are equal and false
* otherwise
*
* name
diff --git a/lib/ldp_sync.h b/lib/ldp_sync.h
index 73e3dac3d8..0429e17d5c 100644
--- a/lib/ldp_sync.h
+++ b/lib/ldp_sync.h
@@ -39,14 +39,10 @@ extern "C" {
#define LDP_IGP_SYNC_HOLDDOWN_DEFAULT 0
-#define LDP_IGP_SYNC_HELLO_TIMEOUT 5
-
/* LDP-IGP Sync structures */
struct ldp_sync_info_cmd {
uint16_t flags;
uint16_t holddown; /* timer value */
- uint32_t sequence; /* hello sequence number */
- struct thread *t_hello; /* hello timer for detecting LDP going down */
};
struct ldp_sync_info {
@@ -79,11 +75,6 @@ struct ldp_igp_sync_if_state_req {
char name[INTERFACE_NAMSIZ];
};
-struct ldp_igp_sync_hello {
- int proto;
- unsigned int sequence;
-};
-
#ifdef __cplusplus
}
#endif
diff --git a/lib/lib_vty.c b/lib/lib_vty.c
index 0cc25f24ed..cd8b5c9809 100644
--- a/lib/lib_vty.c
+++ b/lib/lib_vty.c
@@ -222,7 +222,7 @@ static struct call_back {
DEFUN_HIDDEN (start_config,
start_config_cmd,
- "start_configuration",
+ "XFRR_start_configuration",
"The Beginning of Configuration\n")
{
callback.readin_time = monotime(NULL);
@@ -235,7 +235,7 @@ DEFUN_HIDDEN (start_config,
DEFUN_HIDDEN (end_config,
end_config_cmd,
- "end_configuration",
+ "XFRR_end_configuration",
"The End of Configuration\n")
{
time_t readin_time;
diff --git a/lib/link_state.c b/lib/link_state.c
new file mode 100644
index 0000000000..f8fdda64f0
--- /dev/null
+++ b/lib/link_state.c
@@ -0,0 +1,1284 @@
+/*
+ * Link State Database - link_state.c
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2020 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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 "if.h"
+#include "linklist.h"
+#include "log.h"
+#include "command.h"
+#include "termtable.h"
+#include "memory.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "zclient.h"
+#include "stream.h"
+#include "link_state.h"
+
+/* Link State Memory allocation */
+DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database")
+
+/**
+ * Link State Node management functions
+ */
+struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
+ struct in6_addr rid6)
+{
+ struct ls_node *new;
+
+ if (adv.origin == NONE)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
+ new->adv = adv;
+ if (!IPV4_NET0(rid.s_addr)) {
+ new->router_id = rid;
+ SET_FLAG(new->flags, LS_NODE_ROUTER_ID);
+ } else {
+ if (adv.origin == OSPFv2 || adv.origin == STATIC
+ || adv.origin == DIRECT) {
+ new->router_id = adv.id.ip.addr;
+ SET_FLAG(new->flags, LS_NODE_ROUTER_ID);
+ }
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&rid6)) {
+ new->router6_id = rid6;
+ SET_FLAG(new->flags, LS_NODE_ROUTER_ID6);
+ }
+ return new;
+}
+
+void ls_node_del(struct ls_node *node)
+{
+ XFREE(MTYPE_LS_DB, node);
+ node = NULL;
+}
+
+int ls_node_same(struct ls_node *n1, struct ls_node *n2)
+{
+ if ((n1 && !n2) || (!n1 && n2))
+ return 0;
+
+ if (n1 == n2)
+ return 1;
+
+ if (n1->flags != n2->flags)
+ return 0;
+
+ if (n1->adv.origin != n2->adv.origin)
+ return 0;
+
+ if (!memcmp(&n1->adv.id, &n2->adv.id, sizeof(struct ls_node_id)))
+ return 0;
+
+ /* Do we need to test individually each field, instead performing a
+ * global memcmp? There is a risk that an old value that is bit masked
+ * i.e. corresponding flag = 0, will result into a false negative
+ */
+ if (!memcmp(n1, n2, sizeof(struct ls_node)))
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * Link State Attributes management functions
+ */
+struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
+ struct in_addr local,
+ struct in6_addr local6,
+ uint32_t local_id)
+{
+ struct ls_attributes *new;
+
+ if (adv.origin == NONE)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
+ new->adv = adv;
+ if (!IPV4_NET0(local.s_addr)) {
+ new->standard.local = local;
+ SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR);
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&local6)) {
+ new->standard.local6 = local6;
+ SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR6);
+ }
+ if (local_id != 0) {
+ new->standard.local_id = local_id;
+ SET_FLAG(new->flags, LS_ATTR_LOCAL_ID);
+ }
+
+ /* Check that almost one identifier is set */
+ if (!CHECK_FLAG(new->flags, LS_ATTR_LOCAL_ADDR | LS_ATTR_LOCAL_ADDR6
+ | LS_ATTR_LOCAL_ID)) {
+ XFREE(MTYPE_LS_DB, new);
+ return NULL;
+ }
+
+ return new;
+}
+
+void ls_attributes_del(struct ls_attributes *attr)
+{
+ if (!attr)
+ return;
+
+ if (attr->srlgs)
+ XFREE(MTYPE_LS_DB, attr->srlgs);
+
+ XFREE(MTYPE_LS_DB, attr);
+ attr = NULL;
+}
+
+int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2)
+{
+ if ((l1 && !l2) || (!l1 && l2))
+ return 0;
+
+ if (l1 == l2)
+ return 1;
+
+ if (l1->flags != l2->flags)
+ return 0;
+
+ if (l1->adv.origin != l2->adv.origin)
+ return 0;
+
+ if (!memcmp(&l1->adv.id, &l2->adv.id, sizeof(struct ls_node_id)))
+ return 0;
+
+ /* Do we need to test individually each field, instead performing a
+ * global memcmp? There is a risk that an old value that is bit masked
+ * i.e. corresponding flag = 0, will result into a false negative
+ */
+ if (!memcmp(l1, l2, sizeof(struct ls_attributes)))
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * Link State Vertices management functions
+ */
+struct ls_vertex *ls_vertex_new(struct ls_node *node)
+{
+ struct ls_vertex *new;
+
+ if (node == NULL)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex));
+ new->node = node;
+ new->incoming_edges = list_new();
+ new->outgoing_edges = list_new();
+ new->prefixes = list_new();
+
+ return new;
+}
+
+void ls_vertex_del(struct ls_vertex *vertex)
+{
+ if (vertex == NULL)
+ return;
+
+ list_delete_all_node(vertex->incoming_edges);
+ list_delete_all_node(vertex->outgoing_edges);
+ list_delete_all_node(vertex->prefixes);
+ XFREE(MTYPE_LS_DB, vertex);
+ vertex = NULL;
+}
+
+struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node)
+{
+ struct ls_vertex *new;
+
+ if ((ted == NULL) || (node == NULL))
+ return NULL;
+
+ new = ls_vertex_new(node);
+ if (!new)
+ return NULL;
+
+ /* set Key as the IPv4/Ipv6 Router ID or ISO System ID */
+ switch (node->adv.origin) {
+ case OSPFv2:
+ case STATIC:
+ case DIRECT:
+ memcpy(&new->key, &node->adv.id.ip.addr, IPV4_MAX_BYTELEN);
+ break;
+ case ISIS_L1:
+ case ISIS_L2:
+ memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN);
+ break;
+ default:
+ new->key = 0;
+ break;
+ }
+
+ /* Remove Vertex if key is not set */
+ if (new->key == 0) {
+ ls_vertex_del(new);
+ return NULL;
+ }
+
+ /* Add Vertex to TED */
+ vertices_add(&ted->vertices, new);
+
+ return new;
+}
+
+struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
+{
+ struct ls_vertex *old;
+
+ if (node == NULL)
+ return NULL;
+
+ old = ls_find_vertex_by_id(ted, node->adv);
+ if (old) {
+ if (!ls_node_same(old->node, node)) {
+ ls_node_del(old->node);
+ old->node = node;
+ }
+ return old;
+ }
+
+ return ls_vertex_add(ted, node);
+}
+
+void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex)
+{
+ vertices_del(&ted->vertices, vertex);
+ ls_vertex_del(vertex);
+}
+
+struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key)
+{
+ struct ls_vertex node = {};
+
+ if (key == 0)
+ return NULL;
+
+ node.key = key;
+ return vertices_find(&ted->vertices, &node);
+}
+
+struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted,
+ struct ls_node_id nid)
+{
+ struct ls_vertex node = {};
+
+ switch (nid.origin) {
+ case OSPFv2:
+ case STATIC:
+ case DIRECT:
+ memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN);
+ break;
+ case ISIS_L1:
+ case ISIS_L2:
+ memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN);
+ break;
+ default:
+ return NULL;
+ }
+
+ return vertices_find(&ted->vertices, &node);
+}
+
+int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
+{
+ if ((v1 && !v2) || (!v1 && v2))
+ return 0;
+
+ if (!v1 && !v2)
+ return 1;
+
+ if (v1->key != v2->key)
+ return 0;
+
+ if (v1->node == v2->node)
+ return 1;
+
+ return ls_node_same(v1->node, v2->node);
+}
+
+/**
+ * Link State Edges management functions
+ */
+
+/**
+ * This function allows to connect the Edge to the vertices present in the TED.
+ * A temporary vertex that corresponds to the source of this Edge i.e. the
+ * advertised router, is created if not found in the Data Base. If a Edge that
+ * corresponds to the reverse path is found, the Edge is attached to the
+ * destination vertex as destination and reverse Edge is attached to the source
+ * vertex as source.
+ *
+ * @param ted Link State Data Base
+ * @param edge Link State Edge to be attached
+ */
+static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge)
+{
+ struct ls_vertex *vertex = NULL;
+ struct ls_node *node;
+ struct ls_edge *dst;
+ const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+ /* First, search if there is a Vertex that correspond to the Node ID */
+ vertex = ls_find_vertex_by_id(ted, edge->attributes->adv);
+ if (vertex == NULL) {
+ /* Create a new temporary Node & Vertex if not found */
+ node = ls_node_new(edge->attributes->adv, inaddr_any,
+ in6addr_any);
+ vertex = ls_vertex_add(ted, node);
+ }
+ /* and attach the edge as source to the vertex */
+ listnode_add(vertex->outgoing_edges, edge);
+ edge->source = vertex;
+
+ /* Then search if there is a reverse Edge */
+ dst = ls_find_edge_by_destination(ted, edge->attributes);
+ /* attach the destination edge to the vertex */
+ if (dst) {
+ listnode_add(vertex->incoming_edges, dst);
+ dst->destination = vertex;
+ /* and destination vertex to this edge */
+ vertex = dst->source;
+ listnode_add(vertex->incoming_edges, edge);
+ edge->destination = vertex;
+ }
+}
+
+struct ls_edge *ls_edge_add(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge *new;
+
+ if (attributes == NULL)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge));
+ new->attributes = attributes;
+ /* Key is the IPv4 local address */
+ if (!IPV4_NET0(attributes->standard.local.s_addr))
+ new->key = ((uint64_t)attributes->standard.local.s_addr)
+ & 0xffffffff;
+ /* or the IPv6 local address if IPv4 is not defined */
+ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6))
+ new->key = (uint64_t)(attributes->standard.local6.s6_addr32[0]
+ & 0xffffffff)
+ | ((uint64_t)attributes->standard.local6.s6_addr32[1]
+ << 32);
+ /* of local identifier if no IP addresses are defined */
+ else if (attributes->standard.local_id != 0)
+ new->key = (uint64_t)(
+ (attributes->standard.local_id & 0xffffffff)
+ | ((uint64_t)attributes->standard.remote_id << 32));
+
+ /* Remove Edge if key is not known */
+ if (new->key == 0) {
+ XFREE(MTYPE_LS_DB, new);
+ return NULL;
+ }
+
+ edges_add(&ted->edges, new);
+
+ /* Finally, connect edge to vertices */
+ ls_edge_connect_to(ted, new);
+
+ return new;
+}
+
+struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key)
+{
+ struct ls_edge edge = {};
+
+ if (key == 0)
+ return NULL;
+
+ edge.key = key;
+ return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge edge = {};
+
+ if (attributes == NULL)
+ return NULL;
+
+ /* Key is the IPv4 local address */
+ if (!IPV4_NET0(attributes->standard.local.s_addr))
+ edge.key = ((uint64_t)attributes->standard.local.s_addr)
+ & 0xffffffff;
+ /* or the IPv6 local address if IPv4 is not defined */
+ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6))
+ edge.key = (uint64_t)(attributes->standard.local6.s6_addr32[0]
+ & 0xffffffff)
+ | ((uint64_t)attributes->standard.local6.s6_addr32[1]
+ << 32);
+ /* of local identifier if no IP addresses are defined */
+ else if (attributes->standard.local_id != 0)
+ edge.key = (uint64_t)(
+ (attributes->standard.local_id & 0xffffffff)
+ | ((uint64_t)attributes->standard.remote_id << 32));
+
+ if (edge.key == 0)
+ return NULL;
+
+ return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge edge = {};
+
+ if (attributes == NULL)
+ return NULL;
+
+ /* Key is the IPv4 local address */
+ if (!IPV4_NET0(attributes->standard.remote.s_addr))
+ edge.key = ((uint64_t)attributes->standard.remote.s_addr)
+ & 0xffffffff;
+ /* or the IPv6 local address if IPv4 is not defined */
+ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.remote6))
+ edge.key =
+ (uint64_t)(attributes->standard.remote6.s6_addr32[0]
+ & 0xffffffff)
+ | ((uint64_t)attributes->standard.remote6.s6_addr32[1]
+ << 32);
+ /* of local identifier if no IP addresses are defined */
+ else if (attributes->standard.remote_id != 0)
+ edge.key = (uint64_t)(
+ (attributes->standard.remote_id & 0xffffffff)
+ | ((uint64_t)attributes->standard.local_id << 32));
+
+ if (edge.key == 0)
+ return NULL;
+
+ return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_edge_update(struct ls_ted *ted,
+ struct ls_attributes *attributes)
+{
+ struct ls_edge *old;
+
+ if (attributes == NULL)
+ return NULL;
+
+ /* First, search for an existing Edge */
+ old = ls_find_edge_by_source(ted, attributes);
+ if (old) {
+ /* Check if attributes are similar */
+ if (!ls_attributes_same(old->attributes, attributes)) {
+ ls_attributes_del(old->attributes);
+ old->attributes = attributes;
+ }
+ return old;
+ }
+
+ /* If not found, add new Edge from the attributes */
+ return ls_edge_add(ted, attributes);
+}
+
+void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
+{
+ /* Fist disconnect Edge */
+ ls_disconnect_edge(edge);
+ /* Then remove it from the Data Base */
+ edges_del(&ted->edges, edge);
+ XFREE(MTYPE_LS_DB, edge);
+}
+
+/**
+ * Link State Subnet Management functions.
+ */
+struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
+ struct ls_prefix *ls_pref)
+{
+ struct ls_subnet *new;
+ struct ls_vertex *vertex;
+ struct ls_node *node;
+ const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+ if (ls_pref == NULL)
+ return NULL;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet));
+ new->ls_pref = ls_pref;
+ new->key = ls_pref->pref;
+
+ /* Find Vertex */
+ vertex = ls_find_vertex_by_id(ted, ls_pref->adv);
+ if (vertex == NULL) {
+ /* Create a new temporary Node & Vertex if not found */
+ node = ls_node_new(ls_pref->adv, inaddr_any, in6addr_any);
+ vertex = ls_vertex_add(ted, node);
+ }
+ /* And attach the subnet to the corresponding Vertex */
+ new->vertex = vertex;
+ listnode_add(vertex->prefixes, new);
+
+ subnets_add(&ted->subnets, new);
+
+ return new;
+}
+
+void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
+{
+ subnets_del(&ted->subnets, subnet);
+ XFREE(MTYPE_LS_DB, subnet);
+}
+
+struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+{
+ struct ls_subnet subnet = {};
+
+ subnet.key = prefix;
+ return subnets_find(&ted->subnets, &subnet);
+}
+
+/**
+ * Link State TED management functions
+ */
+struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
+ uint32_t as_number)
+{
+ struct ls_ted *new;
+
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_ted));
+ if (new == NULL)
+ return new;
+
+ /* Set basic information for this ted */
+ new->key = key;
+ new->as_number = as_number;
+ strlcpy(new->name, name, MAX_NAME_LENGTH);
+
+ /* Initialize the various RB tree */
+ vertices_init(&new->vertices);
+ edges_init(&new->edges);
+ subnets_init(&new->subnets);
+
+ return new;
+}
+
+void ls_ted_del(struct ls_ted *ted)
+{
+ if (ted == NULL)
+ return;
+
+ /* Release RB Tree */
+ vertices_fini(&ted->vertices);
+ edges_fini(&ted->edges);
+ subnets_fini(&ted->subnets);
+
+ XFREE(MTYPE_LS_DB, ted);
+ ted = NULL;
+}
+
+void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
+{
+ if (vertex == NULL || edge == NULL)
+ return;
+
+ if (source) {
+ listnode_add(vertex->outgoing_edges, edge);
+ edge->source = vertex;
+ } else {
+ listnode_add(vertex->incoming_edges, edge);
+ edge->destination = vertex;
+ }
+}
+
+void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
+{
+
+ if (vertex == NULL || edge == NULL)
+ return;
+
+ if (source) {
+ listnode_delete(vertex->outgoing_edges, edge);
+ edge->source = NULL;
+ } else {
+ listnode_delete(vertex->incoming_edges, edge);
+ edge->destination = NULL;
+ }
+}
+
+void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst,
+ struct ls_edge *edge)
+{
+ if (edge == NULL)
+ return;
+
+ edge->source = src;
+ edge->destination = dst;
+
+ if (src != NULL)
+ listnode_add(src->outgoing_edges, edge);
+
+ if (dst != NULL)
+ listnode_add(dst->incoming_edges, edge);
+
+}
+
+void ls_disconnect_edge(struct ls_edge *edge)
+{
+ if (edge == NULL)
+ return;
+
+ ls_disconnect(edge->source, edge, true);
+ ls_disconnect(edge->destination, edge, false);
+}
+
+/**
+ * Link State Message management functions
+ */
+
+static struct ls_node *ls_parse_node(struct stream *s)
+{
+ struct ls_node *node;
+ size_t len;
+
+ node = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
+ if (node == NULL)
+ return NULL;
+
+ STREAM_GET(&node->adv, s, sizeof(struct ls_node_id));
+ STREAM_GETW(s, node->flags);
+ if (CHECK_FLAG(node->flags, LS_NODE_NAME)) {
+ STREAM_GETC(s, len);
+ STREAM_GET(node->name, s, len);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID))
+ node->router_id.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6))
+ STREAM_GET(&node->router6_id, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(node->flags, LS_NODE_FLAG))
+ STREAM_GETC(s, node->node_flag);
+ if (CHECK_FLAG(node->flags, LS_NODE_TYPE))
+ STREAM_GETC(s, node->type);
+ if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER))
+ STREAM_GETL(s, node->as_number);
+ if (CHECK_FLAG(node->flags, LS_NODE_SR)) {
+ STREAM_GETL(s, node->srgb.lower_bound);
+ STREAM_GETL(s, node->srgb.range_size);
+ STREAM_GETC(s, node->srgb.flag);
+ STREAM_GET(node->algo, s, 2);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) {
+ STREAM_GETL(s, node->srlb.lower_bound);
+ STREAM_GETL(s, node->srlb.range_size);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_MSD))
+ STREAM_GETC(s, node->msd);
+
+ return node;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse Link State Node. Abort!", __func__);
+ XFREE(MTYPE_LS_DB, node);
+ return NULL;
+}
+
+static struct ls_attributes *ls_parse_attributes(struct stream *s)
+{
+ struct ls_attributes *attr;
+ size_t len;
+
+ attr = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
+ if (attr == NULL)
+ return NULL;
+ attr->srlgs = NULL;
+
+ STREAM_GET(&attr->adv, s, sizeof(struct ls_node_id));
+ STREAM_GETL(s, attr->flags);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) {
+ STREAM_GETC(s, len);
+ STREAM_GET(attr->name, s, len);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
+ STREAM_GETL(s, attr->standard.metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
+ STREAM_GETL(s, attr->standard.te_metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
+ STREAM_GETL(s, attr->standard.admin_group);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+ attr->standard.local.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
+ attr->standard.remote.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+ STREAM_GET(&attr->standard.local6, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
+ STREAM_GET(&attr->standard.remote6, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+ STREAM_GETL(s, attr->standard.local_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+ STREAM_GETL(s, attr->standard.remote_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+ STREAM_GETF(s, attr->standard.max_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+ STREAM_GETF(s, attr->standard.max_rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW))
+ for (len = 0; len < MAX_CLASS_TYPE; len++)
+ STREAM_GETF(s, attr->standard.unrsv_bw[len]);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+ STREAM_GETL(s, attr->standard.remote_as);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR))
+ attr->standard.remote_addr.s_addr = stream_get_ipv4(s);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6))
+ STREAM_GET(&attr->standard.remote_addr6, s, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+ STREAM_GETL(s, attr->extended.delay);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) {
+ STREAM_GETL(s, attr->extended.min_delay);
+ STREAM_GETL(s, attr->extended.max_delay);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+ STREAM_GETL(s, attr->extended.jitter);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+ STREAM_GETL(s, attr->extended.pkt_loss);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+ STREAM_GETF(s, attr->extended.ava_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+ STREAM_GETF(s, attr->extended.rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+ STREAM_GETF(s, attr->extended.used_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+ STREAM_GETL(s, attr->adj_sid[0].sid);
+ STREAM_GETC(s, attr->adj_sid[0].flags);
+ STREAM_GETC(s, attr->adj_sid[0].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ STREAM_GET(attr->adj_sid[0].neighbor.sysid, s,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ attr->adj_sid[0].neighbor.addr.s_addr =
+ stream_get_ipv4(s);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+ STREAM_GETL(s, attr->adj_sid[1].sid);
+ STREAM_GETC(s, attr->adj_sid[1].flags);
+ STREAM_GETC(s, attr->adj_sid[1].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ STREAM_GET(attr->adj_sid[1].neighbor.sysid, s,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ attr->adj_sid[1].neighbor.addr.s_addr =
+ stream_get_ipv4(s);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+ STREAM_GETC(s, len);
+ attr->srlgs = XCALLOC(MTYPE_LS_DB, len*sizeof(uint32_t));
+ attr->srlg_len = len;
+ for (len = 0; len < attr->srlg_len; len++)
+ STREAM_GETL(s, attr->srlgs[len]);
+ }
+
+ return attr;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse Link State Attributes. Abort!",
+ __func__);
+ /* Clean memeory allocation */
+ if (attr->srlgs != NULL)
+ XFREE(MTYPE_LS_DB, attr->srlgs);
+ XFREE(MTYPE_LS_DB, attr);
+ return NULL;
+
+}
+
+static struct ls_prefix *ls_parse_prefix(struct stream *s)
+{
+ struct ls_prefix *ls_pref;
+ size_t len;
+
+ ls_pref = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix));
+ if (ls_pref == NULL)
+ return NULL;
+
+ STREAM_GET(&ls_pref->adv, s, sizeof(struct ls_node_id));
+ STREAM_GETW(s, ls_pref->flags);
+ STREAM_GETC(s, ls_pref->pref.family);
+ STREAM_GETW(s, ls_pref->pref.prefixlen);
+ len = prefix_blen(&ls_pref->pref);
+ STREAM_GET(&ls_pref->pref.u.prefix, s, len);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG))
+ STREAM_GETC(s, ls_pref->igp_flag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG))
+ STREAM_GETL(s, ls_pref->route_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG))
+ STREAM_GETQ(s, ls_pref->extended_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC))
+ STREAM_GETL(s, ls_pref->metric);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) {
+ STREAM_GETL(s, ls_pref->sr.sid);
+ STREAM_GETC(s, ls_pref->sr.sid_flag);
+ STREAM_GETC(s, ls_pref->sr.algo);
+ }
+
+ return ls_pref;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse Link State Prefix. Abort!", __func__);
+ XFREE(MTYPE_LS_DB, ls_pref);
+ return NULL;
+}
+
+struct ls_message *ls_parse_msg(struct stream *s)
+{
+ struct ls_message *msg;
+
+ msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+ if (msg == NULL)
+ return NULL;
+
+ /* Read LS Message header */
+ STREAM_GETC(s, msg->event);
+ STREAM_GETC(s, msg->type);
+ STREAM_GET(&msg->remote_id, s, sizeof(struct ls_node_id));
+
+ /* Read Message Payload */
+ switch (msg->type) {
+ case LS_MSG_TYPE_NODE:
+ msg->data.node = ls_parse_node(s);
+ break;
+ case LS_MSG_TYPE_ATTRIBUTES:
+ msg->data.attr = ls_parse_attributes(s);
+ break;
+ case LS_MSG_TYPE_PREFIX:
+ msg->data.prefix = ls_parse_prefix(s);
+ break;
+ default:
+ zlog_err("Unsupported Payload");
+ goto stream_failure;
+ }
+
+ if (msg->data.node == NULL || msg->data.attr == NULL
+ || msg->data.prefix == NULL)
+ goto stream_failure;
+
+ return msg;
+
+stream_failure:
+ zlog_err("LS(%s): Could not parse LS message. Abort!", __func__);
+ XFREE(MTYPE_LS_DB, msg);
+ return NULL;
+}
+
+static int ls_format_node(struct stream *s, struct ls_node *node)
+{
+ size_t len;
+
+ /* Push Advertise node information first */
+ stream_put(s, &node->adv, sizeof(struct ls_node_id));
+
+ /* Push Flags & Origin then Node information if there are present */
+ stream_putw(s, node->flags);
+ if (CHECK_FLAG(node->flags, LS_NODE_NAME)) {
+ len = strlen(node->name);
+ stream_putc(s, len + 1);
+ stream_put(s, node->name, len);
+ stream_putc(s, '\0');
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID))
+ stream_put_ipv4(s, node->router_id.s_addr);
+ if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6))
+ stream_put(s, &node->router6_id, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(node->flags, LS_NODE_FLAG))
+ stream_putc(s, node->node_flag);
+ if (CHECK_FLAG(node->flags, LS_NODE_TYPE))
+ stream_putc(s, node->type);
+ if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER))
+ stream_putl(s, node->as_number);
+ if (CHECK_FLAG(node->flags, LS_NODE_SR)) {
+ stream_putl(s, node->srgb.lower_bound);
+ stream_putl(s, node->srgb.range_size);
+ stream_putc(s, node->srgb.flag);
+ stream_put(s, node->algo, 2);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) {
+ stream_putl(s, node->srlb.lower_bound);
+ stream_putl(s, node->srlb.range_size);
+ }
+ if (CHECK_FLAG(node->flags, LS_NODE_MSD))
+ stream_putc(s, node->msd);
+
+ return 0;
+}
+
+static int ls_format_attributes(struct stream *s, struct ls_attributes *attr)
+{
+ size_t len;
+
+ /* Push Advertise node information first */
+ stream_put(s, &attr->adv, sizeof(struct ls_node_id));
+
+ /* Push Flags & Origin then LS attributes if there are present */
+ stream_putl(s, attr->flags);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) {
+ len = strlen(attr->name);
+ stream_putc(s, len + 1);
+ stream_put(s, attr->name, len);
+ stream_putc(s, '\0');
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
+ stream_putl(s, attr->standard.metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
+ stream_putl(s, attr->standard.te_metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
+ stream_putl(s, attr->standard.admin_group);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+ stream_put_ipv4(s, attr->standard.local.s_addr);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
+ stream_put_ipv4(s, attr->standard.remote.s_addr);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+ stream_put(s, &attr->standard.local6, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
+ stream_put(s, &attr->standard.remote6, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+ stream_putl(s, attr->standard.local_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+ stream_putl(s, attr->standard.remote_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+ stream_putf(s, attr->standard.max_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+ stream_putf(s, attr->standard.max_rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW))
+ for (len = 0; len < MAX_CLASS_TYPE; len++)
+ stream_putf(s, attr->standard.unrsv_bw[len]);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+ stream_putl(s, attr->standard.remote_as);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR))
+ stream_put_ipv4(s, attr->standard.remote_addr.s_addr);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6))
+ stream_put(s, &attr->standard.remote_addr6, IPV6_MAX_BYTELEN);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+ stream_putl(s, attr->extended.delay);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) {
+ stream_putl(s, attr->extended.min_delay);
+ stream_putl(s, attr->extended.max_delay);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+ stream_putl(s, attr->extended.jitter);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+ stream_putl(s, attr->extended.pkt_loss);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+ stream_putf(s, attr->extended.ava_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+ stream_putf(s, attr->extended.rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+ stream_putf(s, attr->extended.used_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+ stream_putl(s, attr->adj_sid[0].sid);
+ stream_putc(s, attr->adj_sid[0].flags);
+ stream_putc(s, attr->adj_sid[0].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ stream_put(s, attr->adj_sid[0].neighbor.sysid,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ stream_put_ipv4(s,
+ attr->adj_sid[0].neighbor.addr.s_addr);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+ stream_putl(s, attr->adj_sid[1].sid);
+ stream_putc(s, attr->adj_sid[1].flags);
+ stream_putc(s, attr->adj_sid[1].weight);
+ if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+ stream_put(s, attr->adj_sid[1].neighbor.sysid,
+ ISO_SYS_ID_LEN);
+ else if (attr->adv.origin == OSPFv2)
+ stream_put_ipv4(s,
+ attr->adj_sid[1].neighbor.addr.s_addr);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+ stream_putc(s, attr->srlg_len);
+ for (len = 0; len < attr->srlg_len; len++)
+ stream_putl(s, attr->srlgs[len]);
+ }
+
+ return 0;
+}
+
+static int ls_format_prefix(struct stream *s, struct ls_prefix *ls_pref)
+{
+ size_t len;
+
+ /* Push Advertise node information first */
+ stream_put(s, &ls_pref->adv, sizeof(struct ls_node_id));
+
+ /* Push Flags, Origin & Prefix then information if there are present */
+ stream_putw(s, ls_pref->flags);
+ stream_putc(s, ls_pref->pref.family);
+ stream_putw(s, ls_pref->pref.prefixlen);
+ len = prefix_blen(&ls_pref->pref);
+ stream_put(s, &ls_pref->pref.u.prefix, len);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG))
+ stream_putc(s, ls_pref->igp_flag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG))
+ stream_putl(s, ls_pref->route_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG))
+ stream_putq(s, ls_pref->extended_tag);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC))
+ stream_putl(s, ls_pref->metric);
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) {
+ stream_putl(s, ls_pref->sr.sid);
+ stream_putc(s, ls_pref->sr.sid_flag);
+ stream_putc(s, ls_pref->sr.algo);
+ }
+
+ return 0;
+}
+
+static int ls_format_msg(struct stream *s, struct ls_message *msg)
+{
+
+ /* Prepare Link State header */
+ stream_putc(s, msg->event);
+ stream_putc(s, msg->type);
+ stream_put(s, &msg->remote_id, sizeof(struct ls_node_id));
+
+ /* Add Message Payload */
+ switch (msg->type) {
+ case LS_MSG_TYPE_NODE:
+ return ls_format_node(s, msg->data.node);
+ case LS_MSG_TYPE_ATTRIBUTES:
+ return ls_format_attributes(s, msg->data.attr);
+ case LS_MSG_TYPE_PREFIX:
+ return ls_format_prefix(s, msg->data.prefix);
+ default:
+ zlog_warn("Unsupported Payload");
+ break;
+ }
+
+ return -1;
+}
+
+int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
+ struct zapi_opaque_reg_info *dst)
+{
+ struct stream *s;
+ uint16_t flags = 0;
+
+ /* Check buffer size */
+ if (STREAM_SIZE(zclient->obuf) <
+ (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg)))
+ return -1;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
+
+ /* Send sub-type, flags and destination for unicast message */
+ stream_putl(s, LINK_STATE_UPDATE);
+ if (dst != NULL) {
+ SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST);
+ stream_putw(s, flags);
+ /* Send destination client info */
+ stream_putc(s, dst->proto);
+ stream_putw(s, dst->instance);
+ stream_putl(s, dst->session_id);
+ } else
+ stream_putw(s, flags);
+
+ /* Format Link State message */
+ if (ls_format_msg(s, msg) < 0) {
+ stream_reset(s);
+ return -1;
+ }
+
+ /* Put length into the header at the start of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+struct ls_message *ls_vertex2msg(struct ls_message *msg,
+ struct ls_vertex *vertex)
+{
+ /* Allocate space if needed */
+ if (msg == NULL)
+ msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+ else
+ memset(msg, 0, sizeof(*msg));
+
+ msg->type = LS_MSG_TYPE_NODE;
+ msg->data.node = vertex->node;
+ msg->remote_id.origin = NONE;
+
+ return msg;
+}
+
+struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge)
+{
+ /* Allocate space if needed */
+ if (msg == NULL)
+ msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+ else
+ memset(msg, 0, sizeof(*msg));
+
+ msg->type = LS_MSG_TYPE_ATTRIBUTES;
+ msg->data.attr = edge->attributes;
+ if (edge->destination != NULL)
+ msg->remote_id = edge->destination->node->adv;
+ else
+ msg->remote_id.origin = NONE;
+
+ return msg;
+}
+
+struct ls_message *ls_subnet2msg(struct ls_message *msg,
+ struct ls_subnet *subnet)
+{
+ /* Allocate space if needed */
+ if (msg == NULL)
+ msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+ else
+ memset(msg, 0, sizeof(*msg));
+
+ msg->type = LS_MSG_TYPE_PREFIX;
+ msg->data.prefix = subnet->ls_pref;
+ msg->remote_id.origin = NONE;
+
+ return msg;
+}
+
+void ls_delete_msg(struct ls_message *msg)
+{
+ if (msg == NULL)
+ return;
+
+ switch (msg->type) {
+ case LS_MSG_TYPE_NODE:
+ if (msg->data.node)
+ XFREE(MTYPE_LS_DB, msg->data.node);
+ break;
+ case LS_MSG_TYPE_ATTRIBUTES:
+ if (msg->data.attr)
+ XFREE(MTYPE_LS_DB, msg->data.attr);
+ break;
+ case LS_MSG_TYPE_PREFIX:
+ if (msg->data.prefix)
+ XFREE(MTYPE_LS_DB, msg->data.prefix);
+ break;
+ default:
+ break;
+ }
+
+ XFREE(MTYPE_LS_DB, msg);
+}
+
+int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
+ struct zapi_opaque_reg_info *dst)
+{
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ struct ls_message msg;
+
+ /* Prepare message */
+ msg.event = LS_MSG_EVENT_SYNC;
+
+ /* Loop TED, start sending Node, then Attributes and finally Prefix */
+ frr_each(vertices, &ted->vertices, vertex) {
+ ls_vertex2msg(&msg, vertex);
+ ls_send_msg(zclient, &msg, dst);
+ }
+ frr_each(edges, &ted->edges, edge) {
+ ls_edge2msg(&msg, edge);
+ ls_send_msg(zclient, &msg, dst);
+ }
+ frr_each(subnets, &ted->subnets, subnet) {
+ ls_subnet2msg(&msg, subnet);
+ ls_send_msg(zclient, &msg, dst);
+ }
+ return 0;
+}
+
+void ls_dump_ted(struct ls_ted *ted)
+{
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ struct ls_message msg;
+
+ zlog_debug("(%s) Ted init", __func__);
+ /* Prepare message */
+ msg.event = LS_MSG_EVENT_SYNC;
+
+ /* Loop TED, start printing Node, then Attributes and finally Prefix */
+ frr_each(vertices, &ted->vertices, vertex) {
+ ls_vertex2msg(&msg, vertex);
+ zlog_debug("\tTed node (%s %pI4 %s)",
+ vertex->node->name[0] ? vertex->node->name
+ : "no name node",
+ &vertex->node->router_id,
+ vertex->node->adv.origin == DIRECT ? "DIRECT"
+ : "NO DIRECT");
+ struct listnode *lst_node;
+ struct ls_edge *vertex_edge;
+
+ for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node,
+ vertex_edge)) {
+ zlog_debug(
+ "\t\tinc edge key:%lldn attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ vertex_edge->key,
+ &vertex_edge->attributes->adv.id.ip.addr,
+ &vertex_edge->attributes->standard.local,
+ &vertex_edge->attributes->standard.remote);
+ }
+ for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node,
+ vertex_edge)) {
+ zlog_debug(
+ "\t\tout edge key:%lld attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ vertex_edge->key,
+ &vertex_edge->attributes->adv.id.ip.addr,
+ &vertex_edge->attributes->standard.local,
+ &vertex_edge->attributes->standard.remote);
+ }
+ }
+ frr_each(edges, &ted->edges, edge) {
+ ls_edge2msg(&msg, edge);
+ zlog_debug("\tTed edge key:%lld src:%s dst:%s", edge->key,
+ edge->source ? edge->source->node->name
+ : "no_source",
+ edge->destination ? edge->destination->node->name
+ : "no_dest");
+ }
+ frr_each(subnets, &ted->subnets, subnet) {
+ ls_subnet2msg(&msg, subnet);
+ zlog_debug(
+ "\tTed subnet key:%s vertex:%pI4 pfx:%pFX",
+ subnet->key.family == AF_INET
+ ? inet_ntoa(subnet->key.u.prefix4)
+ : inet6_ntoa(subnet->key.u.prefix6),
+ &subnet->vertex->node->adv.id.ip.addr,
+ &subnet->ls_pref->pref);
+ }
+ zlog_debug("(%s) Ted end", __func__);
+}
diff --git a/lib/link_state.h b/lib/link_state.h
new file mode 100644
index 0000000000..93669f5b23
--- /dev/null
+++ b/lib/link_state.h
@@ -0,0 +1,780 @@
+/*
+ * Link State Database definition - ted.h
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2020 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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
+ */
+
+#ifndef _FRR_LINK_STATE_H_
+#define _FRR_LINK_STATE_H_
+
+#include "typesafe.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file defines the model used to implement a Link State Database
+ * suitable to be used by various protocol like RSVP-TE, BGP-LS, PCEP ...
+ * This database is normally fulfill by the link state routing protocol,
+ * commonly OSPF or ISIS, carrying Traffic Engineering information within
+ * Link State Attributes. See, RFC3630.(OSPF-TE) and RFC5305 (ISIS-TE).
+ *
+ * At least, 3 types of Link State structure are defined:
+ * - Link State Node that groups all information related to a node
+ * - Link State Attributes that groups all information related to a link
+ * - Link State Prefix that groups all information related to a prefix
+ *
+ * These 3 types of structures are those handled by BGP-LS (see RFC7752).
+ *
+ * Each structure, in addition to the specific parameters, embed the node
+ * identifier which advertises the Link State and a bit mask as flags to
+ * indicates which parameters are valid i.e. for which the value corresponds
+ * to a Link State information convey by the routing protocol.
+ * Node identifier is composed of the route id as IPv4 address plus the area
+ * id for OSPF and the ISO System id plus the IS-IS level for IS-IS.
+ */
+
+/* Link State Common definitions */
+#define MAX_NAME_LENGTH 256
+#define ISO_SYS_ID_LEN 6
+
+/* Type of Node */
+enum ls_node_type {
+ STANDARD, /* a P or PE node */
+ ABR, /* an Array Border Node */
+ ASBR, /* an Autonomous System Border Node */
+ PSEUDO, /* a Pseudo Node */
+};
+
+/* Origin of the Link State information */
+enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC};
+
+/**
+ * Link State Node Identifier as:
+ * - IPv4 address + Area ID for OSPF
+ * - ISO System ID + ISIS Level for ISIS
+ */
+struct ls_node_id {
+ enum ls_origin origin; /* Origin of the LS information */
+ union {
+ struct {
+ struct in_addr addr; /* OSPF Router IS */
+ struct in_addr area_id; /* OSPF Area ID */
+ } ip;
+ struct {
+ uint8_t sys_id[ISO_SYS_ID_LEN]; /* ISIS System ID */
+ uint8_t level; /* ISIS Level */
+ uint8_t padding;
+ } iso;
+ } id __attribute__((aligned(8)));
+};
+
+/* Link State flags to indicate which Node parameters are valid */
+#define LS_NODE_UNSET 0x0000
+#define LS_NODE_NAME 0x0001
+#define LS_NODE_ROUTER_ID 0x0002
+#define LS_NODE_ROUTER_ID6 0x0004
+#define LS_NODE_FLAG 0x0008
+#define LS_NODE_TYPE 0x0010
+#define LS_NODE_AS_NUMBER 0x0020
+#define LS_NODE_SR 0x0040
+#define LS_NODE_SRLB 0x0080
+#define LS_NODE_MSD 0x0100
+
+/* Link State Node structure */
+struct ls_node {
+ uint16_t flags; /* Flag for parameters validity */
+ struct ls_node_id adv; /* Adv. Router of this Link State */
+ char name[MAX_NAME_LENGTH]; /* Name of the Node (IS-IS only) */
+ struct in_addr router_id; /* IPv4 Router ID */
+ struct in6_addr router6_id; /* IPv6 Router ID */
+ uint8_t node_flag; /* IS-IS or OSPF Node flag */
+ enum node_type type; /* Type of Node */
+ uint32_t as_number; /* Local or neighbor AS number */
+ struct { /* Segment Routing Global Block */
+ uint32_t lower_bound; /* MPLS label lower bound */
+ uint32_t range_size; /* MPLS label range size */
+ uint8_t flag; /* IS-IS SRGB flags */
+ } srgb;
+#define LS_NODE_SRGB_SIZE 9
+ struct { /* Segment Routing Local Block */
+ uint32_t lower_bound; /* MPLS label lower bound */
+ uint32_t range_size; /* MPLS label range size */
+ } srlb;
+#define LS_NODE_SRLB_SIZE 8
+ uint8_t algo[2]; /* Segment Routing Algorithms */
+ uint8_t msd; /* Maximum Stack Depth */
+};
+
+/* Link State flags to indicate which Attribute parameters are valid */
+#define LS_ATTR_UNSET 0x00000000
+#define LS_ATTR_NAME 0x00000001
+#define LS_ATTR_METRIC 0x00000002
+#define LS_ATTR_TE_METRIC 0x00000004
+#define LS_ATTR_ADM_GRP 0x00000008
+#define LS_ATTR_LOCAL_ADDR 0x00000010
+#define LS_ATTR_NEIGH_ADDR 0x00000020
+#define LS_ATTR_LOCAL_ADDR6 0x00000040
+#define LS_ATTR_NEIGH_ADDR6 0x00000080
+#define LS_ATTR_LOCAL_ID 0x00000100
+#define LS_ATTR_NEIGH_ID 0x00000200
+#define LS_ATTR_MAX_BW 0x00000400
+#define LS_ATTR_MAX_RSV_BW 0x00000800
+#define LS_ATTR_UNRSV_BW 0x00001000
+#define LS_ATTR_REMOTE_AS 0x00002000
+#define LS_ATTR_REMOTE_ADDR 0x00004000
+#define LS_ATTR_REMOTE_ADDR6 0x00008000
+#define LS_ATTR_DELAY 0x00010000
+#define LS_ATTR_MIN_MAX_DELAY 0x00020000
+#define LS_ATTR_JITTER 0x00040000
+#define LS_ATTR_PACKET_LOSS 0x00080000
+#define LS_ATTR_AVA_BW 0x00100000
+#define LS_ATTR_RSV_BW 0x00200000
+#define LS_ATTR_USE_BW 0x00400000
+#define LS_ATTR_ADJ_SID 0x00800000
+#define LS_ATTR_BCK_ADJ_SID 0x01000000
+#define LS_ATTR_SRLG 0x02000000
+
+/* Link State Attributes */
+struct ls_attributes {
+ uint32_t flags; /* Flag for parameters validity */
+ struct ls_node_id adv; /* Adv. Router of this Link State */
+ char name[MAX_NAME_LENGTH]; /* Name of the Edge. Could be null */
+ struct { /* Standard TE metrics */
+ uint32_t metric; /* IGP standard metric */
+ uint32_t te_metric; /* Traffic Engineering metric */
+ uint32_t admin_group; /* Administrative Group */
+ struct in_addr local; /* Local IPv4 address */
+ struct in_addr remote; /* Remote IPv4 address */
+ struct in6_addr local6; /* Local IPv6 address */
+ struct in6_addr remote6; /* Remote IPv6 address */
+ uint32_t local_id; /* Local Identifier */
+ uint32_t remote_id; /* Remote Identifier */
+ float max_bw; /* Maximum Link Bandwidth */
+ float max_rsv_bw; /* Maximum Reservable BW */
+ float unrsv_bw[8]; /* Unreserved BW per CT (8) */
+ uint32_t remote_as; /* Remote AS number */
+ struct in_addr remote_addr; /* Remote IPv4 address */
+ struct in6_addr remote_addr6; /* Remote IPv6 address */
+ } standard;
+#define LS_ATTR_STANDARD_SIZE 124
+ struct { /* Extended TE Metrics */
+ uint32_t delay; /* Unidirectional average delay */
+ uint32_t min_delay; /* Unidirectional minimum delay */
+ uint32_t max_delay; /* Unidirectional maximum delay */
+ uint32_t jitter; /* Unidirectional delay variation */
+ uint32_t pkt_loss; /* Unidirectional packet loss */
+ float ava_bw; /* Available Bandwidth */
+ float rsv_bw; /* Reserved Bandwidth */
+ float used_bw; /* Utilized Bandwidth */
+ } extended;
+#define LS_ATTR_EXTENDED_SIZE 32
+ struct { /* (LAN)-Adjacency SID for OSPF */
+ uint32_t sid; /* SID as MPLS label or index */
+ uint8_t flags; /* Flags */
+ uint8_t weight; /* Administrative weight */
+ union {
+ struct in_addr addr; /* Neighbor @IP for OSPF */
+ uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */
+ } neighbor;
+ } adj_sid[2]; /* Primary & Backup (LAN)-Adj. SID */
+#define LS_ATTR_ADJ_SID_SIZE 120
+ uint32_t *srlgs; /* List of Shared Risk Link Group */
+ uint8_t srlg_len; /* number of SRLG in the list */
+};
+
+/* Link State flags to indicate which Prefix parameters are valid */
+#define LS_PREF_UNSET 0x00
+#define LS_PREF_IGP_FLAG 0x01
+#define LS_PREF_ROUTE_TAG 0x02
+#define LS_PREF_EXTENDED_TAG 0x04
+#define LS_PREF_METRIC 0x08
+#define LS_PREF_SR 0x10
+
+/* Link State Prefix */
+struct ls_prefix {
+ uint8_t flags; /* Flag for parameters validity */
+ struct ls_node_id adv; /* Adv. Router of this Link State */
+ struct prefix pref; /* IPv4 or IPv6 prefix */
+ uint8_t igp_flag; /* IGP Flags associated to the prefix */
+ uint32_t route_tag; /* IGP Route Tag */
+ uint64_t extended_tag; /* IGP Extended Route Tag */
+ uint32_t metric; /* Route metric for this prefix */
+ struct {
+ uint32_t sid; /* Segment Routing ID */
+ uint8_t sid_flag; /* Segment Routing Flags */
+ uint8_t algo; /* Algorithm for Segment Routing */
+ } sr;
+};
+
+/**
+ * Create a new Link State Node. Structure is dynamically allocated.
+ *
+ * @param adv Mandatory Link State Node ID i.e. advertise router information
+ * @param rid Router ID as IPv4 address
+ * @param rid6 Router ID as IPv6 address
+ *
+ * @return New Link State Node
+ */
+extern struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
+ struct in6_addr rid6);
+
+/**
+ * Remove Link State Node. Data structure is freed.
+ *
+ * @param node Pointer to a valid Link State Node structure
+ */
+extern void ls_node_del(struct ls_node *node);
+
+/**
+ * Check if two Link State Nodes are equal. Note that this routine has the same
+ * return value sense as '==' (which is different from a comparison).
+ *
+ * @param n1 First Link State Node to be compare
+ * @param n2 Second Link State Node to be compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_node_same(struct ls_node *n1, struct ls_node *n2);
+
+/**
+ * Create a new Link State Attributes. Structure is dynamically allocated.
+ * At least one of parameters MUST be valid and not equal to 0.
+ *
+ * @param adv Mandatory Link State Node ID i.e. advertise router ID
+ * @param local Local IPv4 address
+ * @param local6 Local Ipv6 address
+ * @param local_id Local Identifier
+ *
+ * @return New Link State Attributes
+ */
+extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
+ struct in_addr local,
+ struct in6_addr local6,
+ uint32_t local_id);
+
+/**
+ * Remove Link State Attributes. Data structure is freed.
+ *
+ * @param attr Pointer to a valid Link State Attribute structure
+ */
+extern void ls_attributes_del(struct ls_attributes *attr);
+
+/**
+ * Check if two Link State Attributes are equal. Note that this routine has the
+ * same return value sense as '==' (which is different from a comparison).
+ *
+ * @param a1 First Link State Attributes to be compare
+ * @param a2 Second Link State Attributes to be compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_attributes_same(struct ls_attributes *a1,
+ struct ls_attributes *a2);
+
+/**
+ * In addition a Graph model is defined as an overlay on top of link state
+ * database in order to ease Path Computation algorithm implementation.
+ * Denoted G(V, E), a graph is composed by a list of Vertices (V) which
+ * represents the network Node and a list of Edges (E) which represents node
+ * Link. An additional list of prefixes (P) is also added.
+ * A prefix (P) is also attached to the Vertex (V) which advertise it.
+ *
+ * Vertex (V) contains the list of outgoing Edges (E) that connect this Vertex
+ * with its direct neighbors and the list of incoming Edges (E) that connect
+ * the direct neighbors to this Vertex. Indeed, the Edge (E) is unidirectional,
+ * thus, it is necessary to add 2 Edges to model a bidirectional relation
+ * between 2 Vertices.
+ *
+ * Edge (E) contains the source and destination Vertex that this Edge
+ * is connecting.
+ *
+ * A unique Key is used to identify both Vertices and Edges within the Graph.
+ * An easy way to build this key is to used the IP address: i.e. loopback
+ * address for Vertices and link IP address for Edges.
+ *
+ * -------------- --------------------------- --------------
+ * | Connected |---->| Connected Edge Va to Vb |--->| Connected |
+ * --->| Vertex | --------------------------- | Vertex |---->
+ * | | | |
+ * | - Key (Va) | | - Key (Vb) |
+ * <---| - Vertex | --------------------------- | - Vertex |<----
+ * | |<----| Connected Edge Vb to Va |<---| |
+ * -------------- --------------------------- --------------
+ *
+ */
+
+/* Link State Vertex structure */
+PREDECL_RBTREE_UNIQ(vertices)
+struct ls_vertex {
+ struct vertices_item entry; /* Entry in RB Tree */
+ uint64_t key; /* Unique Key identifier */
+ struct ls_node *node; /* Link State Node */
+ struct list *incoming_edges; /* List of incoming Link State links */
+ struct list *outgoing_edges; /* List of outgoing Link State links */
+ struct list *prefixes; /* List of advertised prefix */
+};
+
+/* Link State Edge structure */
+PREDECL_RBTREE_UNIQ(edges)
+struct ls_edge {
+ struct edges_item entry; /* Entry in RB tree */
+ uint64_t key; /* Unique Key identifier */
+ struct ls_attributes *attributes; /* Link State attributes */
+ struct ls_vertex *source; /* Pointer to the source Vertex */
+ struct ls_vertex *destination; /* Pointer to the destination Vertex */
+};
+
+/* Link State Subnet structure */
+PREDECL_RBTREE_UNIQ(subnets)
+struct ls_subnet {
+ struct subnets_item entry; /* Entry in RB tree */
+ struct prefix key; /* Unique Key identifier */
+ struct ls_vertex *vertex; /* Back pointer to the Vertex owner */
+ struct ls_prefix *ls_pref; /* Link State Prefix */
+};
+
+/* Declaration of Vertices, Edges and Prefixes RB Trees */
+macro_inline int vertex_cmp(const struct ls_vertex *node1,
+ const struct ls_vertex *node2)
+{
+ return (node1->key - node2->key);
+}
+DECLARE_RBTREE_UNIQ(vertices, struct ls_vertex, entry, vertex_cmp)
+
+macro_inline int edge_cmp(const struct ls_edge *edge1,
+ const struct ls_edge *edge2)
+{
+ return (edge1->key - edge2->key);
+}
+DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp)
+
+macro_inline int subnet_cmp(const struct ls_subnet *a,
+ const struct ls_subnet *b)
+{
+ return prefix_cmp(&a->key, &b->key);
+}
+DECLARE_RBTREE_UNIQ(subnets, struct ls_subnet, entry, subnet_cmp)
+
+/* Link State TED Structure */
+struct ls_ted {
+ uint32_t key; /* Unique identifier */
+ char name[MAX_NAME_LENGTH]; /* Name of this graph. Could be null */
+ uint32_t as_number; /* AS number of the modeled network */
+ struct ls_vertex *self; /* Vertex of the FRR instance */
+ struct vertices_head vertices; /* List of Vertices */
+ struct edges_head edges; /* List of Edges */
+ struct subnets_head subnets; /* List of Subnets */
+};
+
+/**
+ * Create a new Link State Vertex structure and initialize is with the Link
+ * State Node parameter.
+ *
+ * @param node Link State Node
+ *
+ * @return New Vertex
+ */
+extern struct ls_vertex *ls_vertex_new(struct ls_node *node);
+
+/**
+ * Delete Link State Vertex. This function clean internal Vertex lists (incoming
+ * and outgoing Link State Edge and Link State Subnet). Note that referenced
+ * objects of the different lists (Edges & SubNet) are not removed as they could
+ * be connected to other Vertices.
+ *
+ * @param vertex Link State Vertex to be removed
+ */
+extern void ls_vertex_del(struct ls_vertex *vertex);
+
+/**
+ * Add new vertex to the Link State DB. Vertex is created from the Link State
+ * Node. Vertex data structure is dynamically allocated.
+ *
+ * @param ted Traffic Engineering Database structure
+ * @param node Link State Node
+ *
+ * @return New Vertex or NULL in case of error
+ */
+extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted,
+ struct ls_node *node);
+
+/**
+ * Update Vertex with the Link State Node. A new vertex is created if no one
+ * corresponds to the Link State Node.
+ *
+ * @param ted Link State Data Base
+ * @param node Link State Node to be updated
+ *
+ * @return Updated Link State Vertex or Null in case of error
+ */
+extern struct ls_vertex *ls_vertex_update(struct ls_ted *ted,
+ struct ls_node *node);
+
+/**
+ * Remove Vertex from the Link State DB. Vertex Data structure is freed but
+ * not the Link State Node. Link State DB is not modified if Vertex is NULL or
+ * not found in the Data Base.
+ *
+ * @param ted Link State Data Base
+ * @param vertex Vertex to be removed
+ */
+extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex);
+
+/**
+ * Find Vertex in the Link State DB by its unique key.
+ *
+ * @param ted Link State Data Base
+ * @param key Vertex Key different from 0
+ *
+ * @return Vertex if found, NULL otherwise
+ */
+extern struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted,
+ const uint64_t key);
+
+/**
+ * Find Vertex in the Link State DB by its Link State Node.
+ *
+ * @param ted Link State Data Base
+ * @param nid Link State Node ID
+ *
+ * @return Vertex if found, NULL otherwise
+ */
+extern struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted,
+ struct ls_node_id nid);
+
+/**
+ * Check if two Vertices are equal. Note that this routine has the same return
+ * value sense as '==' (which is different from a comparison).
+ *
+ * @param v1 First vertex to compare
+ * @param v2 Second vertex to compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2);
+
+/**
+ * Add new Edge to the Link State DB. Edge is created from the Link State
+ * Attributes. Edge data structure is dynamically allocated.
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State attributes
+ *
+ * @return New Edge or NULL in case of error
+ */
+extern struct ls_edge *ls_edge_add(struct ls_ted *ted,
+ struct ls_attributes *attributes);
+
+/**
+ * Update the Link State Attributes information of an existing Edge. If there is
+ * no corresponding Edge in the Link State Data Base, a new Edge is created.
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State Attributes
+ *
+ * @return Updated Link State Edge, or NULL in case of error
+ */
+extern struct ls_edge *ls_edge_update(struct ls_ted *ted,
+ struct ls_attributes *attributes);
+
+/**
+ * Remove Edge from the Link State DB. Edge data structure is freed but not the
+ * Link State Attributes data structure. Link State DB is not modified if Edge
+ * is NULL or not found in the Data Base.
+ *
+ * @param ted Link State Data Base
+ * @param edge Edge to be removed
+ */
+extern void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge);
+
+/**
+ * Find Edge in the Link State Data Base by Edge key.
+ *
+ * @param ted Link State Data Base
+ * @param key Edge key
+ *
+ * @return Edge if found, NULL otherwise
+ */
+extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
+ const uint64_t key);
+
+/**
+ * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6
+ * address or local ID) informations of the Link
+ * State Attributes
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State Attributes
+ *
+ * @return Edge if found, NULL otherwise
+ */
+extern struct ls_edge *
+ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes);
+
+/**
+ * Find Edge in the Link State Data Base by the destination (remote IPv4 or IPv6
+ * address of remote ID) information of the Link State Attributes
+ *
+ * @param ted Link State Data Base
+ * @param attributes Link State Attributes
+ *
+ * @return Edge if found, NULL otherwise
+ */
+extern struct ls_edge *
+ls_find_edge_by_destination(struct ls_ted *ted,
+ struct ls_attributes *attributes);
+
+/**
+ * Add new Subnet to the Link State DB. Subnet is created from the Link State
+ * prefix. Subnet data structure is dynamically allocated.
+ *
+ * @param ted Link State Data Base
+ * @param pref Link State Prefix
+ *
+ * @return New Subnet
+ */
+extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
+ struct ls_prefix *pref);
+
+/**
+ * Remove Subnet from the Link State DB. Subnet data structure is freed but
+ * not the Link State prefix data structure. Link State DB is not modified
+ * if Subnet is NULL or not found in the Data Base.
+ *
+ * @param ted Link State Data Base
+ * @param subnet Subnet to be removed
+ */
+extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet);
+
+/**
+ * Find Subnet in the Link State Data Base by prefix.
+ *
+ * @param ted Link State Data Base
+ * @param prefix Link State Prefix
+ *
+ * @return Subnet if found, NULL otherwise
+ */
+extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
+ const struct prefix prefix);
+
+/**
+ * Create a new Link State Data Base.
+ *
+ * @param key Unique key of the data base. Must be different from 0
+ * @param name Name of the data base (may be NULL)
+ * @param asn AS Number for this data base. Must be different from 0
+ *
+ * @return New Link State Database or NULL in case of error
+ */
+extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
+ uint32_t asn);
+
+/**
+ * Delete existing Link State Data Base.
+ *
+ * @param ted Link State Data Base
+ */
+extern void ls_ted_del(struct ls_ted *ted);
+
+/**
+ * Connect Source and Destination Vertices by given Edge. Only non NULL source
+ * and destination vertices are connected.
+ *
+ * @param src Link State Source Vertex
+ * @param dst Link State Destination Vertex
+ * @param edge Link State Edge. Must not be NULL
+ */
+extern void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst,
+ struct ls_edge *edge);
+
+/**
+ * Connect Link State Edge to the Link State Vertex which could be a Source or
+ * a Destination Vertex.
+ *
+ * @param vertex Link State Vertex to be connected. Must not be NULL
+ * @param edge Link State Edge connection. Must not be NULL
+ * @param source True for a Source, false for a Destination Vertex
+ */
+extern void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge,
+ bool source);
+
+/**
+ * Disconnect Link State Edge from the Link State Vertex which could be a
+ * Source or a Destination Vertex.
+ *
+ * @param vertex Link State Vertex to be connected. Must not be NULL
+ * @param edge Link State Edge connection. Must not be NULL
+ * @param source True for a Source, false for a Destination Vertex
+ */
+extern void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge,
+ bool source);
+
+/**
+ * Disconnect Link State Edge from both Source and Destination Vertex.
+ *
+ * @param edge Link State Edge to be disconnected
+ */
+extern void ls_disconnect_edge(struct ls_edge *edge);
+
+
+/**
+ * The Link State Message is defined to convey Link State parameters from
+ * the routing protocol (OSPF or IS-IS) to other daemons e.g. BGP.
+ *
+ * The structure is composed of:
+ * - Event of the message:
+ * - Sync: Send the whole LS DB following a request
+ * - Add: Send the a new Link State element
+ * - Update: Send an update of an existing Link State element
+ * - Delete: Indicate that the given Link State element is removed
+ * - Type of Link State element: Node, Attribute or Prefix
+ * - Remote node id when known
+ * - Data: Node, Attributes or Prefix
+ *
+ * A Link State Message can carry only one Link State Element (Node, Attributes
+ * of Prefix) at once, and only one Link State Message is sent through ZAPI
+ * Opaque Link State type at once.
+ */
+
+/* ZAPI Opaque Link State Message Event */
+#define LS_MSG_EVENT_SYNC 1
+#define LS_MSG_EVENT_ADD 2
+#define LS_MSG_EVENT_UPDATE 3
+#define LS_MSG_EVENT_DELETE 4
+
+/* ZAPI Opaque Link State Message sub-Type */
+#define LS_MSG_TYPE_NODE 1
+#define LS_MSG_TYPE_ATTRIBUTES 2
+#define LS_MSG_TYPE_PREFIX 3
+
+/* Link State Message */
+struct ls_message {
+ uint8_t event; /* Message Event: Sync, Add, Update, Delete */
+ uint8_t type; /* Message Data Type: Node, Attribute, Prefix */
+ struct ls_node_id remote_id; /* Remote Link State Node ID */
+ union {
+ struct ls_node *node; /* Link State Node */
+ struct ls_attributes *attr; /* Link State Attributes */
+ struct ls_prefix *prefix; /* Link State Prefix */
+ } data;
+};
+
+/**
+ * Parse Link State Message from stream. Used this function once receiving a
+ * new ZAPI Opaque message of type Link State.
+ *
+ * @param s Stream buffer. Must not be NULL.
+ *
+ * @return New Link State Message or NULL in case of error
+ */
+extern struct ls_message *ls_parse_msg(struct stream *s);
+
+/**
+ * Delete existing message, freeing all substructure.
+ *
+ * @param msg Link state message to be deleted
+ */
+extern void ls_delete_msg(struct ls_message *msg);
+
+/**
+ * Send Link State Message as new ZAPI Opaque message of type Link State.
+ * If destination is not NULL, message is sent as Unicast otherwise it is
+ * broadcast to all registered daemon.
+ *
+ * @param zclient Zebra Client
+ * @param msg Link State Message to be sent
+ * @param dst Destination daemon for unicast message,
+ * NULL for broadcast message
+ *
+ * @return 0 on success, -1 otherwise
+ */
+extern int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
+ struct zapi_opaque_reg_info *dst);
+
+/**
+ * Create a new Link State Message from a Link State Vertex. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg Link State Message to be filled or NULL
+ * @param vertex Link State Vertex. Must not be NULL
+ *
+ * @return New Link State Message msg parameter is NULL or pointer
+ * to the provided Link State Message
+ */
+extern struct ls_message *ls_vertex2msg(struct ls_message *msg,
+ struct ls_vertex *vertex);
+
+/**
+ * Create a new Link State Message from a Link State Edge. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg Link State Message to be filled or NULL
+ * @param edge Link State Edge. Must not be NULL
+ *
+ * @return New Link State Message msg parameter is NULL or pointer
+ * to the provided Link State Message
+ */
+extern struct ls_message *ls_edge2msg(struct ls_message *msg,
+ struct ls_edge *edge);
+
+/**
+ * Create a new Link State Message from a Link State Subnet. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg Link State Message to be filled or NULL
+ * @param subnet Link State Subnet. Must not be NULL
+ *
+ * @return New Link State Message msg parameter is NULL or pointer
+ * to the provided Link State Message
+ */
+extern struct ls_message *ls_subnet2msg(struct ls_message *msg,
+ struct ls_subnet *subnet);
+
+/**
+ * Send all the content of the Link State Data Base to the given destination.
+ * Link State content is sent is this order: Vertices, Edges, Subnet.
+ * This function must be used when a daemon request a Link State Data Base
+ * Synchronization.
+ *
+ * @param ted Link State Data Base. Must not be NULL
+ * @param zclient Zebra Client. Must not be NULL
+ * @param dst Destination FRR daemon. Must not be NULL
+ *
+ * @return 0 on success, -1 otherwise
+ */
+extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
+ struct zapi_opaque_reg_info *dst);
+
+/**
+ * Dump all Link State Data Base elements for debugging purposes
+ *
+ * @param ted Link State Data Base. Must not be NULL
+ *
+ */
+extern void ls_dump_ted(struct ls_ted *ted);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_LINK_STATE_H_ */
diff --git a/lib/log.c b/lib/log.c
index 7b37ba7f27..03ed23a04b 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -456,7 +456,8 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_NHG_ADD),
DESC_ENTRY(ZEBRA_NHG_DEL),
DESC_ENTRY(ZEBRA_NHG_NOTIFY_OWNER),
- DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST)};
+ DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST),
+ DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY)};
#undef DESC_ENTRY
static const struct zebra_desc_table unknown = {0, "unknown", '?'};
diff --git a/lib/northbound.h b/lib/northbound.h
index 3f6e4dc46e..c37d66d580 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -600,6 +600,7 @@ enum nb_client {
NB_CLIENT_CONFD,
NB_CLIENT_SYSREPO,
NB_CLIENT_GRPC,
+ NB_CLIENT_PCEP,
};
/* Northbound context. */
@@ -621,6 +622,8 @@ struct nb_context {
} sysrepo;
struct {
} grpc;
+ struct {
+ } pcep;
} client_data;
#endif
};
diff --git a/lib/routemap.c b/lib/routemap.c
index 004beb3628..1c2f43d968 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -1347,7 +1347,7 @@ enum rmap_compile_rets route_map_add_match(struct route_map_index *index,
get_route_map_delete_event(type);
route_map_upd8_dependency(
delete_rmap_event_type,
- rule_key,
+ rule->rule_str,
index->map->name);
}
@@ -2586,22 +2586,23 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg)
struct route_map_dep *dep = bucket->data;
struct route_map_dep_data *dep_data = NULL, tmp_dep_data;
- if (arg) {
- memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
- tmp_dep_data.rname = arg;
- dep_data = hash_release(dep->dep_rmap_hash,
- &tmp_dep_data);
- if (dep_data) {
- XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname);
- XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data);
- }
- if (!dep->dep_rmap_hash->count) {
- dep = hash_release(dep->this_hash,
- (void *)dep->dep_name);
- hash_free(dep->dep_rmap_hash);
- XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
- XFREE(MTYPE_ROUTE_MAP_DEP, dep);
- }
+ memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
+ tmp_dep_data.rname = arg;
+ dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data);
+ if (dep_data) {
+ if (rmap_debug)
+ zlog_debug("Clearing reference for %s to %s count: %d",
+ dep->dep_name, tmp_dep_data.rname,
+ dep_data->refcnt);
+
+ XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname);
+ XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data);
+ }
+ if (!dep->dep_rmap_hash->count) {
+ dep = hash_release(dep->this_hash, (void *)dep->dep_name);
+ hash_free(dep->dep_rmap_hash);
+ XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name);
+ XFREE(MTYPE_ROUTE_MAP_DEP, dep);
}
}
@@ -2609,6 +2610,9 @@ static void route_map_clear_all_references(char *rmap_name)
{
int i;
+ if (rmap_debug)
+ zlog_debug("Clearing references for %s", rmap_name);
+
for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
hash_iterate(route_map_dep_hash[i], route_map_clear_reference,
(void *)rmap_name);
@@ -2763,12 +2767,19 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
tmp_dep_data.rname = rname;
dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
-
- if (!dep_data)
+ /*
+ * If dep_data is NULL then something has gone seriously
+ * wrong in route-map handling. Note it and prevent
+ * the crash.
+ */
+ if (!dep_data) {
+ zlog_warn(
+ "route-map dependency for route-map %s: %s is not correct",
+ rmap_name, dep_name);
goto out;
+ }
- if (dep_data->refcnt)
- dep_data->refcnt--;
+ dep_data->refcnt--;
if (!dep_data->refcnt) {
ret_dep_data = hash_release(dep->dep_rmap_hash,
diff --git a/lib/sockunion.c b/lib/sockunion.c
index c999845659..1dbf77efa4 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -708,3 +708,20 @@ static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt,
fb.pos[0] = '\0';
return consumed;
}
+
+int sockunion_is_null(const union sockunion *su)
+{
+ unsigned char null_s6_addr[16] = {0};
+
+ switch (sockunion_family(su)) {
+ case AF_UNSPEC:
+ return 1;
+ case AF_INET:
+ return (su->sin.sin_addr.s_addr == 0);
+ case AF_INET6:
+ return !memcmp(su->sin6.sin6_addr.s6_addr, null_s6_addr,
+ sizeof(null_s6_addr));
+ default:
+ return 0;
+ }
+}
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 72f12b77ca..5e80ba1090 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -102,6 +102,7 @@ extern union sockunion *sockunion_getpeername(int);
extern union sockunion *sockunion_dup(const union sockunion *);
extern void sockunion_free(union sockunion *);
extern void sockunion_init(union sockunion *);
+extern int sockunion_is_null(const union sockunion *su);
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pSU" (union sockunion *)
diff --git a/lib/subdir.am b/lib/subdir.am
index 038282a99b..ee9e827ee8 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -48,6 +48,7 @@ lib_libfrr_la_SOURCES = \
lib/libfrr.c \
lib/libfrr_trace.c \
lib/linklist.c \
+ lib/link_state.c \
lib/log.c \
lib/log_filter.c \
lib/log_vty.c \
@@ -208,6 +209,7 @@ pkginclude_HEADERS += \
lib/libfrr_trace.h \
lib/libospf.h \
lib/linklist.h \
+ lib/link_state.h \
lib/log.h \
lib/log_vty.h \
lib/md5.h \
diff --git a/lib/yang.c b/lib/yang.c
index 22fe938e4c..383dc9f5eb 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -92,6 +92,7 @@ static const char *const frr_native_modules[] = {
"frr-isisd",
"frr-vrrpd",
"frr-zebra",
+ "frr-pathd",
};
/* clang-format on */
@@ -468,7 +469,7 @@ void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
dnode = set->set.d[i];
ret = (*cb)(dnode, arg);
if (ret == YANG_ITER_STOP)
- return;
+ break;
}
ly_set_free(set);
diff --git a/lib/zclient.c b/lib/zclient.c
index 053014f86d..cb4555650d 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -1182,6 +1182,12 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID))
stream_putl(s, api->tableid);
+ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) {
+ assert(api->opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH);
+
+ stream_putw(s, api->opaque.length);
+ stream_write(s, api->opaque.data, api->opaque.length);
+ }
/* Put length at the first point of the stream. */
stream_putw_at(s, 0, stream_get_endp(s));
@@ -1403,6 +1409,13 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID))
STREAM_GETL(s, api->tableid);
+ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) {
+ STREAM_GETW(s, api->opaque.length);
+ assert(api->opaque.length < ZAPI_MESSAGE_OPAQUE_LENGTH);
+
+ STREAM_GET(api->opaque.data, s, api->opaque.length);
+ }
+
return 0;
stream_failure:
return -1;
@@ -3528,6 +3541,23 @@ stream_failure:
return -1;
}
+/* Utility to decode client close notify info */
+int zapi_client_close_notify_decode(struct stream *s,
+ struct zapi_client_close_info *info)
+{
+ memset(info, 0, sizeof(*info));
+
+ STREAM_GETC(s, info->proto);
+ STREAM_GETW(s, info->instance);
+ STREAM_GETL(s, info->session_id);
+
+ return 0;
+
+stream_failure:
+
+ return -1;
+}
+
/* Zebra client message read function. */
static int zclient_read(struct thread *thread)
{
@@ -3868,6 +3898,12 @@ static int zclient_read(struct thread *thread)
if (zclient->sr_policy_notify_status)
(*zclient->sr_policy_notify_status)(command, zclient,
length, vrf_id);
+ break;
+ case ZEBRA_CLIENT_CLOSE_NOTIFY:
+ if (zclient->zebra_client_close_notify)
+ (*zclient->zebra_client_close_notify)(command, zclient,
+ length, vrf_id);
+ break;
default:
break;
}
diff --git a/lib/zclient.h b/lib/zclient.h
index ae94237b76..910a4dbae5 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -220,6 +220,7 @@ typedef enum {
ZEBRA_OPAQUE_UNREGISTER,
ZEBRA_NEIGH_DISCOVER,
ZEBRA_ROUTE_NOTIFY_REQUEST,
+ ZEBRA_CLIENT_CLOSE_NOTIFY,
} zebra_message_types_t;
enum zebra_error_types {
@@ -377,6 +378,7 @@ struct zclient {
int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS);
int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS);
int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS);
+ int (*zebra_client_close_notify)(ZAPI_CALLBACK_ARGS);
};
/* Zebra API message flag. */
@@ -389,14 +391,14 @@ struct zclient {
/* Backup nexthops are present */
#define ZAPI_MESSAGE_BACKUP_NEXTHOPS 0x40
#define ZAPI_MESSAGE_NHG 0x80
-
/*
* This should only be used by a DAEMON that needs to communicate
* the table being used is not in the VRF. You must pass the
* default vrf, else this will be ignored.
*/
-#define ZAPI_MESSAGE_TABLEID 0x0080
-#define ZAPI_MESSAGE_SRTE 0x0100
+#define ZAPI_MESSAGE_TABLEID 0x0100
+#define ZAPI_MESSAGE_SRTE 0x0200
+#define ZAPI_MESSAGE_OPAQUE 0x0400
#define ZSERV_VERSION 6
/* Zserv protocol message header */
@@ -570,6 +572,12 @@ struct zapi_route {
/* SR-TE color (used for nexthop updates only). */
uint32_t srte_color;
+
+#define ZAPI_MESSAGE_OPAQUE_LENGTH 1024
+ struct {
+ uint16_t length;
+ uint8_t data[ZAPI_MESSAGE_OPAQUE_LENGTH];
+ } opaque;
};
struct zapi_labels {
@@ -626,6 +634,52 @@ struct zapi_pw_status {
uint32_t status;
};
+/* IGP instance data associated to a RLFA. */
+struct zapi_rlfa_igp {
+ vrf_id_t vrf_id;
+ int protocol;
+ union {
+ struct {
+ char area_tag[32];
+ struct {
+ int tree_id;
+ int level;
+ unsigned int run_id;
+ } spf;
+ } isis;
+ };
+};
+
+/* IGP -> LDP RLFA (un)registration message. */
+struct zapi_rlfa_request {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* PQ node address. */
+ struct in_addr pq_address;
+};
+
+/* LDP -> IGP RLFA label update. */
+struct zapi_rlfa_response {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* Resolved LDP labels. */
+ mpls_label_t pq_label;
+ uint16_t nexthop_num;
+ struct {
+ int family;
+ union g_addr gate;
+ mpls_label_t label;
+ } nexthops[MULTIPATH_NUM];
+};
+
enum zapi_route_notify_owner {
ZAPI_ROUTE_FAIL_INSTALL,
ZAPI_ROUTE_BETTER_ADMIN_WON,
@@ -1083,8 +1137,12 @@ enum zapi_opaque_registry {
LDP_IGP_SYNC_IF_STATE_UPDATE = 4,
/* Announce that LDP is up */
LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5,
- /* Heartbeat indicating that LDP is running */
- LDP_IGP_SYNC_HELLO_UPDATE = 6,
+ /* Register RLFA with LDP */
+ LDP_RLFA_REGISTER = 7,
+ /* Unregister all RLFAs with LDP */
+ LDP_RLFA_UNREGISTER_ALL = 8,
+ /* Announce LDP labels associated to a previously registered RLFA */
+ LDP_RLFA_LABELS = 9,
};
/* Send the hello message.
@@ -1097,6 +1155,17 @@ zclient_send_neigh_discovery_req(struct zclient *zclient,
const struct interface *ifp,
const struct prefix *p);
+struct zapi_client_close_info {
+ /* Client session tuple */
+ uint8_t proto;
+ uint16_t instance;
+ uint32_t session_id;
+};
+
+/* Decode incoming client close notify */
+extern int zapi_client_close_notify_decode(struct stream *s,
+ struct zapi_client_close_info *info);
+
#ifdef __cplusplus
}
#endif