diff options
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/interface.c | 23 | ||||
| -rw-r--r-- | zebra/interface.h | 3 | ||||
| -rw-r--r-- | zebra/rib.h | 8 | ||||
| -rw-r--r-- | zebra/zebra_dplane.c | 9 | ||||
| -rw-r--r-- | zebra/zebra_gr.c | 383 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 72 | ||||
| -rw-r--r-- | zebra/zebra_routemap.c | 8 | ||||
| -rw-r--r-- | zebra/zebra_vxlan.c | 214 | ||||
| -rw-r--r-- | zebra/zserv.c | 4 | ||||
| -rw-r--r-- | zebra/zserv.h | 8 |
10 files changed, 412 insertions, 320 deletions
diff --git a/zebra/interface.c b/zebra/interface.c index 03376afc09..496a85e676 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -137,6 +137,8 @@ static int if_zebra_new_hook(struct interface *ifp) zebra_if->multicast = IF_ZEBRA_DATA_UNSPEC; zebra_if->shutdown = IF_ZEBRA_DATA_OFF; + zebra_if->link_nsid = NS_UNKNOWN; + zebra_if_nhg_dependents_init(zebra_if); zebra_ptm_if_init(zebra_if); @@ -305,6 +307,14 @@ struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns, return NULL; } +struct interface *if_lookup_by_index_per_nsid(ns_id_t ns_id, uint32_t ifindex) +{ + struct zebra_ns *zns; + + zns = zebra_ns_lookup(ns_id); + return zns ? if_lookup_by_index_per_ns(zns, ifindex) : NULL; +} + const char *ifindex2ifname_per_ns(struct zebra_ns *zns, unsigned int ifindex) { struct interface *ifp; @@ -991,7 +1001,6 @@ void if_up(struct interface *ifp, bool install_connected) { struct zebra_if *zif; struct interface *link_if; - struct zebra_vrf *zvrf = ifp->vrf->info; zif = ifp->info; zif->up_count++; @@ -1024,8 +1033,7 @@ void if_up(struct interface *ifp, bool install_connected) link_if = ifp; zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zvrf->zns, - zif->link_ifindex); + link_if = zif->link; if (link_if) zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_MACVLAN(ifp)) { @@ -1049,7 +1057,6 @@ void if_down(struct interface *ifp) { struct zebra_if *zif; struct interface *link_if; - struct zebra_vrf *zvrf = ifp->vrf->info; zif = ifp->info; zif->down_count++; @@ -1068,8 +1075,7 @@ void if_down(struct interface *ifp) link_if = ifp; zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zvrf->zns, - zif->link_ifindex); + link_if = zif->link; if (link_if) zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_MACVLAN(ifp)) { @@ -1109,6 +1115,7 @@ void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, if (IS_ZEBRA_IF_VETH(ifp)) return; zif = (struct zebra_if *)ifp->info; + zif->link_nsid = ns_id; zif->link_ifindex = link_ifindex; zif->link = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), link_ifindex); @@ -1145,8 +1152,8 @@ void zebra_if_update_all_links(struct zebra_ns *zns) /* update SVI linkages */ if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) { - zif->link = if_lookup_by_index_per_ns( - zns, zif->link_ifindex); + zif->link = if_lookup_by_index_per_nsid( + zif->link_nsid, zif->link_ifindex); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("interface %s/%d's lower fixup to %s/%d", ifp->name, ifp->ifindex, diff --git a/zebra/interface.h b/zebra/interface.h index 4c6ebaa11d..e5545d6ba0 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -195,6 +195,7 @@ struct zebra_if { struct list *mac_list; /* Link fields - for sub-interfaces. */ + ns_id_t link_nsid; ifindex_t link_ifindex; struct interface *link; @@ -259,6 +260,8 @@ extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, uint32_t); extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *, const char *); extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *); +extern struct interface *if_lookup_by_index_per_nsid(ns_id_t nsid, + uint32_t ifindex); extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int); extern void if_unlink_per_ns(struct interface *); diff --git a/zebra/rib.h b/zebra/rib.h index 26425a331f..a56bb05d68 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -180,7 +180,7 @@ struct route_entry { * sub-queue 9: any other origin (if any) typically those that * don't generate routes */ -#define MQ_SIZE 10 +#define MQ_SIZE 11 struct meta_queue { struct list *subq[MQ_SIZE]; uint32_t size; /* sum of lengths of all subqueues */ @@ -602,6 +602,12 @@ static inline struct nexthop_group *rib_get_fib_backup_nhg( return &(re->fib_backup_ng); } +extern void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance); + +extern int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance); + extern void zebra_vty_init(void); extern pid_t pid; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index fb1ebc6827..e821572c5d 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -3745,6 +3745,11 @@ dplane_route_update_internal(struct route_node *rn, NEXTHOP_FLAG_FIB); } + if ((op == DPLANE_OP_ROUTE_UPDATE) && old_re && re && + (old_re != re) && + !CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + dplane_ctx_free(&ctx); return ZEBRA_DPLANE_REQUEST_SUCCESS; } @@ -6853,10 +6858,6 @@ void zebra_dplane_shutdown(void) zdplane_info.dg_run = false; - if (zdplane_info.dg_t_update) - event_cancel_async(zdplane_info.dg_t_update->master, - &zdplane_info.dg_t_update, NULL); - frr_pthread_stop(zdplane_info.dg_pthread, NULL); /* Destroy pthread */ diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c index 96d598f7c4..cf2056b7ac 100644 --- a/zebra/zebra_gr.c +++ b/zebra/zebra_gr.c @@ -42,8 +42,8 @@ static struct zserv *zebra_gr_find_stale_client(struct zserv *client); static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread); static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info); static void zebra_gr_process_client_stale_routes(struct zserv *client, - vrf_id_t vrf_id); - + struct client_gr_info *info); +static void zebra_gr_delete_stale_route_table_afi(struct event *event); /* * Debug macros. */ @@ -53,7 +53,6 @@ static void zebra_gr_process_client_stale_routes(struct zserv *client, zlog_debug(msg, ##__VA_ARGS__); \ } while (0) - /* * Client connection functions */ @@ -82,11 +81,12 @@ void zebra_gr_stale_client_cleanup(struct list *client_list) if (info->t_stale_removal != NULL) { EVENT_OFF(info->t_stale_removal); info->t_stale_removal = NULL; + info->do_delete = true; /* Process the stale routes */ event_execute( zrouter.master, zebra_gr_route_stale_delete_timer_expiry, - info, 1); + info, 0); } } } @@ -101,6 +101,8 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) info = XCALLOC(MTYPE_ZEBRA_GR, sizeof(struct client_gr_info)); + info->stale_client_ptr = client; + TAILQ_INSERT_TAIL(&(client->gr_info_queue), info, gr_info); return info; } @@ -108,8 +110,8 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) /* * A helper function to delete and destroy client info. */ -static void zebra_gr_client_info_delte(struct zserv *client, - struct client_gr_info *info) +static void zebra_gr_client_info_delete(struct zserv *client, + struct client_gr_info *info) { struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); @@ -117,8 +119,6 @@ static void zebra_gr_client_info_delte(struct zserv *client, EVENT_OFF(info->t_stale_removal); - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); - LOG_GR("%s: Instance info is being deleted for client %s vrf %s(%u)", __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), info->vrf_id); @@ -164,7 +164,6 @@ int32_t zebra_gr_client_disconnect(struct zserv *client) zebra_gr_route_stale_delete_timer_expiry, info, info->stale_removal_time, &info->t_stale_removal); - info->current_afi = AFI_IP; info->stale_client_ptr = client; info->stale_client = true; LOG_GR("%s: Client %s vrf %s(%u) Stale timer update to %d", @@ -287,31 +286,65 @@ void zebra_gr_client_reconnect(struct zserv *client) zserv_client_delete(old_client); } +struct zebra_gr_afi_clean { + struct client_gr_info *info; + afi_t afi; + uint8_t proto; + uint8_t instance; + + struct event *t_gac; +}; + /* * Functions to deal with capabilities */ /* - * Update the graceful restart information - * for the client instance. - * This function handles all the capabilities that are received. + * Function to decode and call appropriate functions + * to handle client capabilities. */ -static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) +void zread_client_capabilities(ZAPI_HANDLER_ARGS) { + struct zapi_cap api; struct client_gr_info *info = NULL; + struct stream *s; + struct vrf *vrf; + + s = msg; + + if (zapi_capabilities_decode(s, &api)) { + LOG_GR("%s: Error in reading capabilities for client %s", + __func__, zebra_route_string(client->proto)); + return; + } + + vrf = vrf_lookup_by_id(api.vrf_id); + + /* + * If this ever matters uncomment and add safi to the + * arrays as needed to track + */ + if (api.safi != SAFI_UNICAST) + return; + + /* GR only for dynamic clients */ + if (client->proto <= ZEBRA_ROUTE_CONNECT) { + LOG_GR("%s: GR capabilities for client %s not supported", + __func__, zebra_route_string(client->proto)); + return; + } /* Find the bgp information for the specified vrf id */ TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { - if (info->vrf_id == api->vrf_id) + if (info->vrf_id == api.vrf_id) break; } - /* * If the command is delete, then cancel the stale timer and * delete the bgp info */ - switch (api->cap) { + switch (api.cap) { case ZEBRA_CLIENT_GR_DISABLE: if (!info) return; @@ -323,7 +356,7 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) if ((info->gr_enable) && (client->gr_instance_count > 0)) client->gr_instance_count--; - zebra_gr_client_info_delte(client, info); + zebra_gr_client_info_delete(client, info); break; case ZEBRA_CLIENT_GR_CAPABILITIES: /* Allocate bgp info */ @@ -332,18 +365,16 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) /* Update other parameters */ if (!info->gr_enable) { - struct vrf *vrf = vrf_lookup_by_id(api->vrf_id); - client->gr_instance_count++; LOG_GR("%s: Cient %s vrf %s(%u) GR enabled count %d", __func__, zebra_route_string(client->proto), - VRF_LOGNAME(vrf), api->vrf_id, + VRF_LOGNAME(vrf), api.vrf_id, client->gr_instance_count); - info->capabilities = api->cap; - info->stale_removal_time = api->stale_removal_time; - info->vrf_id = api->vrf_id; + info->capabilities = api.cap; + info->stale_removal_time = api.stale_removal_time; + info->vrf_id = api.vrf_id; info->gr_enable = true; } break; @@ -353,107 +384,54 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) /* Update the stale removal timer */ if (info && info->t_stale_removal == NULL) { - struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); LOG_GR("%s: vrf %s(%u) Stale time: %d is now update to: %d", __func__, VRF_LOGNAME(vrf), info->vrf_id, info->stale_removal_time, - api->stale_removal_time); + api.stale_removal_time); - info->stale_removal_time = api->stale_removal_time; + info->stale_removal_time = api.stale_removal_time; } break; case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: if (!info) { - LOG_GR("%s: Client %s route update complete for AFI %d, SAFI %d", - __func__, zebra_route_string(client->proto), - api->afi, api->safi); - } else { - struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); - - LOG_GR("%s: Client %s vrf %s(%u) route update complete for AFI %d, SAFI %d", + LOG_GR("%s: Client %s route update complete for AFI %d, SAFI %d, no Graceful Restart communication, returning", __func__, zebra_route_string(client->proto), - VRF_LOGNAME(vrf), info->vrf_id, api->afi, - api->safi); - info->route_sync[api->afi][api->safi] = true; + api.afi, api.safi); + return; } + + LOG_GR("%s: Client %s vrf %s(%u) route update complete for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, api.afi, api.safi); + info->route_sync[api.afi] = true; + + /* + * Schedule for after anything already in the meta Q + */ + rib_add_gr_run(api.afi, api.vrf_id, client->proto, + client->instance); + zebra_gr_process_client_stale_routes(client, info); break; case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: if (!info) { LOG_GR("%s: Client %s route update pending for AFI %d, SAFI %d", __func__, zebra_route_string(client->proto), - api->afi, api->safi); + api.afi, api.safi); } else { - struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); - LOG_GR("%s: Client %s vrf %s(%u) route update pending for AFI %d, SAFI %d", __func__, zebra_route_string(client->proto), - VRF_LOGNAME(vrf), info->vrf_id, api->afi, - api->safi); + VRF_LOGNAME(vrf), info->vrf_id, api.afi, + api.safi); - info->af_enabled[api->afi][api->safi] = true; + info->af_enabled[api.afi] = true; } break; } } /* - * Handler for capabilities that are received from client. - */ -static void zebra_client_capabilities_handler(struct zserv *client, - struct zapi_cap *api) -{ - switch (api->cap) { - case ZEBRA_CLIENT_GR_CAPABILITIES: - case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: - case ZEBRA_CLIENT_GR_DISABLE: - case ZEBRA_CLIENT_RIB_STALE_TIME: - /* - * For all the cases we need to update the client info. - */ - zebra_client_update_info(client, api); - break; - case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: - /* - * After client info has been updated delete all - * stale routes - */ - zebra_client_update_info(client, api); - zebra_gr_process_client_stale_routes(client, api->vrf_id); - break; - } -} - -/* - * Function to decode and call appropriate functions - * to handle client capabilities. - */ -void zread_client_capabilities(ZAPI_HANDLER_ARGS) -{ - struct zapi_cap api; - struct stream *s; - - s = msg; - - if (zapi_capabilities_decode(s, &api)) { - LOG_GR("%s: Error in reading capabilities for client %s", - __func__, zebra_route_string(client->proto)); - return; - } - - /* GR only for dynamic clients */ - if (client->proto <= ZEBRA_ROUTE_CONNECT) { - LOG_GR("%s: GR capabilities for client %s not supported", - __func__, zebra_route_string(client->proto)); - return; - } - /* Call the capabilities handler */ - zebra_client_capabilities_handler(client, &api); -} - - -/* * Stale route handling */ @@ -470,10 +448,6 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread) client = (struct zserv *)info->stale_client_ptr; - /* Set the flag to indicate all stale route deletion */ - if (thread->u.val == 1) - info->do_delete = true; - cnt = zebra_gr_delete_stale_routes(info); /* Restart the timer */ @@ -492,8 +466,6 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread) __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), info->vrf_id); - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); - info->current_afi = 0; zebra_gr_delete_stale_client(info); } } @@ -502,14 +474,13 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread) /* * Function to process to check if route entry is stale * or has been updated. + * + * Returns true when a node is deleted else false */ -static void zebra_gr_process_route_entry(struct zserv *client, +static bool zebra_gr_process_route_entry(struct zserv *client, struct route_node *rn, struct route_entry *re) { - if ((client == NULL) || (rn == NULL) || (re == NULL)) - return; - /* If the route is not refreshed after restart, delete the entry */ if (re->uptime < client->restart_time) { if (IS_ZEBRA_DEBUG_RIB) @@ -517,7 +488,62 @@ static void zebra_gr_process_route_entry(struct zserv *client, __func__, zebra_route_string(client->proto), &rn->p); rib_delnode(rn, re); + + return true; } + + return false; +} + +static void zebra_gr_delete_stale_route_table_afi(struct event *event) +{ + struct zebra_gr_afi_clean *gac = EVENT_ARG(event); + struct route_table *table; + struct route_node *rn; + struct route_entry *re, *next; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(gac->info->vrf_id); + int32_t n = 0; + + if (!zvrf) + goto done; + + table = zvrf->table[gac->afi][SAFI_UNICAST]; + if (!table) + goto done; + + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) + continue; + + /* If the route refresh is received + * after restart then do not delete + * the route + */ + + if (re->type == gac->proto && + re->instance == gac->instance && + zebra_gr_process_route_entry( + gac->info->stale_client_ptr, rn, re)) + n++; + + /* If the max route count is reached + * then timer thread will be restarted + * Store the current prefix and afi + */ + if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT) && + (gac->info->do_delete == false)) { + event_add_timer( + zrouter.master, + zebra_gr_delete_stale_route_table_afi, + gac, ZEBRA_DEFAULT_STALE_UPDATE_DELAY, + &gac->t_gac); + } + } + } + +done: + XFREE(MTYPE_ZEBRA_GR, gac); } /* @@ -528,19 +554,11 @@ static void zebra_gr_process_route_entry(struct zserv *client, static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, struct zebra_vrf *zvrf) { - struct route_node *rn, *curr; - struct route_entry *re; - struct route_entry *next; - struct route_table *table; - int32_t n = 0; - afi_t afi, curr_afi; + afi_t afi; uint8_t proto; uint16_t instance; struct zserv *s_client; - if ((info == NULL) || (zvrf == NULL)) - return -1; - s_client = info->stale_client_ptr; if (s_client == NULL) { LOG_GR("%s: Stale client %s(%u) not present", __func__, @@ -550,69 +568,18 @@ static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, proto = s_client->proto; instance = s_client->instance; - curr_afi = info->current_afi; LOG_GR("%s: Client %s %s(%u) stale routes are being deleted", __func__, zebra_route_string(proto), zvrf->vrf->name, zvrf->vrf->vrf_id); /* Process routes for all AFI */ - for (afi = curr_afi; afi < AFI_MAX; afi++) { - table = zvrf->table[afi][SAFI_UNICAST]; + for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (table) { - /* - * If the current prefix is NULL then get the first - * route entry in the table - */ - if (info->current_prefix == NULL) { - rn = route_top(table); - if (rn == NULL) - continue; - curr = rn; - } else - /* Get the next route entry */ - curr = route_table_get_next( - table, info->current_prefix); - - for (rn = curr; rn; rn = srcdest_route_next(rn)) { - RNODE_FOREACH_RE_SAFE (rn, re, next) { - if (CHECK_FLAG(re->status, - ROUTE_ENTRY_REMOVED)) - continue; - /* If the route refresh is received - * after restart then do not delete - * the route - */ - if (re->type == proto - && re->instance == instance) { - zebra_gr_process_route_entry( - s_client, rn, re); - n++; - } - - /* If the max route count is reached - * then timer thread will be restarted - * Store the current prefix and afi - */ - if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT) - && (info->do_delete == false)) { - info->current_afi = afi; - info->current_prefix = XCALLOC( - MTYPE_ZEBRA_GR, - sizeof(struct prefix)); - prefix_copy( - info->current_prefix, - &rn->p); - return n; - } - } - } - } /* - * Reset the current prefix to indicate processing completion - * of the current AFI + * Schedule for immediately after anything in the + * meta-Q */ - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); + rib_add_gr_run(afi, info->vrf_id, proto, instance); } return 0; } @@ -623,21 +590,13 @@ static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, */ static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info) { - struct vrf *vrf; struct zebra_vrf *zvrf; uint64_t cnt = 0; if (info == NULL) return -1; - /* Get the current VRF */ - vrf = vrf_lookup_by_id(info->vrf_id); - if (vrf == NULL) { - LOG_GR("%s: Invalid VRF specified %u", __func__, info->vrf_id); - return -1; - } - - zvrf = vrf->info; + zvrf = zebra_vrf_lookup_by_id(info->vrf_id); if (zvrf == NULL) { LOG_GR("%s: Invalid VRF entry %u", __func__, info->vrf_id); return -1; @@ -652,49 +611,63 @@ static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info) * and cancels the stale timer */ static void zebra_gr_process_client_stale_routes(struct zserv *client, - vrf_id_t vrf_id) + struct client_gr_info *info) { - struct client_gr_info *info = NULL; afi_t afi; - safi_t safi; - - TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { - if (info->vrf_id == vrf_id) - break; - } if (info == NULL) return; /* Check if route update completed for all AFI, SAFI */ - FOREACH_AFI_SAFI_NSF (afi, safi) { - if (info->af_enabled[afi][safi]) { - if (!info->route_sync[afi][safi]) { - struct vrf *vrf = vrf_lookup_by_id(vrf_id); - - LOG_GR("%s: Client %s vrf: %s(%u) route update not completed for AFI %d, SAFI %d", - __func__, - zebra_route_string(client->proto), - VRF_LOGNAME(vrf), info->vrf_id, afi, - safi); - return; - } + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + if (info->af_enabled[afi] && !info->route_sync[afi]) { + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); + + LOG_GR("%s: Client %s vrf: %s(%u) route update not completed for AFI %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, afi); + return; } } /* * Route update completed for all AFI, SAFI - * Cancel the stale timer and process the routes + * Cancel the stale timer, routes are already being processed */ if (info->t_stale_removal) { - struct vrf *vrf = vrf_lookup_by_id(vrf_id); + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); LOG_GR("%s: Client %s canceled stale delete timer vrf %s(%d)", __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), info->vrf_id); EVENT_OFF(info->t_stale_removal); - event_execute(zrouter.master, - zebra_gr_route_stale_delete_timer_expiry, info, - 0); } } + +void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance) +{ + struct zserv *client = zserv_find_client(proto, instance); + struct client_gr_info *info = NULL; + struct zebra_gr_afi_clean *gac; + + if (client == NULL) + return; + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (info->vrf_id == vrf_id) + break; + } + + if (info == NULL) + return; + + gac = XCALLOC(MTYPE_ZEBRA_GR, sizeof(*gac)); + gac->info = info; + gac->afi = afi; + gac->proto = proto; + gac->instance = instance; + + event_add_event(zrouter.master, zebra_gr_delete_stale_route_table_afi, + gac, 0, &gac->t_gac); +} diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ceb0640553..32cfa4d04c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -64,7 +64,12 @@ DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), DEFINE_HOOK(rib_shutdown, (struct route_node * rn), (rn)); -/* Meta Q's specific names */ +/* + * Meta Q's specific names + * + * If you add something here ensure that you + * change MQ_SIZE as well over in rib.h + */ enum meta_queue_indexes { META_QUEUE_NHG, META_QUEUE_EVPN, @@ -76,6 +81,7 @@ enum meta_queue_indexes { META_QUEUE_NOTBGP, META_QUEUE_BGP, META_QUEUE_OTHER, + META_QUEUE_GR_RUN, }; /* Each route type's string and default distance value. */ @@ -250,6 +256,8 @@ static const char *subqueue2str(enum meta_queue_indexes index) return "BGP Routes"; case META_QUEUE_OTHER: return "Other Routes"; + case META_QUEUE_GR_RUN: + return "Graceful Restart"; } return "Unknown"; @@ -3089,6 +3097,23 @@ static void process_subq_early_route(struct listnode *lnode) process_subq_early_route_add(ere); } +struct meta_q_gr_run { + afi_t afi; + vrf_id_t vrf_id; + uint8_t proto; + uint8_t instance; +}; + +static void process_subq_gr_run(struct listnode *lnode) +{ + struct meta_q_gr_run *gr_run = listgetdata(lnode); + + zebra_gr_process_client(gr_run->afi, gr_run->vrf_id, gr_run->proto, + gr_run->instance); + + XFREE(MTYPE_WQ_WRAPPER, gr_run); +} + /* * Examine the specified subqueue; process one entry and return 1 if * there is a node, return 0 otherwise. @@ -3122,6 +3147,9 @@ static unsigned int process_subq(struct list *subq, case META_QUEUE_OTHER: process_subq_route(lnode, qindex); break; + case META_QUEUE_GR_RUN: + process_subq_gr_run(lnode); + break; } list_delete_node(subq, lnode); @@ -3727,6 +3755,20 @@ static void early_route_meta_queue_free(struct meta_queue *mq, struct list *l, } } +static void rib_meta_queue_gr_run_free(struct meta_queue *mq, struct list *l, + struct zebra_vrf *zvrf) +{ + struct meta_q_gr_run *gr_run; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(l, node, nnode, gr_run)) { + if (zvrf && zvrf->vrf->vrf_id != gr_run->vrf_id) + continue; + + XFREE(MTYPE_WQ_WRAPPER, gr_run); + } +} + void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf) { enum meta_queue_indexes i; @@ -3754,6 +3796,9 @@ void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf) case META_QUEUE_OTHER: rib_meta_queue_free(mq, mq->subq[i], zvrf); break; + case META_QUEUE_GR_RUN: + rib_meta_queue_gr_run_free(mq, mq->subq[i], zvrf); + break; } if (!zvrf) list_delete(&mq->subq[i]); @@ -4094,6 +4139,17 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, zlog_debug("%s: dump complete", straddr); } +static int rib_meta_queue_gr_run_add(struct meta_queue *mq, void *data) +{ + listnode_add(mq->subq[META_QUEUE_GR_RUN], data); + mq->size++; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("Graceful Run adding"); + + return 0; +} + static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data) { struct zebra_early_route *ere = data; @@ -4110,6 +4166,20 @@ static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data) return 0; } +int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto, uint8_t instance) +{ + struct meta_q_gr_run *gr_run; + + gr_run = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(*gr_run)); + + gr_run->afi = afi; + gr_run->proto = proto; + gr_run->vrf_id = vrf_id; + gr_run->instance = instance; + + return mq_add_handler(gr_run, rib_meta_queue_gr_run_add); +} + struct route_entry *zebra_rib_route_entry_new(vrf_id_t vrf_id, int type, uint8_t instance, uint32_t flags, uint32_t nhe_id, diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index d9a7ee465a..142501b149 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -1041,7 +1041,7 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) } alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1104,7 +1104,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, } plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1145,7 +1145,7 @@ route_match_address(afi_t afi, void *rule, const struct prefix *prefix, alist = access_list_lookup(afi, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1207,7 +1207,7 @@ route_match_address_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(afi, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index c370ad9169..36290f99e0 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -2582,19 +2582,18 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, struct zebra_mac *zrmac = NULL; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% L3-VNI %u doesn't exist\n", l3vni); return; @@ -2603,7 +2602,7 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, zrmac = zl3vni_rmac_lookup(zl3vni, rmac); if (!zrmac) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% Requested RMAC doesn't exist in L3-VNI %u\n", @@ -2624,13 +2623,18 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json) struct rmac_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); return; @@ -2639,9 +2643,6 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json) if (!num_rmacs) return; - if (use_json) - json = json_object_new_object(); - memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; wctx.json = json; @@ -2663,15 +2664,14 @@ void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json) json_object *json = NULL; void *args[2]; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; hash_iterate(zrouter.l3vni_table, @@ -2690,15 +2690,14 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, struct zebra_neigh *n = NULL; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - /* If vni=0 passed, assume svd lookup */ if (!l3vni) n = svd_nh_lookup(ip); @@ -2799,15 +2798,14 @@ void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json) json_object *json = NULL; void *args[2]; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; hash_iterate(zrouter.l3vni_table, @@ -2828,24 +2826,23 @@ void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, bool use_json) json_object *json = NULL; struct zebra_l3vni *zl3vni = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } zl3vni = zl3vni_lookup(vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; zl3vni_print(zl3vni, (void *)args); @@ -2900,12 +2897,18 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -2914,9 +2917,6 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing * the maximum width. @@ -2951,12 +2951,14 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; void *args[3]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; @@ -2979,12 +2981,14 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, json_object *json = NULL; void *args[3]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; @@ -3008,12 +3012,18 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_neigh *n; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3026,8 +3036,6 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, vni); return; } - if (use_json) - json = json_object_new_object(); zebra_evpn_print_neigh(n, vty, json); @@ -3048,12 +3056,18 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3062,9 +3076,6 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - memset(&wctx, 0, sizeof(wctx)); wctx.zevpn = zevpn; wctx.vty = vty; @@ -3094,12 +3105,20 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_json(vty, json); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } @@ -3111,9 +3130,6 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing * the maximum width. @@ -3155,8 +3171,12 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; json_object *json_mac = NULL; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) @@ -3218,13 +3238,13 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, struct mac_walk_ctx wctx; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; @@ -3246,13 +3266,13 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, struct mac_walk_ctx wctx; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; @@ -3275,12 +3295,14 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct mac_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; @@ -3303,13 +3325,18 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, struct zebra_mac *mac; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3317,7 +3344,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% Requested MAC does not exist in VNI %u\n", @@ -3325,10 +3352,8 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, return; } - if (use_json) - json = json_object_new_object(); - zebra_evpn_print_mac(mac, vty, json); + if (use_json) vty_json(vty, json); } @@ -3693,8 +3718,11 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; json_object *json_mac = NULL; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) @@ -3745,12 +3773,14 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct zebra_l3vni *zl3vni = NULL; struct zebra_evpn *zevpn = NULL; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; @@ -3787,8 +3817,13 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json_object *json = NULL; struct zebra_vrf *zvrf = NULL; - if (!is_evpn_enabled()) + if (uj) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zvrf = zebra_vrf_get_evpn(); @@ -3797,7 +3832,6 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) num_vnis = num_l2vnis + num_l3vnis; if (uj) { - json = json_object_new_object(); json_object_string_add(json, "advertiseGatewayMacip", zvrf->advertise_gw_macip ? "Yes" : "No"); json_object_string_add(json, "advertiseSviMacip", @@ -3860,12 +3894,15 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; void *args[2]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); - else + + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + + if (!use_json) vty_out(vty, "%-10s %-4s %-21s %-8s %-8s %-15s %-37s\n", "VNI", "Type", "VxLAN IF", "# MACs", "# ARPs", "# Remote VTEPs", "Tenant VRF"); @@ -3943,8 +3980,11 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, struct zebra_ns *zns = NULL; struct zebra_evpn_show zes; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); return; + } zns = zebra_ns_lookup(NS_DEFAULT); if (!zns) diff --git a/zebra/zserv.c b/zebra/zserv.c index 70707866ee..6abd49310c 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1152,10 +1152,6 @@ static void zebra_show_stale_client_detail(struct vty *vty, info->t_stale_removal)); } } - vty_out(vty, "Current AFI : %d\n", info->current_afi); - if (info->current_prefix) - vty_out(vty, "Current prefix : %pFX\n", - info->current_prefix); } } vty_out(vty, "\n"); diff --git a/zebra/zserv.h b/zebra/zserv.h index aa58a3a299..90aa4d53f4 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -51,9 +51,6 @@ struct client_gr_info { /* VRF for which GR enabled */ vrf_id_t vrf_id; - /* AFI */ - afi_t current_afi; - /* Stale time and GR cap */ uint32_t stale_removal_time; enum zserv_client_capabilities capabilities; @@ -64,11 +61,10 @@ struct client_gr_info { bool stale_client; /* Route sync and enable flags for AFI/SAFI */ - bool af_enabled[AFI_MAX][SAFI_MAX]; - bool route_sync[AFI_MAX][SAFI_MAX]; + bool af_enabled[AFI_MAX]; + bool route_sync[AFI_MAX]; /* Book keeping */ - struct prefix *current_prefix; void *stale_client_ptr; struct event *t_stale_removal; |
