mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-29 03:55:20 +00:00

This addresses two issues for L1L2 operation: a) If an L1 route has ROUTE_ACTIVE unset and an L2 route for the same destination has ROUTE_ACTIVE set, isisd would still put the L1 route into the merged table. This causes the route for the destination to get uninstalled from zebra until the next SPF run, which is incorrect. To fix this, look at the ROUTE_ACTIVE flag and allow L2 routes to win against L1 routes, when the L1 has ROUTE_ACTIVE unset. b) If an L1 route wins against an existing L2 route, the ZEBRA_SYNCED flag would remain on the L2 route. This leads to the problem that when the L1 route disappears again, the L2 doesn't get reinstalled, since isisd assumes it's already in the RIB because ZEBRA_SYNCED is set. Solve this by clearing ZEBRA_SYNCED on L2 routes, if they lose against an L1 route. Signed-off-by: Christian Franke <chris@opensourcerouting.org>
546 lines
14 KiB
C
546 lines
14 KiB
C
/*
|
|
* IS-IS Rout(e)ing protocol - isis_route.c
|
|
* Copyright (C) 2001,2002 Sampo Saaristo
|
|
* Tampere University of Technology
|
|
* Institute of Communications Engineering
|
|
*
|
|
* based on ../ospf6d/ospf6_route.[ch]
|
|
* by Yasuhiro Ohara
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public Licenseas published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; see the file COPYING; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
|
|
#include "thread.h"
|
|
#include "linklist.h"
|
|
#include "vty.h"
|
|
#include "log.h"
|
|
#include "memory.h"
|
|
#include "prefix.h"
|
|
#include "hash.h"
|
|
#include "if.h"
|
|
#include "table.h"
|
|
|
|
#include "isis_constants.h"
|
|
#include "isis_common.h"
|
|
#include "isis_flags.h"
|
|
#include "dict.h"
|
|
#include "isisd.h"
|
|
#include "isis_misc.h"
|
|
#include "isis_adjacency.h"
|
|
#include "isis_circuit.h"
|
|
#include "isis_pdu.h"
|
|
#include "isis_lsp.h"
|
|
#include "isis_spf.h"
|
|
#include "isis_route.h"
|
|
#include "isis_zebra.h"
|
|
|
|
static struct isis_nexthop *isis_nexthop_create(struct in_addr *ip,
|
|
ifindex_t ifindex)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_nexthop *nexthop;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(isis->nexthops, node, nexthop)) {
|
|
if (nexthop->ifindex != ifindex)
|
|
continue;
|
|
if (ip && memcmp(&nexthop->ip, ip, sizeof(struct in_addr)) != 0)
|
|
continue;
|
|
|
|
nexthop->lock++;
|
|
return nexthop;
|
|
}
|
|
|
|
nexthop = XCALLOC(MTYPE_ISIS_NEXTHOP, sizeof(struct isis_nexthop));
|
|
|
|
nexthop->ifindex = ifindex;
|
|
memcpy(&nexthop->ip, ip, sizeof(struct in_addr));
|
|
listnode_add(isis->nexthops, nexthop);
|
|
nexthop->lock++;
|
|
|
|
return nexthop;
|
|
}
|
|
|
|
static void isis_nexthop_delete(struct isis_nexthop *nexthop)
|
|
{
|
|
nexthop->lock--;
|
|
if (nexthop->lock == 0) {
|
|
listnode_delete(isis->nexthops, nexthop);
|
|
XFREE(MTYPE_ISIS_NEXTHOP, nexthop);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int nexthoplookup(struct list *nexthops, struct in_addr *ip,
|
|
ifindex_t ifindex)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_nexthop *nh;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(nexthops, node, nh)) {
|
|
if (!(memcmp(ip, &nh->ip, sizeof(struct in_addr)))
|
|
&& ifindex == nh->ifindex)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct isis_nexthop6 *isis_nexthop6_new(struct in6_addr *ip6,
|
|
ifindex_t ifindex)
|
|
{
|
|
struct isis_nexthop6 *nexthop6;
|
|
|
|
nexthop6 = XCALLOC(MTYPE_ISIS_NEXTHOP6, sizeof(struct isis_nexthop6));
|
|
|
|
nexthop6->ifindex = ifindex;
|
|
memcpy(&nexthop6->ip6, ip6, sizeof(struct in6_addr));
|
|
nexthop6->lock++;
|
|
|
|
return nexthop6;
|
|
}
|
|
|
|
static struct isis_nexthop6 *isis_nexthop6_create(struct in6_addr *ip6,
|
|
ifindex_t ifindex)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_nexthop6 *nexthop6;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(isis->nexthops6, node, nexthop6)) {
|
|
if (nexthop6->ifindex != ifindex)
|
|
continue;
|
|
if (ip6
|
|
&& memcmp(&nexthop6->ip6, ip6, sizeof(struct in6_addr))
|
|
!= 0)
|
|
continue;
|
|
|
|
nexthop6->lock++;
|
|
return nexthop6;
|
|
}
|
|
|
|
nexthop6 = isis_nexthop6_new(ip6, ifindex);
|
|
|
|
return nexthop6;
|
|
}
|
|
|
|
static void isis_nexthop6_delete(struct isis_nexthop6 *nexthop6)
|
|
{
|
|
|
|
nexthop6->lock--;
|
|
if (nexthop6->lock == 0) {
|
|
listnode_delete(isis->nexthops6, nexthop6);
|
|
XFREE(MTYPE_ISIS_NEXTHOP6, nexthop6);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int nexthop6lookup(struct list *nexthops6, struct in6_addr *ip6,
|
|
ifindex_t ifindex)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_nexthop6 *nh6;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(nexthops6, node, nh6)) {
|
|
if (!(memcmp(ip6, &nh6->ip6, sizeof(struct in6_addr)))
|
|
&& ifindex == nh6->ifindex)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void adjinfo2nexthop(struct list *nexthops, struct isis_adjacency *adj)
|
|
{
|
|
struct isis_nexthop *nh;
|
|
|
|
for (unsigned int i = 0; i < adj->ipv4_address_count; i++) {
|
|
struct in_addr *ipv4_addr = &adj->ipv4_addresses[i];
|
|
if (!nexthoplookup(nexthops, ipv4_addr,
|
|
adj->circuit->interface->ifindex)) {
|
|
nh = isis_nexthop_create(
|
|
ipv4_addr, adj->circuit->interface->ifindex);
|
|
nh->router_address = adj->router_address;
|
|
listnode_add(nexthops, nh);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void adjinfo2nexthop6(struct list *nexthops6, struct isis_adjacency *adj)
|
|
{
|
|
struct isis_nexthop6 *nh6;
|
|
|
|
for (unsigned int i = 0; i < adj->ipv6_address_count; i++) {
|
|
struct in6_addr *ipv6_addr = &adj->ipv6_addresses[i];
|
|
if (!nexthop6lookup(nexthops6, ipv6_addr,
|
|
adj->circuit->interface->ifindex)) {
|
|
nh6 = isis_nexthop6_create(
|
|
ipv6_addr, adj->circuit->interface->ifindex);
|
|
nh6->router_address6 = adj->router_address6;
|
|
listnode_add(nexthops6, nh6);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct isis_route_info *isis_route_info_new(struct prefix *prefix,
|
|
uint32_t cost,
|
|
uint32_t depth,
|
|
struct list *adjacencies)
|
|
{
|
|
struct isis_route_info *rinfo;
|
|
struct isis_adjacency *adj;
|
|
struct listnode *node;
|
|
|
|
rinfo = XCALLOC(MTYPE_ISIS_ROUTE_INFO, sizeof(struct isis_route_info));
|
|
|
|
if (prefix->family == AF_INET) {
|
|
rinfo->nexthops = list_new();
|
|
for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) {
|
|
/* check for force resync this route */
|
|
if (CHECK_FLAG(adj->circuit->flags,
|
|
ISIS_CIRCUIT_FLAPPED_AFTER_SPF))
|
|
SET_FLAG(rinfo->flag,
|
|
ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
|
|
/* update neighbor router address */
|
|
if (depth == 2 && prefix->prefixlen == 32)
|
|
adj->router_address = prefix->u.prefix4;
|
|
adjinfo2nexthop(rinfo->nexthops, adj);
|
|
}
|
|
}
|
|
if (prefix->family == AF_INET6) {
|
|
rinfo->nexthops6 = list_new();
|
|
for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) {
|
|
/* check for force resync this route */
|
|
if (CHECK_FLAG(adj->circuit->flags,
|
|
ISIS_CIRCUIT_FLAPPED_AFTER_SPF))
|
|
SET_FLAG(rinfo->flag,
|
|
ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
|
|
/* update neighbor router address */
|
|
if (depth == 2 && prefix->prefixlen == 128)
|
|
adj->router_address6 = prefix->u.prefix6;
|
|
adjinfo2nexthop6(rinfo->nexthops6, adj);
|
|
}
|
|
}
|
|
|
|
rinfo->cost = cost;
|
|
rinfo->depth = depth;
|
|
|
|
return rinfo;
|
|
}
|
|
|
|
static void isis_route_info_delete(struct isis_route_info *route_info)
|
|
{
|
|
if (route_info->nexthops) {
|
|
route_info->nexthops->del =
|
|
(void (*)(void *))isis_nexthop_delete;
|
|
list_delete_and_null(&route_info->nexthops);
|
|
}
|
|
|
|
if (route_info->nexthops6) {
|
|
route_info->nexthops6->del =
|
|
(void (*)(void *))isis_nexthop6_delete;
|
|
list_delete_and_null(&route_info->nexthops6);
|
|
}
|
|
|
|
XFREE(MTYPE_ISIS_ROUTE_INFO, route_info);
|
|
}
|
|
|
|
static int isis_route_info_same_attrib(struct isis_route_info *new,
|
|
struct isis_route_info *old)
|
|
{
|
|
if (new->cost != old->cost)
|
|
return 0;
|
|
if (new->depth != old->depth)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int isis_route_info_same(struct isis_route_info *new,
|
|
struct isis_route_info *old, uint8_t family)
|
|
{
|
|
struct listnode *node;
|
|
struct isis_nexthop *nexthop;
|
|
struct isis_nexthop6 *nexthop6;
|
|
|
|
if (!CHECK_FLAG(old->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
|
|
return 0;
|
|
|
|
if (CHECK_FLAG(new->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC))
|
|
return 0;
|
|
|
|
if (!isis_route_info_same_attrib(new, old))
|
|
return 0;
|
|
|
|
if (family == AF_INET) {
|
|
for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, nexthop))
|
|
if (nexthoplookup(old->nexthops, &nexthop->ip,
|
|
nexthop->ifindex)
|
|
== 0)
|
|
return 0;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(old->nexthops, node, nexthop))
|
|
if (nexthoplookup(new->nexthops, &nexthop->ip,
|
|
nexthop->ifindex)
|
|
== 0)
|
|
return 0;
|
|
} else if (family == AF_INET6) {
|
|
for (ALL_LIST_ELEMENTS_RO(new->nexthops6, node, nexthop6))
|
|
if (nexthop6lookup(old->nexthops6, &nexthop6->ip6,
|
|
nexthop6->ifindex)
|
|
== 0)
|
|
return 0;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(old->nexthops6, node, nexthop6))
|
|
if (nexthop6lookup(new->nexthops6, &nexthop6->ip6,
|
|
nexthop6->ifindex)
|
|
== 0)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
struct isis_route_info *isis_route_create(struct prefix *prefix, uint32_t cost,
|
|
uint32_t depth,
|
|
struct list *adjacencies,
|
|
struct isis_area *area,
|
|
struct route_table *table)
|
|
{
|
|
struct route_node *route_node;
|
|
struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL;
|
|
char buff[PREFIX2STR_BUFFER];
|
|
uint8_t family;
|
|
|
|
family = prefix->family;
|
|
/* for debugs */
|
|
prefix2str(prefix, buff, sizeof(buff));
|
|
|
|
if (!table)
|
|
return NULL;
|
|
|
|
rinfo_new = isis_route_info_new(prefix, cost, depth, adjacencies);
|
|
route_node = route_node_get(table, prefix);
|
|
|
|
rinfo_old = route_node->info;
|
|
if (!rinfo_old) {
|
|
if (isis->debugs & DEBUG_RTE_EVENTS)
|
|
zlog_debug("ISIS-Rte (%s) route created: %s",
|
|
area->area_tag, buff);
|
|
route_info = rinfo_new;
|
|
UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
|
|
} else {
|
|
if (isis->debugs & DEBUG_RTE_EVENTS)
|
|
zlog_debug("ISIS-Rte (%s) route already exists: %s",
|
|
area->area_tag, buff);
|
|
if (isis_route_info_same(rinfo_new, rinfo_old, family)) {
|
|
if (isis->debugs & DEBUG_RTE_EVENTS)
|
|
zlog_debug("ISIS-Rte (%s) route unchanged: %s",
|
|
area->area_tag, buff);
|
|
isis_route_info_delete(rinfo_new);
|
|
route_info = rinfo_old;
|
|
} else {
|
|
if (isis->debugs & DEBUG_RTE_EVENTS)
|
|
zlog_debug("ISIS-Rte (%s) route changed: %s",
|
|
area->area_tag, buff);
|
|
isis_route_info_delete(rinfo_old);
|
|
route_info = rinfo_new;
|
|
UNSET_FLAG(route_info->flag,
|
|
ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
|
|
}
|
|
}
|
|
|
|
SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE);
|
|
route_node->info = route_info;
|
|
|
|
return route_info;
|
|
}
|
|
|
|
static void isis_route_delete(struct prefix *prefix, struct route_table *table)
|
|
{
|
|
struct route_node *rode;
|
|
struct isis_route_info *rinfo;
|
|
char buff[PREFIX2STR_BUFFER];
|
|
|
|
/* for log */
|
|
prefix2str(prefix, buff, sizeof(buff));
|
|
|
|
|
|
rode = route_node_get(table, prefix);
|
|
rinfo = rode->info;
|
|
|
|
if (rinfo == NULL) {
|
|
if (isis->debugs & DEBUG_RTE_EVENTS)
|
|
zlog_debug(
|
|
"ISIS-Rte: tried to delete non-existant route %s",
|
|
buff);
|
|
return;
|
|
}
|
|
|
|
if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) {
|
|
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_route_info_delete(rinfo);
|
|
rode->info = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
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];
|
|
|
|
for (rnode = route_top(table); rnode; rnode = route_next(rnode)) {
|
|
if (rnode->info == NULL)
|
|
continue;
|
|
rinfo = rnode->info;
|
|
|
|
if (isis->debugs & DEBUG_RTE_EVENTS) {
|
|
prefix2str(&rnode->p, buff, sizeof(buff));
|
|
zlog_debug(
|
|
"ISIS-Rte (%s): route validate: %s %s %s %s",
|
|
area->area_tag,
|
|
(CHECK_FLAG(rinfo->flag,
|
|
ISIS_ROUTE_FLAG_ZEBRA_SYNCED)
|
|
? "synced"
|
|
: "not-synced"),
|
|
(CHECK_FLAG(rinfo->flag,
|
|
ISIS_ROUTE_FLAG_ZEBRA_RESYNC)
|
|
? "resync"
|
|
: "not-resync"),
|
|
(CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)
|
|
? "active"
|
|
: "inactive"),
|
|
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 (!tables) {
|
|
isis_route_delete(&rnode->p, 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 = route_node_get(tables[level - 1], &rnode->p);
|
|
if (drnode->info == rnode->info)
|
|
drnode->info = NULL;
|
|
}
|
|
|
|
isis_route_delete(&rnode->p, 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.
|
|
*
|
|
* Merge algorithm is trivial (at least for now). All L1 paths are copied into
|
|
* merge table at first, then L2 paths are added if L1 path for same prefix
|
|
* doesn't already exists there.
|
|
*
|
|
* 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? */
|
|
void isis_route_verify_merge(struct isis_area *area,
|
|
struct route_table *level1_table,
|
|
struct route_table *level2_table)
|
|
{
|
|
struct route_table *tables[] = { level1_table, level2_table };
|
|
struct route_table *merge;
|
|
struct route_node *rnode, *mrnode;
|
|
|
|
merge = route_table_init();
|
|
|
|
for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
|
|
for (rnode = route_top(tables[level - 1]); rnode;
|
|
rnode = route_next(rnode)) {
|
|
struct isis_route_info *rinfo = rnode->info;
|
|
if (!rinfo)
|
|
continue;
|
|
|
|
mrnode = route_node_get(merge, &rnode->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_verify_table(area, merge, tables);
|
|
route_table_finish(merge);
|
|
}
|
|
|
|
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)) {
|
|
if (rode->info == NULL)
|
|
continue;
|
|
rinfo = rode->info;
|
|
|
|
UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
|
|
}
|
|
}
|