Merge pull request #14546 from adrianomarto/ospf6-point-to-multipoint

OSPF6 point to multipoint
This commit is contained in:
Donald Sharp 2023-10-31 18:20:04 -04:00 committed by GitHub
commit 8e3a96e846
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 2036 additions and 625 deletions

View file

@ -312,10 +312,135 @@ OSPF6 interface
Sets interface's Inf-Trans-Delay. Default value is 1.
.. clicmd:: ipv6 ospf6 network (broadcast|point-to-point)
.. clicmd:: ipv6 ospf6 network (broadcast|point-to-point|point-to-multipoint)
Set explicitly network type for specified interface.
The only functional difference between ``point-to-point`` (PtP) and
``point-to-multipoint`` (PtMP) mode is the packet addressing for database
flooding and updates. PtP will use multicast packets while PtMP will
unicast them. Apart from this,
:clicmd:`ipv6 ospf6 p2p-p2mp connected-prefixes <include|exclude>` has a
different default for PtP and PtMP. There are no other differences, in
particular FRR does not impose a limit of one neighbor in PtP mode.
FRR does not support NBMA mode for IPv6 and likely never will, as NBMA is
considered deprecated for IPv6. Refer to `this IETF OSPF working group
discussion
<https://mailarchive.ietf.org/arch/msg/ospf/8GAbr4qSMMt5J7SvAcZQ1H7ARhk/>`_
for context.
OSPF6 point-to-point and point-to-multipoint operation
======================================================
OSPFv3, by default, operates in broadcast mode where it elects a DR and BDR
for each network segment. This can be changed to point-to-point (PtP) /
point-to-multipoint (PtMP) mode by configuration. The actual physical
interface characteristics do not matter for this setting, all interfaces can
be configured for all modes. However, routers must be configured for the same
mode to form adjacencies.
The main advantages of PtP/PtMP mode are:
- no DR/BDR election
- adjacencies can be suppressed in a pairwise manner for any two routers, e.g.
to represent the underlying topology if it isn't a true full mesh
- distinct costs can be set for each pair of routers and direction
The main downside is less efficient flooding on networks with a large number
of OSPFv3 routers.
.. warning::
All options in this section should be considered "advanced" configuration
options. Inconsistent or nonsensical combinations can easily result in a
non-functional setup.
.. clicmd:: ipv6 ospf6 p2p-p2mp disable-multicast-hello
Disables sending normal multicast hellos when in PtP/PtMP mode. Some
vendors do this automatically for PtMP mode while others have a separate
``no-broadcast`` option matching this.
If this setting is used, you must issue
:clicmd:`ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535)` for each
neighbor to send unicast hello packets.
.. clicmd:: ipv6 ospf6 p2p-p2mp config-neighbors-only
Only form adjacencies with neighbors that are explicitly configured with
the :clicmd:`ipv6 ospf6 neighbor X:X::X:X` command. Hellos from other
routers are ignored.
.. warning::
This setting is not intended to provide any security benefit. Do not
run OSPFv3 over untrusted links without additional security measures
(e.g. IPsec.)
.. clicmd:: ipv6 ospf6 p2p-p2mp connected-prefixes <include|exclude>
For global/ULA prefixes configured on this interfaces, do (not) advertise
the full prefix to the area. Regardless of this setting, the router's own
address, as a /128 host route with the "LA" (Local Address) bit set, will
always be advertised.
The default is to include connected prefixes for PtP mode and exclude them
for PtMP mode. Since these prefixes will cover other router's addresses,
these addresses can become unreachable if the link is partitioned if the
other router does not advertise the address as a /128. However, conversely,
if all routers have this flag set, the overall prefix will not be advertised
anywhere. End hosts on this link will therefore be unreachable (and
blackholing best-practices for non-existing prefixes apply.) It may be
preferable to have only one router announce the connected prefix.
The Link LSA (which is not propagated into the area) always includes all
prefixes on the interface. This setting only affects the Router LSA that
is visible to all routers in the area.
.. note::
Before interacting with this setting, consider either not configuring
any global/ULA IPv6 address on the interface, or directly configuring a
/128 if needed. OSPFv3 relies exclusively on link-local addresses to do
its signaling and there is absolutely no reason to configure global/ULA
addresses as far as OSPFv3 is concerned.
.. clicmd:: ipv6 ospf6 neighbor X:X::X:X
Explicitly configure a neighbor by its link-local address on this interface.
This statement has no effect other than allowing an adjacency when
:clicmd:`ipv6 ospf6 p2p-p2mp config-neighbors-only` is set. This command
does **not** cause unicast hellos to be sent.
Only link-local addresses can be used to establish explicit neighbors.
When using this command, you should probably assign static IPv6 link-local
addresses to all routers on this link. It would technically be possible to
use the neighbor's Router ID (IPv4 address) here to ease working with
changing link-local addresses but this is not planned as a feature at the
time of writing. Global/ULA IPv6 addresses cannot be supported here due to
the way OSPFv3 works.
.. clicmd:: ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535)
Send unicast hellos to this neighbor at the specified interval (in seconds.)
The interval is only used while there is no adjacency with this neighbor.
As soon as an adjacency is formed, the interface's
:clicmd:`ipv6 ospf6 hello-interval HELLOINTERVAL` value is used.
(``hello-interval`` must be the same on all routers on this link.)
:rfc:`2328` recommends a "much larger" value than ``hello-interval`` for
this setting, but this is a legacy of ATM and X.25 networks and nowadays you
should probably just use the same value as for ``hello-interval``.
.. clicmd:: ipv6 ospf6 neighbor X:X::X:X cost (1-65535)
Use a distinct cost for paths traversing this neighbor. The default is
to use the interface's cost value (which may be automatically calculated
based on link bandwidth.) Note that costs are directional in OSPF and the
reverse direction must be set on the other router.
OSPF6 route-map
===============

View file

