]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_rnh.c
zebra: Add check for nexthop loop to prevent hanging
[mirror_frr.git] / zebra / zebra_rnh.c
index 65df4e15aa2be782323f41f480bcc8a09b1ab0b5..d482e0ab3da3ec1d796c304e3eaddf9eb031022d 100644 (file)
@@ -66,10 +66,16 @@ 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);
 
 int zebra_rnh_ip_default_route = 0;
 int zebra_rnh_ipv6_default_route = 0;
 
+void zebra_rnh_init(void)
+{
+       hook_register(zserv_client_close, zebra_client_cleanup_rnh);
+}
+
 static inline struct route_table *get_rnh_table(vrf_id_t vrfid, int family,
                                                rnh_type_t type)
 {
@@ -514,6 +520,61 @@ static void zebra_rnh_notify_protocol_clients(vrf_id_t vrfid, int family,
        }
 }
 
+static void zebra_rnh_process_pbr_tables(int family,
+                                        struct route_node *nrn,
+                                        struct rnh *rnh,
+                                        struct route_node *prn,
+                                        struct route_entry *re)
+{
+       struct zebra_ns_table *znst;
+       struct route_entry *o_re;
+       struct route_node *o_rn;
+       struct listnode *node;
+       struct zserv *client;
+       struct zebra_ns *zns;
+       afi_t afi = AFI_IP;
+
+       if (family == AF_INET6)
+               afi = AFI_IP6;
+
+       /*
+        * We are only concerned about nexthops that change for
+        * anyone using PBR
+        */
+       for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
+               if (client->proto == ZEBRA_ROUTE_PBR)
+                       break;
+       }
+
+       if (!client)
+               return;
+
+       zns = zebra_ns_lookup(NS_DEFAULT);
+       RB_FOREACH (znst, zebra_ns_table_head, &zns->ns_tables) {
+               if (afi != znst->afi)
+                       continue;
+
+               for (o_rn = route_top(znst->table);
+                    o_rn; o_rn = srcdest_route_next(o_rn)) {
+                       RNODE_FOREACH_RE (o_rn, o_re) {
+                               if (o_re->type == ZEBRA_ROUTE_PBR)
+                                       break;
+
+                       }
+
+                       /*
+                        * If we have a PBR route and a nexthop changes
+                        * just rethink it.  Yes this is a hammer, but
+                        * a small one
+                        */
+                       if (o_re) {
+                               SET_FLAG(o_re->status, ROUTE_ENTRY_CHANGED);
+                               rib_queue_add(o_rn);
+                       }
+               }
+       }
+}
+
 static void zebra_rnh_process_static_routes(vrf_id_t vrfid, int family,
                                            struct route_node *nrn,
                                            struct rnh *rnh,
@@ -752,6 +813,9 @@ static void zebra_rnh_eval_nexthop_entry(vrf_id_t vrfid, int family, int force,
                zebra_rnh_process_static_routes(vrfid, family, nrn, rnh, prn,
                                                rnh->state);
 
+               zebra_rnh_process_pbr_tables(family, nrn, rnh, prn,
+                                            rnh->state);
+
                /* Process pseudowires attached to this nexthop */
                zebra_rnh_process_pseudowires(vrfid, rnh);
        }
@@ -887,34 +951,6 @@ void zebra_print_rnh_table(vrf_id_t vrfid, int af, struct vty *vty,
                        print_rnh(rn, vty);
 }
 
-int zebra_cleanup_rnh_client(vrf_id_t vrf_id, int family, struct zserv *client,
-                            rnh_type_t type)
-{
-       struct route_table *ntable;
-       struct route_node *nrn;
-       struct rnh *rnh;
-
-       if (IS_ZEBRA_DEBUG_NHT)
-               zlog_debug("%u: Client %s RNH cleanup for family %d type %d",
-                          vrf_id, zebra_route_string(client->proto), family,
-                          type);
-
-       ntable = get_rnh_table(vrf_id, family, type);
-       if (!ntable) {
-               zlog_debug("cleanup_rnh_client: rnh table not found\n");
-               return -1;
-       }
-
-       for (nrn = route_top(ntable); nrn; nrn = route_next(nrn)) {
-               if (!nrn->info)
-                       continue;
-
-               rnh = nrn->info;
-               zebra_remove_rnh_client(rnh, client, type);
-       }
-       return 1;
-}
-
 /**
  * free_state - free up the re structure associated with the rnh.
  */
@@ -1070,7 +1106,7 @@ 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 zebra_server_send_message(client, s);
+       return zserv_send_message(client, s);
 }
 
 static void print_nh(struct nexthop *nexthop, struct vty *vty)
@@ -1144,3 +1180,61 @@ static void print_rnh(struct route_node *rn, struct vty *vty)
                vty_out(vty, " zebra[pseudowires]");
        vty_out(vty, "\n");
 }
+
+static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, int family,
+                                   struct zserv *client, rnh_type_t type)
+{
+       struct route_table *ntable;
+       struct route_node *nrn;
+       struct rnh *rnh;
+
+       if (IS_ZEBRA_DEBUG_NHT)
+               zlog_debug("%u: Client %s RNH cleanup for family %d type %d",
+                          vrf_id, zebra_route_string(client->proto), family,
+                          type);
+
+       ntable = get_rnh_table(vrf_id, family, type);
+       if (!ntable) {
+               zlog_debug("cleanup_rnh_client: rnh table not found\n");
+               return -1;
+       }
+
+       for (nrn = route_top(ntable); nrn; nrn = route_next(nrn)) {
+               if (!nrn->info)
+                       continue;
+
+               rnh = nrn->info;
+               zebra_remove_rnh_client(rnh, client, type);
+       }
+       return 1;
+}
+
+/* Cleanup registered nexthops (across VRFs) upon client disconnect. */
+static int zebra_client_cleanup_rnh(struct zserv *client)
+{
+       struct vrf *vrf;
+       struct zebra_vrf *zvrf;
+
+       RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+               zvrf = vrf->info;
+               if (zvrf) {
+                       zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET, client,
+                                                RNH_NEXTHOP_TYPE);
+                       zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET6,
+                                                client, RNH_NEXTHOP_TYPE);
+                       zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET, client,
+                                                RNH_IMPORT_CHECK_TYPE);
+                       zebra_cleanup_rnh_client(zvrf_id(zvrf), AF_INET6,
+                                                client, RNH_IMPORT_CHECK_TYPE);
+                       if (client->proto == ZEBRA_ROUTE_LDP) {
+                               hash_iterate(zvrf->lsp_table,
+                                            mpls_ldp_lsp_uninstall_all,
+                                            zvrf->lsp_table);
+                               mpls_ldp_ftn_uninstall_all(zvrf, AFI_IP);
+                               mpls_ldp_ftn_uninstall_all(zvrf, AFI_IP6);
+                       }
+               }
+       }
+
+       return 0;
+}