]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_rnh.c
lib, zebra: Add SR-TE policy infrastructure to zebra
[mirror_frr.git] / zebra / zebra_rnh.c
index f9c74c7462e87d458b13c551d55c8e46586856ff..cdc00f6026504ded3828c23bf7b55a8c4ea228ce 100644 (file)
@@ -46,6 +46,7 @@
 #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"
@@ -57,8 +58,6 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re,
 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);
 
@@ -68,7 +67,7 @@ void zebra_rnh_init(void)
 }
 
 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;
@@ -148,7 +147,7 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh)
        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;
@@ -207,7 +206,8 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type,
        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;
@@ -258,7 +258,7 @@ void zebra_free_rnh(struct rnh *rnh)
        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;
 
@@ -289,7 +289,7 @@ static void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type)
  * 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];
@@ -304,11 +304,11 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
         * 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];
@@ -342,25 +342,32 @@ static void addr2hostprefix(int af, const union g_addr *addr,
        }
 }
 
-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)
@@ -411,7 +418,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf,
                                at_least_one++; /* at least one valid NH */
                        else {
                                SET_FLAG(nexthop->flags,
-                                               NEXTHOP_FLAG_RNH_FILTERED);
+                                        NEXTHOP_FLAG_RNH_FILTERED);
                        }
                }
        }
@@ -450,12 +457,12 @@ zebra_rnh_resolve_import_entry(struct zebra_vrf *zvrf, afi_t afi,
 
        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. */
@@ -523,8 +530,9 @@ static void zebra_rnh_eval_import_check_entry(struct zebra_vrf *zvrf, afi_t afi,
                }
                /* 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);
                }
        }
 }
@@ -586,7 +594,8 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi,
                                        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)
@@ -804,7 +813,7 @@ static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
 
 /* 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;
@@ -851,7 +860,7 @@ static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi,
  * 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;
@@ -875,7 +884,7 @@ static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi,
  * 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;
@@ -911,7 +920,7 @@ void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force,
 }
 
 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;
@@ -966,12 +975,119 @@ static void copy_state(struct rnh *rnh, const struct route_entry *re,
        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)
@@ -986,26 +1102,24 @@ static int compare_state(struct route_entry *r1, struct route_entry *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;
 
@@ -1017,6 +1131,11 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
 
        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:
@@ -1031,10 +1150,14 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
                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);
@@ -1043,12 +1166,33 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
                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
@@ -1062,6 +1206,11 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t 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)
@@ -1134,7 +1283,7 @@ static void print_rnh(struct route_node *rn, 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;