@ -579,7 +579,6 @@ extern ifindex_t ifname2ifindex(const char *ifname, vrf_id_t vrf_id);
/* Connected address functions. */
extern struct connected *connected_new(void);
extern void connected_free(struct connected **connected);
extern void connected_add(struct interface *, struct connected *);
extern struct connected *
connected_add_by_prefix(struct interface *, struct prefix *, struct prefix *);
extern struct connected *connected_delete_by_prefix(struct interface *,

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,25 @@ struct ospf6_auth_data {
uint32_t rx_drop; /* Pkt drop due to auth fail while reading */
};
PREDECL_RBTREE_UNIQ(ospf6_if_p2xp_neighcfgs);
struct ospf6_if_p2xp_neighcfg {
struct ospf6_if_p2xp_neighcfgs_item item;
struct ospf6_interface *ospf6_if;
struct in6_addr addr;
bool cfg_cost : 1;
uint32_t cost;
uint16_t poll_interval;
/* NULL if down */
struct ospf6_neighbor *active;
struct event *t_unicast_hello;
};
/* Interface structure */
struct ospf6_interface {
/* IF info from zebra */
@ -66,6 +85,20 @@ struct ospf6_interface {
uint8_t type;
bool type_cfg;
/* P2P/P2MP behavior: */
/* disable hellos on standard multicast? */
bool p2xp_no_multicast_hello;
/* only allow explicitly configured neighbors? */
bool p2xp_only_cfg_neigh;
/* override mode default for advertising connected prefixes.
* both false by default (= do include for PtP, exclude for PtMP)
*/
bool p2xp_connected_pfx_include;
bool p2xp_connected_pfx_exclude;
struct ospf6_if_p2xp_neighcfgs_head p2xp_neighs;
/* Router Priority */
uint8_t priority;
@ -176,10 +209,11 @@ DECLARE_QOBJ_TYPE(ospf6_interface);
#define OSPF6_INTERFACE_LOOPBACK 2
#define OSPF6_INTERFACE_WAITING 3
#define OSPF6_INTERFACE_POINTTOPOINT 4
#define OSPF6_INTERFACE_DROTHER 5
#define OSPF6_INTERFACE_BDR 6
#define OSPF6_INTERFACE_DR 7
#define OSPF6_INTERFACE_MAX 8
#define OSPF6_INTERFACE_POINTTOMULTIPOINT 5
#define OSPF6_INTERFACE_DROTHER 6
#define OSPF6_INTERFACE_BDR 7
#define OSPF6_INTERFACE_DR 8
#define OSPF6_INTERFACE_MAX 9
extern const char *const ospf6_interface_state_str[];

View file

@ -321,13 +321,14 @@ void ospf6_router_lsa_originate(struct event *thread)
}
/* Point-to-Point interfaces */
if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
if (oi->type == OSPF_IFTYPE_POINTOPOINT
|| oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) {
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->metric = htons(ospf6_neighbor_cost(on));
lsdesc->interface_id =
htonl(oi->interface->ifindex);
lsdesc->neighbor_interface_id =
@ -1068,6 +1069,7 @@ void ospf6_intra_prefix_lsa_originate_stub(struct event *thread)
if (oi->state != OSPF6_INTERFACE_LOOPBACK
&& oi->state != OSPF6_INTERFACE_POINTTOPOINT
&& oi->state != OSPF6_INTERFACE_POINTTOMULTIPOINT
&& full_count != 0) {
if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
zlog_debug(" Interface %s is not stub, ignore",

View file

@ -268,6 +268,18 @@ static struct ospf6_packet *ospf6_packet_new(size_t size)
return new;
}
static struct ospf6_packet *ospf6_packet_dup(struct ospf6_packet *old)
{
struct ospf6_packet *new;
new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet));
new->s = stream_dup(old->s);
new->dst = old->dst;
new->length = old->length;
return new;
}
static void ospf6_packet_free(struct ospf6_packet *op)
{
if (op->s)
@ -407,6 +419,25 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
hello = (struct ospf6_hello *)((caddr_t)oh
+ sizeof(struct ospf6_header));
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT
|| oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT)
&& oi->p2xp_only_cfg_neigh) {
/* NEVER, never, ever, do this on broadcast (or NBMA)!
* DR/BDR election requires everyone to talk to everyone else
* only for PtP/PtMP we can be selective in adjacencies!
*/
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
p2xp_cfg = ospf6_if_p2xp_find(oi, src);
if (!p2xp_cfg) {
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
zlog_debug(
"ignoring PtP/PtMP hello from %pI6, neighbor not configured",
src);
return;
}
}
/* HelloInterval check */
if (ntohs(hello->hello_interval) != oi->hello_interval) {
zlog_warn(
@ -479,7 +510,7 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
on->hello_in++;
/* Always override neighbor's source address */
memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr));
ospf6_neighbor_lladdr_set(on, src);
/* Neighbor ifindex check */
if (on->ifindex != (ifindex_t)ntohl(hello->interface_id)) {
@ -2239,8 +2270,6 @@ static void ospf6_write(struct event *thread)
void ospf6_hello_send(struct event *thread)
{
struct ospf6_interface *oi;
struct ospf6_packet *op;
uint16_t length = OSPF6_HEADER_SIZE;
oi = (struct ospf6_interface *)EVENT_ARG(thread);
@ -2266,6 +2295,20 @@ void ospf6_hello_send(struct event *thread)
return;
}
event_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
&oi->thread_send_hello);
ospf6_hello_send_addr(oi, NULL);
}
/* used to send polls for PtP/PtMP too */
void ospf6_hello_send_addr(struct ospf6_interface *oi,
const struct in6_addr *addr)
{
struct ospf6_packet *op;
uint16_t length = OSPF6_HEADER_SIZE;
bool anything = false;
op = ospf6_packet_new(oi->ifmtu);
ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s);
@ -2284,19 +2327,39 @@ void ospf6_hello_send(struct event *thread)
/* Set packet length. */
op->length = length;
op->dst = allspfrouters6;
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT
|| oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT)
&& !addr && oi->p2xp_no_multicast_hello) {
struct listnode *node;
struct ospf6_neighbor *on;
struct ospf6_packet *opdup;
ospf6_fill_hdr_checksum(oi, op);
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, on)) {
if (on->state < OSPF6_NEIGHBOR_INIT)
/* poll-interval for these */
continue;
/* Add packet to the top of the interface output queue, so that they
* can't get delayed by things like long queues of LS Update packets
opdup = ospf6_packet_dup(op);
opdup->dst = on->linklocal_addr;
ospf6_fill_hdr_checksum(oi, opdup);
ospf6_packet_add_top(oi, opdup);
anything = true;
}
ospf6_packet_free(op);
} else {
op->dst = addr ? *addr : allspfrouters6;
/* Add packet to the top of the interface output queue, so that
* they can't get delayed by things like long queues of LS
* Update packets
*/
ospf6_fill_hdr_checksum(oi, op);
ospf6_packet_add_top(oi, op);
anything = true;
}
/* set next thread */
event_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
&oi->thread_send_hello);
if (anything)
OSPF6_MESSAGE_WRITE_ON(oi);
}

View file

