#include "checksum.h"
#include "md5.h"
#include "table.h"
+#include "srcdest_table.h"
#include "isisd/dict.h"
#include "isisd/isis_constants.h"
return;
for (struct route_node *rn = route_top(er_table); rn;
- rn = route_next(rn)) {
+ rn = srcdest_route_next(rn)) {
if (!rn->info)
continue;
-
- struct prefix_ipv6 *ipv6 = (struct prefix_ipv6 *)&rn->p;
struct isis_ext_info *info = rn->info;
+ struct prefix_ipv6 *p, *src_p;
+ srcdest_rnode_prefixes(rn, (const struct prefix **)&p,
+ (const struct prefix **)&src_p);
+
uint32_t metric = info->metric;
if (info->metric > MAX_WIDE_PATH_METRIC)
metric = MAX_WIDE_PATH_METRIC;
- isis_tlvs_add_ipv6_reach(
- lsp->tlvs, isis_area_ipv6_topology(area), ipv6, metric);
+
+ if (!src_p || !src_p->prefixlen) {
+ isis_tlvs_add_ipv6_reach(lsp->tlvs,
+ isis_area_ipv6_topology(area),
+ p, metric);
+ } else if (isis_area_ipv6_dstsrc_enabled(area)) {
+ isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs,
+ ISIS_MT_IPV6_DSTSRC,
+ p, src_p, metric);
+ }
}
}
DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting")
DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info")
+bool isis_area_ipv6_dstsrc_enabled(struct isis_area *area)
+{
+ struct isis_area_mt_setting *area_mt_setting;
+ area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_DSTSRC);
+
+ return (area_mt_setting && area_mt_setting->enabled);
+}
+
uint16_t isis_area_ipv6_topology(struct isis_area *area)
{
struct isis_area_mt_setting *area_mt_setting;
return "ipv6-multicast";
case ISIS_MT_IPV6_MGMT:
return "ipv6-mgmt";
+ case ISIS_MT_IPV6_DSTSRC:
+ return "ipv6-dstsrc";
default:
snprintf(buf, sizeof(buf), "%" PRIu16, mtid);
return buf;
return ISIS_MT_IPV6_MULTICAST;
if (!strcmp(name, "ipv6-mgmt"))
return ISIS_MT_IPV6_MGMT;
+ if (!strcmp(name, "ipv6-dstsrc"))
+ return ISIS_MT_IPV6_DSTSRC;
return -1;
}
#define ISIS_MT_IPV4_MULTICAST 3
#define ISIS_MT_IPV6_MULTICAST 4
#define ISIS_MT_IPV6_MGMT 5
+#define ISIS_MT_IPV6_DSTSRC 3996 /* FIXME: IANA */
#define ISIS_MT_NAMES \
"<ipv4-unicast" \
"|ipv4-multicast" \
"|ipv6-multicast" \
"|ipv6-mgmt" \
+ "|ipv6-dstsrc" \
">"
#define ISIS_MT_DESCRIPTIONS \
"IPv6 unicast topology\n" \
"IPv4 multicast topology\n" \
"IPv6 multicast topology\n" \
- "IPv6 management topology\n"
+ "IPv6 management topology\n" \
+ "IPv6 dst-src topology\n" \
+ ""
#define ISIS_MT_INFO_FIELDS uint16_t mtid;
struct te_is_neigh;
struct isis_tlvs;
+bool isis_area_ipv6_dstsrc_enabled(struct isis_area *area);
+
uint16_t isis_area_ipv6_topology(struct isis_area *area);
struct isis_area_mt_setting *area_lookup_mt_setting(struct isis_area *area,
iih.v6_usable = (circuit->ipv6_link && listcount(circuit->ipv6_link)
&& iih.tlvs->ipv6_address.count);
- if (!iih.v4_usable && !iih.v6_usable)
+ if (!iih.v4_usable && !iih.v6_usable) {
+ if (isis->debugs & DEBUG_ADJ_PACKETS) {
+ zlog_warn(
+ "ISIS-Adj (%s): Neither IPv4 nor IPv6 considered usable. Ignoring IIH",
+ circuit->area->area_tag);
+ }
+
goto out;
+ }
retval = p2p_hello ? process_p2p_hello(&iih) : process_lan_hello(&iih);
out:
#include "stream.h"
#include "table.h"
#include "vty.h"
+#include "srcdest_table.h"
#include "isisd/dict.h"
#include "isisd/isis_constants.h"
return area->ext_reach[protocol][level - 1];
}
-static struct route_node *
-isis_redist_route_node_create(route_table_delegate_t *delegate,
- struct route_table *table)
-{
- struct route_node *node;
- node = XCALLOC(MTYPE_ISIS_EXT_ROUTE, sizeof(*node));
- return node;
-}
-
-static void isis_redist_route_node_destroy(route_table_delegate_t *delegate,
- struct route_table *table,
- struct route_node *node)
-{
- if (node->info)
- XFREE(MTYPE_ISIS_EXT_INFO, node->info);
- XFREE(MTYPE_ISIS_EXT_ROUTE, node);
-}
-
-static route_table_delegate_t isis_redist_rt_delegate = {
- .create_node = isis_redist_route_node_create,
- .destroy_node = isis_redist_route_node_destroy};
-
/* Install external reachability information into a
* specific area for a specific level.
* Schedule an lsp regenerate if necessary */
static void isis_redist_install(struct isis_area *area, int level,
- struct prefix *p, struct isis_ext_info *info)
+ const struct prefix *p,
+ const struct prefix_ipv6 *src_p,
+ struct isis_ext_info *info)
{
int family = p->family;
struct route_table *er_table = get_ext_reach(area, family, level);
return;
}
- er_node = route_node_get(er_table, p);
+ er_node = srcdest_rnode_get(er_table, p, src_p);
if (er_node->info) {
route_unlock_node(er_node);
* specific area for a specific level.
* Schedule an lsp regenerate if necessary. */
static void isis_redist_uninstall(struct isis_area *area, int level,
- struct prefix *p)
+ const struct prefix *p,
+ const struct prefix_ipv6 *src_p)
{
int family = p->family;
struct route_table *er_table = get_ext_reach(area, family, level);
return;
}
- er_node = route_node_lookup(er_table, p);
+ er_node = srcdest_rnode_lookup(er_table, p, src_p);
if (!er_node)
return;
else
* and prefix, using the given redistribution settings. */
static void isis_redist_update_ext_reach(struct isis_area *area, int level,
struct isis_redist *redist,
- struct prefix *p,
+ const struct prefix *p,
+ const struct prefix_ipv6 *src_p,
struct isis_ext_info *info)
{
struct isis_ext_info area_info;
if (redist->map_name) {
map_ret =
- route_map_apply(redist->map, p, RMAP_ISIS, &area_info);
+ route_map_apply(redist->map, (struct prefix *)p,
+ RMAP_ISIS, &area_info);
if (map_ret == RMAP_DENYMATCH)
area_info.distance = 255;
}
area_info.distance = 255;
if (area_info.distance < 255)
- isis_redist_install(area, level, p, &area_info);
+ isis_redist_install(area, level, p, src_p, &area_info);
else
- isis_redist_uninstall(area, level, p);
+ isis_redist_uninstall(area, level, p, src_p);
}
static void isis_redist_ensure_default(struct isis *isis, int family)
} else
assert(!"Unknown family!");
- ei_node = route_node_get(ei_table, &p);
+ ei_node = srcdest_rnode_get(ei_table, &p, NULL);
if (ei_node->info) {
route_unlock_node(ei_node);
return;
}
/* Handle notification about route being added */
-void isis_redist_add(int type, struct prefix *p, uint8_t distance,
- uint32_t metric)
+void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
+ uint8_t distance, uint32_t metric)
{
int family = p->family;
struct route_table *ei_table = get_ext_info(isis, family);
return;
}
- ei_node = route_node_get(ei_table, p);
+ ei_node = srcdest_rnode_get(ei_table, p, src_p);
if (ei_node->info)
route_unlock_node(ei_node);
else
info->distance = distance;
info->metric = metric;
- if (is_default_prefix(p))
+ if (is_default_prefix(p)
+ && (!src_p || !src_p->prefixlen)) {
type = DEFAULT_ROUTE;
+ }
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
for (level = 1; level <= ISIS_LEVELS; level++) {
continue;
isis_redist_update_ext_reach(area, level, redist, p,
- info);
+ src_p, info);
}
}
-void isis_redist_delete(int type, struct prefix *p)
+void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p)
{
int family = p->family;
struct route_table *ei_table = get_ext_info(isis, family);
zlog_debug("%s: Removing route %s from %s.", __func__, debug_buf,
zebra_route_string(type));
- if (is_default_prefix(p)) {
+ if (is_default_prefix(p)
+ && (!src_p || !src_p->prefixlen)) {
/* Don't remove default route but add synthetic route for use
* by "default-information originate always". Areas without the
* "always" setting will ignore routes with origin
* DEFAULT_ROUTE. */
- isis_redist_add(DEFAULT_ROUTE, p, 254, MAX_WIDE_PATH_METRIC);
+ isis_redist_add(DEFAULT_ROUTE, p, NULL,
+ 254, MAX_WIDE_PATH_METRIC);
return;
}
return;
}
- ei_node = route_node_lookup(ei_table, p);
+ ei_node = srcdest_rnode_lookup(ei_table, p, src_p);
if (!ei_node || !ei_node->info) {
char buf[BUFSIZ];
prefix2str(p, buf, sizeof(buf));
route_unlock_node(ei_node);
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
- for (level = 1; level < ISIS_LEVELS; level++) {
+ for (level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
redist = get_redist_settings(area, family, type, level);
if (!redist->redist)
continue;
- isis_redist_uninstall(area, level, p);
+ isis_redist_uninstall(area, level, p, src_p);
}
XFREE(MTYPE_ISIS_EXT_INFO, ei_node->info);
isis_redist_routemap_set(redist, routemap);
if (!area->ext_reach[protocol][level - 1]) {
- area->ext_reach[protocol][level - 1] =
- route_table_init_with_delegate(
- &isis_redist_rt_delegate);
+ area->ext_reach[protocol][level - 1] = srcdest_table_init();
}
- for (i = 0; i < REDIST_PROTOCOL_COUNT; i++)
+ for (i = 0; i < REDIST_PROTOCOL_COUNT; i++) {
if (!area->isis->ext_info[i]) {
- area->isis->ext_info[i] =
- route_table_init_with_delegate(
- &isis_redist_rt_delegate);
+ area->isis->ext_info[i] = srcdest_table_init();
}
+ }
isis_redist_update_zebra_subscriptions(area->isis);
isis_redist_ensure_default(area->isis, family);
ei_table = get_ext_info(area->isis, family);
- for (rn = route_top(ei_table); rn; rn = route_next(rn)) {
+ for (rn = route_top(ei_table); rn; rn = srcdest_route_next(rn)) {
if (!rn->info)
continue;
info = rn->info;
+ const struct prefix *p, *src_p;
+
+ srcdest_rnode_prefixes(rn, &p, &src_p);
+
if (type == DEFAULT_ROUTE) {
- if (!is_default_prefix(&rn->p))
+ if (!is_default_prefix(p)
+ || (src_p && src_p->prefixlen)) {
continue;
+ }
} else {
if (info->origin != type)
continue;
}
- isis_redist_update_ext_reach(area, level, redist, &rn->p, info);
+ isis_redist_update_ext_reach(area, level, redist, p,
+ (struct prefix_ipv6 *)src_p, info);
}
}
return;
}
- for (rn = route_top(er_table); rn; rn = route_next(rn)) {
+ for (rn = route_top(er_table); rn; rn = srcdest_route_next(rn)) {
if (!rn->info)
continue;
info = rn->info;
+ const struct prefix *p, *src_p;
+ srcdest_rnode_prefixes(rn, &p, &src_p);
+
if (type == DEFAULT_ROUTE) {
- if (!is_default_prefix(&rn->p))
+ if (!is_default_prefix(p)
+ || (src_p && src_p->prefixlen)) {
continue;
+ }
} else {
if (info->origin != type)
continue;
struct isis_area;
struct prefix;
+struct prefix_ipv6;
struct vty;
struct route_table *get_ext_reach(struct isis_area *area, int family,
int level);
-void isis_redist_add(int type, struct prefix *p, uint8_t distance,
- uint32_t metric);
-void isis_redist_delete(int type, struct prefix *p);
+void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
+ uint8_t distance, uint32_t metric);
+void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p);
int isis_redist_config_write(struct vty *vty, struct isis_area *area,
int family);
void isis_redist_init(void);
#include "hash.h"
#include "if.h"
#include "table.h"
+#include "srcdest_table.h"
#include "isis_constants.h"
#include "isis_common.h"
}
static struct isis_route_info *isis_route_info_new(struct prefix *prefix,
+ struct prefix_ipv6 *src_p,
uint32_t cost,
uint32_t depth,
struct list *adjacencies)
SET_FLAG(rinfo->flag,
ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
/* update neighbor router address */
- if (depth == 2 && prefix->prefixlen == 128)
+ if (depth == 2 && prefix->prefixlen == 128
+ && (!src_p || !src_p->prefixlen)) {
adj->router_address6 = prefix->u.prefix6;
+ }
adjinfo2nexthop6(rinfo->nexthops6, adj);
}
}
return 1;
}
-struct isis_route_info *isis_route_create(struct prefix *prefix, uint32_t cost,
+struct isis_route_info *isis_route_create(struct prefix *prefix,
+ struct prefix_ipv6 *src_p,
+ uint32_t cost,
uint32_t depth,
struct list *adjacencies,
- struct isis_area *area, int level)
+ struct isis_area *area,
+ struct route_table *table)
{
struct route_node *route_node;
struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL;
/* for debugs */
prefix2str(prefix, buff, sizeof(buff));
- rinfo_new = isis_route_info_new(prefix, cost, depth, adjacencies);
-
- if (family == AF_INET)
- route_node =
- route_node_get(area->route_table[level - 1], prefix);
- else if (family == AF_INET6)
- route_node =
- route_node_get(area->route_table6[level - 1], prefix);
- else {
- isis_route_info_delete(rinfo_new);
+ if (!table)
return NULL;
- }
+
+ rinfo_new = isis_route_info_new(prefix, src_p, cost,
+ depth, adjacencies);
+ route_node = srcdest_rnode_get(table, prefix, src_p);
rinfo_old = route_node->info;
if (!rinfo_old) {
route_info = rinfo_new;
UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
} else {
+ route_unlock_node(route_node);
if (isis->debugs & DEBUG_RTE_EVENTS)
zlog_debug("ISIS-Rte (%s) route already exists: %s",
area->area_tag, buff);
return route_info;
}
-static void isis_route_delete(struct prefix *prefix, struct route_table *table)
+static void isis_route_delete(struct route_node *rode,
+ struct route_table *table)
{
- struct route_node *rode;
struct isis_route_info *rinfo;
- char buff[PREFIX2STR_BUFFER];
+ char buff[SRCDEST2STR_BUFFER];
+ struct prefix *prefix;
+ struct prefix_ipv6 *src_p;
/* for log */
- prefix2str(prefix, buff, sizeof(buff));
+ srcdest_rnode2str(rode, buff, sizeof(buff));
+ srcdest_rnode_prefixes(rode, (const struct prefix **)&prefix,
+ (const struct prefix **)&src_p);
- rode = route_node_get(table, prefix);
rinfo = rode->info;
-
if (rinfo == NULL) {
if (isis->debugs & DEBUG_RTE_EVENTS)
zlog_debug(
UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
if (isis->debugs & DEBUG_RTE_EVENTS)
zlog_debug("ISIS-Rte: route delete %s", buff);
- isis_zebra_route_update(prefix, rinfo);
+ isis_zebra_route_update(prefix, src_p, rinfo);
}
isis_route_info_delete(rinfo);
rode->info = NULL;
-
- return;
+ route_unlock_node(rode);
}
-/* Validating routes in particular table. */
-static void isis_route_validate_table(struct isis_area *area,
- struct route_table *table)
+static void _isis_route_verify_table(struct isis_area *area,
+ struct route_table *table,
+ struct route_table **tables)
{
struct route_node *rnode, *drnode;
struct isis_route_info *rinfo;
- char buff[PREFIX2STR_BUFFER];
+ char buff[SRCDEST2STR_BUFFER];
- for (rnode = route_top(table); rnode; rnode = route_next(rnode)) {
+ for (rnode = route_top(table); rnode;
+ rnode = srcdest_route_next(rnode)) {
if (rnode->info == NULL)
continue;
rinfo = rnode->info;
+ struct prefix *dst_p;
+ struct prefix_ipv6 *src_p;
+
+ srcdest_rnode_prefixes(rnode,
+ (const struct prefix **)&dst_p,
+ (const struct prefix **)&src_p);
+
if (isis->debugs & DEBUG_RTE_EVENTS) {
- prefix2str(&rnode->p, buff, sizeof(buff));
+ srcdest2str(dst_p, src_p, buff, sizeof(buff));
zlog_debug(
"ISIS-Rte (%s): route validate: %s %s %s %s",
area->area_tag,
buff);
}
- isis_zebra_route_update(&rnode->p, rinfo);
- if (!CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) {
- /* Area is either L1 or L2 => we use level route tables
- * directly for
- * validating => no problems with deleting routes. */
- if (area->is_type != IS_LEVEL_1_AND_2) {
- isis_route_delete(&rnode->p, table);
+ isis_zebra_route_update(dst_p, src_p, rinfo);
+
+ if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE))
+ continue;
+
+ /* Area is either L1 or L2 => we use level route tables
+ * directly for
+ * validating => no problems with deleting routes. */
+ if (!tables) {
+ isis_route_delete(rnode, table);
+ continue;
+ }
+
+ /* If area is L1L2, we work with merge table and
+ * therefore must
+ * delete node from level tables as well before deleting
+ * route info. */
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+ drnode = srcdest_rnode_lookup(tables[level - 1],
+ dst_p, src_p);
+ if (!drnode)
continue;
- }
- /* If area is L1L2, we work with merge table and
- * therefore must
- * delete node from level tables as well before deleting
- * route info.
- * FIXME: Is it performance problem? There has to be the
- * better way.
- * Like not to deal with it here at all (see the next
- * comment)? */
- if (rnode->p.family == AF_INET) {
- drnode = route_node_get(area->route_table[0],
- &rnode->p);
- if (drnode->info == rnode->info)
- drnode->info = NULL;
- drnode = route_node_get(area->route_table[1],
- &rnode->p);
- if (drnode->info == rnode->info)
- drnode->info = NULL;
- }
- if (rnode->p.family == AF_INET6) {
- drnode = route_node_get(area->route_table6[0],
- &rnode->p);
- if (drnode->info == rnode->info)
- drnode->info = NULL;
- drnode = route_node_get(area->route_table6[1],
- &rnode->p);
- if (drnode->info == rnode->info)
- drnode->info = NULL;
- }
+ route_unlock_node(drnode);
+
+ if (drnode->info != rnode->info)
+ continue;
- isis_route_delete(&rnode->p, table);
+ drnode->info = NULL;
+ route_unlock_node(drnode);
}
+
+ isis_route_delete(rnode, table);
}
}
+void isis_route_verify_table(struct isis_area *area, struct route_table *table)
+{
+ return _isis_route_verify_table(area, table, NULL);
+}
+
/* Function to validate route tables for L1L2 areas. In this case we can't use
* level route tables directly, we have to merge them at first. L1 routes are
* preferred over the L2 ones.
*
* FIXME: Is it right place to do it at all? Maybe we should push both levels
* to the RIB with different zebra route types and let RIB handle this? */
-static void isis_route_validate_merge(struct isis_area *area, int family)
+void isis_route_verify_merge(struct isis_area *area,
+ struct route_table *level1_table,
+ struct route_table *level2_table)
{
- struct route_table *table = NULL;
+ struct route_table *tables[] = { level1_table, level2_table };
struct route_table *merge;
struct route_node *rnode, *mrnode;
- merge = route_table_init();
+ merge = srcdest_table_init();
- if (family == AF_INET)
- table = area->route_table[0];
- else if (family == AF_INET6)
- table = area->route_table6[0];
- else {
- zlog_warn("ISIS-Rte (%s) %s called for unknown family %d",
- area->area_tag, __func__, family);
- route_table_finish(merge);
- return;
- }
-
- for (rnode = route_top(table); rnode; rnode = route_next(rnode)) {
- if (rnode->info == NULL)
- continue;
- mrnode = route_node_get(merge, &rnode->p);
- mrnode->info = rnode->info;
- }
-
- if (family == AF_INET)
- table = area->route_table[1];
- else if (family == AF_INET6)
- table = area->route_table6[1];
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+ for (rnode = route_top(tables[level - 1]); rnode;
+ rnode = srcdest_route_next(rnode)) {
+ struct isis_route_info *rinfo = rnode->info;
+ if (!rinfo)
+ continue;
- for (rnode = route_top(table); rnode; rnode = route_next(rnode)) {
- if (rnode->info == NULL)
- continue;
- mrnode = route_node_get(merge, &rnode->p);
- if (mrnode->info != NULL)
- continue;
- mrnode->info = rnode->info;
+ struct prefix *prefix;
+ struct prefix_ipv6 *src_p;
+
+ srcdest_rnode_prefixes(rnode,
+ (const struct prefix **)&prefix,
+ (const struct prefix **)&src_p);
+ mrnode = srcdest_rnode_get(merge, prefix, src_p);
+ struct isis_route_info *mrinfo = mrnode->info;
+ if (mrinfo) {
+ route_unlock_node(mrnode);
+ if (CHECK_FLAG(mrinfo->flag,
+ ISIS_ROUTE_FLAG_ACTIVE)) {
+ /* Clear the ZEBRA_SYNCED flag on the
+ * L2 route when L1 wins, otherwise L2
+ * won't get reinstalled when L1
+ * disappears.
+ */
+ UNSET_FLAG(
+ rinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED
+ );
+ continue;
+ } else {
+ /* Clear the ZEBRA_SYNCED flag on the L1
+ * route when L2 wins, otherwise L1
+ * won't get reinstalled when it
+ * reappears.
+ */
+ UNSET_FLAG(
+ mrinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED
+ );
+ }
+ }
+ mrnode->info = rnode->info;
+ }
}
- isis_route_validate_table(area, merge);
+ _isis_route_verify_table(area, merge, tables);
route_table_finish(merge);
}
-/* Walk through route tables and propagate necessary changes into RIB. In case
- * of L1L2 area, level tables have to be merged at first. */
-void isis_route_validate(struct isis_area *area)
-{
- struct listnode *node;
- struct isis_circuit *circuit;
-
- if (area->is_type == IS_LEVEL_1)
- isis_route_validate_table(area, area->route_table[0]);
- else if (area->is_type == IS_LEVEL_2)
- isis_route_validate_table(area, area->route_table[1]);
- else
- isis_route_validate_merge(area, AF_INET);
-
- if (area->is_type == IS_LEVEL_1)
- isis_route_validate_table(area, area->route_table6[0]);
- else if (area->is_type == IS_LEVEL_2)
- isis_route_validate_table(area, area->route_table6[1]);
- else
- isis_route_validate_merge(area, AF_INET6);
-
- if (!area->circuit_list) {
- return;
- }
- /* walk all circuits and reset any spf specific flags */
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
- UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
-
- return;
-}
-
void isis_route_invalidate_table(struct isis_area *area,
struct route_table *table)
{
struct route_node *rode;
struct isis_route_info *rinfo;
- for (rode = route_top(table); rode; rode = route_next(rode)) {
+ for (rode = route_top(table); rode; rode = srcdest_route_next(rode)) {
if (rode->info == NULL)
continue;
rinfo = rode->info;
UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
}
}
-
-void isis_route_invalidate(struct isis_area *area)
-{
- if (area->is_type & IS_LEVEL_1)
- isis_route_invalidate_table(area, area->route_table[0]);
- if (area->is_type & IS_LEVEL_2)
- isis_route_invalidate_table(area, area->route_table[1]);
-}
struct list *nexthops6;
};
-struct isis_route_info *isis_route_create(struct prefix *prefix, uint32_t cost,
+struct isis_route_info *isis_route_create(struct prefix *prefix,
+ struct prefix_ipv6 *src_p,
+ uint32_t cost,
uint32_t depth,
struct list *adjacencies,
- struct isis_area *area, int level);
+ struct isis_area *area,
+ struct route_table *table);
-void isis_route_validate(struct isis_area *area);
+/* Walk the given table and install new routes to zebra and remove old ones.
+ * route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */
+void isis_route_verify_table(struct isis_area *area,
+ struct route_table *table);
+
+/* Same as isis_route_verify_table, but merge L1 and L2 routes before */
+void isis_route_verify_merge(struct isis_area *area,
+ struct route_table *level1_table,
+ struct route_table *level2_table);
+
+/* Unset ISIS_ROUTE_FLAG_ACTIVE on all routes. Used before running spf. */
void isis_route_invalidate_table(struct isis_area *area,
struct route_table *table);
-void isis_route_invalidate(struct isis_area *area);
#endif /* _ZEBRA_ISIS_ROUTE_H */
#include "spf_backoff.h"
#include "jhash.h"
#include "skiplist.h"
+#include "srcdest_table.h"
#include "isis_constants.h"
#include "isis_common.h"
#define VTYPE_ES(t) ((t) == VTYPE_ES)
#define VTYPE_IP(t) ((t) >= VTYPE_IPREACH_INTERNAL && (t) <= VTYPE_IP6REACH_EXTERNAL)
+struct prefix_pair {
+ struct prefix dest;
+ struct prefix_ipv6 src;
+};
+
/*
* Triple <N, d(N), {Adj(N)}>
*/
union isis_N {
uint8_t id[ISIS_SYS_ID_LEN + 1];
- struct prefix prefix;
+ struct prefix_pair ip;
};
struct isis_vertex {
enum vertextype type;
{
struct isis_vertex *vertex = vp;
- if (VTYPE_IP(vertex->type))
- return prefix_hash_key(&vertex->N.prefix);
+ if (VTYPE_IP(vertex->type)) {
+ uint32_t key;
+
+ key = prefix_hash_key(&vertex->N.ip.dest);
+ key = jhash_1word(prefix_hash_key(&vertex->N.ip.src), key);
+ return key;
+ }
return jhash(vertex->N.id, ISIS_SYS_ID_LEN + 1, 0x55aa5a5a);
}
if (va->type != vb->type)
return 0;
- if (VTYPE_IP(va->type))
- return prefix_cmp(&va->N.prefix, &vb->N.prefix) == 0;
+ if (VTYPE_IP(va->type)) {
+ if (prefix_cmp(&va->N.ip.dest, &vb->N.ip.dest))
+ return 0;
+
+ return prefix_cmp((struct prefix *)&va->N.ip.src,
+ (struct prefix *)&vb->N.ip.src) == 0;
+ }
return memcmp(va->N.id, vb->N.id, ISIS_SYS_ID_LEN + 1) == 0;
}
struct isis_spftree {
struct isis_vertex_queue paths; /* the SPT */
struct isis_vertex_queue tents; /* TENT */
+ struct route_table *route_table;
struct isis_area *area; /* back pointer to area */
unsigned int runcount; /* number of runs since uptime */
time_t last_run_timestamp; /* last run timestamp as wall time for display */
uint16_t mtid;
int family;
int level;
+ enum spf_tree_id tree_id;
};
return NULL; /* Not reached */
}
+#define VID2STR_BUFFER SRCDEST2STR_BUFFER
static const char *vid2string(struct isis_vertex *vertex, char *buff, int size)
{
if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) {
}
if (VTYPE_IP(vertex->type)) {
- prefix2str((struct prefix *)&vertex->N.prefix, buff, size);
+ srcdest2str(&vertex->N.ip.dest,
+ &vertex->N.ip.src,
+ buff, size);
return buff;
}
if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) {
memcpy(vertex->N.id, n->id, ISIS_SYS_ID_LEN + 1);
} else if (VTYPE_IP(vtype)) {
- memcpy(&vertex->N.prefix, &n->prefix, sizeof(struct prefix));
+ memcpy(&vertex->N.ip, &n->ip, sizeof(n->ip));
} else {
zlog_err("WTF!");
}
isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true);
isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false);
+ tree->route_table = srcdest_table_init();
tree->area = area;
tree->last_run_timestamp = 0;
tree->last_run_monotime = 0;
{
isis_vertex_queue_free(&spftree->tents);
isis_vertex_queue_free(&spftree->paths);
- XFREE(MTYPE_ISIS_SPFTREE, spftree);
+ route_table_finish(spftree->route_table);
+ spftree->route_table = NULL;
+ XFREE(MTYPE_ISIS_SPFTREE, spftree);
return;
}
void spftree_area_init(struct isis_area *area)
{
- if (area->is_type & IS_LEVEL_1) {
- if (area->spftree[0] == NULL)
- area->spftree[0] = isis_spftree_new(area);
- if (area->spftree6[0] == NULL)
- area->spftree6[0] = isis_spftree_new(area);
- }
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+ if (!(area->is_type & level))
+ continue;
+ if (area->spftree[tree][level - 1])
+ continue;
- if (area->is_type & IS_LEVEL_2) {
- if (area->spftree[1] == NULL)
- area->spftree[1] = isis_spftree_new(area);
- if (area->spftree6[1] == NULL)
- area->spftree6[1] = isis_spftree_new(area);
+ area->spftree[tree][level - 1] = isis_spftree_new(area);
+ }
}
-
- return;
}
void spftree_area_del(struct isis_area *area)
{
- if (area->is_type & IS_LEVEL_1) {
- if (area->spftree[0] != NULL) {
- isis_spftree_del(area->spftree[0]);
- area->spftree[0] = NULL;
- }
- if (area->spftree6[0]) {
- isis_spftree_del(area->spftree6[0]);
- area->spftree6[0] = NULL;
- }
- }
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+ if (!(area->is_type & level))
+ continue;
+ if (!area->spftree[tree][level - 1])
+ continue;
- if (area->is_type & IS_LEVEL_2) {
- if (area->spftree[1] != NULL) {
- isis_spftree_del(area->spftree[1]);
- area->spftree[1] = NULL;
- }
- if (area->spftree6[1] != NULL) {
- isis_spftree_del(area->spftree6[1]);
- area->spftree6[1] = NULL;
+ isis_spftree_del(area->spftree[tree][level - 1]);
}
}
-
- return;
}
void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj)
{
- if (area->is_type & IS_LEVEL_1) {
- if (area->spftree[0] != NULL)
- isis_spftree_adj_del(area->spftree[0], adj);
- if (area->spftree6[0] != NULL)
- isis_spftree_adj_del(area->spftree6[0], adj);
- }
-
- if (area->is_type & IS_LEVEL_2) {
- if (area->spftree[1] != NULL)
- isis_spftree_adj_del(area->spftree[1], adj);
- if (area->spftree6[1] != NULL)
- isis_spftree_adj_del(area->spftree6[1], adj);
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+ if (!(area->is_type & level))
+ continue;
+ if (!area->spftree[tree][level - 1])
+ continue;
+ isis_spftree_adj_del(area->spftree[tree][level - 1],
+ adj);
+ }
}
-
- return;
}
/*
struct isis_vertex *vertex;
struct isis_lsp *lsp;
#ifdef EXTREME_DEBUG
- char buff[PREFIX2STR_BUFFER];
+ char buff[VID2STR_BUFFER];
#endif /* EXTREME_DEBUG */
union isis_N n;
struct listnode *node;
struct isis_adjacency *parent_adj;
#ifdef EXTREME_DEBUG
- char buff[PREFIX2STR_BUFFER];
+ char buff[VID2STR_BUFFER];
#endif
assert(isis_find_vertex(&spftree->paths, id, vtype) == NULL);
{
struct isis_vertex *vertex;
#ifdef EXTREME_DEBUG
- char buff[PREFIX2STR_BUFFER];
+ char buff[VID2STR_BUFFER];
#endif
assert(spftree && parent);
- struct prefix p;
+ struct prefix_pair p;
if (vtype >= VTYPE_IPREACH_INTERNAL) {
- prefix_copy(&p, id);
- apply_mask(&p);
+ memcpy(&p, id, sizeof(p));
+ apply_mask(&p.dest);
+ apply_mask((struct prefix *)&p.src);
id = &p;
}
enum vertextype vtype;
static const uint8_t null_sysid[ISIS_SYS_ID_LEN];
struct isis_mt_router_info *mt_router_info = NULL;
+ struct prefix_pair ip_info;
if (!lsp->tlvs)
return ISIS_OK;
vtype = i ? VTYPE_IPREACH_EXTERNAL
: VTYPE_IPREACH_INTERNAL;
+ memset(&ip_info, 0, sizeof(ip_info));
+ ip_info.dest.family = AF_INET;
+
struct isis_oldstyle_ip_reach *r;
for (r = (struct isis_oldstyle_ip_reach *)reachs[i]
->head;
r; r = r->next) {
dist = cost + r->metric;
- process_N(spftree, vtype, (void *)&r->prefix,
+ ip_info.dest.u.prefix4 = r->prefix.prefix;
+ ip_info.dest.prefixlen = r->prefix.prefixlen;
+ process_N(spftree, vtype, &ip_info,
dist, depth + 1, parent);
}
}
ipv4_reachs = isis_lookup_mt_items(
&lsp->tlvs->mt_ip_reach, spftree->mtid);
+ memset(&ip_info, 0, sizeof(ip_info));
+ ip_info.dest.family = AF_INET;
+
struct isis_extended_ip_reach *r;
for (r = ipv4_reachs
? (struct isis_extended_ip_reach *)
: NULL;
r; r = r->next) {
dist = cost + r->metric;
- process_N(spftree, VTYPE_IPREACH_TE, (void *)&r->prefix,
+ ip_info.dest.u.prefix4 = r->prefix.prefix;
+ ip_info.dest.prefixlen = r->prefix.prefixlen;
+ process_N(spftree, VTYPE_IPREACH_TE, &ip_info,
dist, depth + 1, parent);
}
}
dist = cost + r->metric;
vtype = r->external ? VTYPE_IP6REACH_EXTERNAL
: VTYPE_IP6REACH_INTERNAL;
- process_N(spftree, vtype, (void *)&r->prefix, dist,
+ memset(&ip_info, 0, sizeof(ip_info));
+ ip_info.dest.family = AF_INET6;
+ ip_info.dest.u.prefix6 = r->prefix.prefix;
+ ip_info.dest.prefixlen = r->prefix.prefixlen;
+
+ if (r->subtlvs
+ && r->subtlvs->source_prefix
+ && r->subtlvs->source_prefix->prefixlen) {
+ if (spftree->tree_id != SPFTREE_DSTSRC) {
+ char buff[VID2STR_BUFFER];
+ zlog_warn("Ignoring dest-src route %s in non dest-src topology",
+ srcdest2str(
+ &ip_info.dest,
+ r->subtlvs->source_prefix,
+ buff, sizeof(buff)
+ )
+ );
+ continue;
+ }
+ ip_info.src = *r->subtlvs->source_prefix;
+ }
+ process_N(spftree, vtype, &ip_info, dist,
depth + 1, parent);
}
}
struct list *adj_list;
struct list *adjdb;
struct prefix_ipv4 *ipv4;
- struct prefix prefix;
+ struct prefix_pair ip_info;
int retval = ISIS_OK;
uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
static uint8_t null_lsp_id[ISIS_SYS_ID_LEN + 2];
* Add IP(v6) addresses of this circuit
*/
if (spftree->family == AF_INET) {
- prefix.family = AF_INET;
+ memset(&ip_info, 0, sizeof(ip_info));
+ ip_info.dest.family = AF_INET;
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode,
ipv4)) {
- prefix.u.prefix4 = ipv4->prefix;
- prefix.prefixlen = ipv4->prefixlen;
- apply_mask(&prefix);
+ ip_info.dest.u.prefix4 = ipv4->prefix;
+ ip_info.dest.prefixlen = ipv4->prefixlen;
+ apply_mask(&ip_info.dest);
isis_spf_add_local(spftree,
VTYPE_IPREACH_INTERNAL,
- &prefix, NULL, 0, parent);
+ &ip_info, NULL, 0, parent);
}
}
if (spftree->family == AF_INET6) {
- prefix.family = AF_INET6;
+ memset(&ip_info, 0, sizeof(ip_info));
+ ip_info.dest.family = AF_INET6;
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link,
ipnode, ipv6)) {
- prefix.prefixlen = ipv6->prefixlen;
- prefix.u.prefix6 = ipv6->prefix;
- apply_mask(&prefix);
+ ip_info.dest.u.prefix6 = ipv6->prefix;
+ ip_info.dest.prefixlen = ipv6->prefixlen;
+ apply_mask(&ip_info.dest);
isis_spf_add_local(spftree,
VTYPE_IP6REACH_INTERNAL,
- &prefix, NULL, 0, parent);
+ &ip_info, NULL, 0, parent);
}
}
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
static void add_to_paths(struct isis_spftree *spftree,
struct isis_vertex *vertex)
{
- char buff[PREFIX2STR_BUFFER];
+ char buff[VID2STR_BUFFER];
if (isis_find_vertex(&spftree->paths, &vertex->N, vertex->type))
return;
if (VTYPE_IP(vertex->type)) {
if (listcount(vertex->Adj_N) > 0)
- isis_route_create((struct prefix *)&vertex->N.prefix,
+ isis_route_create(&vertex->N.ip.dest,
+ &vertex->N.ip.src,
vertex->d_N, vertex->depth,
vertex->Adj_N, spftree->area,
- spftree->level);
+ spftree->route_table);
else if (isis->debugs & DEBUG_SPF_EVENTS)
zlog_debug(
"ISIS-Spf: no adjacencies do not install route for "
}
static void init_spt(struct isis_spftree *spftree, int mtid, int level,
- int family)
+ int family, enum spf_tree_id tree_id)
{
isis_vertex_queue_clear(&spftree->tents);
isis_vertex_queue_clear(&spftree->paths);
spftree->mtid = mtid;
spftree->level = level;
spftree->family = family;
+ spftree->tree_id = tree_id;
return;
}
-static int isis_run_spf(struct isis_area *area, int level, int family,
+static int isis_run_spf(struct isis_area *area, int level,
+ enum spf_tree_id tree_id,
uint8_t *sysid, struct timeval *nowtv)
{
int retval = ISIS_OK;
struct isis_vertex *vertex;
struct isis_vertex *root_vertex;
- struct isis_spftree *spftree = NULL;
+ struct isis_spftree *spftree = area->spftree[tree_id][level - 1];
uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
struct isis_lsp *lsp;
- struct route_table *table = NULL;
struct timeval time_now;
unsigned long long start_time, end_time;
- uint16_t mtid;
+ uint16_t mtid = 0;
/* Get time that can't roll backwards. */
start_time = nowtv->tv_sec;
start_time = (start_time * 1000000) + nowtv->tv_usec;
- if (family == AF_INET)
- spftree = area->spftree[level - 1];
- else if (family == AF_INET6)
- spftree = area->spftree6[level - 1];
+ int family = -1;
+ switch (tree_id) {
+ case SPFTREE_IPV4:
+ family = AF_INET;
+ mtid = ISIS_MT_IPV4_UNICAST;
+ break;
+ case SPFTREE_IPV6:
+ family = AF_INET6;
+ mtid = isis_area_ipv6_topology(area);
+ break;
+ case SPFTREE_DSTSRC:
+ family = AF_INET6;
+ mtid = ISIS_MT_IPV6_DSTSRC;
+ break;
+ case SPFTREE_COUNT:
+ assert(!"isis_run_spf should never be called with SPFTREE_COUNT as argument!");
+ return ISIS_WARNING;
+ }
+
assert(spftree);
assert(sysid);
- /* Make all routes in current route table inactive. */
- if (family == AF_INET)
- table = area->route_table[level - 1];
- else if (family == AF_INET6)
- table = area->route_table6[level - 1];
-
- isis_route_invalidate_table(area, table);
-
- /* We only support ipv4-unicast and ipv6-unicast as topologies for now
- */
- if (family == AF_INET6)
- mtid = isis_area_ipv6_topology(area);
- else
- mtid = ISIS_MT_IPV4_UNICAST;
-
/*
* C.2.5 Step 0
*/
- init_spt(spftree, mtid, level, family);
+ init_spt(spftree, mtid, level, family, tree_id);
/* a) */
root_vertex = isis_spf_add_root(spftree, sysid);
/* b) */
}
out:
- isis_route_validate(area);
spftree->runcount++;
spftree->last_run_timestamp = time(NULL);
spftree->last_run_monotime = monotime(&time_now);
return retval;
}
+void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees)
+{
+ if (area->is_type == IS_LEVEL_1) {
+ isis_route_verify_table(area, trees[0]->route_table);
+ } else if (area->is_type == IS_LEVEL_2) {
+ isis_route_verify_table(area, trees[1]->route_table);
+ } else {
+ isis_route_verify_merge(area, trees[0]->route_table,
+ trees[1]->route_table);
+ }
+}
+
+void isis_spf_invalidate_routes(struct isis_spftree *tree)
+{
+ isis_route_invalidate_table(tree->area, tree->route_table);
+}
+
static int isis_run_spf_cb(struct thread *thread)
{
struct isis_spf_run *run = THREAD_ARG(thread);
return ISIS_WARNING;
}
+ isis_area_invalidate_routes(area, level);
+
if (isis->debugs & DEBUG_SPF_EVENTS)
zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF",
area->area_tag, level);
if (area->ip_circuits)
- retval = isis_run_spf(area, level, AF_INET, isis->sysid,
+ retval = isis_run_spf(area, level, SPFTREE_IPV4, isis->sysid,
&thread->real);
if (area->ipv6_circuits)
- retval = isis_run_spf(area, level, AF_INET6, isis->sysid,
+ retval = isis_run_spf(area, level, SPFTREE_IPV6, isis->sysid,
+ &thread->real);
+ if (area->ipv6_circuits
+ && isis_area_ipv6_dstsrc_enabled(area))
+ retval = isis_run_spf(area, level, SPFTREE_DSTSRC, isis->sysid,
&thread->real);
+ isis_area_verify_routes(area);
+
+ /* walk all circuits and reset any spf specific flags */
+ struct listnode *node;
+ struct isis_circuit *circuit;
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
+ UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
+
return retval;
}
int isis_spf_schedule(struct isis_area *area, int level)
{
- struct isis_spftree *spftree = area->spftree[level - 1];
+ struct isis_spftree *spftree = area->spftree[SPFTREE_IPV4][level - 1];
time_t now = monotime(NULL);
int diff = now - spftree->last_run_monotime;
{
struct listnode *node;
struct isis_vertex *vertex;
- char buff[PREFIX2STR_BUFFER];
+ char buff[VID2STR_BUFFER];
vty_out(vty,
"Vertex Type Metric Next-Hop Interface Parent\n");
}
}
+static void isis_print_spftree(struct vty *vty, int level,
+ struct isis_area *area,
+ enum spf_tree_id tree_id)
+{
+ const char *tree_id_text = NULL;
+
+ switch (tree_id) {
+ case SPFTREE_IPV4:
+ tree_id_text = "that speak IP";
+ break;
+ case SPFTREE_IPV6:
+ tree_id_text = "that speak IPv6";
+ break;
+ case SPFTREE_DSTSRC:
+ tree_id_text = "that support IPv6 dst-src routing";
+ break;
+ case SPFTREE_COUNT:
+ assert(!"isis_print_spftree shouldn't be called with SPFTREE_COUNT as type");
+ return;
+ }
+
+ if (!area->spftree[tree_id][level - 1]
+ || !isis_vertex_queue_count(
+ &area->spftree[tree_id][level - 1]->paths))
+ return;
+
+ vty_out(vty, "IS-IS paths to level-%d routers %s\n",
+ level, tree_id_text);
+ isis_print_paths(vty, &area->spftree[tree_id][level - 1]->paths,
+ isis->sysid);
+ vty_out(vty, "\n");
+}
+
DEFUN (show_isis_topology,
show_isis_topology_cmd,
"show isis topology [<level-1|level-2>]",
if ((level & levels) == 0)
continue;
- if (area->ip_circuits > 0 && area->spftree[level - 1]
- && isis_vertex_queue_count(&area->spftree[level - 1]->paths) > 0) {
- vty_out(vty,
- "IS-IS paths to level-%d routers that speak IP\n",
- level);
- isis_print_paths(
- vty, &area->spftree[level - 1]->paths,
- isis->sysid);
- vty_out(vty, "\n");
+ if (area->ip_circuits > 0) {
+ isis_print_spftree(vty, level, area,
+ SPFTREE_IPV4);
}
- if (area->ipv6_circuits > 0 && area->spftree6[level - 1]
- && isis_vertex_queue_count(&area->spftree6[level - 1]->paths) > 0) {
- vty_out(vty,
- "IS-IS paths to level-%d routers that speak IPv6\n",
- level);
- isis_print_paths(
- vty, &area->spftree6[level - 1]->paths,
- isis->sysid);
- vty_out(vty, "\n");
+ if (area->ipv6_circuits > 0) {
+ isis_print_spftree(vty, level, area,
+ SPFTREE_IPV6);
+ }
+ if (isis_area_ipv6_dstsrc_enabled(area)) {
+ isis_print_spftree(vty, level, area,
+ SPFTREE_DSTSRC);
}
}
struct isis_spftree;
struct isis_spftree *isis_spftree_new(struct isis_area *area);
+void isis_spf_invalidate_routes(struct isis_spftree *tree);
+void isis_spf_verify_routes(struct isis_area *area,
+ struct isis_spftree **trees);
void isis_spftree_del(struct isis_spftree *spftree);
void spftree_area_init(struct isis_area *area);
void spftree_area_del(struct isis_area *area);
dest->count++;
}
+static struct isis_item *last_item(struct isis_item_list *list)
+{
+ return container_of(list->tail, struct isis_item, next);
+}
+
static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
uint8_t tlv_type, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent)
append_item(l, (struct isis_item *)r);
}
+void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
+ struct prefix_ipv6 *dest,
+ struct prefix_ipv6 *src,
+ uint32_t metric)
+{
+ isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric);
+ struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
+ mtid);
+
+ struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l);
+ r->subtlvs = isis_alloc_subtlvs();
+ r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src));
+ memcpy(r->subtlvs->source_prefix, src, sizeof(*src));
+}
+
void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
uint8_t metric)
{
struct prefix_ipv4 *dest, uint32_t metric);
void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
struct prefix_ipv6 *dest, uint32_t metric);
+void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
+ struct prefix_ipv6 *dest,
+ struct prefix_ipv6 *src,
+ uint32_t metric);
void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
uint8_t metric);
void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
}
static void isis_zebra_route_add_route(struct prefix *prefix,
+ struct prefix_ipv6 *src_p,
struct isis_route_info *route_info)
{
struct zapi_route api;
api.type = ZEBRA_ROUTE_ISIS;
api.safi = SAFI_UNICAST;
api.prefix = *prefix;
+ if (src_p && src_p->prefixlen) {
+ api.src_prefix = *src_p;
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRCPFX);
+ }
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
api.metric = route_info->cost;
}
static void isis_zebra_route_del_route(struct prefix *prefix,
+ struct prefix_ipv6 *src_p,
struct isis_route_info *route_info)
{
struct zapi_route api;
api.type = ZEBRA_ROUTE_ISIS;
api.safi = SAFI_UNICAST;
api.prefix = *prefix;
+ if (src_p && src_p->prefixlen) {
+ api.src_prefix = *src_p;
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRCPFX);
+ }
zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
}
void isis_zebra_route_update(struct prefix *prefix,
+ struct prefix_ipv6 *src_p,
struct isis_route_info *route_info)
{
if (zclient->sock < 0)
return;
if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE))
- isis_zebra_route_add_route(prefix, route_info);
+ isis_zebra_route_add_route(prefix, src_p, route_info);
else
- isis_zebra_route_del_route(prefix, route_info);
+ isis_zebra_route_del_route(prefix, src_p, route_info);
}
static int isis_zebra_read(int command, struct zclient *zclient,
if (zapi_route_decode(zclient->ibuf, &api) < 0)
return -1;
- /* we completely ignore srcdest routes for now. */
- if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
- return 0;
-
/*
* Avoid advertising a false default reachability. (A default
* route installed by IS-IS gets redistributed from zebra back
* into IS-IS causing us to start advertising default reachabity
* without this check)
*/
- if (api.prefix.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS)
+ if (api.prefix.prefixlen == 0
+ && api.src_prefix.prefixlen == 0
+ && api.type == ZEBRA_ROUTE_ISIS) {
command = ZEBRA_REDISTRIBUTE_ROUTE_DEL;
+ }
if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
- isis_redist_add(api.type, &api.prefix, api.distance,
- api.metric);
+ isis_redist_add(api.type, &api.prefix, &api.src_prefix,
+ api.distance, api.metric);
else
- isis_redist_delete(api.type, &api.prefix);
+ isis_redist_delete(api.type, &api.prefix, &api.src_prefix);
return 0;
}
void isis_zebra_stop(void);
void isis_zebra_route_update(struct prefix *prefix,
+ struct prefix_ipv6 *src_p,
struct isis_route_info *route_info);
int isis_distribute_list_update(int routetype);
void isis_zebra_redistribute_set(afi_t afi, int type);
*/
if (area->is_type & IS_LEVEL_1) {
area->lspdb[0] = lsp_db_init();
- area->route_table[0] = route_table_init();
- area->route_table6[0] = route_table_init();
}
if (area->is_type & IS_LEVEL_2) {
area->lspdb[1] = lsp_db_init();
- area->route_table[1] = route_table_init();
- area->route_table6[1] = route_table_init();
}
spftree_area_init(area);
area->lspdb[1] = NULL;
}
+ /* invalidate and verify to delete all routes from zebra */
+ isis_area_invalidate_routes(area, ISIS_LEVEL1 & ISIS_LEVEL2);
+ isis_area_verify_routes(area);
+
spftree_area_del(area);
THREAD_TIMER_OFF(area->spf_timer[0]);
spf_backoff_free(area->spf_delay_ietf[0]);
spf_backoff_free(area->spf_delay_ietf[1]);
- /* invalidate and validate would delete all routes from zebra */
- isis_route_invalidate(area);
- isis_route_validate(area);
-
- if (area->route_table[0]) {
- route_table_finish(area->route_table[0]);
- area->route_table[0] = NULL;
- }
- if (area->route_table[1]) {
- route_table_finish(area->route_table[1]);
- area->route_table[1] = NULL;
- }
- if (area->route_table6[0]) {
- route_table_finish(area->route_table6[0]);
- area->route_table6[0] = NULL;
- }
- if (area->route_table6[1]) {
- route_table_finish(area->route_table6[1]);
- area->route_table6[1] = NULL;
- }
-
isis_redist_area_finish(area);
for (ALL_LIST_ELEMENTS(area->area_addrs, node, nnode, addr)) {
vty_out(vty, "\n");
vty_out(vty, " IPv4 route computation:\n");
- isis_spf_print(area->spftree[level - 1], vty);
+ isis_spf_print(area->spftree[SPFTREE_IPV4][level - 1],
+ vty);
vty_out(vty, " IPv6 route computation:\n");
- isis_spf_print(area->spftree6[level - 1], vty);
+ isis_spf_print(area->spftree[SPFTREE_IPV6][level - 1],
+ vty);
+
+ vty_out(vty, " IPv6 dst-src route computation:\n");
+ isis_spf_print(area->spftree[SPFTREE_DSTSRC][level-1],
+ vty);
}
}
vty_out(vty, "\n");
passwd, snp_auth);
}
+void isis_area_invalidate_routes(struct isis_area *area, int levels)
+{
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+ if (!(level & levels))
+ continue;
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+ isis_spf_invalidate_routes(
+ area->spftree[tree][level - 1]);
+ }
+ }
+}
+
+void isis_area_verify_routes(struct isis_area *area)
+{
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++)
+ isis_spf_verify_routes(area, area->spftree[tree]);
+}
+
static void area_resign_level(struct isis_area *area, int level)
{
+ isis_area_invalidate_routes(area, level);
+ isis_area_verify_routes(area);
+
if (area->lspdb[level - 1]) {
lsp_db_destroy(area->lspdb[level - 1]);
area->lspdb[level - 1] = NULL;
}
- if (area->spftree[level - 1]) {
- isis_spftree_del(area->spftree[level - 1]);
- area->spftree[level - 1] = NULL;
- }
- if (area->spftree6[level - 1]) {
- isis_spftree_del(area->spftree6[level - 1]);
- area->spftree6[level - 1] = NULL;
+
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+ if (area->spftree[tree][level - 1]) {
+ isis_spftree_del(area->spftree[tree][level - 1]);
+ area->spftree[tree][level - 1] = NULL;
+ }
}
+
THREAD_TIMER_OFF(area->spf_timer[level - 1]);
- if (area->route_table[level - 1]) {
- route_table_finish(area->route_table[level - 1]);
- area->route_table[level - 1] = NULL;
- }
- if (area->route_table6[level - 1]) {
- route_table_finish(area->route_table6[level - 1]);
- area->route_table6[level - 1] = NULL;
- }
sched_debug(
"ISIS (%s): Resigned from L%d - canceling LSP regeneration timer.",
if (area->lspdb[1] == NULL)
area->lspdb[1] = lsp_db_init();
- if (area->route_table[1] == NULL)
- area->route_table[1] = route_table_init();
- if (area->route_table6[1] == NULL)
- area->route_table6[1] = route_table_init();
break;
case IS_LEVEL_1_AND_2:
if (area->lspdb[0] == NULL)
area->lspdb[0] = lsp_db_init();
- if (area->route_table[0] == NULL)
- area->route_table[0] = route_table_init();
- if (area->route_table6[0] == NULL)
- area->route_table6[0] = route_table_init();
break;
default:
extern struct isis *isis;
DECLARE_QOBJ_TYPE(isis_area)
+enum spf_tree_id {
+ SPFTREE_IPV4 = 0,
+ SPFTREE_IPV6,
+ SPFTREE_DSTSRC,
+ SPFTREE_COUNT
+};
+
struct isis_area {
struct isis *isis; /* back pointer */
dict_t *lspdb[ISIS_LEVELS]; /* link-state dbs */
- struct isis_spftree *spftree[ISIS_LEVELS]; /* The v4 SPTs */
- struct route_table *route_table[ISIS_LEVELS]; /* IPv4 routes */
- struct isis_spftree *spftree6[ISIS_LEVELS]; /* The v6 SPTs */
- struct route_table *route_table6[ISIS_LEVELS]; /* IPv6 routes */
+ struct isis_spftree *spftree[SPFTREE_COUNT][ISIS_LEVELS];
#define DEFAULT_LSP_MTU 1497
unsigned int lsp_mtu; /* Size of LSPs to generate */
struct list *circuit_list; /* IS-IS circuits */
int isis_area_get(struct vty *vty, const char *area_tag);
void print_debug(struct vty *, int, int);
+void isis_area_invalidate_routes(struct isis_area *area, int levels);
+void isis_area_verify_routes(struct isis_area *area);
+
void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit);
void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit);
void isis_area_dynhostname_set(struct isis_area *area, bool dynhostname);
}
}
-const char *srcdest_rnode2str(struct route_node *rn, char *str, int size)
+const char *srcdest2str(const struct prefix *dst_p,
+ const struct prefix_ipv6 *src_p,
+ char *str, int size)
{
- const struct prefix *dst_p, *src_p;
char dst_buf[PREFIX_STRLEN], src_buf[PREFIX_STRLEN];
- srcdest_rnode_prefixes(rn, &dst_p, &src_p);
-
snprintf(str, size, "%s%s%s",
prefix2str(dst_p, dst_buf, sizeof(dst_buf)),
(src_p && src_p->prefixlen) ? " from " : "",
: "");
return str;
}
+
+const char *srcdest_rnode2str(struct route_node *rn, char *str, int size)
+{
+ const struct prefix *dst_p, *src_p;
+
+ srcdest_rnode_prefixes(rn, &dst_p, &src_p);
+ return srcdest2str(dst_p, (struct prefix_ipv6*)src_p, str, size);
+}
extern void srcdest_rnode_prefixes(struct route_node *rn,
const struct prefix **p,
const struct prefix **src_p);
+extern const char *srcdest2str(const struct prefix *dst_p,
+ const struct prefix_ipv6 *src_p,
+ char *str, int size);
extern const char *srcdest_rnode2str(struct route_node *rn, char *str,
int size);
extern struct route_node *srcdest_route_next(struct route_node *rn);
_a < _b ? _a : _b; \
})
+#ifndef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) \
+ ({ \
+ const typeof(((type *)0)->member) *__mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); \
+ })
+#endif
+
#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0]))
/* For old definition. */
static void setup_test_vertices(void)
{
union isis_N nid, nip = {
- .prefix.family = AF_UNSPEC
+ .ip.dest.family = AF_UNSPEC
};
vertices = XMALLOC(MTYPE_TMP, sizeof(*vertices) * 16);
- nip.prefix.family = AF_INET;
- nip.prefix.prefixlen = 24;
- inet_pton(AF_INET, "192.168.1.0", &nip.prefix.u.prefix4);
+ nip.ip.dest.family = AF_INET;
+ nip.ip.dest.prefixlen = 24;
+ inet_pton(AF_INET, "192.168.1.0", &nip.ip.dest.u.prefix4);
vertices[vertex_count] = isis_vertex_new(&nip, VTYPE_IPREACH_TE);
vertices[vertex_count]->d_N = 20;
vertex_count++;
- nip.prefix.family = AF_INET;
- nip.prefix.prefixlen = 24;
- inet_pton(AF_INET, "192.168.2.0", &nip.prefix.u.prefix4);
+ nip.ip.dest.family = AF_INET;
+ nip.ip.dest.prefixlen = 24;
+ inet_pton(AF_INET, "192.168.2.0", &nip.ip.dest.u.prefix4);
vertices[vertex_count] = isis_vertex_new(&nip, VTYPE_IPREACH_TE);
vertices[vertex_count]->d_N = 20;
vertex_count++;
vertices[vertex_count]->d_N = 15;
vertex_count++;
- nip.prefix.family = AF_INET;
- nip.prefix.prefixlen = 24;
- inet_pton(AF_INET, "192.168.3.0", &nip.prefix.u.prefix4);
+ nip.ip.dest.family = AF_INET;
+ nip.ip.dest.prefixlen = 24;
+ inet_pton(AF_INET, "192.168.3.0", &nip.ip.dest.u.prefix4);
vertices[vertex_count] = isis_vertex_new(&nip, VTYPE_IPREACH_TE);
vertices[vertex_count]->d_N = 20;
vertex_count++;