mirror of
https://github.com/FRRouting/frr.git
synced 2025-05-01 05:57:15 +02:00

Handle ECMP for Intra Area Prefix LSAs routes. Ticket:CM-16139 Testing Done: Configure ospf6 passive interface R2 and R3 with same prefix address. Check Intra Area Prefix LSA update at R1 and R3 which would have ECMP paths with effective two paths and two nexthops (from R2 and R4). stop frr at R3 and R1 and R4 route changes back to one nexthop and one paht. R1 ---- R2 | | R3 ---- R4 Signed-off-by: Chirag Shah <chirag@cumulusnetworks.com>
2356 lines
67 KiB
C
2356 lines
67 KiB
C
/*
|
|
* Copyright (C) 2003 Yasuhiro Ohara
|
|
*
|
|
* This file is part of GNU Zebra.
|
|
*
|
|
* GNU Zebra is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
* later version.
|
|
*
|
|
* GNU Zebra 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 "log.h"
|
|
#include "linklist.h"
|
|
#include "thread.h"
|
|
#include "memory.h"
|
|
#include "if.h"
|
|
#include "prefix.h"
|
|
#include "table.h"
|
|
#include "vty.h"
|
|
#include "command.h"
|
|
#include "vrf.h"
|
|
|
|
#include "ospf6_proto.h"
|
|
#include "ospf6_message.h"
|
|
#include "ospf6_route.h"
|
|
#include "ospf6_lsa.h"
|
|
#include "ospf6_lsdb.h"
|
|
|
|
#include "ospf6_top.h"
|
|
#include "ospf6_area.h"
|
|
#include "ospf6_interface.h"
|
|
#include "ospf6_neighbor.h"
|
|
#include "ospf6_intra.h"
|
|
#include "ospf6_asbr.h"
|
|
#include "ospf6_abr.h"
|
|
#include "ospf6_flood.h"
|
|
#include "ospf6d.h"
|
|
#include "ospf6_spf.h"
|
|
|
|
unsigned char conf_debug_ospf6_brouter = 0;
|
|
uint32_t conf_debug_ospf6_brouter_specific_router_id;
|
|
uint32_t conf_debug_ospf6_brouter_specific_area_id;
|
|
|
|
#define MAX_LSA_PAYLOAD (1024 + 256)
|
|
/******************************/
|
|
/* RFC2740 3.4.3.1 Router-LSA */
|
|
/******************************/
|
|
|
|
static char *ospf6_router_lsa_get_nbr_id(struct ospf6_lsa *lsa, char *buf,
|
|
int buflen, int pos)
|
|
{
|
|
struct ospf6_router_lsa *router_lsa;
|
|
struct ospf6_router_lsdesc *lsdesc;
|
|
char *start, *end;
|
|
char buf1[INET_ADDRSTRLEN], buf2[INET_ADDRSTRLEN];
|
|
|
|
if (lsa) {
|
|
router_lsa = (struct ospf6_router_lsa
|
|
*)((char *)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
|
|
lsdesc = (struct ospf6_router_lsdesc
|
|
*)(start
|
|
+ pos * (sizeof(struct
|
|
ospf6_router_lsdesc)));
|
|
if ((char *)lsdesc < end) {
|
|
if (buf && (buflen > INET_ADDRSTRLEN * 2)) {
|
|
inet_ntop(AF_INET,
|
|
&lsdesc->neighbor_interface_id, buf1,
|
|
sizeof(buf1));
|
|
inet_ntop(AF_INET, &lsdesc->neighbor_router_id,
|
|
buf2, sizeof(buf2));
|
|
sprintf(buf, "%s/%s", buf2, buf1);
|
|
}
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa)
|
|
{
|
|
char *start, *end, *current;
|
|
char buf[32], name[32], bits[16], options[32];
|
|
struct ospf6_router_lsa *router_lsa;
|
|
struct ospf6_router_lsdesc *lsdesc;
|
|
|
|
router_lsa =
|
|
(struct ospf6_router_lsa *)((char *)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
ospf6_capability_printbuf(router_lsa->bits, bits, sizeof(bits));
|
|
ospf6_options_printbuf(router_lsa->options, options, sizeof(options));
|
|
vty_out(vty, " Bits: %s Options: %s\n", bits, options);
|
|
|
|
start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
for (current = start;
|
|
current + sizeof(struct ospf6_router_lsdesc) <= end;
|
|
current += sizeof(struct ospf6_router_lsdesc)) {
|
|
lsdesc = (struct ospf6_router_lsdesc *)current;
|
|
|
|
if (lsdesc->type == OSPF6_ROUTER_LSDESC_POINTTOPOINT)
|
|
snprintf(name, sizeof(name), "Point-To-Point");
|
|
else if (lsdesc->type == OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK)
|
|
snprintf(name, sizeof(name), "Transit-Network");
|
|
else if (lsdesc->type == OSPF6_ROUTER_LSDESC_STUB_NETWORK)
|
|
snprintf(name, sizeof(name), "Stub-Network");
|
|
else if (lsdesc->type == OSPF6_ROUTER_LSDESC_VIRTUAL_LINK)
|
|
snprintf(name, sizeof(name), "Virtual-Link");
|
|
else
|
|
snprintf(name, sizeof(name), "Unknown (%#x)",
|
|
lsdesc->type);
|
|
|
|
vty_out(vty, " Type: %s Metric: %d\n", name,
|
|
ntohs(lsdesc->metric));
|
|
vty_out(vty, " Interface ID: %s\n",
|
|
inet_ntop(AF_INET, &lsdesc->interface_id, buf,
|
|
sizeof(buf)));
|
|
vty_out(vty, " Neighbor Interface ID: %s\n",
|
|
inet_ntop(AF_INET, &lsdesc->neighbor_interface_id, buf,
|
|
sizeof(buf)));
|
|
vty_out(vty, " Neighbor Router ID: %s\n",
|
|
inet_ntop(AF_INET, &lsdesc->neighbor_router_id, buf,
|
|
sizeof(buf)));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ospf6_router_lsa_options_set(struct ospf6_area *oa,
|
|
struct ospf6_router_lsa *router_lsa)
|
|
{
|
|
OSPF6_OPT_CLEAR_ALL(router_lsa->options);
|
|
memcpy(router_lsa->options, oa->options, 3);
|
|
|
|
if (ospf6_is_router_abr(ospf6))
|
|
SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B);
|
|
else
|
|
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B);
|
|
|
|
if (!IS_AREA_STUB(oa) && ospf6_asbr_is_asbr(oa->ospf6)) {
|
|
SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_E);
|
|
} else {
|
|
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_E);
|
|
}
|
|
|
|
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_V);
|
|
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_W);
|
|
}
|
|
|
|
int ospf6_router_is_stub_router(struct ospf6_lsa *lsa)
|
|
{
|
|
struct ospf6_router_lsa *rtr_lsa;
|
|
|
|
if (lsa != NULL && OSPF6_LSA_IS_TYPE(ROUTER, lsa)) {
|
|
rtr_lsa = (struct ospf6_router_lsa
|
|
*)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
if (!OSPF6_OPT_ISSET(rtr_lsa->options, OSPF6_OPT_R)) {
|
|
return (OSPF6_IS_STUB_ROUTER);
|
|
} else if (!OSPF6_OPT_ISSET(rtr_lsa->options, OSPF6_OPT_V6)) {
|
|
return (OSPF6_IS_STUB_ROUTER_V6);
|
|
}
|
|
}
|
|
|
|
return (OSPF6_NOT_STUB_ROUTER);
|
|
}
|
|
|
|
int ospf6_router_lsa_originate(struct thread *thread)
|
|
{
|
|
struct ospf6_area *oa;
|
|
|
|
char buffer[OSPF6_MAX_LSASIZE];
|
|
struct ospf6_lsa_header *lsa_header;
|
|
struct ospf6_lsa *lsa;
|
|
|
|
uint32_t link_state_id = 0;
|
|
struct listnode *node, *nnode;
|
|
struct listnode *j;
|
|
struct ospf6_interface *oi;
|
|
struct ospf6_neighbor *on, *drouter = NULL;
|
|
struct ospf6_router_lsa *router_lsa;
|
|
struct ospf6_router_lsdesc *lsdesc;
|
|
uint16_t type;
|
|
uint32_t router;
|
|
int count;
|
|
|
|
oa = (struct ospf6_area *)THREAD_ARG(thread);
|
|
oa->thread_router_lsa = NULL;
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
|
|
zlog_debug("Originate Router-LSA for Area %s", oa->name);
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
lsa_header = (struct ospf6_lsa_header *)buffer;
|
|
router_lsa =
|
|
(struct ospf6_router_lsa *)((caddr_t)lsa_header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
ospf6_router_lsa_options_set(oa, router_lsa);
|
|
|
|
/* describe links for each interfaces */
|
|
lsdesc = (struct ospf6_router_lsdesc
|
|
*)((caddr_t)router_lsa
|
|
+ sizeof(struct ospf6_router_lsa));
|
|
|
|
for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) {
|
|
/* Interfaces in state Down or Loopback are not described */
|
|
if (oi->state == OSPF6_INTERFACE_DOWN
|
|
|| oi->state == OSPF6_INTERFACE_LOOPBACK)
|
|
continue;
|
|
|
|
/* Nor are interfaces without any full adjacencies described */
|
|
count = 0;
|
|
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on))
|
|
if (on->state == OSPF6_NEIGHBOR_FULL)
|
|
count++;
|
|
|
|
if (count == 0)
|
|
continue;
|
|
|
|
/* Multiple Router-LSA instance according to size limit setting
|
|
*/
|
|
if ((oa->router_lsa_size_limit != 0)
|
|
&& ((size_t)((char *)lsdesc - buffer)
|
|
+ sizeof(struct ospf6_router_lsdesc)
|
|
> oa->router_lsa_size_limit)) {
|
|
if ((caddr_t)lsdesc
|
|
== (caddr_t)router_lsa
|
|
+ sizeof(struct ospf6_router_lsa)) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
|
|
zlog_debug(
|
|
"Size limit setting for Router-LSA too short");
|
|
return 0;
|
|
}
|
|
|
|
/* Fill LSA Header */
|
|
lsa_header->age = 0;
|
|
lsa_header->type = htons(OSPF6_LSTYPE_ROUTER);
|
|
lsa_header->id = htonl(link_state_id);
|
|
lsa_header->adv_router = oa->ospf6->router_id;
|
|
lsa_header->seqnum = ospf6_new_ls_seqnum(
|
|
lsa_header->type, lsa_header->id,
|
|
lsa_header->adv_router, oa->lsdb);
|
|
lsa_header->length =
|
|
htons((caddr_t)lsdesc - (caddr_t)buffer);
|
|
|
|
/* LSA checksum */
|
|
ospf6_lsa_checksum(lsa_header);
|
|
|
|
/* create LSA */
|
|
lsa = ospf6_lsa_create(lsa_header);
|
|
|
|
/* Originate */
|
|
ospf6_lsa_originate_area(lsa, oa);
|
|
|
|
/* Reset Buffer to fill next Router LSA */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
lsa_header = (struct ospf6_lsa_header *)buffer;
|
|
router_lsa =
|
|
(struct ospf6_router_lsa
|
|
*)((caddr_t)lsa_header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
ospf6_router_lsa_options_set(oa, router_lsa);
|
|
|
|
/* describe links for each interfaces */
|
|
lsdesc = (struct ospf6_router_lsdesc
|
|
*)((caddr_t)router_lsa
|
|
+ sizeof(struct ospf6_router_lsa));
|
|
|
|
link_state_id++;
|
|
}
|
|
|
|
/* Point-to-Point interfaces */
|
|
if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
|
|
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) {
|
|
if (on->state != OSPF6_NEIGHBOR_FULL)
|
|
continue;
|
|
|
|
lsdesc->type = OSPF6_ROUTER_LSDESC_POINTTOPOINT;
|
|
lsdesc->metric = htons(oi->cost);
|
|
lsdesc->interface_id =
|
|
htonl(oi->interface->ifindex);
|
|
lsdesc->neighbor_interface_id =
|
|
htonl(on->ifindex);
|
|
lsdesc->neighbor_router_id = on->router_id;
|
|
|
|
lsdesc++;
|
|
}
|
|
}
|
|
|
|
/* Broadcast and NBMA interfaces */
|
|
else if (oi->type == OSPF_IFTYPE_BROADCAST) {
|
|
/* If this router is not DR,
|
|
and If this router not fully adjacent with DR,
|
|
this interface is not transit yet: ignore. */
|
|
if (oi->state != OSPF6_INTERFACE_DR) {
|
|
drouter =
|
|
ospf6_neighbor_lookup(oi->drouter, oi);
|
|
if (drouter == NULL
|
|
|| drouter->state != OSPF6_NEIGHBOR_FULL)
|
|
continue;
|
|
}
|
|
|
|
lsdesc->type = OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK;
|
|
lsdesc->metric = htons(oi->cost);
|
|
lsdesc->interface_id = htonl(oi->interface->ifindex);
|
|
if (oi->state != OSPF6_INTERFACE_DR) {
|
|
lsdesc->neighbor_interface_id =
|
|
htonl(drouter->ifindex);
|
|
lsdesc->neighbor_router_id = drouter->router_id;
|
|
} else {
|
|
lsdesc->neighbor_interface_id =
|
|
htonl(oi->interface->ifindex);
|
|
lsdesc->neighbor_router_id =
|
|
oi->area->ospf6->router_id;
|
|
}
|
|
|
|
lsdesc++;
|
|
} else {
|
|
assert(0); /* Unknown interface type */
|
|
}
|
|
|
|
/* Virtual links */
|
|
/* xxx */
|
|
/* Point-to-Multipoint interfaces */
|
|
/* xxx */
|
|
}
|
|
|
|
/* Fill LSA Header */
|
|
lsa_header->age = 0;
|
|
lsa_header->type = htons(OSPF6_LSTYPE_ROUTER);
|
|
lsa_header->id = htonl(link_state_id);
|
|
lsa_header->adv_router = oa->ospf6->router_id;
|
|
lsa_header->seqnum =
|
|
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
|
|
lsa_header->adv_router, oa->lsdb);
|
|
lsa_header->length = htons((caddr_t)lsdesc - (caddr_t)buffer);
|
|
|
|
/* LSA checksum */
|
|
ospf6_lsa_checksum(lsa_header);
|
|
|
|
/* create LSA */
|
|
lsa = ospf6_lsa_create(lsa_header);
|
|
|
|
/* Originate */
|
|
ospf6_lsa_originate_area(lsa, oa);
|
|
|
|
link_state_id++;
|
|
|
|
/* Do premature-aging of rest, undesired Router-LSAs */
|
|
type = ntohs(OSPF6_LSTYPE_ROUTER);
|
|
router = oa->ospf6->router_id;
|
|
count = 0;
|
|
for (ALL_LSDB_TYPED_ADVRTR(oa->lsdb, type, router, lsa)) {
|
|
if (ntohl(lsa->header->id) < link_state_id)
|
|
continue;
|
|
ospf6_lsa_purge(lsa);
|
|
count++;
|
|
}
|
|
|
|
/*
|
|
* Waiting till the LSA is actually removed from the database to trigger
|
|
* SPF delays network convergence. Unlike IPv4, for an ABR, when all
|
|
* interfaces associated with an area are gone, triggering an SPF right
|
|
* away
|
|
* helps convergence with inter-area routes.
|
|
*/
|
|
if (count && !link_state_id)
|
|
ospf6_spf_schedule(oa->ospf6,
|
|
OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************/
|
|
/* RFC2740 3.4.3.2 Network-LSA */
|
|
/*******************************/
|
|
|
|
static char *ospf6_network_lsa_get_ar_id(struct ospf6_lsa *lsa, char *buf,
|
|
int buflen, int pos)
|
|
{
|
|
char *start, *end, *current;
|
|
struct ospf6_network_lsa *network_lsa;
|
|
struct ospf6_network_lsdesc *lsdesc;
|
|
|
|
if (lsa) {
|
|
network_lsa = (struct ospf6_network_lsa
|
|
*)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
start = (char *)network_lsa + sizeof(struct ospf6_network_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
current = start + pos * (sizeof(struct ospf6_network_lsdesc));
|
|
|
|
if ((current + sizeof(struct ospf6_network_lsdesc)) <= end) {
|
|
lsdesc = (struct ospf6_network_lsdesc *)current;
|
|
if (buf)
|
|
inet_ntop(AF_INET, &lsdesc->router_id, buf,
|
|
buflen);
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
return (buf);
|
|
}
|
|
|
|
static int ospf6_network_lsa_show(struct vty *vty, struct ospf6_lsa *lsa)
|
|
{
|
|
char *start, *end, *current;
|
|
struct ospf6_network_lsa *network_lsa;
|
|
struct ospf6_network_lsdesc *lsdesc;
|
|
char buf[128], options[32];
|
|
|
|
network_lsa =
|
|
(struct ospf6_network_lsa *)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
ospf6_options_printbuf(network_lsa->options, options, sizeof(options));
|
|
vty_out(vty, " Options: %s\n", options);
|
|
|
|
start = (char *)network_lsa + sizeof(struct ospf6_network_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
for (current = start;
|
|
current + sizeof(struct ospf6_network_lsdesc) <= end;
|
|
current += sizeof(struct ospf6_network_lsdesc)) {
|
|
lsdesc = (struct ospf6_network_lsdesc *)current;
|
|
inet_ntop(AF_INET, &lsdesc->router_id, buf, sizeof(buf));
|
|
vty_out(vty, " Attached Router: %s\n", buf);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ospf6_network_lsa_originate(struct thread *thread)
|
|
{
|
|
struct ospf6_interface *oi;
|
|
|
|
char buffer[OSPF6_MAX_LSASIZE];
|
|
struct ospf6_lsa_header *lsa_header;
|
|
|
|
int count;
|
|
struct ospf6_lsa *old, *lsa;
|
|
struct ospf6_network_lsa *network_lsa;
|
|
struct ospf6_network_lsdesc *lsdesc;
|
|
struct ospf6_neighbor *on;
|
|
struct ospf6_link_lsa *link_lsa;
|
|
struct listnode *i;
|
|
uint16_t type;
|
|
|
|
oi = (struct ospf6_interface *)THREAD_ARG(thread);
|
|
oi->thread_network_lsa = NULL;
|
|
|
|
/* The interface must be enabled until here. A Network-LSA of a
|
|
disabled interface (but was once enabled) should be flushed
|
|
by ospf6_lsa_refresh (), and does not come here. */
|
|
assert(oi->area);
|
|
|
|
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
|
|
htonl(oi->interface->ifindex),
|
|
oi->area->ospf6->router_id, oi->area->lsdb);
|
|
|
|
/* Do not originate Network-LSA if not DR */
|
|
if (oi->state != OSPF6_INTERFACE_DR) {
|
|
if (old) {
|
|
ospf6_lsa_purge(old);
|
|
/*
|
|
* Waiting till the LSA is actually removed from the
|
|
* database to
|
|
* trigger SPF delays network convergence.
|
|
*/
|
|
ospf6_spf_schedule(
|
|
oi->area->ospf6,
|
|
OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(NETWORK))
|
|
zlog_debug("Originate Network-LSA for Interface %s",
|
|
oi->interface->name);
|
|
|
|
/* If none of neighbor is adjacent to us */
|
|
count = 0;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, i, on))
|
|
if (on->state == OSPF6_NEIGHBOR_FULL)
|
|
count++;
|
|
|
|
if (count == 0) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(NETWORK))
|
|
zlog_debug("Interface stub, ignore");
|
|
if (old)
|
|
ospf6_lsa_purge(old);
|
|
return 0;
|
|
}
|
|
|
|
/* prepare buffer */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
lsa_header = (struct ospf6_lsa_header *)buffer;
|
|
network_lsa =
|
|
(struct ospf6_network_lsa *)((caddr_t)lsa_header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
/* Collect the interface's Link-LSAs to describe
|
|
network's optional capabilities */
|
|
type = htons(OSPF6_LSTYPE_LINK);
|
|
for (ALL_LSDB_TYPED(oi->lsdb, type, lsa)) {
|
|
link_lsa = (struct ospf6_link_lsa
|
|
*)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
network_lsa->options[0] |= link_lsa->options[0];
|
|
network_lsa->options[1] |= link_lsa->options[1];
|
|
network_lsa->options[2] |= link_lsa->options[2];
|
|
}
|
|
|
|
lsdesc = (struct ospf6_network_lsdesc
|
|
*)((caddr_t)network_lsa
|
|
+ sizeof(struct ospf6_network_lsa));
|
|
|
|
/* set Link Description to the router itself */
|
|
lsdesc->router_id = oi->area->ospf6->router_id;
|
|
lsdesc++;
|
|
|
|
/* Walk through the neighbors */
|
|
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, i, on)) {
|
|
if (on->state != OSPF6_NEIGHBOR_FULL)
|
|
continue;
|
|
|
|
/* set this neighbor's Router-ID to LSA */
|
|
lsdesc->router_id = on->router_id;
|
|
lsdesc++;
|
|
}
|
|
|
|
/* Fill LSA Header */
|
|
lsa_header->age = 0;
|
|
lsa_header->type = htons(OSPF6_LSTYPE_NETWORK);
|
|
lsa_header->id = htonl(oi->interface->ifindex);
|
|
lsa_header->adv_router = oi->area->ospf6->router_id;
|
|
lsa_header->seqnum =
|
|
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
|
|
lsa_header->adv_router, oi->area->lsdb);
|
|
lsa_header->length = htons((caddr_t)lsdesc - (caddr_t)buffer);
|
|
|
|
/* LSA checksum */
|
|
ospf6_lsa_checksum(lsa_header);
|
|
|
|
/* create LSA */
|
|
lsa = ospf6_lsa_create(lsa_header);
|
|
|
|
/* Originate */
|
|
ospf6_lsa_originate_area(lsa, oi->area);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************************/
|
|
/* RFC2740 3.4.3.6 Link-LSA */
|
|
/****************************/
|
|
|
|
static char *ospf6_link_lsa_get_prefix_str(struct ospf6_lsa *lsa, char *buf,
|
|
int buflen, int pos)
|
|
{
|
|
char *start, *end, *current;
|
|
struct ospf6_link_lsa *link_lsa;
|
|
struct in6_addr in6;
|
|
struct ospf6_prefix *prefix;
|
|
int cnt = 0, prefixnum;
|
|
|
|
if (lsa) {
|
|
link_lsa = (struct ospf6_link_lsa
|
|
*)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
if (pos == 0) {
|
|
inet_ntop(AF_INET6, &link_lsa->linklocal_addr, buf,
|
|
buflen);
|
|
return (buf);
|
|
}
|
|
|
|
prefixnum = ntohl(link_lsa->prefix_num);
|
|
if (pos > prefixnum)
|
|
return (NULL);
|
|
|
|
start = (char *)link_lsa + sizeof(struct ospf6_link_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
current = start;
|
|
|
|
do {
|
|
prefix = (struct ospf6_prefix *)current;
|
|
if (prefix->prefix_length == 0
|
|
|| current + OSPF6_PREFIX_SIZE(prefix) > end) {
|
|
return (NULL);
|
|
}
|
|
|
|
if (cnt < pos) {
|
|
current =
|
|
start + pos * OSPF6_PREFIX_SIZE(prefix);
|
|
cnt++;
|
|
} else {
|
|
memset(&in6, 0, sizeof(in6));
|
|
memcpy(&in6, OSPF6_PREFIX_BODY(prefix),
|
|
OSPF6_PREFIX_SPACE(
|
|
prefix->prefix_length));
|
|
inet_ntop(AF_INET6, &in6, buf, buflen);
|
|
return (buf);
|
|
}
|
|
} while (current <= end);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa)
|
|
{
|
|
char *start, *end, *current;
|
|
struct ospf6_link_lsa *link_lsa;
|
|
int prefixnum;
|
|
char buf[128], options[32];
|
|
struct ospf6_prefix *prefix;
|
|
const char *p, *mc, *la, *nu;
|
|
struct in6_addr in6;
|
|
|
|
link_lsa = (struct ospf6_link_lsa *)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
ospf6_options_printbuf(link_lsa->options, options, sizeof(options));
|
|
inet_ntop(AF_INET6, &link_lsa->linklocal_addr, buf, sizeof(buf));
|
|
prefixnum = ntohl(link_lsa->prefix_num);
|
|
|
|
vty_out(vty, " Priority: %d Options: %s\n", link_lsa->priority,
|
|
options);
|
|
vty_out(vty, " LinkLocal Address: %s\n", buf);
|
|
vty_out(vty, " Number of Prefix: %d\n", prefixnum);
|
|
|
|
start = (char *)link_lsa + sizeof(struct ospf6_link_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
for (current = start; current < end;
|
|
current += OSPF6_PREFIX_SIZE(prefix)) {
|
|
prefix = (struct ospf6_prefix *)current;
|
|
if (prefix->prefix_length == 0
|
|
|| current + OSPF6_PREFIX_SIZE(prefix) > end)
|
|
break;
|
|
|
|
p = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_P)
|
|
? "P"
|
|
: "--");
|
|
mc = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_MC)
|
|
? "MC"
|
|
: "--");
|
|
la = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_LA)
|
|
? "LA"
|
|
: "--");
|
|
nu = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_NU)
|
|
? "NU"
|
|
: "--");
|
|
vty_out(vty, " Prefix Options: %s|%s|%s|%s\n", p, mc, la,
|
|
nu);
|
|
|
|
memset(&in6, 0, sizeof(in6));
|
|
memcpy(&in6, OSPF6_PREFIX_BODY(prefix),
|
|
OSPF6_PREFIX_SPACE(prefix->prefix_length));
|
|
inet_ntop(AF_INET6, &in6, buf, sizeof(buf));
|
|
vty_out(vty, " Prefix: %s/%d\n", buf,
|
|
prefix->prefix_length);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ospf6_link_lsa_originate(struct thread *thread)
|
|
{
|
|
struct ospf6_interface *oi;
|
|
|
|
char buffer[OSPF6_MAX_LSASIZE];
|
|
struct ospf6_lsa_header *lsa_header;
|
|
struct ospf6_lsa *old, *lsa;
|
|
|
|
struct ospf6_link_lsa *link_lsa;
|
|
struct ospf6_route *route;
|
|
struct ospf6_prefix *op;
|
|
|
|
oi = (struct ospf6_interface *)THREAD_ARG(thread);
|
|
oi->thread_link_lsa = NULL;
|
|
|
|
assert(oi->area);
|
|
|
|
/* find previous LSA */
|
|
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_LINK),
|
|
htonl(oi->interface->ifindex),
|
|
oi->area->ospf6->router_id, oi->lsdb);
|
|
|
|
if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) {
|
|
if (old)
|
|
ospf6_lsa_purge(old);
|
|
return 0;
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
|
|
zlog_debug("Originate Link-LSA for Interface %s",
|
|
oi->interface->name);
|
|
|
|
/* can't make Link-LSA if linklocal address not set */
|
|
if (oi->linklocal_addr == NULL) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
|
|
zlog_debug(
|
|
"No Linklocal address on %s, defer originating",
|
|
oi->interface->name);
|
|
if (old)
|
|
ospf6_lsa_purge(old);
|
|
return 0;
|
|
}
|
|
|
|
/* prepare buffer */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
lsa_header = (struct ospf6_lsa_header *)buffer;
|
|
link_lsa = (struct ospf6_link_lsa *)((caddr_t)lsa_header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
/* Fill Link-LSA */
|
|
link_lsa->priority = oi->priority;
|
|
memcpy(link_lsa->options, oi->area->options, 3);
|
|
memcpy(&link_lsa->linklocal_addr, oi->linklocal_addr,
|
|
sizeof(struct in6_addr));
|
|
link_lsa->prefix_num = htonl(oi->route_connected->count);
|
|
|
|
op = (struct ospf6_prefix *)((caddr_t)link_lsa
|
|
+ sizeof(struct ospf6_link_lsa));
|
|
|
|
/* connected prefix to advertise */
|
|
for (route = ospf6_route_head(oi->route_connected); route;
|
|
route = ospf6_route_next(route)) {
|
|
op->prefix_length = route->prefix.prefixlen;
|
|
op->prefix_options = route->path.prefix_options;
|
|
op->prefix_metric = htons(0);
|
|
memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
|
|
OSPF6_PREFIX_SPACE(op->prefix_length));
|
|
op = OSPF6_PREFIX_NEXT(op);
|
|
}
|
|
|
|
/* Fill LSA Header */
|
|
lsa_header->age = 0;
|
|
lsa_header->type = htons(OSPF6_LSTYPE_LINK);
|
|
lsa_header->id = htonl(oi->interface->ifindex);
|
|
lsa_header->adv_router = oi->area->ospf6->router_id;
|
|
lsa_header->seqnum =
|
|
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
|
|
lsa_header->adv_router, oi->lsdb);
|
|
lsa_header->length = htons((caddr_t)op - (caddr_t)buffer);
|
|
|
|
/* LSA checksum */
|
|
ospf6_lsa_checksum(lsa_header);
|
|
|
|
/* create LSA */
|
|
lsa = ospf6_lsa_create(lsa_header);
|
|
|
|
/* Originate */
|
|
ospf6_lsa_originate_interface(lsa, oi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*****************************************/
|
|
/* RFC2740 3.4.3.7 Intra-Area-Prefix-LSA */
|
|
/*****************************************/
|
|
static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa,
|
|
char *buf, int buflen,
|
|
int pos)
|
|
{
|
|
char *start, *end, *current;
|
|
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
|
|
struct in6_addr in6;
|
|
int prefixnum, cnt = 0;
|
|
struct ospf6_prefix *prefix;
|
|
|
|
if (lsa) {
|
|
intra_prefix_lsa =
|
|
(struct ospf6_intra_prefix_lsa
|
|
*)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
prefixnum = ntohs(intra_prefix_lsa->prefix_num);
|
|
if (pos > prefixnum)
|
|
return (NULL);
|
|
|
|
start = (char *)intra_prefix_lsa
|
|
+ sizeof(struct ospf6_intra_prefix_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
current = start;
|
|
|
|
do {
|
|
prefix = (struct ospf6_prefix *)current;
|
|
if (prefix->prefix_length == 0
|
|
|| current + OSPF6_PREFIX_SIZE(prefix) > end) {
|
|
return NULL;
|
|
}
|
|
|
|
if (cnt < pos) {
|
|
current =
|
|
start + pos * OSPF6_PREFIX_SIZE(prefix);
|
|
cnt++;
|
|
} else {
|
|
memset(&in6, 0, sizeof(in6));
|
|
memcpy(&in6, OSPF6_PREFIX_BODY(prefix),
|
|
OSPF6_PREFIX_SPACE(
|
|
prefix->prefix_length));
|
|
inet_ntop(AF_INET6, &in6, buf, buflen);
|
|
sprintf(&buf[strlen(buf)], "/%d",
|
|
prefix->prefix_length);
|
|
return (buf);
|
|
}
|
|
} while (current <= end);
|
|
}
|
|
return (buf);
|
|
}
|
|
|
|
static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa)
|
|
{
|
|
char *start, *end, *current;
|
|
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
|
|
int prefixnum;
|
|
char buf[128];
|
|
struct ospf6_prefix *prefix;
|
|
char id[16], adv_router[16];
|
|
const char *p, *mc, *la, *nu;
|
|
struct in6_addr in6;
|
|
|
|
intra_prefix_lsa = (struct ospf6_intra_prefix_lsa
|
|
*)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
prefixnum = ntohs(intra_prefix_lsa->prefix_num);
|
|
|
|
vty_out(vty, " Number of Prefix: %d\n", prefixnum);
|
|
|
|
inet_ntop(AF_INET, &intra_prefix_lsa->ref_id, id, sizeof(id));
|
|
inet_ntop(AF_INET, &intra_prefix_lsa->ref_adv_router, adv_router,
|
|
sizeof(adv_router));
|
|
vty_out(vty, " Reference: %s Id: %s Adv: %s\n",
|
|
ospf6_lstype_name(intra_prefix_lsa->ref_type), id, adv_router);
|
|
|
|
start = (char *)intra_prefix_lsa
|
|
+ sizeof(struct ospf6_intra_prefix_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
for (current = start; current < end;
|
|
current += OSPF6_PREFIX_SIZE(prefix)) {
|
|
prefix = (struct ospf6_prefix *)current;
|
|
if (prefix->prefix_length == 0
|
|
|| current + OSPF6_PREFIX_SIZE(prefix) > end)
|
|
break;
|
|
|
|
p = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_P)
|
|
? "P"
|
|
: "--");
|
|
mc = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_MC)
|
|
? "MC"
|
|
: "--");
|
|
la = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_LA)
|
|
? "LA"
|
|
: "--");
|
|
nu = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_NU)
|
|
? "NU"
|
|
: "--");
|
|
vty_out(vty, " Prefix Options: %s|%s|%s|%s\n", p, mc, la,
|
|
nu);
|
|
|
|
memset(&in6, 0, sizeof(in6));
|
|
memcpy(&in6, OSPF6_PREFIX_BODY(prefix),
|
|
OSPF6_PREFIX_SPACE(prefix->prefix_length));
|
|
inet_ntop(AF_INET6, &in6, buf, sizeof(buf));
|
|
vty_out(vty, " Prefix: %s/%d\n", buf,
|
|
prefix->prefix_length);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
|
|
{
|
|
struct ospf6_area *oa;
|
|
|
|
char buffer[OSPF6_MAX_LSASIZE];
|
|
struct ospf6_lsa_header *lsa_header;
|
|
struct ospf6_lsa *old, *lsa, *old_next = NULL;
|
|
|
|
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
|
|
struct ospf6_interface *oi;
|
|
struct ospf6_neighbor *on;
|
|
struct ospf6_route *route;
|
|
struct ospf6_prefix *op;
|
|
struct listnode *i, *j;
|
|
int full_count = 0;
|
|
unsigned short prefix_num = 0;
|
|
char buf[PREFIX2STR_BUFFER];
|
|
struct ospf6_route_table *route_advertise;
|
|
int ls_id = 0;
|
|
|
|
oa = (struct ospf6_area *)THREAD_ARG(thread);
|
|
oa->thread_intra_prefix_lsa = NULL;
|
|
|
|
/* find previous LSA */
|
|
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(0),
|
|
oa->ospf6->router_id, oa->lsdb);
|
|
|
|
if (!IS_AREA_ENABLED(oa)) {
|
|
if (old) {
|
|
ospf6_lsa_purge(old);
|
|
/* find previous LSA */
|
|
old_next = ospf6_lsdb_lookup(
|
|
htons(OSPF6_LSTYPE_INTRA_PREFIX),
|
|
htonl(++ls_id), oa->ospf6->router_id, oa->lsdb);
|
|
|
|
while (old_next) {
|
|
ospf6_lsa_purge(old_next);
|
|
old_next = ospf6_lsdb_lookup(
|
|
htons(OSPF6_LSTYPE_INTRA_PREFIX),
|
|
htonl(++ls_id), oa->ospf6->router_id,
|
|
oa->lsdb);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(
|
|
"Originate Intra-Area-Prefix-LSA for area %s's stub prefix",
|
|
oa->name);
|
|
|
|
/* prepare buffer */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
lsa_header = (struct ospf6_lsa_header *)buffer;
|
|
intra_prefix_lsa = (struct ospf6_intra_prefix_lsa
|
|
*)((caddr_t)lsa_header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
/* Fill Intra-Area-Prefix-LSA */
|
|
intra_prefix_lsa->ref_type = htons(OSPF6_LSTYPE_ROUTER);
|
|
intra_prefix_lsa->ref_id = htonl(0);
|
|
intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id;
|
|
|
|
route_advertise = ospf6_route_table_create(0, 0);
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(oa->if_list, i, oi)) {
|
|
if (oi->state == OSPF6_INTERFACE_DOWN) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(" Interface %s is down, ignore",
|
|
oi->interface->name);
|
|
continue;
|
|
}
|
|
|
|
full_count = 0;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on))
|
|
if (on->state == OSPF6_NEIGHBOR_FULL)
|
|
full_count++;
|
|
|
|
if (oi->state != OSPF6_INTERFACE_LOOPBACK
|
|
&& oi->state != OSPF6_INTERFACE_POINTTOPOINT
|
|
&& full_count != 0) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(" Interface %s is not stub, ignore",
|
|
oi->interface->name);
|
|
continue;
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(" Interface %s:", oi->interface->name);
|
|
|
|
/* connected prefix to advertise */
|
|
for (route = ospf6_route_head(oi->route_connected); route;
|
|
route = ospf6_route_best_next(route)) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix, buf, sizeof(buf));
|
|
zlog_debug(" include %s", buf);
|
|
}
|
|
ospf6_route_add(ospf6_route_copy(route),
|
|
route_advertise);
|
|
}
|
|
}
|
|
|
|
if (route_advertise->count == 0) {
|
|
if (old) {
|
|
ls_id = 0;
|
|
ospf6_lsa_purge(old);
|
|
/* find previous LSA */
|
|
old_next = ospf6_lsdb_lookup(
|
|
htons(OSPF6_LSTYPE_INTRA_PREFIX),
|
|
htonl(++ls_id), oa->ospf6->router_id, oa->lsdb);
|
|
|
|
while (old_next) {
|
|
ospf6_lsa_purge(old_next);
|
|
old_next = ospf6_lsdb_lookup(
|
|
htons(OSPF6_LSTYPE_INTRA_PREFIX),
|
|
htonl(++ls_id), oa->ospf6->router_id,
|
|
oa->lsdb);
|
|
}
|
|
}
|
|
ospf6_route_table_delete(route_advertise);
|
|
return 0;
|
|
}
|
|
|
|
/* Neighbor change to FULL, if INTRA-AREA-PREFIX LSA
|
|
* has not change, Flush old LSA and Re-Originate INP,
|
|
* as ospf6_flood() checks if LSA is same as DB,
|
|
* it won't be updated to neighbor's DB.
|
|
*/
|
|
if (oa->intra_prefix_originate) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug("%s: Re-originate intra prefix LSA, Current full nbrs %u",
|
|
__PRETTY_FUNCTION__, oa->full_nbrs);
|
|
if (old)
|
|
ospf6_lsa_purge_multi_ls_id(oa, old);
|
|
oa->intra_prefix_originate = 0;
|
|
}
|
|
|
|
/* put prefixes to advertise */
|
|
prefix_num = 0;
|
|
op = (struct ospf6_prefix *)((caddr_t)intra_prefix_lsa
|
|
+ sizeof(struct ospf6_intra_prefix_lsa));
|
|
for (route = ospf6_route_head(route_advertise); route;
|
|
route = ospf6_route_best_next(route)) {
|
|
if (((caddr_t)op - (caddr_t)lsa_header) > MAX_LSA_PAYLOAD) {
|
|
|
|
intra_prefix_lsa->prefix_num = htons(prefix_num);
|
|
|
|
/* Fill LSA Header */
|
|
lsa_header->age = 0;
|
|
lsa_header->type = htons(OSPF6_LSTYPE_INTRA_PREFIX);
|
|
lsa_header->id = htonl(ls_id++);
|
|
lsa_header->adv_router = oa->ospf6->router_id;
|
|
lsa_header->seqnum = ospf6_new_ls_seqnum(
|
|
lsa_header->type, lsa_header->id,
|
|
lsa_header->adv_router, oa->lsdb);
|
|
lsa_header->length =
|
|
htons((caddr_t)op - (caddr_t)lsa_header);
|
|
|
|
/* LSA checksum */
|
|
ospf6_lsa_checksum(lsa_header);
|
|
|
|
/* Create LSA */
|
|
lsa = ospf6_lsa_create(lsa_header);
|
|
|
|
/* Originate */
|
|
ospf6_lsa_originate_area(lsa, oa);
|
|
|
|
/* Prepare next buffer */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
lsa_header = (struct ospf6_lsa_header *)buffer;
|
|
intra_prefix_lsa =
|
|
(struct ospf6_intra_prefix_lsa
|
|
*)((caddr_t)lsa_header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
/* Fill Intra-Area-Prefix-LSA */
|
|
intra_prefix_lsa->ref_type = htons(OSPF6_LSTYPE_ROUTER);
|
|
intra_prefix_lsa->ref_id = htonl(0);
|
|
intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id;
|
|
|
|
/* Put next set of prefixes to advertise */
|
|
prefix_num = 0;
|
|
op = (struct ospf6_prefix
|
|
*)((caddr_t)intra_prefix_lsa
|
|
+ sizeof(struct
|
|
ospf6_intra_prefix_lsa));
|
|
}
|
|
|
|
op->prefix_length = route->prefix.prefixlen;
|
|
op->prefix_options = route->path.prefix_options;
|
|
op->prefix_metric = htons(route->path.cost);
|
|
memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
|
|
OSPF6_PREFIX_SPACE(op->prefix_length));
|
|
prefix_num++;
|
|
|
|
op = OSPF6_PREFIX_NEXT(op);
|
|
}
|
|
|
|
ospf6_route_table_delete(route_advertise);
|
|
|
|
if (prefix_num == 0) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(
|
|
"Quit to Advertise Intra-Prefix: no route to advertise");
|
|
return 0;
|
|
}
|
|
|
|
intra_prefix_lsa->prefix_num = htons(prefix_num);
|
|
|
|
/* Fill LSA Header */
|
|
lsa_header->age = 0;
|
|
lsa_header->type = htons(OSPF6_LSTYPE_INTRA_PREFIX);
|
|
lsa_header->id = htonl(ls_id++);
|
|
lsa_header->adv_router = oa->ospf6->router_id;
|
|
lsa_header->seqnum =
|
|
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
|
|
lsa_header->adv_router, oa->lsdb);
|
|
lsa_header->length = htons((caddr_t)op - (caddr_t)lsa_header);
|
|
|
|
/* LSA checksum */
|
|
ospf6_lsa_checksum(lsa_header);
|
|
|
|
/* create LSA */
|
|
lsa = ospf6_lsa_create(lsa_header);
|
|
|
|
/* Originate */
|
|
ospf6_lsa_originate_area(lsa, oa);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
|
|
{
|
|
struct ospf6_interface *oi;
|
|
|
|
char buffer[OSPF6_MAX_LSASIZE];
|
|
struct ospf6_lsa_header *lsa_header;
|
|
struct ospf6_lsa *old, *lsa;
|
|
|
|
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
|
|
struct ospf6_neighbor *on;
|
|
struct ospf6_route *route;
|
|
struct ospf6_prefix *op;
|
|
struct listnode *i;
|
|
int full_count = 0;
|
|
unsigned short prefix_num = 0;
|
|
struct ospf6_route_table *route_advertise;
|
|
struct ospf6_link_lsa *link_lsa;
|
|
char *start, *end, *current;
|
|
uint16_t type;
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
oi = (struct ospf6_interface *)THREAD_ARG(thread);
|
|
oi->thread_intra_prefix_lsa = NULL;
|
|
|
|
assert(oi->area);
|
|
|
|
/* find previous LSA */
|
|
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX),
|
|
htonl(oi->interface->ifindex),
|
|
oi->area->ospf6->router_id, oi->area->lsdb);
|
|
|
|
if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) {
|
|
if (old)
|
|
ospf6_lsa_purge(old);
|
|
return 0;
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(
|
|
"Originate Intra-Area-Prefix-LSA for interface %s's prefix",
|
|
oi->interface->name);
|
|
|
|
/* prepare buffer */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
lsa_header = (struct ospf6_lsa_header *)buffer;
|
|
intra_prefix_lsa = (struct ospf6_intra_prefix_lsa
|
|
*)((caddr_t)lsa_header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
/* Fill Intra-Area-Prefix-LSA */
|
|
intra_prefix_lsa->ref_type = htons(OSPF6_LSTYPE_NETWORK);
|
|
intra_prefix_lsa->ref_id = htonl(oi->interface->ifindex);
|
|
intra_prefix_lsa->ref_adv_router = oi->area->ospf6->router_id;
|
|
|
|
if (oi->state != OSPF6_INTERFACE_DR) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(" Interface is not DR");
|
|
if (old)
|
|
ospf6_lsa_purge(old);
|
|
return 0;
|
|
}
|
|
|
|
full_count = 0;
|
|
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, i, on))
|
|
if (on->state == OSPF6_NEIGHBOR_FULL)
|
|
full_count++;
|
|
|
|
if (full_count == 0) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(" Interface is stub");
|
|
if (old)
|
|
ospf6_lsa_purge(old);
|
|
return 0;
|
|
}
|
|
|
|
/* connected prefix to advertise */
|
|
route_advertise = ospf6_route_table_create(0, 0);
|
|
|
|
type = ntohs(OSPF6_LSTYPE_LINK);
|
|
for (ALL_LSDB_TYPED(oi->lsdb, type, lsa)) {
|
|
if (OSPF6_LSA_IS_MAXAGE(lsa))
|
|
continue;
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(" include prefix from %s", lsa->name);
|
|
|
|
if (lsa->header->adv_router != oi->area->ospf6->router_id) {
|
|
on = ospf6_neighbor_lookup(lsa->header->adv_router, oi);
|
|
if (on == NULL || on->state != OSPF6_NEIGHBOR_FULL) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(
|
|
" Neighbor not found or not Full, ignore");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
link_lsa = (struct ospf6_link_lsa
|
|
*)((caddr_t)lsa->header
|
|
+ sizeof(struct ospf6_lsa_header));
|
|
|
|
prefix_num = (unsigned short)ntohl(link_lsa->prefix_num);
|
|
start = (char *)link_lsa + sizeof(struct ospf6_link_lsa);
|
|
end = (char *)lsa->header + ntohs(lsa->header->length);
|
|
for (current = start; current < end && prefix_num;
|
|
current += OSPF6_PREFIX_SIZE(op)) {
|
|
op = (struct ospf6_prefix *)current;
|
|
if (op->prefix_length == 0
|
|
|| current + OSPF6_PREFIX_SIZE(op) > end)
|
|
break;
|
|
|
|
route = ospf6_route_create();
|
|
|
|
route->type = OSPF6_DEST_TYPE_NETWORK;
|
|
route->prefix.family = AF_INET6;
|
|
route->prefix.prefixlen = op->prefix_length;
|
|
memset(&route->prefix.u.prefix6, 0,
|
|
sizeof(struct in6_addr));
|
|
memcpy(&route->prefix.u.prefix6, OSPF6_PREFIX_BODY(op),
|
|
OSPF6_PREFIX_SPACE(op->prefix_length));
|
|
|
|
route->path.origin.type = lsa->header->type;
|
|
route->path.origin.id = lsa->header->id;
|
|
route->path.origin.adv_router = lsa->header->adv_router;
|
|
route->path.options[0] = link_lsa->options[0];
|
|
route->path.options[1] = link_lsa->options[1];
|
|
route->path.options[2] = link_lsa->options[2];
|
|
route->path.prefix_options = op->prefix_options;
|
|
route->path.area_id = oi->area->area_id;
|
|
route->path.type = OSPF6_PATH_TYPE_INTRA;
|
|
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix, buf, sizeof(buf));
|
|
zlog_debug(" include %s", buf);
|
|
}
|
|
|
|
ospf6_route_add(route, route_advertise);
|
|
prefix_num--;
|
|
}
|
|
if (current != end && IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug("Trailing garbage in %s", lsa->name);
|
|
}
|
|
|
|
op = (struct ospf6_prefix *)((caddr_t)intra_prefix_lsa
|
|
+ sizeof(struct ospf6_intra_prefix_lsa));
|
|
|
|
prefix_num = 0;
|
|
for (route = ospf6_route_head(route_advertise); route;
|
|
route = ospf6_route_best_next(route)) {
|
|
op->prefix_length = route->prefix.prefixlen;
|
|
op->prefix_options = route->path.prefix_options;
|
|
op->prefix_metric = htons(0);
|
|
memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
|
|
OSPF6_PREFIX_SPACE(op->prefix_length));
|
|
op = OSPF6_PREFIX_NEXT(op);
|
|
prefix_num++;
|
|
}
|
|
|
|
ospf6_route_table_delete(route_advertise);
|
|
|
|
if (prefix_num == 0) {
|
|
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
|
|
zlog_debug(
|
|
"Quit to Advertise Intra-Prefix: no route to advertise");
|
|
return 0;
|
|
}
|
|
|
|
intra_prefix_lsa->prefix_num = htons(prefix_num);
|
|
|
|
/* Fill LSA Header */
|
|
lsa_header->age = 0;
|
|
lsa_header->type = htons(OSPF6_LSTYPE_INTRA_PREFIX);
|
|
lsa_header->id = htonl(oi->interface->ifindex);
|
|
lsa_header->adv_router = oi->area->ospf6->router_id;
|
|
lsa_header->seqnum =
|
|
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
|
|
lsa_header->adv_router, oi->area->lsdb);
|
|
lsa_header->length = htons((caddr_t)op - (caddr_t)lsa_header);
|
|
|
|
/* LSA checksum */
|
|
ospf6_lsa_checksum(lsa_header);
|
|
|
|
/* create LSA */
|
|
lsa = ospf6_lsa_create(lsa_header);
|
|
|
|
/* Originate */
|
|
ospf6_lsa_originate_area(lsa, oi->area);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
|
|
struct ospf6_route *old,
|
|
struct ospf6_route *route)
|
|
{
|
|
struct ospf6_route *old_route;
|
|
struct ospf6_path *ecmp_path, *o_path = NULL;
|
|
struct listnode *anode, *anext;
|
|
struct listnode *nnode, *rnode, *rnext;
|
|
struct ospf6_nexthop *nh, *rnh;
|
|
char buf[PREFIX2STR_BUFFER];
|
|
bool route_found = false;
|
|
|
|
/* check for old entry match with new route origin,
|
|
* delete old entry.
|
|
*/
|
|
for (old_route = old; old_route; old_route = old_route->next) {
|
|
bool route_updated = false;
|
|
|
|
if (!ospf6_route_is_same(old_route, route) ||
|
|
(old_route->path.type != route->path.type))
|
|
continue;
|
|
|
|
/* Current and New route has same origin,
|
|
* delete old entry.
|
|
*/
|
|
for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext,
|
|
o_path)) {
|
|
/* Check old route path and route has same
|
|
* origin.
|
|
*/
|
|
if (o_path->area_id != route->path.area_id ||
|
|
(memcmp(&(o_path)->origin, &(route)->path.origin,
|
|
sizeof(struct ospf6_ls_origin)) != 0))
|
|
continue;
|
|
|
|
/* Cost is not same then delete current path */
|
|
if (o_path->cost == route->path.cost)
|
|
continue;
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
prefix2str(&old_route->prefix, buf,
|
|
sizeof(buf));
|
|
zlog_debug("%s: route %s cost old %u new %u is not same, replace route",
|
|
__PRETTY_FUNCTION__, buf,
|
|
o_path->cost, route->path.cost);
|
|
}
|
|
|
|
/* Remove selected current rout path's nh from
|
|
* effective nh list.
|
|
*/
|
|
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
|
|
for (ALL_LIST_ELEMENTS(old_route->nh_list,
|
|
rnode, rnext, rnh)) {
|
|
if (!ospf6_nexthop_is_same(rnh, nh))
|
|
continue;
|
|
listnode_delete(old_route->nh_list,
|
|
rnh);
|
|
ospf6_nexthop_delete(rnh);
|
|
route_updated = true;
|
|
}
|
|
}
|
|
|
|
listnode_delete(old_route->paths, o_path);
|
|
ospf6_path_free(o_path);
|
|
|
|
/* Current route's path (adv_router info) is similar
|
|
* to route being added.
|
|
* Replace current route's path with paths list head.
|
|
* Update FIB with effective NHs.
|
|
*/
|
|
if (listcount(old_route->paths)) {
|
|
if (old_route->path.origin.id ==
|
|
route->path.origin.id &&
|
|
old_route->path.origin.adv_router ==
|
|
route->path.origin.adv_router) {
|
|
struct ospf6_path *h_path;
|
|
|
|
h_path = (struct ospf6_path *)
|
|
listgetdata(listhead(old_route->paths));
|
|
old_route->path.origin.type =
|
|
h_path->origin.type;
|
|
old_route->path.origin.id =
|
|
h_path->origin.id;
|
|
old_route->path.origin.adv_router =
|
|
h_path->origin.adv_router;
|
|
}
|
|
|
|
if (route_updated) {
|
|
for (ALL_LIST_ELEMENTS(old_route->paths,
|
|
anode, anext, o_path)) {
|
|
ospf6_merge_nexthops(
|
|
old_route->nh_list,
|
|
o_path->nh_list);
|
|
}
|
|
/* Update ospf6 route table and
|
|
* RIB/FIB with effective
|
|
* nh_list
|
|
*/
|
|
if (oa->route_table->hook_add)
|
|
(*oa->route_table->hook_add)
|
|
(old_route);
|
|
break;
|
|
}
|
|
} else {
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
prefix2str(&old_route->prefix, buf,
|
|
sizeof(buf));
|
|
zlog_debug("%s: route %s old cost %u new cost %u, delete old entry.",
|
|
__PRETTY_FUNCTION__, buf,
|
|
old_route->path.cost,
|
|
route->path.cost);
|
|
}
|
|
ospf6_route_remove(old_route,
|
|
oa->route_table);
|
|
break;
|
|
}
|
|
}
|
|
if (route_updated)
|
|
break;
|
|
}
|
|
|
|
for (old_route = old; old_route; old_route = old_route->next) {
|
|
|
|
if (!ospf6_route_is_same(old_route, route) ||
|
|
(old_route->path.type != route->path.type))
|
|
continue;
|
|
|
|
/* Old Route and New Route have Equal Cost, Merge NHs */
|
|
if (old_route->path.cost == route->path.cost) {
|
|
route_found = true;
|
|
|
|
/* check if this path exists already in
|
|
* route->paths list, if so, replace nh_list.
|
|
*/
|
|
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
|
|
o_path)) {
|
|
if (o_path->area_id == route->path.area_id &&
|
|
(memcmp(&(o_path)->origin,
|
|
&(route)->path.origin,
|
|
sizeof(struct ospf6_ls_origin)) == 0))
|
|
break;
|
|
}
|
|
/* If path is not found in old_route paths's list,
|
|
* add a new path to route paths list and merge
|
|
* nexthops in route->path->nh_list.
|
|
* Otherwise replace existing path's nh_list.
|
|
*/
|
|
if (o_path == NULL) {
|
|
ecmp_path = ospf6_path_dup(&route->path);
|
|
|
|
/* Add a nh_list to new ecmp path */
|
|
ospf6_copy_nexthops(ecmp_path->nh_list,
|
|
route->nh_list);
|
|
/* Merge nexthop to existing route's nh_list */
|
|
ospf6_route_merge_nexthops(old_route, route);
|
|
/* Add the new path to route's path list */
|
|
listnode_add_sort(old_route->paths, ecmp_path);
|
|
|
|
UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
|
|
SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE);
|
|
/* Update RIB/FIB */
|
|
if (oa->route_table->hook_add)
|
|
(*oa->route_table->hook_add)
|
|
(old_route);
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix, buf,
|
|
sizeof(buf));
|
|
zlog_debug("%s: route %s %p another path added with nh %u, effective paths %u nh %u",
|
|
__PRETTY_FUNCTION__, buf,
|
|
(void *)old_route,
|
|
listcount(ecmp_path->nh_list),
|
|
old_route->paths ?
|
|
listcount(old_route->paths)
|
|
: 0,
|
|
listcount(old_route->nh_list));
|
|
}
|
|
} else {
|
|
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
|
|
nnode, nh)) {
|
|
for (ALL_LIST_ELEMENTS(
|
|
old_route->nh_list,
|
|
rnode, rnext, rnh)) {
|
|
if (!ospf6_nexthop_is_same(rnh,
|
|
nh))
|
|
continue;
|
|
|
|
listnode_delete(
|
|
old_route->nh_list,
|
|
rnh);
|
|
ospf6_nexthop_delete(rnh);
|
|
}
|
|
}
|
|
list_delete_all_node(o_path->nh_list);
|
|
ospf6_copy_nexthops(o_path->nh_list,
|
|
route->nh_list);
|
|
|
|
/* Merge nexthop to existing route's nh_list */
|
|
ospf6_route_merge_nexthops(old_route,
|
|
route);
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix,
|
|
buf, sizeof(buf));
|
|
zlog_debug("%s: existing route %s %p with effective paths %u nh count %u",
|
|
__PRETTY_FUNCTION__, buf,
|
|
(void *)old_route,
|
|
listcount(old_route->paths),
|
|
old_route->nh_list ?
|
|
listcount(old_route->nh_list)
|
|
: 0);
|
|
}
|
|
|
|
UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
|
|
SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE);
|
|
/* Update ospf6 route table and RIB/FIB */
|
|
if (oa->route_table->hook_add)
|
|
(*oa->route_table->hook_add)
|
|
(old_route);
|
|
}
|
|
/* Delete the new route its info added to existing
|
|
* route.
|
|
*/
|
|
ospf6_route_delete(route);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!route_found) {
|
|
/* Add new route to existing node in ospf6 route table. */
|
|
ospf6_route_add(route, oa->route_table);
|
|
}
|
|
}
|
|
|
|
void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
|
|
{
|
|
struct ospf6_area *oa;
|
|
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
|
|
struct prefix ls_prefix;
|
|
struct ospf6_route *route, *ls_entry, *old;
|
|
int prefix_num;
|
|
struct ospf6_prefix *op;
|
|
char *start, *current, *end;
|
|
char buf[PREFIX2STR_BUFFER];
|
|
struct interface *ifp;
|
|
int direct_connect = 0;
|
|
struct ospf6_path *path;
|
|
|
|
if (OSPF6_LSA_IS_MAXAGE(lsa))
|
|
return;
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
|
|
zlog_debug("%s: LSA %s found", __PRETTY_FUNCTION__, lsa->name);
|
|
|
|
oa = OSPF6_AREA(lsa->lsdb->data);
|
|
|
|
intra_prefix_lsa =
|
|
(struct ospf6_intra_prefix_lsa *)OSPF6_LSA_HEADER_END(
|
|
lsa->header);
|
|
if (intra_prefix_lsa->ref_type == htons(OSPF6_LSTYPE_ROUTER))
|
|
ospf6_linkstate_prefix(intra_prefix_lsa->ref_adv_router,
|
|
intra_prefix_lsa->ref_id, &ls_prefix);
|
|
else if (intra_prefix_lsa->ref_type == htons(OSPF6_LSTYPE_NETWORK))
|
|
ospf6_linkstate_prefix(intra_prefix_lsa->ref_adv_router,
|
|
intra_prefix_lsa->ref_id, &ls_prefix);
|
|
else {
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
|
|
zlog_debug("Unknown reference LS-type: %#hx",
|
|
ntohs(intra_prefix_lsa->ref_type));
|
|
return;
|
|
}
|
|
|
|
ls_entry = ospf6_route_lookup(&ls_prefix, oa->spf_table);
|
|
if (ls_entry == NULL) {
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
ospf6_linkstate_prefix2str(&ls_prefix, buf,
|
|
sizeof(buf));
|
|
zlog_debug("LS entry does not exist: %s", buf);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (intra_prefix_lsa->ref_adv_router == oa->ospf6->router_id) {
|
|
/* the intra-prefix are directly connected */
|
|
direct_connect = 1;
|
|
}
|
|
|
|
prefix_num = ntohs(intra_prefix_lsa->prefix_num);
|
|
start = (caddr_t)intra_prefix_lsa
|
|
+ sizeof(struct ospf6_intra_prefix_lsa);
|
|
end = OSPF6_LSA_END(lsa->header);
|
|
for (current = start; current < end; current += OSPF6_PREFIX_SIZE(op)) {
|
|
op = (struct ospf6_prefix *)current;
|
|
if (prefix_num == 0)
|
|
break;
|
|
if (end < current + OSPF6_PREFIX_SIZE(op))
|
|
break;
|
|
|
|
/* Appendix A.4.1.1 */
|
|
if (CHECK_FLAG(op->prefix_options, OSPF6_PREFIX_OPTION_NU)) {
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
ospf6_linkstate_prefix2str(
|
|
(struct prefix *)OSPF6_PREFIX_BODY(op),
|
|
buf, sizeof(buf));
|
|
zlog_debug(
|
|
"%s: Skipping Prefix %s has NU option set",
|
|
__func__, buf);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
route = ospf6_route_create();
|
|
|
|
memset(&route->prefix, 0, sizeof(struct prefix));
|
|
route->prefix.family = AF_INET6;
|
|
route->prefix.prefixlen = op->prefix_length;
|
|
ospf6_prefix_in6_addr(&route->prefix.u.prefix6, op);
|
|
|
|
route->type = OSPF6_DEST_TYPE_NETWORK;
|
|
route->path.origin.type = lsa->header->type;
|
|
route->path.origin.id = lsa->header->id;
|
|
route->path.origin.adv_router = lsa->header->adv_router;
|
|
route->path.prefix_options = op->prefix_options;
|
|
route->path.area_id = oa->area_id;
|
|
route->path.type = OSPF6_PATH_TYPE_INTRA;
|
|
route->path.metric_type = 1;
|
|
route->path.cost =
|
|
ls_entry->path.cost + ntohs(op->prefix_metric);
|
|
|
|
if (direct_connect) {
|
|
ifp = if_lookup_prefix(&route->prefix, VRF_DEFAULT);
|
|
if (ifp)
|
|
ospf6_route_add_nexthop(route, ifp->ifindex,
|
|
NULL);
|
|
} else {
|
|
ospf6_route_copy_nexthops(route, ls_entry);
|
|
}
|
|
|
|
path = ospf6_path_dup(&route->path);
|
|
ospf6_copy_nexthops(path->nh_list, route->path.nh_list);
|
|
listnode_add_sort(route->paths, path);
|
|
|
|
old = ospf6_route_lookup(&route->prefix, oa->route_table);
|
|
if (old && (ospf6_route_cmp(route, old) == 0)) {
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix, buf, sizeof(buf));
|
|
zlog_debug(" Update route: %s old cost %u new cost %u nh count %u paths %u",
|
|
buf,
|
|
old->path.cost, route->path.cost,
|
|
listcount(route->nh_list),
|
|
listcount(route->paths));
|
|
}
|
|
ospf6_intra_prefix_route_ecmp_path(oa, old, route);
|
|
} else {
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix, buf, sizeof(buf));
|
|
zlog_debug(" route %s add with cost %u nh %u paths %u",
|
|
buf, route->path.cost,
|
|
listcount(route->nh_list),
|
|
listcount(route->paths));
|
|
}
|
|
ospf6_route_add(route, oa->route_table);
|
|
}
|
|
prefix_num--;
|
|
}
|
|
|
|
if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
|
|
zlog_debug("Trailing garbage ignored");
|
|
}
|
|
|
|
void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
|
|
{
|
|
struct ospf6_area *oa;
|
|
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
|
|
struct prefix prefix;
|
|
struct ospf6_route *route, *nroute, *route_to_del;
|
|
int prefix_num;
|
|
struct ospf6_prefix *op;
|
|
char *start, *current, *end;
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
|
|
zlog_debug("%s: %s disappearing", __PRETTY_FUNCTION__,
|
|
lsa->name);
|
|
|
|
oa = OSPF6_AREA(lsa->lsdb->data);
|
|
|
|
intra_prefix_lsa =
|
|
(struct ospf6_intra_prefix_lsa *)OSPF6_LSA_HEADER_END(
|
|
lsa->header);
|
|
|
|
prefix_num = ntohs(intra_prefix_lsa->prefix_num);
|
|
start = (caddr_t)intra_prefix_lsa
|
|
+ sizeof(struct ospf6_intra_prefix_lsa);
|
|
end = OSPF6_LSA_END(lsa->header);
|
|
for (current = start; current < end; current += OSPF6_PREFIX_SIZE(op)) {
|
|
op = (struct ospf6_prefix *)current;
|
|
if (prefix_num == 0)
|
|
break;
|
|
if (end < current + OSPF6_PREFIX_SIZE(op))
|
|
break;
|
|
prefix_num--;
|
|
|
|
route_to_del = ospf6_route_create();
|
|
|
|
memset(&route_to_del->prefix, 0, sizeof(struct prefix));
|
|
route_to_del->prefix.family = AF_INET6;
|
|
route_to_del->prefix.prefixlen = op->prefix_length;
|
|
ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, op);
|
|
|
|
route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
|
|
route_to_del->path.origin.type = lsa->header->type;
|
|
route_to_del->path.origin.id = lsa->header->id;
|
|
route_to_del->path.origin.adv_router = lsa->header->adv_router;
|
|
route_to_del->path.prefix_options = op->prefix_options;
|
|
route_to_del->path.area_id = oa->area_id;
|
|
route_to_del->path.type = OSPF6_PATH_TYPE_INTRA;
|
|
route_to_del->path.metric_type = 1;
|
|
|
|
memset(&prefix, 0, sizeof(struct prefix));
|
|
prefix.family = AF_INET6;
|
|
prefix.prefixlen = op->prefix_length;
|
|
ospf6_prefix_in6_addr(&prefix.u.prefix6, op);
|
|
|
|
route = ospf6_route_lookup(&prefix, oa->route_table);
|
|
if (route == NULL)
|
|
continue;
|
|
|
|
for (ospf6_route_lock(route);
|
|
route && ospf6_route_is_prefix(&prefix, route);
|
|
route = nroute) {
|
|
nroute = ospf6_route_next(route);
|
|
if (route->type != OSPF6_DEST_TYPE_NETWORK)
|
|
continue;
|
|
if (route->path.area_id != oa->area_id)
|
|
continue;
|
|
if (route->path.type != OSPF6_PATH_TYPE_INTRA)
|
|
continue;
|
|
/* Route has multiple ECMP paths, remove matching
|
|
* path. Update current route's effective nh list
|
|
* after removal of one of the path.
|
|
*/
|
|
if (listcount(route->paths) > 1) {
|
|
struct listnode *anode, *anext;
|
|
struct listnode *nnode, *rnode, *rnext;
|
|
struct ospf6_nexthop *nh, *rnh;
|
|
struct ospf6_path *o_path;
|
|
bool nh_updated = false;
|
|
|
|
/* Iterate all paths of route to find maching
|
|
* with LSA remove info.
|
|
* If route->path is same, replace
|
|
* from paths list.
|
|
*/
|
|
for (ALL_LIST_ELEMENTS(route->paths, anode,
|
|
anext, o_path)) {
|
|
if ((o_path->origin.type !=
|
|
lsa->header->type) ||
|
|
(o_path->origin.adv_router !=
|
|
lsa->header->adv_router) ||
|
|
(o_path->origin.id !=
|
|
lsa->header->id))
|
|
continue;
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN
|
|
(INTRA_PREFIX)) {
|
|
prefix2str(&prefix, buf,
|
|
sizeof(buf));
|
|
zlog_debug(
|
|
"%s: route %s path found with cost %u nh %u to remove.",
|
|
__PRETTY_FUNCTION__,
|
|
buf, o_path->cost,
|
|
listcount(
|
|
o_path->nh_list));
|
|
}
|
|
/* Remove old route from global
|
|
* ospf6 route table.
|
|
* nh_update section will add
|
|
* back with effective nh.
|
|
*/
|
|
if (oa->route_table->hook_remove)
|
|
(*oa->route_table->hook_remove)
|
|
(route);
|
|
/* Remove found path's nh_list from
|
|
* the route's nh_list.
|
|
*/
|
|
for (ALL_LIST_ELEMENTS_RO(
|
|
o_path->nh_list,
|
|
nnode, nh)) {
|
|
for (ALL_LIST_ELEMENTS(
|
|
route->nh_list,
|
|
rnode, rnext, rnh)) {
|
|
if (
|
|
!ospf6_nexthop_is_same(
|
|
rnh, nh))
|
|
continue;
|
|
listnode_delete(
|
|
route->nh_list,
|
|
rnh);
|
|
ospf6_nexthop_delete(
|
|
rnh);
|
|
}
|
|
}
|
|
/* Delete the path from route's
|
|
* path list
|
|
*/
|
|
listnode_delete(route->paths, o_path);
|
|
ospf6_path_free(o_path);
|
|
nh_updated = true;
|
|
break;
|
|
}
|
|
|
|
if (nh_updated) {
|
|
|
|
/* Iterate all paths and merge nexthop,
|
|
* unlesss any of the nexthop similar to
|
|
* ones deleted as part of path
|
|
* deletion.
|
|
*/
|
|
for (ALL_LIST_ELEMENTS(route->paths,
|
|
anode, anext, o_path)) {
|
|
ospf6_merge_nexthops(
|
|
route->nh_list,
|
|
o_path->nh_list);
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(
|
|
INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix, buf,
|
|
sizeof(buf));
|
|
zlog_debug("%s: route %s update paths %u nh %u"
|
|
, __PRETTY_FUNCTION__,
|
|
buf,
|
|
listcount(route->paths),
|
|
listcount(
|
|
route->nh_list));
|
|
}
|
|
|
|
/* route's primary path is similar
|
|
* to LSA, replace route's primary
|
|
* path with route's paths list
|
|
* head.
|
|
*/
|
|
if ((route->path.origin.id ==
|
|
lsa->header->id) &&
|
|
(route->path.origin.adv_router
|
|
== lsa->header->adv_router)) {
|
|
struct ospf6_path *h_path;
|
|
|
|
h_path = (struct ospf6_path *)
|
|
listgetdata(listhead(
|
|
route->paths));
|
|
route->path.origin.type =
|
|
h_path->origin.type;
|
|
route->path.origin.id =
|
|
h_path->origin.id;
|
|
route->path.origin.adv_router =
|
|
h_path->origin.adv_router;
|
|
}
|
|
|
|
/* Update Global Route table and
|
|
* RIB/FIB with effective
|
|
* nh_list
|
|
*/
|
|
if (oa->route_table->hook_add)
|
|
(*oa->route_table->hook_add)
|
|
(route);
|
|
}
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if (route->path.origin.type != lsa->header->type
|
|
|| route->path.origin.id != lsa->header->id
|
|
|| route->path.origin.adv_router
|
|
!= lsa->header->adv_router)
|
|
continue;
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
|
|
prefix2str(&route->prefix, buf,
|
|
sizeof(buf));
|
|
zlog_debug("route remove %s with path %u cost %u nh %u",
|
|
buf, route->path.type,
|
|
route->path.cost,
|
|
listcount(route->nh_list));
|
|
}
|
|
ospf6_route_remove(route, oa->route_table);
|
|
}
|
|
}
|
|
if (route)
|
|
ospf6_route_unlock(route);
|
|
|
|
ospf6_route_delete(route_to_del);
|
|
}
|
|
|
|
if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
|
|
zlog_debug("Trailing garbage ignored");
|
|
}
|
|
|
|
void ospf6_intra_route_calculation(struct ospf6_area *oa)
|
|
{
|
|
struct ospf6_route *route, *nroute;
|
|
uint16_t type;
|
|
struct ospf6_lsa *lsa;
|
|
void (*hook_add)(struct ospf6_route *) = NULL;
|
|
void (*hook_remove)(struct ospf6_route *) = NULL;
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
|
|
zlog_debug("Re-examin intra-routes for area %s", oa->name);
|
|
|
|
hook_add = oa->route_table->hook_add;
|
|
hook_remove = oa->route_table->hook_remove;
|
|
oa->route_table->hook_add = NULL;
|
|
oa->route_table->hook_remove = NULL;
|
|
|
|
for (route = ospf6_route_head(oa->route_table); route;
|
|
route = ospf6_route_next(route))
|
|
route->flag = OSPF6_ROUTE_REMOVE;
|
|
|
|
type = htons(OSPF6_LSTYPE_INTRA_PREFIX);
|
|
for (ALL_LSDB_TYPED(oa->lsdb, type, lsa))
|
|
ospf6_intra_prefix_lsa_add(lsa);
|
|
|
|
oa->route_table->hook_add = hook_add;
|
|
oa->route_table->hook_remove = hook_remove;
|
|
|
|
for (route = ospf6_route_head(oa->route_table); route; route = nroute) {
|
|
nroute = ospf6_route_next(route);
|
|
if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)
|
|
&& CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) {
|
|
UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE);
|
|
UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD);
|
|
}
|
|
|
|
if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE))
|
|
ospf6_route_remove(route, oa->route_table);
|
|
else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)
|
|
|| CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) {
|
|
if (hook_add)
|
|
(*hook_add)(route);
|
|
route->flag = 0;
|
|
} else {
|
|
/* Redo the summaries as things might have changed */
|
|
ospf6_abr_originate_summary(route);
|
|
route->flag = 0;
|
|
}
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
|
|
zlog_debug("Re-examin intra-routes for area %s: Done",
|
|
oa->name);
|
|
}
|
|
|
|
static void ospf6_brouter_debug_print(struct ospf6_route *brouter)
|
|
{
|
|
uint32_t brouter_id;
|
|
char brouter_name[16];
|
|
char area_name[16];
|
|
char destination[64];
|
|
char installed[64], changed[64];
|
|
struct timeval now, res;
|
|
char id[16], adv_router[16];
|
|
char capa[16], options[16];
|
|
|
|
brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix);
|
|
inet_ntop(AF_INET, &brouter_id, brouter_name, sizeof(brouter_name));
|
|
inet_ntop(AF_INET, &brouter->path.area_id, area_name,
|
|
sizeof(area_name));
|
|
ospf6_linkstate_prefix2str(&brouter->prefix, destination,
|
|
sizeof(destination));
|
|
|
|
monotime(&now);
|
|
timersub(&now, &brouter->installed, &res);
|
|
timerstring(&res, installed, sizeof(installed));
|
|
|
|
monotime(&now);
|
|
timersub(&now, &brouter->changed, &res);
|
|
timerstring(&res, changed, sizeof(changed));
|
|
|
|
inet_ntop(AF_INET, &brouter->path.origin.id, id, sizeof(id));
|
|
inet_ntop(AF_INET, &brouter->path.origin.adv_router, adv_router,
|
|
sizeof(adv_router));
|
|
|
|
ospf6_options_printbuf(brouter->path.options, options, sizeof(options));
|
|
ospf6_capability_printbuf(brouter->path.router_bits, capa,
|
|
sizeof(capa));
|
|
|
|
zlog_info("Brouter: %s via area %s", brouter_name, area_name);
|
|
zlog_info(" memory: prev: %p this: %p next: %p parent rnode: %p",
|
|
(void *)brouter->prev, (void *)brouter, (void *)brouter->next,
|
|
(void *)brouter->rnode);
|
|
zlog_info(" type: %d prefix: %s installed: %s changed: %s",
|
|
brouter->type, destination, installed, changed);
|
|
zlog_info(" lock: %d flags: %s%s%s%s", brouter->lock,
|
|
(CHECK_FLAG(brouter->flag, OSPF6_ROUTE_BEST) ? "B" : "-"),
|
|
(CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD) ? "A" : "-"),
|
|
(CHECK_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE) ? "R" : "-"),
|
|
(CHECK_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE) ? "C" : "-"));
|
|
zlog_info(" path type: %s ls-origin %s id: %s adv-router %s",
|
|
OSPF6_PATH_TYPE_NAME(brouter->path.type),
|
|
ospf6_lstype_name(brouter->path.origin.type), id, adv_router);
|
|
zlog_info(" options: %s router-bits: %s metric-type: %d metric: %d/%d",
|
|
options, capa, brouter->path.metric_type, brouter->path.cost,
|
|
brouter->path.u.cost_e2);
|
|
}
|
|
|
|
void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
|
|
{
|
|
struct ospf6_route *brouter, *nbrouter, *copy;
|
|
void (*hook_add)(struct ospf6_route *) = NULL;
|
|
void (*hook_remove)(struct ospf6_route *) = NULL;
|
|
uint32_t brouter_id;
|
|
char brouter_name[16];
|
|
|
|
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id))
|
|
zlog_info("border-router calculation for area %s", oa->name);
|
|
|
|
hook_add = oa->ospf6->brouter_table->hook_add;
|
|
hook_remove = oa->ospf6->brouter_table->hook_remove;
|
|
oa->ospf6->brouter_table->hook_add = NULL;
|
|
oa->ospf6->brouter_table->hook_remove = NULL;
|
|
|
|
/* withdraw the previous router entries for the area */
|
|
for (brouter = ospf6_route_head(oa->ospf6->brouter_table); brouter;
|
|
brouter = ospf6_route_next(brouter)) {
|
|
brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix);
|
|
inet_ntop(AF_INET, &brouter_id, brouter_name,
|
|
sizeof(brouter_name));
|
|
|
|
if (brouter->path.area_id != oa->area_id)
|
|
continue;
|
|
|
|
SET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE);
|
|
|
|
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id)
|
|
|| IS_OSPF6_DEBUG_ROUTE(MEMORY)) {
|
|
zlog_info("%p: mark as removing: area %s brouter %s",
|
|
(void *)brouter, oa->name, brouter_name);
|
|
ospf6_brouter_debug_print(brouter);
|
|
}
|
|
}
|
|
|
|
for (brouter = ospf6_route_head(oa->spf_table); brouter;
|
|
brouter = ospf6_route_next(brouter)) {
|
|
brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix);
|
|
inet_ntop(AF_INET, &brouter_id, brouter_name,
|
|
sizeof(brouter_name));
|
|
|
|
if (brouter->type != OSPF6_DEST_TYPE_LINKSTATE)
|
|
continue;
|
|
|
|
if (ospf6_linkstate_prefix_id(&brouter->prefix) != htonl(0))
|
|
continue;
|
|
|
|
if (!CHECK_FLAG(brouter->path.router_bits, OSPF6_ROUTER_BIT_E)
|
|
&& !CHECK_FLAG(brouter->path.router_bits,
|
|
OSPF6_ROUTER_BIT_B))
|
|
continue;
|
|
|
|
if (!OSPF6_OPT_ISSET(brouter->path.options, OSPF6_OPT_V6)
|
|
|| !OSPF6_OPT_ISSET(brouter->path.options, OSPF6_OPT_R))
|
|
continue;
|
|
|
|
copy = ospf6_route_copy(brouter);
|
|
copy->type = OSPF6_DEST_TYPE_ROUTER;
|
|
copy->path.area_id = oa->area_id;
|
|
ospf6_route_add(copy, oa->ospf6->brouter_table);
|
|
|
|
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id)
|
|
|| IS_OSPF6_DEBUG_ROUTE(MEMORY)) {
|
|
zlog_info("%p: transfer: area %s brouter %s",
|
|
(void *)brouter, oa->name, brouter_name);
|
|
ospf6_brouter_debug_print(brouter);
|
|
}
|
|
}
|
|
|
|
oa->ospf6->brouter_table->hook_add = hook_add;
|
|
oa->ospf6->brouter_table->hook_remove = hook_remove;
|
|
|
|
for (brouter = ospf6_route_head(oa->ospf6->brouter_table); brouter;
|
|
brouter = nbrouter) {
|
|
/*
|
|
* brouter may have been "deleted" in the last loop iteration.
|
|
* If this is the case there is still 1 final refcount lock
|
|
* taken by ospf6_route_next, that will be released by the same
|
|
* call and result in deletion. To avoid heap UAF we must then
|
|
* skip processing the deleted route.
|
|
*/
|
|
if (brouter->lock == 1) {
|
|
nbrouter = ospf6_route_next(brouter);
|
|
continue;
|
|
} else {
|
|
nbrouter = ospf6_route_next(brouter);
|
|
}
|
|
|
|
brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix);
|
|
inet_ntop(AF_INET, &brouter_id, brouter_name,
|
|
sizeof(brouter_name));
|
|
|
|
if (brouter->path.area_id != oa->area_id)
|
|
continue;
|
|
|
|
if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_WAS_REMOVED))
|
|
continue;
|
|
|
|
/* After iterating spf_table for all routers including
|
|
* intra brouter, clear mark for remove flag for
|
|
* inter border router if its adv router present in
|
|
* SPF table.
|
|
*/
|
|
if (brouter->path.type == OSPF6_PATH_TYPE_INTER) {
|
|
struct prefix adv_prefix;
|
|
|
|
ospf6_linkstate_prefix(brouter->path.origin.adv_router,
|
|
htonl(0), &adv_prefix);
|
|
|
|
if (ospf6_route_lookup(&adv_prefix, oa->spf_table)) {
|
|
if (IS_OSPF6_DEBUG_BROUTER) {
|
|
zlog_debug("%s: keep inter brouter %s as adv router 0x%x found in spf",
|
|
__PRETTY_FUNCTION__,
|
|
brouter_name,
|
|
brouter->path.origin.adv_router);
|
|
ospf6_brouter_debug_print(brouter);
|
|
}
|
|
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE);
|
|
}
|
|
}
|
|
|
|
if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE)
|
|
&& CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD)) {
|
|
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE);
|
|
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_ADD);
|
|
}
|
|
|
|
if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE)) {
|
|
if (IS_OSPF6_DEBUG_BROUTER
|
|
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(
|
|
brouter_id)
|
|
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
|
|
oa->area_id))
|
|
zlog_info("brouter %s disappears via area %s",
|
|
brouter_name, oa->name);
|
|
ospf6_route_remove(brouter, oa->ospf6->brouter_table);
|
|
brouter = NULL;
|
|
} else if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD)
|
|
|| CHECK_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE)) {
|
|
if (IS_OSPF6_DEBUG_BROUTER
|
|
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(
|
|
brouter_id)
|
|
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
|
|
oa->area_id))
|
|
zlog_info("brouter %s appears via area %s",
|
|
brouter_name, oa->name);
|
|
|
|
/* newly added */
|
|
if (hook_add)
|
|
(*hook_add)(brouter);
|
|
} else {
|
|
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(
|
|
brouter_id)
|
|
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
|
|
oa->area_id))
|
|
zlog_info("brouter %s still exists via area %s",
|
|
brouter_name, oa->name);
|
|
/* But re-originate summaries */
|
|
ospf6_abr_originate_summary(brouter);
|
|
}
|
|
|
|
if (brouter) {
|
|
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_ADD);
|
|
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE);
|
|
}
|
|
}
|
|
|
|
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id))
|
|
zlog_info("border-router calculation for area %s: done",
|
|
oa->name);
|
|
}
|
|
|
|
struct ospf6_lsa_handler router_handler = {.lh_type = OSPF6_LSTYPE_ROUTER,
|
|
.lh_name = "Router",
|
|
.lh_short_name = "Rtr",
|
|
.lh_show = ospf6_router_lsa_show,
|
|
.lh_get_prefix_str =
|
|
ospf6_router_lsa_get_nbr_id,
|
|
.lh_debug = 0};
|
|
|
|
struct ospf6_lsa_handler network_handler = {.lh_type = OSPF6_LSTYPE_NETWORK,
|
|
.lh_name = "Network",
|
|
.lh_short_name = "Net",
|
|
.lh_show = ospf6_network_lsa_show,
|
|
.lh_get_prefix_str =
|
|
ospf6_network_lsa_get_ar_id,
|
|
.lh_debug = 0};
|
|
|
|
struct ospf6_lsa_handler link_handler = {.lh_type = OSPF6_LSTYPE_LINK,
|
|
.lh_name = "Link",
|
|
.lh_short_name = "Lnk",
|
|
.lh_show = ospf6_link_lsa_show,
|
|
.lh_get_prefix_str =
|
|
ospf6_link_lsa_get_prefix_str,
|
|
.lh_debug = 0};
|
|
|
|
struct ospf6_lsa_handler intra_prefix_handler = {
|
|
.lh_type = OSPF6_LSTYPE_INTRA_PREFIX,
|
|
.lh_name = "Intra-Prefix",
|
|
.lh_short_name = "INP",
|
|
.lh_show = ospf6_intra_prefix_lsa_show,
|
|
.lh_get_prefix_str = ospf6_intra_prefix_lsa_get_prefix_str,
|
|
.lh_debug = 0};
|
|
|
|
void ospf6_intra_init(void)
|
|
{
|
|
ospf6_install_lsa_handler(&router_handler);
|
|
ospf6_install_lsa_handler(&network_handler);
|
|
ospf6_install_lsa_handler(&link_handler);
|
|
ospf6_install_lsa_handler(&intra_prefix_handler);
|
|
}
|
|
|
|
DEFUN (debug_ospf6_brouter,
|
|
debug_ospf6_brouter_cmd,
|
|
"debug ospf6 border-routers",
|
|
DEBUG_STR
|
|
OSPF6_STR
|
|
"Debug border router\n"
|
|
)
|
|
{
|
|
OSPF6_DEBUG_BROUTER_ON();
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN (no_debug_ospf6_brouter,
|
|
no_debug_ospf6_brouter_cmd,
|
|
"no debug ospf6 border-routers",
|
|
NO_STR
|
|
DEBUG_STR
|
|
OSPF6_STR
|
|
"Debug border router\n"
|
|
)
|
|
{
|
|
OSPF6_DEBUG_BROUTER_OFF();
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN (debug_ospf6_brouter_router,
|
|
debug_ospf6_brouter_router_cmd,
|
|
"debug ospf6 border-routers router-id A.B.C.D",
|
|
DEBUG_STR
|
|
OSPF6_STR
|
|
"Debug border router\n"
|
|
"Debug specific border router\n"
|
|
"Specify border-router's router-id\n"
|
|
)
|
|
{
|
|
int idx_ipv4 = 4;
|
|
uint32_t router_id;
|
|
inet_pton(AF_INET, argv[idx_ipv4]->arg, &router_id);
|
|
OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ON(router_id);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN (no_debug_ospf6_brouter_router,
|
|
no_debug_ospf6_brouter_router_cmd,
|
|
"no debug ospf6 border-routers router-id",
|
|
NO_STR
|
|
DEBUG_STR
|
|
OSPF6_STR
|
|
"Debug border router\n"
|
|
"Debug specific border router\n"
|
|
)
|
|
{
|
|
OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF();
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN (debug_ospf6_brouter_area,
|
|
debug_ospf6_brouter_area_cmd,
|
|
"debug ospf6 border-routers area-id A.B.C.D",
|
|
DEBUG_STR
|
|
OSPF6_STR
|
|
"Debug border router\n"
|
|
"Debug border routers in specific Area\n"
|
|
"Specify Area-ID\n"
|
|
)
|
|
{
|
|
int idx_ipv4 = 4;
|
|
uint32_t area_id;
|
|
inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id);
|
|
OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ON(area_id);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN (no_debug_ospf6_brouter_area,
|
|
no_debug_ospf6_brouter_area_cmd,
|
|
"no debug ospf6 border-routers area-id",
|
|
NO_STR
|
|
DEBUG_STR
|
|
OSPF6_STR
|
|
"Debug border router\n"
|
|
"Debug border routers in specific Area\n"
|
|
)
|
|
{
|
|
OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF();
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
int config_write_ospf6_debug_brouter(struct vty *vty)
|
|
{
|
|
char buf[16];
|
|
if (IS_OSPF6_DEBUG_BROUTER)
|
|
vty_out(vty, "debug ospf6 border-routers\n");
|
|
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER) {
|
|
inet_ntop(AF_INET, &conf_debug_ospf6_brouter_specific_router_id,
|
|
buf, sizeof(buf));
|
|
vty_out(vty, "debug ospf6 border-routers router-id %s\n", buf);
|
|
}
|
|
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA) {
|
|
inet_ntop(AF_INET, &conf_debug_ospf6_brouter_specific_area_id,
|
|
buf, sizeof(buf));
|
|
vty_out(vty, "debug ospf6 border-routers area-id %s\n", buf);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void install_element_ospf6_debug_brouter(void)
|
|
{
|
|
install_element(ENABLE_NODE, &debug_ospf6_brouter_cmd);
|
|
install_element(ENABLE_NODE, &debug_ospf6_brouter_router_cmd);
|
|
install_element(ENABLE_NODE, &debug_ospf6_brouter_area_cmd);
|
|
install_element(ENABLE_NODE, &no_debug_ospf6_brouter_cmd);
|
|
install_element(ENABLE_NODE, &no_debug_ospf6_brouter_router_cmd);
|
|
install_element(ENABLE_NODE, &no_debug_ospf6_brouter_area_cmd);
|
|
install_element(CONFIG_NODE, &debug_ospf6_brouter_cmd);
|
|
install_element(CONFIG_NODE, &debug_ospf6_brouter_router_cmd);
|
|
install_element(CONFIG_NODE, &debug_ospf6_brouter_area_cmd);
|
|
install_element(CONFIG_NODE, &no_debug_ospf6_brouter_cmd);
|
|
install_element(CONFIG_NODE, &no_debug_ospf6_brouter_router_cmd);
|
|
install_element(CONFIG_NODE, &no_debug_ospf6_brouter_area_cmd);
|
|
}
|