summaryrefslogtreecommitdiffstats
path: root/zebra/redistribute.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/redistribute.c')
-rw-r--r--zebra/redistribute.c922
1 files changed, 922 insertions, 0 deletions
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
new file mode 100644
index 0000000..7559e31
--- /dev/null
+++ b/zebra/redistribute.c
@@ -0,0 +1,922 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Redistribution Handler
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "prefix.h"
+#include "table.h"
+#include "stream.h"
+#include "zclient.h"
+#include "linklist.h"
+#include "log.h"
+#include "vrf.h"
+#include "srcdest_table.h"
+
+#include "zebra/rib.h"
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_ns.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_routemap.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/router-id.h"
+#include "zebra/zapi_msg.h"
+#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/zebra_neigh.h"
+
+#define ZEBRA_PTM_SUPPORT
+
+/* array holding redistribute info about table redistribution */
+/* bit AFI is set if that AFI is redistributing routes from this table */
+static int zebra_import_table_used[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX];
+static uint32_t zebra_import_table_distance[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX];
+
+int is_zebra_import_table_enabled(afi_t afi, vrf_id_t vrf_id, uint32_t table_id)
+{
+ /*
+ * Make sure that what we are called with actualy makes sense
+ */
+ if (afi == AFI_MAX)
+ return 0;
+
+ if (is_zebra_valid_kernel_table(table_id) &&
+ table_id < ZEBRA_KERNEL_TABLE_MAX)
+ return zebra_import_table_used[afi][table_id];
+ return 0;
+}
+
+static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
+{
+ int afi;
+ struct prefix p;
+ struct route_table *table;
+ struct route_node *rn;
+ struct route_entry *newre;
+
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+
+ if (!vrf_bitmap_check(&client->redist_default[afi], vrf_id))
+ continue;
+
+ /* Lookup table. */
+ table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
+ if (!table)
+ continue;
+
+ /* Lookup default route. */
+ memset(&p, 0, sizeof(p));
+ p.family = afi2family(afi);
+ rn = route_node_lookup(table, &p);
+ if (!rn)
+ continue;
+
+ RNODE_FOREACH_RE (rn, newre) {
+ if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED))
+ zsend_redistribute_route(
+ ZEBRA_REDISTRIBUTE_ROUTE_ADD, client,
+ rn, newre);
+ }
+
+ route_unlock_node(rn);
+ }
+}
+
+/* Redistribute routes. */
+static void zebra_redistribute(struct zserv *client, int type,
+ unsigned short instance, vrf_id_t vrf_id,
+ int afi)
+{
+ struct route_entry *newre;
+ struct route_table *table;
+ struct route_node *rn;
+
+ table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
+ if (!table)
+ return;
+
+ for (rn = route_top(table); rn; rn = srcdest_route_next(rn))
+ RNODE_FOREACH_RE (rn, newre) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug(
+ "%s: client %s %pRN(%u:%u) checking: selected=%d, type=%s, instance=%u, distance=%d, metric=%d zebra_check_addr=%d",
+ __func__,
+ zebra_route_string(client->proto), rn,
+ vrf_id, newre->instance,
+ !!CHECK_FLAG(newre->flags,
+ ZEBRA_FLAG_SELECTED),
+ zebra_route_string(newre->type),
+ newre->instance,
+ newre->distance,
+ newre->metric,
+ zebra_check_addr(&rn->p));
+
+ if (!CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED))
+ continue;
+ if ((type != ZEBRA_ROUTE_ALL
+ && (newre->type != type
+ || newre->instance != instance)))
+ continue;
+ if (!zebra_check_addr(&rn->p))
+ continue;
+
+ zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
+ client, rn, newre);
+ }
+}
+
+/*
+ * Function to check if prefix is candidate for
+ * redistribute.
+ */
+static bool zebra_redistribute_check(const struct route_node *rn,
+ const struct route_entry *re,
+ struct zserv *client)
+{
+ struct zebra_vrf *zvrf;
+ afi_t afi;
+
+ /* Process only if there is valid re */
+ if (!re)
+ return false;
+
+ afi = family2afi(rn->p.family);
+ zvrf = zebra_vrf_lookup_by_id(re->vrf_id);
+ if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table)
+ return false;
+
+ /* If default route and redistributed */
+ if (is_default_prefix(&rn->p) &&
+ vrf_bitmap_check(&client->redist_default[afi], re->vrf_id))
+ return true;
+
+ /* If redistribute in enabled for zebra route all */
+ if (vrf_bitmap_check(&client->redist[afi][ZEBRA_ROUTE_ALL], re->vrf_id))
+ return true;
+
+ /*
+ * If multi-instance then check for route
+ * redistribution for given instance.
+ */
+ if (re->instance) {
+ if (redist_check_instance(&client->mi_redist[afi][re->type],
+ re->instance))
+ return true;
+ else
+ return false;
+ }
+
+ /* If redistribution is enabled for give route type. */
+ if (vrf_bitmap_check(&client->redist[afi][re->type], re->vrf_id))
+ return true;
+
+ return false;
+}
+
+/* Either advertise a route for redistribution to registered clients or */
+/* withdraw redistribution if add cannot be done for client */
+void redistribute_update(const struct route_node *rn,
+ const struct route_entry *re,
+ const struct route_entry *prev_re)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug(
+ "(%u:%u):%pRN(%u): Redist update re %p (%s), old %p (%s)",
+ re->vrf_id, re->table, rn, re->instance, re,
+ zebra_route_string(re->type), prev_re,
+ prev_re ? zebra_route_string(prev_re->type) : "None");
+
+ if (!zebra_check_addr(&rn->p)) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("Redist update filter prefix %pRN", rn);
+ return;
+ }
+
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ if (zebra_redistribute_check(rn, re, client)) {
+ if (IS_ZEBRA_DEBUG_RIB) {
+ zlog_debug(
+ "%s: client %s %pRN(%u:%u), type=%d, distance=%d, metric=%d",
+ __func__,
+ zebra_route_string(client->proto), rn,
+ re->vrf_id, re->table, re->type,
+ re->distance, re->metric);
+ }
+ zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
+ client, rn, re);
+ } else if (zebra_redistribute_check(rn, prev_re, client))
+ zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL,
+ client, rn, prev_re);
+ }
+}
+
+/*
+ * During a route delete, where 'new_re' is NULL, redist a delete to all
+ * clients registered for the type of 'old_re'.
+ * During a route update, redist a delete to any clients who will not see
+ * an update when the new route is installed. There are cases when a client
+ * may have seen a redist for 'old_re', but will not see
+ * the redist for 'new_re'.
+ */
+void redistribute_delete(const struct route_node *rn,
+ const struct route_entry *old_re,
+ const struct route_entry *new_re)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+ vrf_id_t vrfid;
+
+ if (old_re)
+ vrfid = old_re->vrf_id;
+ else if (new_re)
+ vrfid = new_re->vrf_id;
+ else
+ return;
+
+ if (IS_ZEBRA_DEBUG_RIB) {
+ uint8_t old_inst, new_inst;
+ uint32_t table = 0;
+
+ old_inst = new_inst = 0;
+
+ if (old_re) {
+ old_inst = old_re->instance;
+ table = old_re->table;
+ }
+ if (new_re) {
+ new_inst = new_re->instance;
+ table = new_re->table;
+ }
+
+ zlog_debug("(%u:%u):%pRN: Redist del: re %p (%u:%s), new re %p (%u:%s)",
+ vrfid, table, rn, old_re, old_inst,
+ old_re ? zebra_route_string(old_re->type) : "None",
+ new_re, new_inst,
+ new_re ? zebra_route_string(new_re->type) : "None");
+ }
+
+ /* Skip invalid (e.g. linklocal) prefix */
+ if (!zebra_check_addr(&rn->p)) {
+ if (IS_ZEBRA_DEBUG_RIB) {
+ zlog_debug(
+ "%u:%pRN: Redist del old: skipping invalid prefix",
+ vrfid, rn);
+ }
+ return;
+ }
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+ /*
+ * Skip this client if it will receive an update for the
+ * 'new' re
+ */
+ if (zebra_redistribute_check(rn, new_re, client))
+ continue;
+
+ /* Send a delete for the 'old' re to any subscribed client. */
+ if (zebra_redistribute_check(rn, old_re, client))
+ zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL,
+ client, rn, old_re);
+ }
+}
+
+
+void zebra_redistribute_add(ZAPI_HANDLER_ARGS)
+{
+ afi_t afi = 0;
+ int type = 0;
+ unsigned short instance;
+
+ STREAM_GETC(msg, afi);
+ STREAM_GETC(msg, type);
+ STREAM_GETW(msg, instance);
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug(
+ "%s: client proto %s afi=%d, wants %s, vrf %s(%u), instance=%d",
+ __func__, zebra_route_string(client->proto), afi,
+ zebra_route_string(type), VRF_LOGNAME(zvrf->vrf),
+ zvrf_id(zvrf), instance);
+
+ if (afi == 0 || afi >= AFI_MAX) {
+ flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF,
+ "%s: Specified afi %d does not exist", __func__, afi);
+ return;
+ }
+
+ if (type == 0 || type >= ZEBRA_ROUTE_MAX) {
+ zlog_debug("%s: Specified Route Type %d does not exist",
+ __func__, type);
+ return;
+ }
+
+ if (instance) {
+ if (!redist_check_instance(&client->mi_redist[afi][type],
+ instance)) {
+ redist_add_instance(&client->mi_redist[afi][type],
+ instance);
+ zebra_redistribute(client, type, instance,
+ zvrf_id(zvrf), afi);
+ }
+ } else {
+ if (!vrf_bitmap_check(&client->redist[afi][type],
+ zvrf_id(zvrf))) {
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug(
+ "%s: setting vrf %s(%u) redist bitmap",
+ __func__, VRF_LOGNAME(zvrf->vrf),
+ zvrf_id(zvrf));
+ vrf_bitmap_set(&client->redist[afi][type],
+ zvrf_id(zvrf));
+ zebra_redistribute(client, type, 0, zvrf_id(zvrf), afi);
+ }
+ }
+
+stream_failure:
+ return;
+}
+
+void zebra_redistribute_delete(ZAPI_HANDLER_ARGS)
+{
+ afi_t afi = 0;
+ int type = 0;
+ unsigned short instance;
+
+ STREAM_GETC(msg, afi);
+ STREAM_GETC(msg, type);
+ STREAM_GETW(msg, instance);
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug(
+ "%s: client proto %s afi=%d, no longer wants %s, vrf %s(%u), instance=%d",
+ __func__, zebra_route_string(client->proto), afi,
+ zebra_route_string(type), VRF_LOGNAME(zvrf->vrf),
+ zvrf_id(zvrf), instance);
+
+
+ if (afi == 0 || afi >= AFI_MAX) {
+ flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF,
+ "%s: Specified afi %d does not exist", __func__, afi);
+ return;
+ }
+
+ if (type == 0 || type >= ZEBRA_ROUTE_MAX) {
+ zlog_debug("%s: Specified Route Type %d does not exist",
+ __func__, type);
+ return;
+ }
+
+ /*
+ * NOTE: no need to withdraw the previously advertised routes. The
+ * clients
+ * themselves should keep track of the received routes from zebra and
+ * withdraw them when necessary.
+ */
+ if (instance)
+ redist_del_instance(&client->mi_redist[afi][type], instance);
+ else
+ vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf));
+
+stream_failure:
+ return;
+}
+
+void zebra_redistribute_default_add(ZAPI_HANDLER_ARGS)
+{
+ afi_t afi = 0;
+
+ STREAM_GETC(msg, afi);
+
+ if (afi == 0 || afi >= AFI_MAX) {
+ flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF,
+ "%s: Specified afi %u does not exist", __func__, afi);
+ return;
+ }
+
+ vrf_bitmap_set(&client->redist_default[afi], zvrf_id(zvrf));
+ zebra_redistribute_default(client, zvrf_id(zvrf));
+
+stream_failure:
+ return;
+}
+
+void zebra_redistribute_default_delete(ZAPI_HANDLER_ARGS)
+{
+ afi_t afi = 0;
+
+ STREAM_GETC(msg, afi);
+
+ if (afi == 0 || afi >= AFI_MAX) {
+ flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF,
+ "%s: Specified afi %u does not exist", __func__, afi);
+ return;
+ }
+
+ vrf_bitmap_unset(&client->redist_default[afi], zvrf_id(zvrf));
+
+stream_failure:
+ return;
+}
+
+/* Interface up information. */
+void zebra_interface_up_update(struct interface *ifp)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_UP %s vrf %s(%u)",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id);
+
+ if (ifp->ptm_status || !ifp->ptm_enable) {
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode,
+ client)) {
+ /* Do not send unsolicited messages to synchronous
+ * clients.
+ */
+ if (client->synchronous)
+ continue;
+
+ zsend_interface_update(ZEBRA_INTERFACE_UP,
+ client, ifp);
+ zsend_interface_link_params(client, ifp);
+ }
+ }
+}
+
+/* Interface down information. */
+void zebra_interface_down_update(struct interface *ifp)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_DOWN %s vrf %s(%u)",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ zsend_interface_update(ZEBRA_INTERFACE_DOWN, client, ifp);
+ }
+
+ zebra_neigh_del_all(ifp);
+}
+
+/* Interface information update. */
+void zebra_interface_add_update(struct interface *ifp)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADD %s vrf %s(%u)",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ client->ifadd_cnt++;
+ zsend_interface_add(client, ifp);
+ zsend_interface_link_params(client, ifp);
+ }
+}
+
+void zebra_interface_delete_update(struct interface *ifp)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_DELETE %s vrf %s(%u)",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ client->ifdel_cnt++;
+ zsend_interface_delete(client, ifp);
+ }
+}
+
+/* Interface address addition. */
+void zebra_interface_address_add_update(struct interface *ifp,
+ struct connected *ifc)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug(
+ "MESSAGE: ZEBRA_INTERFACE_ADDRESS_ADD %pFX on %s vrf %s(%u)",
+ ifc->address, ifp->name, ifp->vrf->name,
+ ifp->vrf->vrf_id);
+
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
+ flog_warn(
+ EC_ZEBRA_ADVERTISING_UNUSABLE_ADDR,
+ "advertising address to clients that is not yet usable.");
+
+ zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 1);
+
+ router_id_add_address(ifc);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) {
+ client->connected_rt_add_cnt++;
+ zsend_interface_address(ZEBRA_INTERFACE_ADDRESS_ADD,
+ client, ifp, ifc);
+ }
+ }
+ /* interface associated NHGs may have been deleted,
+ * re-sync zebra -> dplane NHGs
+ */
+ zebra_interface_nhg_reinstall(ifp);
+}
+
+/* Interface address deletion. */
+void zebra_interface_address_delete_update(struct interface *ifp,
+ struct connected *ifc)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug(
+ "MESSAGE: ZEBRA_INTERFACE_ADDRESS_DELETE %pFX on %s vrf %s(%u)",
+ ifc->address, ifp->name, ifp->vrf->name,
+ ifp->vrf->vrf_id);
+
+ zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 0);
+
+ router_id_del_address(ifc);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) {
+ client->connected_rt_del_cnt++;
+ zsend_interface_address(ZEBRA_INTERFACE_ADDRESS_DELETE,
+ client, ifp, ifc);
+ }
+ }
+}
+
+/* Interface VRF change. May need to delete from clients not interested in
+ * the new VRF. Note that this function is invoked *prior* to the VRF change.
+ */
+void zebra_interface_vrf_update_del(struct interface *ifp, vrf_id_t new_vrf_id)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_DELETE %s VRF Id %u -> %u",
+ ifp->name, ifp->vrf->vrf_id, new_vrf_id);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ /* Need to delete if the client is not interested in the new
+ * VRF. */
+ zsend_interface_update(ZEBRA_INTERFACE_DOWN, client, ifp);
+ client->ifdel_cnt++;
+ zsend_interface_delete(client, ifp);
+ }
+}
+
+/* Interface VRF change. This function is invoked *post* VRF change and sends an
+ * add to clients who are interested in the new VRF but not in the old VRF.
+ */
+void zebra_interface_vrf_update_add(struct interface *ifp, vrf_id_t old_vrf_id)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_ADD %s VRF Id %u -> %u",
+ ifp->name, old_vrf_id, ifp->vrf->vrf_id);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ /* Need to add if the client is interested in the new VRF. */
+ client->ifadd_cnt++;
+ zsend_interface_add(client, ifp);
+ zsend_interface_addresses(client, ifp);
+ }
+}
+
+int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
+ struct route_entry *re, const char *rmap_name)
+{
+ struct route_entry *newre;
+ struct route_entry *same;
+ struct prefix p;
+ struct nexthop_group *ng;
+ route_map_result_t ret = RMAP_PERMITMATCH;
+ afi_t afi;
+
+ afi = family2afi(rn->p.family);
+ if (rmap_name)
+ ret = zebra_import_table_route_map_check(afi, re, &rn->p,
+ re->nhe->nhg.nexthop,
+ rmap_name);
+
+ if (ret != RMAP_PERMITMATCH) {
+ UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED);
+ zebra_del_import_table_entry(zvrf, rn, re);
+ return 0;
+ }
+
+ prefix_copy(&p, &rn->p);
+
+ RNODE_FOREACH_RE (rn, same) {
+ if (CHECK_FLAG(same->status, ROUTE_ENTRY_REMOVED))
+ continue;
+
+ if (same->type == re->type && same->instance == re->instance
+ && same->table == re->table
+ && same->type != ZEBRA_ROUTE_CONNECT)
+ break;
+ }
+
+ if (same) {
+ UNSET_FLAG(same->flags, ZEBRA_FLAG_SELECTED);
+ zebra_del_import_table_entry(zvrf, rn, same);
+ }
+
+ UNSET_FLAG(re->flags, ZEBRA_FLAG_RR_USE_DISTANCE);
+
+ newre = zebra_rib_route_entry_new(
+ 0, ZEBRA_ROUTE_TABLE, re->table, re->flags, re->nhe_id,
+ zvrf->table_id, re->metric, re->mtu,
+ zebra_import_table_distance[afi][re->table], re->tag);
+
+ ng = nexthop_group_new();
+ copy_nexthops(&ng->nexthop, re->nhe->nhg.nexthop, NULL);
+
+ rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre, ng, false);
+ nexthop_group_delete(&ng);
+
+ return 0;
+}
+
+int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
+ struct route_entry *re)
+{
+ struct prefix p;
+ afi_t afi;
+
+ afi = family2afi(rn->p.family);
+ prefix_copy(&p, &rn->p);
+
+ rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE,
+ re->table, re->flags, &p, NULL, re->nhe->nhg.nexthop,
+ re->nhe_id, zvrf->table_id, re->metric, re->distance,
+ false);
+
+ return 0;
+}
+
+/* Assuming no one calls this with the main routing table */
+int zebra_import_table(afi_t afi, vrf_id_t vrf_id, uint32_t table_id,
+ uint32_t distance, const char *rmap_name, int add)
+{
+ struct route_table *table;
+ struct route_entry *re;
+ struct route_node *rn;
+ struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(vrf_id);
+
+ if (!is_zebra_valid_kernel_table(table_id)
+ || (table_id == rt_table_main_id))
+ return -1;
+
+ if (afi >= AFI_MAX)
+ return -1;
+
+ table = zebra_vrf_get_table_with_table_id(afi, SAFI_UNICAST, vrf_id,
+ table_id);
+ if (table == NULL) {
+ return 0;
+ } else if (IS_ZEBRA_DEBUG_RIB) {
+ zlog_debug("%s routes from table %d",
+ add ? "Importing" : "Unimporting", table_id);
+ }
+
+ if (add) {
+ if (rmap_name)
+ zebra_add_import_table_route_map(afi, rmap_name,
+ table_id);
+ else {
+ rmap_name =
+ zebra_get_import_table_route_map(afi, table_id);
+ if (rmap_name) {
+ zebra_del_import_table_route_map(afi, table_id);
+ rmap_name = NULL;
+ }
+ }
+
+ zebra_import_table_used[afi][table_id] = 1;
+ zebra_import_table_distance[afi][table_id] = distance;
+ } else {
+ zebra_import_table_used[afi][table_id] = 0;
+ zebra_import_table_distance[afi][table_id] =
+ ZEBRA_TABLE_DISTANCE_DEFAULT;
+
+ rmap_name = zebra_get_import_table_route_map(afi, table_id);
+ if (rmap_name) {
+ zebra_del_import_table_route_map(afi, table_id);
+ rmap_name = NULL;
+ }
+ }
+
+ for (rn = route_top(table); rn; rn = route_next(rn)) {
+ /* For each entry in the non-default routing table,
+ * add the entry in the main table
+ */
+ if (!rn->info)
+ continue;
+
+ RNODE_FOREACH_RE (rn, re) {
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
+ continue;
+ break;
+ }
+
+ if (!re)
+ continue;
+
+ if (((afi == AFI_IP) && (rn->p.family == AF_INET))
+ || ((afi == AFI_IP6) && (rn->p.family == AF_INET6))) {
+ if (add)
+ zebra_add_import_table_entry(zvrf, rn, re,
+ rmap_name);
+ else
+ zebra_del_import_table_entry(zvrf, rn, re);
+ }
+ }
+ return 0;
+}
+
+int zebra_import_table_config(struct vty *vty, vrf_id_t vrf_id)
+{
+ int i;
+ afi_t afi;
+ int write = 0;
+ char afi_str[AFI_MAX][10] = {"", "ip", "ipv6", "ethernet"};
+ const char *rmap_name;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 1; i < ZEBRA_KERNEL_TABLE_MAX; i++) {
+ if (!is_zebra_import_table_enabled(afi, vrf_id, i))
+ continue;
+
+ if (zebra_import_table_distance[afi][i]
+ != ZEBRA_TABLE_DISTANCE_DEFAULT) {
+ vty_out(vty, "%s import-table %d distance %d",
+ afi_str[afi], i,
+ zebra_import_table_distance[afi][i]);
+ } else {
+ vty_out(vty, "%s import-table %d", afi_str[afi],
+ i);
+ }
+
+ rmap_name = zebra_get_import_table_route_map(afi, i);
+ if (rmap_name)
+ vty_out(vty, " route-map %s", rmap_name);
+
+ vty_out(vty, "\n");
+ write = 1;
+ }
+ }
+
+ return write;
+}
+
+static void zebra_import_table_rm_update_vrf_afi(struct zebra_vrf *zvrf,
+ afi_t afi, int table_id,
+ const char *rmap)
+{
+ struct route_table *table;
+ struct route_entry *re;
+ struct route_node *rn;
+ const char *rmap_name;
+
+ rmap_name = zebra_get_import_table_route_map(afi, table_id);
+ if ((!rmap_name) || (strcmp(rmap_name, rmap) != 0))
+ return;
+
+ table = zebra_vrf_get_table_with_table_id(afi, SAFI_UNICAST,
+ zvrf->vrf->vrf_id, table_id);
+ if (!table) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: Table id=%d not found", __func__,
+ table_id);
+ return;
+ }
+
+ for (rn = route_top(table); rn; rn = route_next(rn)) {
+ /*
+ * For each entry in the non-default routing table,
+ * add the entry in the main table
+ */
+ if (!rn->info)
+ continue;
+
+ RNODE_FOREACH_RE (rn, re) {
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
+ continue;
+ break;
+ }
+
+ if (!re)
+ continue;
+
+ if (((afi == AFI_IP) && (rn->p.family == AF_INET))
+ || ((afi == AFI_IP6) && (rn->p.family == AF_INET6)))
+ zebra_add_import_table_entry(zvrf, rn, re, rmap_name);
+ }
+
+ return;
+}
+
+static void zebra_import_table_rm_update_vrf(struct zebra_vrf *zvrf,
+ const char *rmap)
+{
+ afi_t afi;
+ int i;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 1; i < ZEBRA_KERNEL_TABLE_MAX; i++) {
+ if (!is_zebra_import_table_enabled(
+ afi, zvrf->vrf->vrf_id, i))
+ continue;
+
+ zebra_import_table_rm_update_vrf_afi(zvrf, afi, i,
+ rmap);
+ }
+ }
+}
+
+void zebra_import_table_rm_update(const char *rmap)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ zvrf = vrf->info;
+
+ if (!zvrf)
+ continue;
+
+ zebra_import_table_rm_update_vrf(zvrf, rmap);
+ }
+}
+
+/* Interface parameters update */
+void zebra_interface_parameters_update(struct interface *ifp)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_INTERFACE_LINK_PARAMS %s vrf %s(%u)",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id);
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ zsend_interface_link_params(client, ifp);
+ }
+}