#include "zebra/debug.h"
#include "zebra/zebra_rnh.h"
#include "zebra/zebra_routemap.h"
+#include "zebra/zebra_srte.h"
#include "zebra/interface.h"
#include "zebra/zebra_memory.h"
#include "zebra/zebra_errors.h"
static void copy_state(struct rnh *rnh, const struct route_entry *re,
struct route_node *rn);
static int compare_state(struct route_entry *r1, struct route_entry *r2);
-static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
- vrf_id_t vrf_id);
static void print_rnh(struct route_node *rn, struct vty *vty);
static int zebra_client_cleanup_rnh(struct zserv *client);
}
static inline struct route_table *get_rnh_table(vrf_id_t vrfid, afi_t afi,
- rnh_type_t type)
+ enum rnh_type type)
{
struct zebra_vrf *zvrf;
struct route_table *t = NULL;
route_unlock_node(rn);
}
-struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type,
+struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, enum rnh_type type,
bool *exists)
{
struct route_table *table;
return (rn->info);
}
-struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type)
+struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid,
+ enum rnh_type type)
{
struct route_table *table;
struct route_node *rn;
XFREE(MTYPE_RNH, rnh);
}
-static void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type)
+static void zebra_delete_rnh(struct rnh *rnh, enum rnh_type type)
{
struct route_node *rn;
* and as such it will have a resolved rnh.
*/
void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
- rnh_type_t type, vrf_id_t vrf_id)
+ enum rnh_type type, vrf_id_t vrf_id)
{
if (IS_ZEBRA_DEBUG_NHT) {
char buf[PREFIX2STR_BUFFER];
* We always need to respond with known information,
* currently multiple daemons expect this behavior
*/
- send_client(rnh, client, type, vrf_id);
+ zebra_send_rnh_update(rnh, client, type, vrf_id, 0);
}
void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,
- rnh_type_t type)
+ enum rnh_type type)
{
if (IS_ZEBRA_DEBUG_NHT) {
char buf[PREFIX2STR_BUFFER];
}
}
-void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw)
+void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw,
+ bool *nht_exists)
{
struct prefix nh;
struct rnh *rnh;
bool exists;
struct zebra_vrf *zvrf;
+ *nht_exists = false;
+
zvrf = vrf_info_lookup(vrf_id);
if (!zvrf)
return;
addr2hostprefix(pw->af, &pw->nexthop, &nh);
rnh = zebra_add_rnh(&nh, vrf_id, RNH_NEXTHOP_TYPE, &exists);
- if (rnh && !listnode_lookup(rnh->zebra_pseudowire_list, pw)) {
+ if (!rnh)
+ return;
+
+ if (!listnode_lookup(rnh->zebra_pseudowire_list, pw)) {
listnode_add(rnh->zebra_pseudowire_list, pw);
pw->rnh = rnh;
zebra_evaluate_rnh(zvrf, family2afi(pw->af), 1,
RNH_NEXTHOP_TYPE, &nh);
- }
+ } else
+ *nht_exists = true;
}
void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw)
at_least_one++; /* at least one valid NH */
else {
SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_RNH_FILTERED);
+ NEXTHOP_FLAG_RNH_FILTERED);
}
}
}
if (IS_ZEBRA_DEBUG_NHT_DETAILED) {
char buf[PREFIX_STRLEN];
- char buf1[PREFIX_STRLEN];
+ char buf1[SRCDEST2STR_BUFFER];
zlog_debug("%s: %u:%s Resolved Import Entry to %s", __func__,
rnh->vrf_id,
prefix2str(&rnh->node->p, buf, sizeof(buf)),
- srcdest_rnode2str(rn, buf1, sizeof(buf)));
+ srcdest_rnode2str(rn, buf1, sizeof(buf1)));
}
/* Identify appropriate route entry. */
}
/* state changed, notify clients */
for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
- send_client(rnh, client,
- RNH_IMPORT_CHECK_TYPE, zvrf->vrf->vrf_id);
+ zebra_send_rnh_update(rnh, client,
+ RNH_IMPORT_CHECK_TYPE,
+ zvrf->vrf->vrf_id, 0);
}
}
}
zebra_route_string(client->proto));
}
- send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id);
+ zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE,
+ zvrf->vrf->vrf_id, 0);
}
if (re)
/* Evaluate one tracked entry */
static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi,
- int force, rnh_type_t type,
+ int force, enum rnh_type type,
struct route_node *nrn)
{
struct rnh *rnh;
* covers multiple nexthops we are interested in.
*/
static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi,
- rnh_type_t type, struct route_node *nrn)
+ enum rnh_type type, struct route_node *nrn)
{
struct rnh *rnh;
struct route_entry *re;
* of a particular VRF and address-family or a specific prefix.
*/
void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force,
- rnh_type_t type, struct prefix *p)
+ enum rnh_type type, struct prefix *p)
{
struct route_table *rnh_table;
struct route_node *nrn;
}
void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty,
- rnh_type_t type, struct prefix *p)
+ enum rnh_type type, struct prefix *p)
{
struct route_table *table;
struct route_node *rn;
state->vrf_id = re->vrf_id;
state->status = re->status;
- state->nhe = zebra_nhg_alloc();
+ state->nhe = zebra_nhe_copy(re->nhe, 0);
+
+ /* Copy the 'fib' nexthops also, if present - we want to capture
+ * the true installed nexthops.
+ */
+ if (re->fib_ng.nexthop)
+ nexthop_group_copy(&state->fib_ng, &re->fib_ng);
+ if (re->fib_backup_ng.nexthop)
+ nexthop_group_copy(&state->fib_backup_ng, &re->fib_backup_ng);
- nexthop_group_copy(&(state->nhe->nhg), &(re->nhe->nhg));
rnh->state = state;
}
+/*
+ * Compare two route_entries' nexthops.
+ */
+static bool compare_valid_nexthops(struct route_entry *r1,
+ struct route_entry *r2)
+{
+ bool matched_p = false;
+ struct nexthop_group *nhg1, *nhg2;
+ struct nexthop *nh1, *nh2;
+
+ /* Account for backup nexthops and for the 'fib' nexthop lists,
+ * if present.
+ */
+ nhg1 = rib_get_fib_nhg(r1);
+ nhg2 = rib_get_fib_nhg(r2);
+
+ nh1 = nhg1->nexthop;
+ nh2 = nhg2->nexthop;
+
+ while (1) {
+ /* Find each list's next valid nexthop */
+ while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1))
+ nh1 = nexthop_next(nh1);
+
+ while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2))
+ nh2 = nexthop_next(nh2);
+
+ if (nh1 && nh2) {
+ /* Any difference is a no-match */
+ if (nexthop_cmp(nh1, nh2) != 0) {
+ if (IS_ZEBRA_DEBUG_NHT_DETAILED)
+ zlog_debug("%s: nh1, nh2 differ",
+ __func__);
+ goto done;
+ }
+
+ nh1 = nexthop_next(nh1);
+ nh2 = nexthop_next(nh2);
+ } else if (nh1 || nh2) {
+ /* One list has more valid nexthops than the other */
+ if (IS_ZEBRA_DEBUG_NHT_DETAILED)
+ zlog_debug("%s: nh1 %s, nh2 %s", __func__,
+ nh1 ? "non-NULL" : "NULL",
+ nh2 ? "non-NULL" : "NULL");
+ goto done;
+ } else
+ break; /* Done with both lists */
+ }
+
+ /* The test for the backups is slightly different: the only installed
+ * backups will be in the 'fib' list.
+ */
+ nhg1 = rib_get_fib_backup_nhg(r1);
+ nhg2 = rib_get_fib_backup_nhg(r2);
+
+ nh1 = nhg1->nexthop;
+ nh2 = nhg2->nexthop;
+
+ while (1) {
+ /* Find each backup list's next valid nexthop */
+ while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1))
+ nh1 = nexthop_next(nh1);
+
+ while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2))
+ nh2 = nexthop_next(nh2);
+
+ if (nh1 && nh2) {
+ /* Any difference is a no-match */
+ if (nexthop_cmp(nh1, nh2) != 0) {
+ if (IS_ZEBRA_DEBUG_NHT_DETAILED)
+ zlog_debug("%s: backup nh1, nh2 differ",
+ __func__);
+ goto done;
+ }
+
+ nh1 = nexthop_next(nh1);
+ nh2 = nexthop_next(nh2);
+ } else if (nh1 || nh2) {
+ /* One list has more valid nexthops than the other */
+ if (IS_ZEBRA_DEBUG_NHT_DETAILED)
+ zlog_debug("%s: backup nh1 %s, nh2 %s",
+ __func__,
+ nh1 ? "non-NULL" : "NULL",
+ nh2 ? "non-NULL" : "NULL");
+ goto done;
+ } else
+ break; /* Done with both lists */
+ }
+
+ /* Well, it's a match */
+ if (IS_ZEBRA_DEBUG_NHT_DETAILED)
+ zlog_debug("%s: matched", __func__);
+
+ matched_p = true;
+
+done:
+
+ return matched_p;
+}
+
static int compare_state(struct route_entry *r1, struct route_entry *r2)
{
if (!r1 && !r2)
if (r1->metric != r2->metric)
return 1;
- if (nexthop_group_nexthop_num(&(r1->nhe->nhg))
- != nexthop_group_nexthop_num(&(r2->nhe->nhg)))
- return 1;
-
- if (nexthop_group_hash(&(r1->nhe->nhg)) !=
- nexthop_group_hash(&(r2->nhe->nhg)))
+ if (!compare_valid_nexthops(r1, r2))
return 1;
return 0;
}
-static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
- vrf_id_t vrf_id)
+int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client,
+ enum rnh_type type, vrf_id_t vrf_id,
+ uint32_t srte_color)
{
- struct stream *s;
+ struct stream *s = NULL;
struct route_entry *re;
unsigned long nump;
uint8_t num;
struct nexthop *nh;
struct route_node *rn;
+ int ret;
+ uint32_t message = 0;
int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE
: ZEBRA_NEXTHOP_UPDATE;
zclient_create_header(s, cmd, vrf_id);
+ /* Message flags. */
+ if (srte_color)
+ SET_FLAG(message, ZAPI_MESSAGE_SRTE);
+ stream_putl(s, message);
+
stream_putw(s, rn->p.family);
switch (rn->p.family) {
case AF_INET:
flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY,
"%s: Unknown family (%d) notification attempted\n",
__func__, rn->p.family);
- break;
+ goto failure;
}
+ if (srte_color)
+ stream_putl(s, srte_color);
+
if (re) {
struct zapi_nexthop znh;
+ struct nexthop_group *nhg;
stream_putc(s, re->type);
stream_putw(s, re->instance);
num = 0;
nump = stream_get_endp(s);
stream_putc(s, 0);
- for (ALL_NEXTHOPS(re->nhe->nhg, nh))
+
+ nhg = rib_get_fib_nhg(re);
+ for (ALL_NEXTHOPS_PTR(nhg, nh))
if (rnh_nexthop_valid(re, nh)) {
zapi_nexthop_from_nexthop(&znh, nh);
- zapi_nexthop_encode(s, &znh, 0 /* flags */);
+ ret = zapi_nexthop_encode(s, &znh, 0, message);
+ if (ret < 0)
+ goto failure;
+
num++;
}
+
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg) {
+ for (ALL_NEXTHOPS_PTR(nhg, nh))
+ if (rnh_nexthop_valid(re, nh)) {
+ zapi_nexthop_from_nexthop(&znh, nh);
+ ret = zapi_nexthop_encode(
+ s, &znh, 0 /* flags */,
+ 0 /* message */);
+ if (ret < 0)
+ goto failure;
+
+ num++;
+ }
+ }
+
stream_putc_at(s, nump, num);
} else {
stream_putc(s, 0); // type
client->nh_last_upd_time = monotime(NULL);
client->last_write_cmd = cmd;
return zserv_send_message(client, s);
+
+failure:
+
+ stream_free(s);
+ return -1;
}
static void print_nh(struct nexthop *nexthop, struct vty *vty)
}
static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, afi_t afi,
- struct zserv *client, rnh_type_t type)
+ struct zserv *client, enum rnh_type type)
{
struct route_table *ntable;
struct route_node *nrn;