@ -50,6 +50,8 @@ extern unsigned char conf_debug_ospf6_message[];
#define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */
#define OSPF6_MESSAGE_TYPE_MAX 0x6 /* same as OSPF6_MESSAGE_TYPE_ALL */
struct ospf6_interface;
struct ospf6_packet {
struct ospf6_packet *next;
@ -169,6 +171,9 @@ extern void ospf6_lsupdate_send_neighbor(struct event *thread);
extern void ospf6_lsack_send_interface(struct event *thread);
extern void ospf6_lsack_send_neighbor(struct event *thread);
extern void ospf6_hello_send_addr(struct ospf6_interface *oi,
const struct in6_addr *addr);
extern int config_write_ospf6_debug_message(struct vty *);
extern void install_element_ospf6_debug_message(void);
extern const char *ospf6_message_type(int type);

View file

@ -34,6 +34,16 @@
#include "lib/json.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR, "OSPF6 neighbor");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR_P2XP_CFG,
"OSPF6 PtP/PtMP neighbor config");
static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a,
const struct ospf6_if_p2xp_neighcfg *b);
DECLARE_RBTREE_UNIQ(ospf6_if_p2xp_neighcfgs, struct ospf6_if_p2xp_neighcfg,
item, ospf6_if_p2xp_neighcfg_cmp);
static void p2xp_neigh_refresh(struct ospf6_neighbor *on, uint32_t prev_cost);
DEFINE_HOOK(ospf6_neighbor_change,
(struct ospf6_neighbor * on, int state, int next_state),
@ -43,7 +53,8 @@ unsigned char conf_debug_ospf6_neighbor = 0;
const char *const ospf6_neighbor_state_str[] = {
"None", "Down", "Attempt", "Init", "Twoway",
"ExStart", "ExChange", "Loading", "Full", NULL};
"ExStart", "ExChange", "Loading", "Full", NULL
};
const char *const ospf6_neighbor_event_str[] = {
"NoEvent", "HelloReceived", "2-WayReceived", "NegotiationDone",
@ -119,8 +130,7 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
on = XCALLOC(MTYPE_OSPF6_NEIGHBOR, sizeof(struct ospf6_neighbor));
inet_ntop(AF_INET, &router_id, buf, sizeof(buf));
snprintf(on->name, sizeof(on->name), "%s%%%s", buf,
oi->interface->name);
snprintf(on->name, sizeof(on->name), "%s%%%s", buf, oi->interface->name);
on->ospf6_if = oi;
on->state = OSPF6_NEIGHBOR_DOWN;
on->state_change = 0;
@ -150,6 +160,9 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
void ospf6_neighbor_delete(struct ospf6_neighbor *on)
{
if (on->p2xp_cfg)
on->p2xp_cfg->active = NULL;
ospf6_neighbor_clear_ls_lists(on);
ospf6_lsdb_remove_all(on->dbdesc_list);
@ -182,6 +195,22 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on)
XFREE(MTYPE_OSPF6_NEIGHBOR, on);
}
void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on,
const struct in6_addr *addr)
{
if (IPV6_ADDR_SAME(addr, &on->linklocal_addr))
return;
memcpy(&on->linklocal_addr, addr, sizeof(struct in6_addr));
if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT ||
on->ospf6_if->type == OSPF_IFTYPE_POINTOMULTIPOINT) {
uint32_t prev_cost = ospf6_neighbor_cost(on);
p2xp_neigh_refresh(on, prev_cost);
}
}
static void ospf6_neighbor_state_change(uint8_t next_state,
struct ospf6_neighbor *on, int event)
{
@ -198,8 +227,7 @@ static void ospf6_neighbor_state_change(uint8_t next_state,
/* log */
if (IS_OSPF6_DEBUG_NEIGHBOR(STATE)) {
zlog_debug(
"Neighbor state change %s (Router-ID: %pI4): [%s]->[%s] (%s)",
zlog_debug("Neighbor state change %s (Router-ID: %pI4): [%s]->[%s] (%s)",
on->name, &on->router_id,
ospf6_neighbor_state_str[prev_state],
ospf6_neighbor_state_str[next_state],
@ -208,21 +236,19 @@ static void ospf6_neighbor_state_change(uint8_t next_state,
/* Optionally notify about adjacency changes */
if (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags,
OSPF6_LOG_ADJACENCY_CHANGES)
&& (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags,
OSPF6_LOG_ADJACENCY_DETAIL)
|| (next_state == OSPF6_NEIGHBOR_FULL)
|| (next_state < prev_state)))
zlog_notice(
"AdjChg: Nbr %pI4(%s) on %s: %s -> %s (%s)",
OSPF6_LOG_ADJACENCY_CHANGES) &&
(CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags,
OSPF6_LOG_ADJACENCY_DETAIL) ||
(next_state == OSPF6_NEIGHBOR_FULL) || (next_state < prev_state)))
zlog_notice("AdjChg: Nbr %pI4(%s) on %s: %s -> %s (%s)",
&on->router_id,
vrf_id_to_name(on->ospf6_if->interface->vrf->vrf_id),
on->name, ospf6_neighbor_state_str[prev_state],
ospf6_neighbor_state_str[next_state],
ospf6_neighbor_event_string(event));
if (prev_state == OSPF6_NEIGHBOR_FULL
|| next_state == OSPF6_NEIGHBOR_FULL) {
if (prev_state == OSPF6_NEIGHBOR_FULL ||
next_state == OSPF6_NEIGHBOR_FULL) {
if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) {
OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area);
if (on->ospf6_if->state == OSPF6_INTERFACE_DR) {
@ -235,12 +261,11 @@ static void ospf6_neighbor_state_change(uint8_t next_state,
on->ospf6_if->area->intra_prefix_originate = 1;
if (!OSPF6_GR_IS_ACTIVE_HELPER(on))
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(
on->ospf6_if->area);
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(on->ospf6_if->area);
if ((prev_state == OSPF6_NEIGHBOR_LOADING
|| prev_state == OSPF6_NEIGHBOR_EXCHANGE)
&& next_state == OSPF6_NEIGHBOR_FULL) {
if ((prev_state == OSPF6_NEIGHBOR_LOADING ||
prev_state == OSPF6_NEIGHBOR_EXCHANGE) &&
next_state == OSPF6_NEIGHBOR_FULL) {
OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if);
on->ospf6_if->area->full_nbrs++;
}
@ -249,10 +274,10 @@ static void ospf6_neighbor_state_change(uint8_t next_state,
on->ospf6_if->area->full_nbrs--;
}
if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE
|| prev_state == OSPF6_NEIGHBOR_LOADING)
&& (next_state != OSPF6_NEIGHBOR_EXCHANGE
&& next_state != OSPF6_NEIGHBOR_LOADING))
if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE ||
prev_state == OSPF6_NEIGHBOR_LOADING) &&
(next_state != OSPF6_NEIGHBOR_EXCHANGE &&
next_state != OSPF6_NEIGHBOR_LOADING))
ospf6_maxage_remove(on->ospf6_if->area->ospf6);
hook_call(ospf6_neighbor_change, on, next_state, prev_state);
@ -262,13 +287,14 @@ static void ospf6_neighbor_state_change(uint8_t next_state,
/* RFC2328 section 10.4 */
static int need_adjacency(struct ospf6_neighbor *on)
{
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT
|| on->ospf6_if->state == OSPF6_INTERFACE_DR
|| on->ospf6_if->state == OSPF6_INTERFACE_BDR)
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT ||
on->ospf6_if->state == OSPF6_INTERFACE_POINTTOMULTIPOINT ||
on->ospf6_if->state == OSPF6_INTERFACE_DR ||
on->ospf6_if->state == OSPF6_INTERFACE_BDR)
return 1;
if (on->ospf6_if->drouter == on->router_id
|| on->ospf6_if->bdrouter == on->router_id)
if (on->ospf6_if->drouter == on->router_id ||
on->ospf6_if->bdrouter == on->router_id)
return 1;
return 0;
@ -422,13 +448,12 @@ void exchange_done(struct event *thread)
/* Check loading state. */
void ospf6_check_nbr_loading(struct ospf6_neighbor *on)
{
/* RFC2328 Section 10.9: When the neighbor responds to these requests
with the proper Link State Update packet(s), the Link state request
list is truncated and a new Link State Request packet is sent.
*/
if ((on->state == OSPF6_NEIGHBOR_LOADING)
|| (on->state == OSPF6_NEIGHBOR_EXCHANGE)) {
if ((on->state == OSPF6_NEIGHBOR_LOADING) ||
(on->state == OSPF6_NEIGHBOR_EXCHANGE)) {
if (on->request_list->count == 0)
event_add_event(master, loading_done, on, 0,
&on->event_loading_done);
@ -587,8 +612,7 @@ void inactivity_timer(struct event *thread)
on->drouter = on->prev_drouter = 0;
on->bdrouter = on->prev_bdrouter = 0;
ospf6_neighbor_state_change(
OSPF6_NEIGHBOR_DOWN, on,
ospf6_neighbor_state_change(OSPF6_NEIGHBOR_DOWN, on,
OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER);
event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL);
@ -597,8 +621,7 @@ void inactivity_timer(struct event *thread)
} else {
if (IS_DEBUG_OSPF6_GR)
zlog_debug(
"%s, Acting as HELPER for this neighbour, So restart the dead timer.",
zlog_debug("%s, Acting as HELPER for this neighbour, So restart the dead timer.",
__PRETTY_FUNCTION__);
event_add_timer(master, inactivity_timer, on,
@ -607,8 +630,224 @@ void inactivity_timer(struct event *thread)
}
}
/* P2P/P2MP stuff */
uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on)
{
if (on->p2xp_cfg && on->p2xp_cfg->cfg_cost)
return on->p2xp_cfg->cost;
return on->ospf6_if->cost;
}
static int ospf6_if_p2xp_neighcfg_cmp(const struct ospf6_if_p2xp_neighcfg *a,
const struct ospf6_if_p2xp_neighcfg *b)
{
return IPV6_ADDR_CMP(&a->addr, &b->addr);
}
struct ospf6_if_p2xp_neighcfg *ospf6_if_p2xp_find(struct ospf6_interface *oi,
const struct in6_addr *addr)
{
struct ospf6_if_p2xp_neighcfg ref;
if (!oi)
return NULL;
ref.addr = *addr;
return ospf6_if_p2xp_neighcfgs_find(&oi->p2xp_neighs, &ref);
}
static struct ospf6_if_p2xp_neighcfg *
ospf6_if_p2xp_get(struct ospf6_interface *oi, const struct in6_addr *addr)
{
struct ospf6_if_p2xp_neighcfg ref, *ret;
if (!oi)
return NULL;
ref.addr = *addr;
ret = ospf6_if_p2xp_neighcfgs_find(&oi->p2xp_neighs, &ref);
if (!ret) {
ret = XCALLOC(MTYPE_OSPF6_NEIGHBOR_P2XP_CFG, sizeof(*ret));
ret->addr = *addr;
ret->ospf6_if = oi;
ospf6_if_p2xp_neighcfgs_add(&oi->p2xp_neighs, ret);
}
return ret;
}
static void ospf6_if_p2xp_destroy(struct ospf6_if_p2xp_neighcfg *p2xp_cfg)
{
EVENT_OFF(p2xp_cfg->t_unicast_hello);
ospf6_if_p2xp_neighcfgs_del(&p2xp_cfg->ospf6_if->p2xp_neighs, p2xp_cfg);
XFREE(MTYPE_OSPF6_NEIGHBOR_P2XP_CFG, p2xp_cfg);
}
static void p2xp_neigh_refresh(struct ospf6_neighbor *on, uint32_t prev_cost)
{
if (on->p2xp_cfg)
on->p2xp_cfg->active = NULL;
on->p2xp_cfg = ospf6_if_p2xp_find(on->ospf6_if, &on->linklocal_addr);
if (on->p2xp_cfg)
on->p2xp_cfg->active = on;
if (ospf6_neighbor_cost(on) != prev_cost)
OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area);
}
/* vty functions */
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_neighbor_clippy.c"
#endif
DEFPY(ipv6_ospf6_p2xp_neigh, ipv6_ospf6_p2xp_neigh_cmd,
"[no] ipv6 ospf6 neighbor X:X::X:X",
NO_STR IP6_STR OSPF6_STR "Configure static neighbor\n"
"Neighbor link-local address\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct ospf6_interface *oi = ifp->info;
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
if (!oi) {
if (no)
return CMD_SUCCESS;
oi = ospf6_interface_create(ifp);
}
if (no) {
struct ospf6_neighbor *on;
uint32_t prev_cost = 0;
p2xp_cfg = ospf6_if_p2xp_find(oi, &neighbor);
if (!p2xp_cfg)
return CMD_SUCCESS;
on = p2xp_cfg->active;
if (on)
prev_cost = ospf6_neighbor_cost(on);
p2xp_cfg->active = NULL;
ospf6_if_p2xp_destroy(p2xp_cfg);
if (on) {
on->p2xp_cfg = NULL;
p2xp_neigh_refresh(on, prev_cost);
}
return CMD_SUCCESS;
}
p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor);
return CMD_SUCCESS;
}
DEFPY(ipv6_ospf6_p2xp_neigh_cost, ipv6_ospf6_p2xp_neigh_cost_cmd,
"[no] ipv6 ospf6 neighbor X:X::X:X cost (1-65535)",
NO_STR IP6_STR OSPF6_STR "Configure static neighbor\n"
"Neighbor link-local address\n"
"Outgoing metric for this neighbor\n"
"Outgoing metric for this neighbor\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct ospf6_interface *oi = ifp->info;
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
uint32_t prev_cost;
if (!oi) {
if (no)
return CMD_SUCCESS;
oi = ospf6_interface_create(ifp);
}
p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor);
if (p2xp_cfg->active)
prev_cost = ospf6_neighbor_cost(p2xp_cfg->active);
if (no) {
p2xp_cfg->cfg_cost = false;
p2xp_cfg->cost = 0;
} else {
p2xp_cfg->cfg_cost = true;
p2xp_cfg->cost = cost;
}
if (p2xp_cfg->active)
p2xp_neigh_refresh(p2xp_cfg->active, prev_cost);
return CMD_SUCCESS;
}
static void p2xp_unicast_hello_send(struct event *event);
static void p2xp_unicast_hello_sched(struct ospf6_if_p2xp_neighcfg *p2xp_cfg)
{
if (!p2xp_cfg->poll_interval ||
(p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOMULTIPOINT &&
p2xp_cfg->ospf6_if->state != OSPF6_INTERFACE_POINTTOPOINT))
/* state check covers DOWN state too */
EVENT_OFF(p2xp_cfg->t_unicast_hello);
else
event_add_timer(master, p2xp_unicast_hello_send, p2xp_cfg,
p2xp_cfg->poll_interval,
&p2xp_cfg->t_unicast_hello);
}
void ospf6_if_p2xp_up(struct ospf6_interface *oi)
{
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
frr_each (ospf6_if_p2xp_neighcfgs, &oi->p2xp_neighs, p2xp_cfg)
p2xp_unicast_hello_sched(p2xp_cfg);
}
static void p2xp_unicast_hello_send(struct event *event)
{
struct ospf6_if_p2xp_neighcfg *p2xp_cfg = EVENT_ARG(event);
struct ospf6_interface *oi = p2xp_cfg->ospf6_if;
if (oi->state != OSPF6_INTERFACE_POINTTOPOINT &&
oi->state != OSPF6_INTERFACE_POINTTOMULTIPOINT)
return;
p2xp_unicast_hello_sched(p2xp_cfg);
if (p2xp_cfg->active && p2xp_cfg->active->state >= OSPF6_NEIGHBOR_INIT)
return;
ospf6_hello_send_addr(oi, &p2xp_cfg->addr);
}
DEFPY(ipv6_ospf6_p2xp_neigh_poll_interval,
ipv6_ospf6_p2xp_neigh_poll_interval_cmd,
"[no] ipv6 ospf6 neighbor X:X::X:X poll-interval (1-65535)",
NO_STR IP6_STR OSPF6_STR "Configure static neighbor\n"
"Neighbor link-local address\n"
"Send unicast hellos to neighbor when down\n"
"Unicast hello interval when down (seconds)\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct ospf6_interface *oi = ifp->info;
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
if (!oi) {
if (no)
return CMD_SUCCESS;
oi = ospf6_interface_create(ifp);
}
if (no)
poll_interval = 0;
p2xp_cfg = ospf6_if_p2xp_get(oi, &neighbor);
p2xp_cfg->poll_interval = poll_interval;
p2xp_unicast_hello_sched(p2xp_cfg);
return CMD_SUCCESS;
}
/* show neighbor structure */
static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on,
json_object *json_array, bool use_json)
@ -631,8 +870,8 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on,
/* Dead time */
h = m = s = 0;
if (on->inactivity_timer) {
s = monotime_until(&on->inactivity_timer->u.sands, NULL)
/ 1000000LL;
s = monotime_until(&on->inactivity_timer->u.sands, NULL) /
1000000LL;
h = s / 3600;
s -= h * 3600;
m = s / 60;
@ -643,6 +882,8 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on,
/* Neighbor State */
if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT)
snprintf(nstate, sizeof(nstate), "PointToPoint");
else if (on->ospf6_if->type == OSPF_IFTYPE_POINTOMULTIPOINT)
snprintf(nstate, sizeof(nstate), "PtMultipoint");
else {
if (on->router_id == on->drouter)
snprintf(nstate, sizeof(nstate), "DR");
@ -673,9 +914,9 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on,
json_object_string_add(json_route, "duration", duration);
json_object_string_add(json_route, "interfaceName",
on->ospf6_if->interface->name);
json_object_string_add(
json_route, "interfaceState",
ospf6_interface_state_str[on->ospf6_if->state]);
json_object_string_add(json_route, "interfaceState",
ospf6_interface_state_str
[on->ospf6_if->state]);
json_object_array_add(json_array, json_route);
} else
@ -720,9 +961,9 @@ static void ospf6_neighbor_show_drchoice(struct vty *vty,
json_object_string_add(json_route, "bdRouter", bdrouter);
json_object_string_add(json_route, "interfaceName",
on->ospf6_if->interface->name);
json_object_string_add(
json_route, "interfaceState",
ospf6_interface_state_str[on->ospf6_if->state]);
json_object_string_add(json_route, "interfaceState",
ospf6_interface_state_str
[on->ospf6_if->state]);
json_object_array_add(json_array, json_route);
} else
@ -777,8 +1018,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)
? "Initial "
: ""),
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT)
? "More"
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) ? "More"
: ""),
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)
? "Master"
@ -793,8 +1033,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
json_object_int_add(json_neighbor, "summaryListCount",
on->summary_list->count);
for (ALL_LSDB(on->summary_list, lsa, lsanext))
json_object_array_add(
json_array, json_object_new_string(lsa->name));
json_object_array_add(json_array,
json_object_new_string(lsa->name));
json_object_object_add(json_neighbor, "summaryListLsa",
json_array);
@ -802,8 +1042,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
json_object_int_add(json_neighbor, "requestListCount",
on->request_list->count);
for (ALL_LSDB(on->request_list, lsa, lsanext))
json_object_array_add(
json_array, json_object_new_string(lsa->name));
json_object_array_add(json_array,
json_object_new_string(lsa->name));
json_object_object_add(json_neighbor, "requestListLsa",
json_array);
@ -811,8 +1051,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
json_object_int_add(json_neighbor, "reTransListCount",
on->retrans_list->count);
for (ALL_LSDB(on->retrans_list, lsa, lsanext))
json_object_array_add(
json_array, json_object_new_string(lsa->name));
json_object_array_add(json_array,
json_object_new_string(lsa->name));
json_object_object_add(json_neighbor, "reTransListLsa",
json_array);
@ -825,14 +1065,14 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
on->dbdesc_list->count);
json_object_string_add(json_neighbor, "pendingLsaDbDescTime",
duration);
json_object_string_add(
json_neighbor, "dbDescSendThread",
(event_is_scheduled(on->thread_send_dbdesc) ? "on"
json_object_string_add(json_neighbor, "dbDescSendThread",
(event_is_scheduled(on->thread_send_dbdesc)
? "on"
: "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->dbdesc_list, lsa, lsanext))
json_object_array_add(
json_array, json_object_new_string(lsa->name));
json_object_array_add(json_array,
json_object_new_string(lsa->name));
json_object_object_add(json_neighbor, "pendingLsaDbDesc",
json_array);
@ -844,35 +1084,35 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
on->request_list->count);
json_object_string_add(json_neighbor, "pendingLsaLsReqTime",
duration);
json_object_string_add(
json_neighbor, "lsReqSendThread",
(event_is_scheduled(on->thread_send_lsreq) ? "on"
json_object_string_add(json_neighbor, "lsReqSendThread",
(event_is_scheduled(on->thread_send_lsreq)
? "on"
: "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->request_list, lsa, lsanext))
json_object_array_add(
json_array, json_object_new_string(lsa->name));
json_object_array_add(json_array,
json_object_new_string(lsa->name));
json_object_object_add(json_neighbor, "pendingLsaLsReq",
json_array);
timerclear(&res);
if (event_is_scheduled(on->thread_send_lsupdate))
timersub(&on->thread_send_lsupdate->u.sands, &now,
&res);
timersub(&on->thread_send_lsupdate->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
json_object_int_add(json_neighbor, "pendingLsaLsUpdateCount",
on->lsupdate_list->count);
json_object_string_add(json_neighbor, "pendingLsaLsUpdateTime",
duration);
json_object_string_add(
json_neighbor, "lsUpdateSendThread",
(event_is_scheduled(on->thread_send_lsupdate) ? "on"
json_object_string_add(json_neighbor, "lsUpdateSendThread",
(event_is_scheduled(
on->thread_send_lsupdate)
? "on"
: "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->lsupdate_list, lsa, lsanext))
json_object_array_add(
json_array, json_object_new_string(lsa->name));
json_object_array_add(json_array,
json_object_new_string(lsa->name));
json_object_object_add(json_neighbor, "pendingLsaLsUpdate",
json_array);
@ -884,14 +1124,14 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
on->lsack_list->count);
json_object_string_add(json_neighbor, "pendingLsaLsAckTime",
duration);
json_object_string_add(
json_neighbor, "lsAckSendThread",
(event_is_scheduled(on->thread_send_lsack) ? "on"
json_object_string_add(json_neighbor, "lsAckSendThread",
(event_is_scheduled(on->thread_send_lsack)
? "on"
: "off"));
json_array = json_object_new_array();
for (ALL_LSDB(on->lsack_list, lsa, lsanext))
json_object_array_add(
json_array, json_object_new_string(lsa->name));
json_object_array_add(json_array,
json_object_new_string(lsa->name));
json_object_object_add(json_neighbor, "pendingLsaLsAck",
json_array);
@ -900,35 +1140,35 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
if (on->auth_present == true) {
json_object_string_add(json_neighbor, "authStatus",
"enabled");
json_object_int_add(
json_neighbor, "recvdHelloHigherSeqNo",
json_object_int_add(json_neighbor,
"recvdHelloHigherSeqNo",
on->seqnum_h[OSPF6_MESSAGE_TYPE_HELLO]);
json_object_int_add(
json_neighbor, "recvdHelloLowerSeqNo",
json_object_int_add(json_neighbor,
"recvdHelloLowerSeqNo",
on->seqnum_l[OSPF6_MESSAGE_TYPE_HELLO]);
json_object_int_add(
json_neighbor, "recvdDBDescHigherSeqNo",
json_object_int_add(json_neighbor,
"recvdDBDescHigherSeqNo",
on->seqnum_h[OSPF6_MESSAGE_TYPE_DBDESC]);
json_object_int_add(
json_neighbor, "recvdDBDescLowerSeqNo",
json_object_int_add(json_neighbor,
"recvdDBDescLowerSeqNo",
on->seqnum_l[OSPF6_MESSAGE_TYPE_DBDESC]);
json_object_int_add(
json_neighbor, "recvdLSReqHigherSeqNo",
json_object_int_add(json_neighbor,
"recvdLSReqHigherSeqNo",
on->seqnum_h[OSPF6_MESSAGE_TYPE_LSREQ]);
json_object_int_add(
json_neighbor, "recvdLSReqLowerSeqNo",
json_object_int_add(json_neighbor,
"recvdLSReqLowerSeqNo",
on->seqnum_l[OSPF6_MESSAGE_TYPE_LSREQ]);
json_object_int_add(
json_neighbor, "recvdLSUpdHigherSeqNo",
json_object_int_add(json_neighbor,
"recvdLSUpdHigherSeqNo",
on->seqnum_h[OSPF6_MESSAGE_TYPE_LSUPDATE]);
json_object_int_add(
json_neighbor, "recvdLSUpdLowerSeqNo",
json_object_int_add(json_neighbor,
"recvdLSUpdLowerSeqNo",
on->seqnum_l[OSPF6_MESSAGE_TYPE_LSUPDATE]);
json_object_int_add(
json_neighbor, "recvdLSAckHigherSeqNo",
json_object_int_add(json_neighbor,
"recvdLSAckHigherSeqNo",
on->seqnum_h[OSPF6_MESSAGE_TYPE_LSACK]);
json_object_int_add(
json_neighbor, "recvdLSAckLowerSeqNo",
json_object_int_add(json_neighbor,
"recvdLSAckLowerSeqNo",
on->seqnum_l[OSPF6_MESSAGE_TYPE_LSACK]);
} else
json_object_string_add(json_neighbor, "authStatus",
@ -951,8 +1191,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)
? "Initial "
: ""),
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT)
? "More "
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) ? "More "
: ""),
(CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)
? "Master"
@ -1000,8 +1239,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
timerclear(&res);
if (event_is_scheduled(on->thread_send_lsupdate))
timersub(&on->thread_send_lsupdate->u.sands, &now,
&res);
timersub(&on->thread_send_lsupdate->u.sands, &now, &res);
timerstring(&res, duration, sizeof(duration));
vty_out(vty,
" %d Pending LSAs for LSUpdate in Time %s [thread %s]\n",
@ -1141,8 +1379,7 @@ DEFUN(show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd,
static int ospf6_neighbor_show_common(struct vty *vty, int argc,
struct cmd_token **argv,
struct ospf6 *ospf6, int idx_ipv4,
bool uj)
struct ospf6 *ospf6, int idx_ipv4, bool uj)
{
struct ospf6_neighbor *on;
struct ospf6_interface *oi;
@ -1214,14 +1451,16 @@ void ospf6_neighbor_init(void)
{
install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd);
install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_one_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_p2xp_neigh_cost_cmd);
install_element(INTERFACE_NODE,
&ipv6_ospf6_p2xp_neigh_poll_interval_cmd);
}
DEFUN (debug_ospf6_neighbor,
debug_ospf6_neighbor_cmd,
DEFUN(debug_ospf6_neighbor, debug_ospf6_neighbor_cmd,
"debug ospf6 neighbor [<state|event>]",
DEBUG_STR
OSPF6_STR
"Debug OSPFv3 Neighbor\n"
DEBUG_STR OSPF6_STR "Debug OSPFv3 Neighbor\n"
"Debug OSPFv3 Neighbor State Change\n"
"Debug OSPFv3 Neighbor Event\n")
{
@ -1241,13 +1480,9 @@ DEFUN (debug_ospf6_neighbor,
}
DEFUN (no_debug_ospf6_neighbor,
no_debug_ospf6_neighbor_cmd,
DEFUN(no_debug_ospf6_neighbor, no_debug_ospf6_neighbor_cmd,
"no debug ospf6 neighbor [<state|event>]",
NO_STR
DEBUG_STR
OSPF6_STR
"Debug OSPFv3 Neighbor\n"
NO_STR DEBUG_STR OSPF6_STR "Debug OSPFv3 Neighbor\n"
"Debug OSPFv3 Neighbor State Change\n"
"Debug OSPFv3 Neighbor Event\n")
{
@ -1267,12 +1502,8 @@ DEFUN (no_debug_ospf6_neighbor,
}
DEFUN (no_debug_ospf6,
no_debug_ospf6_cmd,
"no debug ospf6",
NO_STR
DEBUG_STR
OSPF6_STR)
DEFUN(no_debug_ospf6, no_debug_ospf6_cmd, "no debug ospf6",
NO_STR DEBUG_STR OSPF6_STR)
{
unsigned int i;
@ -1287,12 +1518,11 @@ DEFUN (no_debug_ospf6,
ospf6_lsa_debug_set_all(false);
for (i = 0; i < 6; i++)
OSPF6_DEBUG_MESSAGE_OFF(i,
OSPF6_DEBUG_NEIGHBOR_STATE
| OSPF6_DEBUG_NEIGHBOR_EVENT);
OSPF6_DEBUG_MESSAGE_OFF(i, OSPF6_DEBUG_NEIGHBOR_STATE |
OSPF6_DEBUG_NEIGHBOR_EVENT);
OSPF6_DEBUG_NEIGHBOR_OFF(OSPF6_DEBUG_NEIGHBOR_STATE
| OSPF6_DEBUG_NEIGHBOR_EVENT);
OSPF6_DEBUG_NEIGHBOR_OFF(OSPF6_DEBUG_NEIGHBOR_STATE |
OSPF6_DEBUG_NEIGHBOR_EVENT);
OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_TABLE);
OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_INTRA);
OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_INTER);
@ -1316,6 +1546,25 @@ int config_write_ospf6_debug_neighbor(struct vty *vty)
return 0;
}
int config_write_ospf6_p2xp_neighbor(struct vty *vty, struct ospf6_interface *oi)
{
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
frr_each (ospf6_if_p2xp_neighcfgs, &oi->p2xp_neighs, p2xp_cfg) {
vty_out(vty, " ipv6 ospf6 neighbor %pI6\n", &p2xp_cfg->addr);
if (p2xp_cfg->poll_interval)
vty_out(vty,
" ipv6 ospf6 neighbor %pI6 poll-interval %u\n",
&p2xp_cfg->addr, p2xp_cfg->poll_interval);
if (p2xp_cfg->cfg_cost)
vty_out(vty, " ipv6 ospf6 neighbor %pI6 cost %u\n",
&p2xp_cfg->addr, p2xp_cfg->cost);
}
return 0;
}
void install_element_ospf6_debug_neighbor(void)
{
install_element(ENABLE_NODE, &debug_ospf6_neighbor_cmd);

View file

@ -6,8 +6,11 @@
#ifndef OSPF6_NEIGHBOR_H
#define OSPF6_NEIGHBOR_H
#include "typesafe.h"
#include "hook.h"
#include "ospf6_message.h"
/* Forward declaration(s). */
struct ospf6_area;
@ -52,6 +55,8 @@ struct ospf6_helper_info {
uint32_t rejected_reason;
};
struct ospf6_if_p2xp_neighcfg;
/* Neighbor structure */
struct ospf6_neighbor {
/* Neighbor Router ID String */
@ -60,6 +65,11 @@ struct ospf6_neighbor {
/* OSPFv3 Interface this neighbor belongs to */
struct ospf6_interface *ospf6_if;
/* P2P/P2MP config for this neighbor.
* can be NULL if not explicitly configured!
*/
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
/* Neighbor state */
uint8_t state;
@ -190,6 +200,14 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
struct ospf6_interface *oi);
void ospf6_neighbor_delete(struct ospf6_neighbor *on);
void ospf6_neighbor_lladdr_set(struct ospf6_neighbor *on,
const struct in6_addr *addr);
struct ospf6_if_p2xp_neighcfg *ospf6_if_p2xp_find(struct ospf6_interface *oi,
const struct in6_addr *addr);
void ospf6_if_p2xp_up(struct ospf6_interface *oi);
uint32_t ospf6_neighbor_cost(struct ospf6_neighbor *on);
/* Neighbor event */
extern void hello_received(struct event *thread);
extern void twoway_received(struct event *thread);
@ -205,6 +223,8 @@ extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on);
extern void ospf6_neighbor_init(void);
extern int config_write_ospf6_debug_neighbor(struct vty *vty);
extern int config_write_ospf6_p2xp_neighbor(struct vty *vty,
struct ospf6_interface *oi);
extern void install_element_ospf6_debug_neighbor(void);
DECLARE_HOOK(ospf6_neighbor_change,

View file

@ -540,6 +540,10 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb)
if (ra->path.area_id != rb->path.area_id)
return (ntohl(ra->path.area_id) - ntohl(rb->path.area_id));
if ((ra->prefix_options & OSPF6_PREFIX_OPTION_LA)
!= (rb->prefix_options & OSPF6_PREFIX_OPTION_LA))
return ra->prefix_options & OSPF6_PREFIX_OPTION_LA ? -1 : 1;
return 0;
}

View file

@ -1126,6 +1126,8 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length,
return SNMP_INTEGER(1);
else if (oi->type == OSPF_IFTYPE_POINTOPOINT)
return SNMP_INTEGER(3);
else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
return SNMP_INTEGER(5);
else
break; /* Unknown, don't put anything */
case OSPFv3IFADMINSTATUS:
@ -1367,6 +1369,7 @@ static int ospf6TrapIfStateChange(struct ospf6_interface *oi, int next_state,
/* Terminal state or regression */
if ((next_state != OSPF6_INTERFACE_POINTTOPOINT)
&& (next_state != OSPF6_INTERFACE_POINTTOMULTIPOINT)
&& (next_state != OSPF6_INTERFACE_DROTHER)
&& (next_state != OSPF6_INTERFACE_BDR)
&& (next_state != OSPF6_INTERFACE_DR) && (next_state >= prev_state))

View file

@ -83,8 +83,10 @@ clippy_scan += \
ospf6d/ospf6_lsa.c \
ospf6d/ospf6_gr_helper.c \
ospf6d/ospf6_gr.c \
ospf6d/ospf6_interface.c \
ospf6d/ospf6_nssa.c \
ospf6d/ospf6_route.c \
ospf6d/ospf6_neighbor.c \
# end
nodist_ospf6d_ospf6d_SOURCES = \

View file

@ -110,15 +110,18 @@ def test_wait_protocol_convergence():
def expect_neighbor_full(router, neighbor):
"Wait until OSPFv3 neighborship is full"
logger.info("waiting for OSPFv3 router '{}' neighborship with '{}'".format(router, neighbor))
logger.info(
"waiting for OSPFv3 router '{}' neighborship with '{}'".format(
router, neighbor
)
)
test_func = partial(
topotest.router_json_cmp,
tgen.gears[router],
"show ipv6 ospf6 neighbor json",
{"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
)
_, result = topotest.run_and_expect(test_func, None,
count=130, wait=1)
_, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
assertmsg = '"{}" convergence failure'.format(router)
assert result is None, assertmsg
@ -143,6 +146,7 @@ def test_wait_protocol_convergence():
expect_neighbor_full("r8", "10.254.254.5")
expect_neighbor_full("r9", "10.254.254.5")
def test_ecmp_inter_area():
"Test whether OSPFv3 ECMP nexthops are properly updated for inter-area routes after link down"
tgen = get_topogen()
@ -156,22 +160,28 @@ def test_ecmp_inter_area():
def expect_num_nexthops(router, expected_num_nexthops, count):
"Wait until number of nexthops for routes matches expectation"
logger.info("waiting for OSPFv3 router '{}' nexthops {}".format(router, expected_num_nexthops))
logger.info(
"waiting for OSPFv3 router '{}' nexthops {}".format(
router, expected_num_nexthops
)
)
test_func = partial(num_nexthops, router)
_, result = topotest.run_and_expect(test_func, expected_num_nexthops,
count=count, wait=3)
assert result == expected_num_nexthops, \
"'{}' wrong number of route nexthops".format(router)
_, result = topotest.run_and_expect(
test_func, expected_num_nexthops, count=count, wait=3
)
assert (
result == expected_num_nexthops
), "'{}' wrong number of route nexthops".format(router)
# Check nexthops pre link-down
expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3], 4)
expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3, 3, 3], 4)
logger.info("triggering R2-R4 link down")
tgen.gears["r2"].run("ip link set r2-eth1 down")
#tgen.mininet_cli()
# tgen.mininet_cli()
# Check nexthops post link-down
expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2], 8)
expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2, 2, 2], 8)
def teardown_module(_mod):

View file

@ -0,0 +1,137 @@
# OSPFv3 (IPv6) Topology Test (point-to-multipoint)
## Topology
-----\
SW1 - Stub Net 1 SW2 - Stub Net 2 \
fc00:1:1:1::/64 fc00:2:2:2::/64 \
\___________________/ \___________________/ |
| | |
| | |
| ::1 | ::2 |
+---------+---------+ +---------+---------+ |
| R1 | | R2 | |
| FRRouting | | FRRouting | |
| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
+---------+---------+ +---------+---------+ |
| ::1 | ::2 \
\______ ___________/ OSPFv3
\ / Area 0.0.0.0
\ / /
~~~~~~~~~~~~~~~~~~ |
~~ SW5 ~~ |
~~ Switch ~~ |
~~ fc00:A:A:A::/64 ~~ |
~~~~~~~~~~~~~~~~~~ |
| /---- |
| ::3 | SW3 - Stub Net 3 |
+---------+---------+ /-+ fc00:3:3:3::/64 |
| R3 | / | /
| FRRouting +--/ \---- /
| Rtr-ID: 10.0.0.3 | ::3 ___________/
+---------+---------+ \
| ::3 \
| \
~~~~~~~~~~~~~~~~~~ |
~~ SW6 ~~ |
~~ Switch ~~ |
~~ fc00:B:B:B::/64 ~~ \
~~~~~~~~~~~~~~~~~~ OSPFv3
| Area 0.0.0.1
| ::4 /
+---------+---------+ /---- |
| R4 | | SW4 - Stub Net 4 |
| FRRouting +------+ fc00:4:4:4::/64 |
| Rtr-ID: 10.0.0.4 | ::4 | /
+-------------------+ \---- /
-----/
## FRR Configuration
Full config as used is in r1 / r2 / r3 / r4 / r5 subdirectories
Simplified `R1` config (R1 is similar)
hostname r1
!
interface r1-stubnet
ipv6 address fc00:1:1:1::1/64
ipv6 ospf6 passive
ipv6 ospf6 area 0.0.0.0
!
interface r1-sw5
ipv6 address fc00:a:a:a::1/64
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.0
!
router ospf6
router-id 10.0.0.1
log-adjacency-changes detail
redistribute static
!
ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234
Simplified `R3` config
hostname r3
!
interface r3-stubnet
ipv6 address fc00:3:3:3::3/64
ipv6 ospf6 passive
ipv6 ospf6 area 0.0.0.0
!
interface r3-sw5
ipv6 address fc00:a:a:a::3/64
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 p2p-p2mp connected-prefixes include
!
interface r3-sw6
ipv6 address fc00:b:b:b::3/64
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.1
ipv6 ospf6 p2p-p2mp connected-prefixes include
!
router ospf6
router-id 10.0.0.3
log-adjacency-changes detail
redistribute static
!
ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234
## Tests executed
### Check if FRR is running
Test is executed by running
vtysh -c "show logging" | grep "Logging configuration for"
on each FRR router. This should return the logging information for all daemons registered
to Zebra and the list of running daemons is compared to the daemons started for this test (`zebra` and `ospf6d`)
### Check if OSPFv3 to converge
OSPFv3 is expected to converge on each view within 60s total time. Convergence is verified by executing (on each node)
vtysh -c "show ipv6 ospf neigh"
and checking for "Full" neighbor status in the output. An additional 15 seconds after the full converge is waited for
routes to populate before the following routing table checks are executed
### Check OSPFv3 Routing Tables
Routing table is verified by running
vtysh -c "show ipv6 route"
on each node and comparing the result to the stored example config (see `show_ipv6_route.ref` in r1 / r2 / r3 / r4 directories).
Link-Local addresses are masked out before the compare.
### Check Linux Kernel Routing Table
Linux Kernel IPv6 Routing table is verified on each FRR node with
ip -6 route
Tables are compared with reference routing table (see `ip_6_address.ref` in r1 / r2 / r3 / r4 directories).
Link-Local addresses are translated after getting collected on each node with interface name to make them consistent

View file

@ -0,0 +1,14 @@
fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
fc00:a:a:a::2 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::4 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,14 @@
fc00:1111:1111:1111::/64 via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
fc00:a:a:a::2 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::3 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::3 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::4 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,30 @@
hostname r1
log file ospf6d.log
!
! debug ospf6 message all
! debug ospf6 lsa unknown
! debug ospf6 zebra
! debug ospf6 interface
! debug ospf6 neighbor
! debug ospf6 route table
! debug ospf6 flooding
!
interface r1-sw5
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
ipv6 ospf6 p2p-p2mp connected-prefixes include
!
interface r1-stubnet
ipv6 ospf6 passive
ipv6 ospf6 area 0.0.0.0
!
router ospf6
ospf6 router-id 10.0.0.1
log-adjacency-changes detail
redistribute static
!
line vty
exec-timeout 0 0
!

View file

@ -0,0 +1,13 @@
O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, weight 1, XX:XX:XX
O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX
O>* fc00:a:a:a::2/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::4/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX

View file

@ -0,0 +1,20 @@
!
hostname r1
log file zebra.log
!
! debug zebra events
! debug zebra rib
!
interface r1-stubnet
ipv6 address fc00:1:1:1::1/64
!
interface r1-sw5
ipv6 address fc00:a:a:a::1/64
!
interface lo
!
ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234
!
!
line vty
!

View file

@ -0,0 +1,14 @@
fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:2222:2222:2222::/64 nhid XXXX via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium
fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium
fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium
fc00:a:a:a::1 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::4 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,14 @@
fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:2222:2222:2222::/64 via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium
fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium
fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium
fc00:a:a:a::1 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::3 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::3 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::4 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,30 @@
hostname r2
log file ospf6d.log
!
! debug ospf6 message all
! debug ospf6 lsa unknown
! debug ospf6 zebra
! debug ospf6 interface
! debug ospf6 neighbor
! debug ospf6 route table
! debug ospf6 flooding
!
interface r2-sw5
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
ipv6 ospf6 p2p-p2mp connected-prefixes include
!
interface r2-stubnet
ipv6 ospf6 passive
ipv6 ospf6 area 0.0.0.0
!
router ospf6
ospf6 router-id 10.0.0.2
log-adjacency-changes detail
redistribute static
!
line vty
exec-timeout 0 0
!

View file

@ -0,0 +1,13 @@
O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, weight 1, XX:XX:XX
O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX
O>* fc00:a:a:a::1/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::4/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX

View file

@ -0,0 +1,20 @@
!
hostname r2
log file zebra.log
!
! debug zebra events
! debug zebra rib
!
interface r2-stubnet
ipv6 address fc00:2:2:2::2/64
!
interface r2-sw5
ipv6 address fc00:a:a:a::2/64
!
interface lo
!
ipv6 route fc00:2222:2222:2222::/64 fc00:2:2:2::1234
!
!
line vty
!

View file

@ -0,0 +1,13 @@
fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:3333:3333:3333::/64 nhid XXXX via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium
fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium
fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
fc00:4:4:4::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium
fc00:a:a:a::1 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::2 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium
fc00:b:b:b::4 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,13 @@
fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:3333:3333:3333::/64 via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium
fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium
fc00:4444:4444:4444::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
fc00:4:4:4::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium
fc00:a:a:a::1 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:a:a:a::2 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium
fc00:b:b:b::4 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,37 @@
hostname r3
log file ospf6d.log
!
! debug ospf6 message all
! debug ospf6 lsa unknown
! debug ospf6 zebra
! debug ospf6 interface
! debug ospf6 neighbor
! debug ospf6 route table
! debug ospf6 flooding
!
interface r3-sw5
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
ipv6 ospf6 p2p-p2mp connected-prefixes include
!
interface r3-sw6
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.1
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
ipv6 ospf6 p2p-p2mp connected-prefixes include
!
interface r3-stubnet
ipv6 ospf6 passive
ipv6 ospf6 area 0.0.0.0
!
router ospf6
ospf6 router-id 10.0.0.3
log-adjacency-changes detail
redistribute static
!
line vty
exec-timeout 0 0
!

View file

@ -0,0 +1,12 @@
O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX:XX
O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX
O>* fc00:a:a:a::1/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
O>* fc00:a:a:a::2/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX
O>* fc00:b:b:b::4/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX

View file

@ -0,0 +1,23 @@
!
hostname r3
log file zebra.log
!
! debug zebra events
! debug zebra rib
!
interface r3-stubnet
ipv6 address fc00:3:3:3::3/64
!
interface r3-sw5
ipv6 address fc00:a:a:a::3/64
!
interface r3-sw6
ipv6 address fc00:b:b:b::3/64
!
interface lo
!
ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234
!
!
line vty
!

View file

@ -0,0 +1,14 @@
fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:1:1:1::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:2:2:2::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:4444:4444:4444::/64 nhid XXXX via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium
fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium
fc00:a:a:a::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::1 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::2 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::3 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium
fc00:b:b:b::3 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,14 @@
fc00:1111:1111:1111::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:1:1:1::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:2222:2222:2222::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:2:2:2::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:3333:3333:3333::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:3:3:3::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:4444:4444:4444::/64 via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium
fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium
fc00:a:a:a::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::1 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::2 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:a:a:a::3 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium
fc00:b:b:b::3 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium

View file

@ -0,0 +1,30 @@
hostname r4
log file ospf6d.log
!
! debug ospf6 message all
! debug ospf6 lsa unknown
! debug ospf6 zebra
! debug ospf6 interface
! debug ospf6 neighbor
! debug ospf6 route table
! debug ospf6 flooding
!
interface r4-sw6
ipv6 ospf6 network point-to-multipoint
ipv6 ospf6 area 0.0.0.1
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
ipv6 ospf6 p2p-p2mp connected-prefixes include
!
interface r4-stubnet
ipv6 ospf6 passive
ipv6 ospf6 area 0.0.0.1
!
router ospf6
ospf6 router-id 10.0.0.4
log-adjacency-changes detail
redistribute static
!
line vty
exec-timeout 0 0
!

View file

@ -0,0 +1,13 @@
O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, weight 1, XX:XX:XX
O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:a:a:a::1/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:a:a:a::2/128 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:a:a:a::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX
O>* fc00:b:b:b::3/128 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX

View file

@ -0,0 +1,20 @@
!
hostname r4
log file zebra.log
!
! debug zebra events
! debug zebra rib
!
interface r4-stubnet
ipv6 address fc00:4:4:4::4/64
!
interface r4-sw6
ipv6 address fc00:b:b:b::4/64
!
interface lo
!
ipv6 route fc00:4444:4444:4444::/64 fc00:4:4:4::1234
!
!
line vty
!

View file

@ -0,0 +1,392 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
#
# test_ospf6_point_to_multipoint.py
# Part of NetDEF Topology Tests
#
# Copyright (c) 2023 by
# Network Device Education Foundation, Inc. ("NetDEF")
#
r"""
test_ospf6_point_to_multipoint.py:
-----\
SW1 - Stub Net 1 SW2 - Stub Net 2 \
fc00:1:1:1::/64 fc00:2:2:2::/64 \
\___________________/ \___________________/ |
| | |
| | |
| ::1 | ::2 |
+---------+---------+ +---------+---------+ |
| R1 | | R2 | |
| FRRouting | | FRRouting | |
| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
+---------+---------+ +---------+---------+ |
| ::1 | ::2 \
\______ ___________/ OSPFv3
\ / Area 0.0.0.0
\ / /
~~~~~~~~~~~~~~~~~~ |
~~ SW5 ~~ |
~~ Switch ~~ |
~~ fc00:A:A:A::/64 ~~ |
~~~~~~~~~~~~~~~~~~ |
| /---- |
| ::3 | SW3 - Stub Net 3 |
+---------+---------+ /-+ fc00:3:3:3::/64 |
| R3 | / | /
| FRRouting +--/ \---- /
| Rtr-ID: 10.0.0.3 | ::3 ___________/
+---------+---------+ \
| ::3 \
| \
~~~~~~~~~~~~~~~~~~ |
~~ SW6 ~~ |
~~ Switch ~~ |
~~ fc00:B:B:B::/64 ~~ \
~~~~~~~~~~~~~~~~~~ OSPFv3
| Area 0.0.0.1
| ::4 /
+---------+---------+ /---- |
| R4 | | SW4 - Stub Net 4 |
| FRRouting +------+ fc00:4:4:4::/64 |
| Rtr-ID: 10.0.0.4 | ::4 | /
+-------------------+ \---- /
-----/
"""
import os
import re
import sys
import pytest
from functools import partial
# Save the Current Working Directory to find configuration files later.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
pytestmark = [pytest.mark.ospfd]
def build_topo(tgen):
# Create 4 routers
for routern in range(1, 5):
tgen.add_router("r{}".format(routern))
#
# Wire up the switches and routers
# Note that we specify the link names so we match the config files
#
# Create a empty network for router 1
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"], nodeif="r1-stubnet")
# Create a empty network for router 2
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["r2"], nodeif="r2-stubnet")
# Create a empty network for router 3
switch = tgen.add_switch("s3")
switch.add_link(tgen.gears["r3"], nodeif="r3-stubnet")
# Create a empty network for router 4
switch = tgen.add_switch("s4")
switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet")
# Interconnect routers 1, 2, and 3
switch = tgen.add_switch("s5")
switch.add_link(tgen.gears["r1"], nodeif="r1-sw5")
switch.add_link(tgen.gears["r2"], nodeif="r2-sw5")
switch.add_link(tgen.gears["r3"], nodeif="r3-sw5")
# Interconnect routers 3 and 4
switch = tgen.add_switch("s6")
switch.add_link(tgen.gears["r3"], nodeif="r3-sw6")
switch.add_link(tgen.gears["r4"], nodeif="r4-sw6")
#####################################################
##
## Tests starting
##
#####################################################
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
logger.info("** %s: Setup Topology" % mod.__name__)
logger.info("******************************************")
# For debugging after starting net, but before starting FRR,
# uncomment the next line
# tgen.mininet_cli()
router_list = tgen.routers()
for rname, router in router_list.items():
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
router.load_config(
TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
)
# Initialize all routers.
tgen.start_router()
# For debugging after starting FRR daemons, uncomment the next line
# tgen.mininet_cli()
def teardown_module(mod):
"Teardown the pytest environment"
tgen = get_topogen()
tgen.stop_topology()
def test_wait_protocol_convergence():
"Wait for OSPFv3 to converge"
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("waiting for protocols to converge")
def expect_neighbor_full(router, neighbor):
"Wait until OSPFv3 convergence."
logger.info("waiting OSPFv3 router '{}'".format(router))
test_func = partial(
topotest.router_json_cmp,
tgen.gears[router],
"show ipv6 ospf6 neighbor json",
{"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
)
_, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
assertmsg = '"{}" convergence failure'.format(router)
assert result is None, assertmsg
expect_neighbor_full("r1", "10.0.0.2")
expect_neighbor_full("r1", "10.0.0.3")
expect_neighbor_full("r2", "10.0.0.1")
expect_neighbor_full("r2", "10.0.0.3")
expect_neighbor_full("r3", "10.0.0.1")
expect_neighbor_full("r3", "10.0.0.2")
expect_neighbor_full("r3", "10.0.0.4")
expect_neighbor_full("r4", "10.0.0.3")
def compare_show_ipv6(rname, expected):
"""
Calls 'show ipv6 route' for router `rname` and compare the obtained
result with the expected output.
"""
tgen = get_topogen()
# Use the vtysh output, with some masking to make comparison easy
current = topotest.ip6_route_zebra(tgen.gears[rname])
# Use just the 'O'spf lines of the output
linearr = []
for line in current.splitlines():
if re.match("^O", line):
linearr.append(line)
current = "\n".join(linearr)
return topotest.difflines(
topotest.normalize_text(current),
topotest.normalize_text(expected),
title1="Current output",
title2="Expected output",
)
def test_ospfv3_routingTable():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip("skipped because of router(s) failure")
# For debugging, uncomment the next line
# tgen.mininet_cli()
# Verify OSPFv3 Routing Table
for router, rnode in tgen.routers().items():
logger.info('Waiting for router "%s" convergence', router)
# Load expected results from the command
reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router))
expected = open(reffile).read()
# Run test function until we get an result. Wait at most 60 seconds.
test_func = partial(compare_show_ipv6, router, expected)
result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
def test_linux_ipv6_kernel_routingTable():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip("skipped because of router(s) failure")
# Verify Linux Kernel Routing Table
logger.info("Verifying Linux IPv6 Kernel Routing Table")
failures = 0
# Get a list of all current link-local addresses first as they change for
# each run and we need to translate them
linklocals = []
for i in range(1, 5):
linklocals += tgen.net["r{}".format(i)].get_ipv6_linklocal()
# Now compare the routing tables (after substituting link-local addresses)
for i in range(1, 5):
# Actual output from router
actual = tgen.gears["r{}".format(i)].run("ip -6 route").rstrip()
if "nhid" in actual:
refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i))
else:
refTableFile = os.path.join(CWD, "r{}/ip_6_address.ref".format(i))
if os.path.isfile(refTableFile):
expected = open(refTableFile).read().rstrip()
# Fix newlines (make them all the same)
expected = ("\n".join(expected.splitlines())).splitlines(1)
# Mask out Link-Local mac addresses
for ll in linklocals:
actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0])
# Mask out protocol name or number
actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual)
actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual)
# Remove ff00::/8 routes (seen on some kernels - not from FRR)
actual = re.sub(r"ff00::/8.*", "", actual)
# Strip empty lines
actual = actual.lstrip()
actual = actual.rstrip()
actual = re.sub(r" +", " ", actual)
filtered_lines = []
for line in sorted(actual.splitlines()):
if line.startswith("fe80::/64 ") or line.startswith(
"unreachable fe80::/64 "
):
continue
filtered_lines.append(line)
actual = "\n".join(filtered_lines).splitlines(1)
# Print Actual table
# logger.info("Router r%s table" % i)
# for line in actual:
# logger.info(line.rstrip())
# Generate Diff
diff = topotest.get_textdiff(
actual,
expected,
title1="actual OSPFv3 IPv6 routing table",
title2="expected OSPFv3 IPv6 routing table",
)
# Empty string if it matches, otherwise diff contains unified diff
if diff:
sys.stderr.write(
"r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n"
% (i, diff)
)
failures += 1
else:
logger.info("r%s ok" % i)
assert failures == 0, (
"Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s"
% (i, diff)
)
else:
logger.error("r{} failed - no nhid ref file: {}".format(i, refTableFile))
assert False, (
"Linux Kernel IPv6 Routing Table verification failed for router r%s\n"
% (i)
)
def test_shutdown_check_stderr():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip("skipped because of router(s) failure")
if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
logger.info(
"SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
)
pytest.skip("Skipping test for Stderr output")
net = tgen.net
logger.info("\n\n** Verifying unexpected STDERR output from daemons")
logger.info("******************************************")
for i in range(1, 5):
net["r%s" % i].stopRouter()
log = net["r%s" % i].getStdErr("ospf6d")
if log:
logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log))
log = net["r%s" % i].getStdErr("zebra")
if log:
logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
def test_shutdown_check_memleak():
"Run the memory leak test and report results."
if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
logger.info(
"SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)"
)
pytest.skip("Skipping test for memory leaks")
tgen = get_topogen()
net = tgen.net
for i in range(1, 5):
net["r%s" % i].stopRouter()
net["r%s" % i].report_memory_leaks(
os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
)
if __name__ == "__main__":
# To suppress tracebacks, either use the following pytest call or
# add "--tb=no" to cli
# retval = pytest.main(["-s", "--tb=no"])
retval = pytest.main(["-s"])
sys.exit(retval)