summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_bfd.c2
-rw-r--r--bgpd/bgp_bmp.c6
-rw-r--r--bgpd/bgp_conditional_adv.c2
-rw-r--r--bgpd/bgp_fsm.c13
-rw-r--r--bgpd/bgp_mac.c2
-rw-r--r--bgpd/bgp_network.c2
-rw-r--r--bgpd/bgp_nexthop.c2
-rw-r--r--bgpd/bgp_packet.c20
-rw-r--r--bgpd/bgp_route.c10
-rw-r--r--bgpd/bgp_routemap.c2
-rw-r--r--bgpd/bgp_updgrp.c6
-rw-r--r--bgpd/bgp_vty.c29
-rw-r--r--bgpd/bgp_zebra.c2
-rw-r--r--bgpd/bgpd.c48
-rw-r--r--bgpd/bgpd.h6
-rw-r--r--doc/developer/conf.py7
-rw-r--r--doc/requirements.txt1
-rw-r--r--doc/user/conf.py17
-rw-r--r--doc/user/ospf6d.rst20
-rw-r--r--doc/user/ospf_fundamentals.rst2
-rw-r--r--doc/user/ospfd.rst34
-rw-r--r--docker/ubuntu20-ci/Dockerfile2
-rw-r--r--lib/command.c5
-rw-r--r--lib/routing_nb.h8
-rw-r--r--lib/zclient.c5
-rw-r--r--lib/zebra.h8
-rw-r--r--ospf6d/ospf6_abr.c100
-rw-r--r--ospf6d/ospf6_abr.h4
-rw-r--r--ospf6d/ospf6_area.c72
-rw-r--r--ospf6d/ospf6_area.h11
-rw-r--r--ospf6d/ospf6_asbr.c303
-rw-r--r--ospf6d/ospf6_asbr.h6
-rw-r--r--ospf6d/ospf6_flood.c44
-rw-r--r--ospf6d/ospf6_flood.h2
-rw-r--r--ospf6d/ospf6_interface.c20
-rw-r--r--ospf6d/ospf6_interface.h2
-rw-r--r--ospf6d/ospf6_intra.c44
-rw-r--r--ospf6d/ospf6_lsa.c6
-rw-r--r--ospf6d/ospf6_lsa.h9
-rw-r--r--ospf6d/ospf6_lsdb.c11
-rw-r--r--ospf6d/ospf6_message.c9
-rw-r--r--ospf6d/ospf6_nssa.c1396
-rw-r--r--ospf6d/ospf6_nssa.h76
-rw-r--r--ospf6d/ospf6_proto.h2
-rw-r--r--ospf6d/ospf6_route.c12
-rw-r--r--ospf6d/ospf6_spf.c164
-rw-r--r--ospf6d/ospf6_spf.h6
-rw-r--r--ospf6d/ospf6_top.c25
-rw-r--r--ospf6d/ospf6_top.h7
-rw-r--r--ospf6d/ospf6d.c5
-rw-r--r--ospf6d/subdir.am3
-rw-r--r--ospfd/ospf_flood.c7
-rw-r--r--ospfd/ospf_gr_helper.c176
-rw-r--r--ospfd/ospf_gr_helper.h6
-rw-r--r--ospfd/ospf_lsa.c16
-rw-r--r--ospfd/ospf_main.c1
-rw-r--r--ospfd/ospf_opaque.c13
-rw-r--r--ospfd/ospf_packet.c8
-rw-r--r--ospfd/ospf_spf.c14
-rw-r--r--ospfd/ospf_vty.c331
-rw-r--r--ospfd/ospfd.c19
-rw-r--r--staticd/static_debug.h8
-rw-r--r--staticd/static_nb.h8
-rw-r--r--staticd/static_nht.h9
-rw-r--r--staticd/static_routes.h9
-rw-r--r--staticd/static_vrf.h8
-rw-r--r--staticd/static_vty.h9
-rw-r--r--staticd/static_zebra.c10
-rw-r--r--staticd/static_zebra.h10
-rw-r--r--tests/lib/test_grpc.cpp979
-rw-r--r--tests/lib/test_grpc.py23
-rw-r--r--tests/subdir.am22
-rw-r--r--tests/topotests/config_timing/r1/staticd.conf1
-rw-r--r--tests/topotests/config_timing/r1/zebra.conf18
-rw-r--r--tests/topotests/config_timing/test_config_timing.py186
-rw-r--r--tests/topotests/ospf6_topo2/r2/ospf6d.conf6
-rw-r--r--tests/topotests/ospf6_topo2/r2/zebra.conf3
-rw-r--r--tests/topotests/ospf6_topo2/r4/ospf6d.conf9
-rw-r--r--tests/topotests/ospf6_topo2/r4/zebra.conf5
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.dot14
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.pngbin31523 -> 44388 bytes
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.py208
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json195
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py779
-rw-r--r--vtysh/vtysh.c2
-rw-r--r--zebra/zebra_gr.c14
-rw-r--r--zebra/zebra_mlag.c29
87 files changed, 5240 insertions, 515 deletions
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index 6004070e68..f6f2f5f6e4 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -72,7 +72,7 @@ static void bfd_session_status_update(struct bfd_session_params *bsp,
}
if (bss->state == BSS_UP && bss->previous_state != BSS_UP
- && peer->status != Established) {
+ && !peer_established(peer)) {
if (!BGP_PEER_START_SUPPRESSED(peer)) {
bgp_fsm_nht_update(peer, true);
BGP_EVENT_ADD(peer, BGP_Start);
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index abe97571c5..dbc35de80b 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -365,7 +365,7 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
#define BGP_BMP_MAX_PACKET_SIZE 1024
s = stream_new(BGP_MAX_PACKET_SIZE);
- if (peer->status == Established && !down) {
+ if (peer_established(peer) && !down) {
struct bmp_bgp_peer *bbpeer;
bmp_common_hdr(s, BMP_VERSION_3,
@@ -1146,7 +1146,7 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
zlog_info("bmp: skipping queued item for deleted peer");
goto out;
}
- if (peer->status != Established)
+ if (!peer_established(peer))
goto out;
bn = bgp_node_lookup(bmp->targets->bgp->rib[afi][safi], &bqe->p);
@@ -1323,7 +1323,7 @@ static int bmp_stats(struct thread *thread)
for (ALL_LIST_ELEMENTS_RO(bt->bgp->peer, node, peer)) {
size_t count = 0, count_pos, len;
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
s = stream_new(BGP_MAX_PACKET_SIZE);
diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c
index 6e80765f86..49bc38be66 100644
--- a/bgpd/bgp_conditional_adv.c
+++ b/bgpd/bgp_conditional_adv.c
@@ -195,7 +195,7 @@ static int bgp_conditional_adv_timer(struct thread *t)
if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
continue;
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
FOREACH_AFI_SAFI (afi, safi) {
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 6bcb31e652..4cc096d8e7 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -811,7 +811,7 @@ void bgp_start_routeadv(struct bgp *bgp)
sizeof(bgp->update_delay_peers_resume_time));
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
BGP_TIMER_OFF(peer->t_routeadv);
BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
@@ -1004,7 +1004,7 @@ static void bgp_maxmed_onstartup_begin(struct bgp *bgp)
static void bgp_maxmed_onstartup_process_status_change(struct peer *peer)
{
- if (peer->status == Established && !peer->bgp->established) {
+ if (peer_established(peer) && !peer->bgp->established) {
bgp_maxmed_onstartup_begin(peer->bgp);
}
}
@@ -1066,7 +1066,7 @@ static void bgp_update_delay_begin(struct bgp *bgp)
static void bgp_update_delay_process_status_change(struct peer *peer)
{
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (!peer->bgp->established++) {
bgp_update_delay_begin(peer->bgp);
zlog_info(
@@ -1102,7 +1102,7 @@ void bgp_fsm_change_status(struct peer *peer, int status)
if (status == Established)
bgp->established_peers++;
- else if ((peer->status == Established) && (status != Established))
+ else if ((peer_established(peer)) && (status != Established))
bgp->established_peers--;
if (bgp_debug_neighbor_events(peer)) {
@@ -1235,7 +1235,7 @@ int bgp_stop(struct peer *peer)
}
/* Increment Dropped count. */
- if (peer->status == Established) {
+ if (peer_established(peer)) {
peer->dropped++;
/* bgp log-neighbor-changes of neighbor Down */
@@ -1396,8 +1396,7 @@ int bgp_stop(struct peer *peer)
/* Received ORF prefix-filter */
peer->orf_plist[afi][safi] = NULL;
- if ((peer->status == OpenConfirm)
- || (peer->status == Established)) {
+ if ((peer->status == OpenConfirm) || (peer_established(peer))) {
/* ORF received prefix-filter pnt */
snprintf(orf_name, sizeof(orf_name), "%s.%d.%d",
peer->host, afi, safi);
diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c
index ec73ebb296..3d7bc08ac5 100644
--- a/bgpd/bgp_mac.c
+++ b/bgpd/bgp_mac.c
@@ -239,7 +239,7 @@ static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr)
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
continue;
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
if (CHECK_FLAG(peer->af_flags[afi][safi],
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 137f0a6b59..a51816116e 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -581,7 +581,7 @@ static int bgp_accept(struct thread *thread)
SET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
/* Make dummy peer until read Open packet. */
- if (peer1->status == Established
+ if (peer_established(peer1)
&& CHECK_FLAG(peer1->sflags, PEER_STATUS_NSF_MODE)) {
/* If we have an existing established connection with graceful
* restart
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index 46942a0bea..c417dda398 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -414,7 +414,7 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc)
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (peer->conf_if
&& (strcmp(peer->conf_if, ifc->ifp->name) == 0)
- && peer->status != Established
+ && !peer_established(peer)
&& !CHECK_FLAG(peer->flags,
PEER_FLAG_IFPEER_V6ONLY)) {
if (peer_active(peer))
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 62982881d8..3c01c3b486 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -232,7 +232,7 @@ void bgp_update_restarted_peers(struct peer *peer)
if (bgp_debug_neighbor_events(peer))
zlog_debug("Peer %s: Checking restarted", peer->host);
- if (peer->status == Established) {
+ if (peer_established(peer)) {
peer->update_delay_over = 1;
peer->bgp->restarted_peers++;
bgp_check_update_delay(peer->bgp);
@@ -255,7 +255,7 @@ void bgp_update_implicit_eors(struct peer *peer)
if (bgp_debug_neighbor_events(peer))
zlog_debug("Peer %s: Checking implicit EORs", peer->host);
- if (peer->status == Established) {
+ if (peer_established(peer)) {
peer->update_delay_over = 1;
peer->bgp->implicit_eors++;
bgp_check_update_delay(peer->bgp);
@@ -404,7 +404,7 @@ int bgp_generate_updgrp_packets(struct thread *thread)
* if peer is Established and updates are not on hold (as part of
* update-delay processing).
*/
- if (peer->status != Established)
+ if (!peer_established(peer))
return 0;
if ((peer->bgp->main_peers_update_hold)
@@ -1019,7 +1019,7 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id)
* states. Note that a peer GR is handled by closing the existing
* connection upon receipt of new one.
*/
- if (peer->status == Established || peer->status == Clearing) {
+ if (peer_established(peer) || peer->status == Clearing) {
bgp_notify_send(new, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
return -1;
@@ -1542,7 +1542,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
struct bgp_nlri nlris[NLRI_TYPE_MAX];
/* Status must be Established. */
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
flog_err(EC_BGP_INVALID_STATUS,
"%s [FSM] Update packet received under status %s",
peer->host,
@@ -1732,7 +1732,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
&& nlri_ret != BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW) {
flog_err(EC_BGP_UPDATE_RCV,
"%s [Error] Error parsing NLRI", peer->host);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_notify_send(
peer, BGP_NOTIFY_UPDATE_ERR,
i <= NLRI_WITHDRAW
@@ -1955,7 +1955,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
}
/* Status must be Established. */
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
flog_err(
EC_BGP_INVALID_STATUS,
"%s [Error] Route refresh packet received under status %s",
@@ -2258,7 +2258,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
bgp_set_stale_route(peer, afi, safi);
}
- if (peer->status == Established)
+ if (peer_established(peer))
thread_add_timer(bm->master,
bgp_refresh_stalepath_timer_expire,
paf, peer->bgp->stalepath_time,
@@ -2490,7 +2490,7 @@ int bgp_capability_receive(struct peer *peer, bgp_size_t size)
}
/* Status must be Established. */
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
flog_err(
EC_BGP_NO_CAP,
"%s [Error] Dynamic capability packet received under status %s",
@@ -2711,7 +2711,7 @@ int bgp_packet_process_error(struct thread *thread)
peer->host, peer->fd, code);
/* Closed connection or error on the socket */
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
|| CHECK_FLAG(peer->flags,
PEER_FLAG_GRACEFUL_RESTART_HELPER))
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index c61aedb560..8438621f68 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2347,7 +2347,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
if (BGP_PATH_HOLDDOWN(pi1))
continue;
if (pi1->peer != bgp->peer_self)
- if (pi1->peer->status != Established)
+ if (!peer_established(pi1->peer))
continue;
new_select = pi1;
@@ -2432,7 +2432,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
if (pi->peer && pi->peer != bgp->peer_self
&& !CHECK_FLAG(pi->peer->sflags, PEER_STATUS_NSF_WAIT))
- if (pi->peer->status != Established) {
+ if (!peer_established(pi->peer)) {
if (debug)
zlog_debug(
@@ -2502,7 +2502,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
if (pi->peer && pi->peer != bgp->peer_self
&& !CHECK_FLAG(pi->peer->sflags,
PEER_STATUS_NSF_WAIT))
- if (pi->peer->status != Established)
+ if (!peer_established(pi->peer))
continue;
if (!bgp_path_info_nexthop_cmp(pi, new_select)) {
@@ -4524,7 +4524,7 @@ static int bgp_announce_route_timer_expired(struct thread *t)
paf = THREAD_ARG(t);
peer = paf->peer;
- if (peer->status != Established)
+ if (!peer_established(peer))
return 0;
if (!peer->afc_nego[paf->afi][paf->safi])
@@ -4646,7 +4646,7 @@ void bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi)
struct bgp_dest *dest;
struct bgp_table *table;
- if (peer->status != Established)
+ if (!peer_established(peer))
return;
if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 7692bb7ffe..921a3e0dd9 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3452,7 +3452,7 @@ static void bgp_route_map_process_peer(const char *rmap_name,
&& (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) {
filter->map[RMAP_IN].map = map;
- if (route_update && peer->status == Established) {
+ if (route_update && peer_established(peer)) {
if (CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_SOFT_RECONFIG)) {
if (bgp_debug_update(peer, NULL, NULL, 1))
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index 2600eda42e..b8545188a4 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -1705,13 +1705,13 @@ int update_group_adjust_soloness(struct peer *peer, int set)
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
peer_lonesoul_or_not(peer, set);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
} else {
group = peer->group;
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
peer_lonesoul_or_not(peer, set);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
}
}
@@ -1901,7 +1901,7 @@ void subgroup_trigger_write(struct update_subgroup *subgrp)
* will trigger a write job on the I/O thread.
*/
SUBGRP_FOREACH_PEER (subgrp, paf)
- if (paf->peer->status == Established)
+ if (peer_established(paf->peer))
thread_add_timer_msec(
bm->master, bgp_generate_updgrp_packets,
paf->peer, 0,
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 2feba00806..a78afdd871 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -10425,7 +10425,7 @@ DEFUN (show_bgp_vrfs,
if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
continue;
peers_cfg++;
- if (peer->status == Established)
+ if (peer_established(peer))
peers_estb++;
}
@@ -10794,8 +10794,7 @@ static void bgp_show_peer_reset(struct vty * vty, struct peer *peer,
static inline bool bgp_has_peer_failed(struct peer *peer, afi_t afi,
safi_t safi)
{
- return ((peer->status != Established) ||
- !peer->afc_recv[afi][safi]);
+ return ((!peer_established(peer)) || !peer->afc_recv[afi][safi]);
}
static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp,
@@ -10822,7 +10821,7 @@ static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp,
peer->dropped);
peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN,
use_json, json_peer);
- if (peer->status == Established)
+ if (peer_established(peer))
json_object_string_add(json_peer, "lastResetDueTo",
"AFI/SAFI Not Negotiated");
else
@@ -10845,7 +10844,7 @@ static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp,
peer->dropped,
peer_uptime(peer->uptime, timebuf,
BGP_UPTIME_LEN, 0, NULL));
- if (peer->status == Established)
+ if (peer_established(peer))
vty_out(vty, " AFI/SAFI Not Negotiated\n");
else
bgp_show_peer_reset(vty, peer, NULL,
@@ -11472,7 +11471,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
BGP_UPTIME_LEN, 0,
NULL));
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (peer->afc_recv[afi][safi]) {
if (CHECK_FLAG(
bgp->flags,
@@ -11901,7 +11900,7 @@ static void bgp_show_neighnor_graceful_restart_rbit(struct vty *vty,
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)
&& (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV))
- && (p->status == Established)) {
+ && (peer_established(p))) {
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_BIT_RCV))
rbit_status = true;
@@ -11933,7 +11932,7 @@ static void bgp_show_neighbor_graceful_restart_remote_mode(struct vty *vty,
vty_out(vty, "\n Remote GR Mode: ");
if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV)
- && (peer->status == Established)) {
+ && (peer_established(peer))) {
if ((peer->nsf_af_count == 0)
&& !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
@@ -13121,7 +13120,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
json_neigh, "bgpState",
lookup_msg(bgp_status_msg, p->status, NULL));
- if (p->status == Established) {
+ if (peer_established(p)) {
time_t uptime;
uptime = bgp_clock();
@@ -13239,7 +13238,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
vty_out(vty, " BGP state = %s",
lookup_msg(bgp_status_msg, p->status, NULL));
- if (p->status == Established)
+ if (peer_established(p))
vty_out(vty, ", up for %8s",
peer_uptime(p->uptime, timebuf, BGP_UPTIME_LEN,
0, NULL));
@@ -13289,7 +13288,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
}
}
/* Capability. */
- if (p->status == Established) {
+ if (peer_established(p)) {
if (p->cap || p->afc_adv[AFI_IP][SAFI_UNICAST]
|| p->afc_recv[AFI_IP][SAFI_UNICAST]
|| p->afc_adv[AFI_IP][SAFI_MULTICAST]
@@ -14201,7 +14200,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
json_grace_send = json_object_new_object();
json_grace_recv = json_object_new_object();
- if ((p->status == Established)
+ if ((peer_established(p))
&& CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
FOREACH_AFI_SAFI (afi, safi) {
if (CHECK_FLAG(p->af_sflags[afi][safi],
@@ -14254,7 +14253,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
json_neigh, "gracefulRestartInfo", json_grace);
} else {
vty_out(vty, " Graceful restart information:\n");
- if ((p->status == Established)
+ if ((peer_established(p))
&& CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
vty_out(vty, " End-of-RIB send: ");
@@ -14653,7 +14652,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
if (use_json) {
json_object_int_add(json_neigh, "connectRetryTimer",
p->v_connect);
- if (p->status == Established && p->rtt)
+ if (peer_established(p) && p->rtt)
json_object_int_add(json_neigh, "estimatedRttInMsecs",
p->rtt);
if (p->t_start)
@@ -14690,7 +14689,7 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
} else {
vty_out(vty, "BGP Connect Retry Timer in Seconds: %d\n",
p->v_connect);
- if (p->status == Established && p->rtt)
+ if (peer_established(p) && p->rtt)
vty_out(vty, "Estimated round trip time: %d ms\n",
p->rtt);
if (p->t_start)
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index e3a795c6f1..c2c114d2c9 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -155,7 +155,7 @@ static void bgp_start_interface_nbrs(struct bgp *bgp, struct interface *ifp)
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (peer->conf_if && (strcmp(peer->conf_if, ifp->name) == 0)
- && peer->status != Established) {
+ && !peer_established(peer)) {
if (peer_active(peer))
BGP_EVENT_ADD(peer, BGP_Stop);
BGP_EVENT_ADD(peer, BGP_Start);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 33429d1d78..81375e37ed 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2153,7 +2153,7 @@ static int peer_activate_af(struct peer *peer, afi_t afi, safi_t safi)
if (!active && peer_active(peer)) {
bgp_timer_set(peer);
} else {
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
peer->afc_adv[afi][safi] = 1;
bgp_capability_send(peer, afi, safi,
@@ -2276,7 +2276,7 @@ static bool non_peergroup_deactivate_af(struct peer *peer, afi_t afi,
return true;
}
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
peer->afc_adv[afi][safi] = 0;
peer->afc_nego[afi][safi] = 0;
@@ -4111,7 +4111,7 @@ void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
return;
- if (peer->status != Established)
+ if (!peer_established(peer))
return;
if (type == peer_change_reset) {
@@ -4603,7 +4603,7 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
/* Execute action when peer is established. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
- && peer->status == Established) {
+ && peer_established(peer)) {
if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
bgp_clear_adj_in(peer, afi, safi);
else {
@@ -4656,7 +4656,7 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
set != member_invert);
/* Execute flag action on peer-group member. */
- if (member->status == Established) {
+ if (peer_established(member)) {
if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
bgp_clear_adj_in(member, afi, safi);
else {
@@ -5058,7 +5058,7 @@ int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
- if (peer->status == Established && peer->afc_nego[afi][safi]) {
+ if (peer_established(peer) && peer->afc_nego[afi][safi]) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
bgp_default_originate(peer, afi, safi, 0);
bgp_announce_route(peer, afi, safi);
@@ -5094,8 +5094,7 @@ int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
}
/* Update peer route announcements. */
- if (member->status == Established
- && member->afc_nego[afi][safi]) {
+ if (peer_established(member) && member->afc_nego[afi][safi]) {
update_group_adjust_peer(
peer_af_find(member, afi, safi));
bgp_default_originate(member, afi, safi, 0);
@@ -5135,7 +5134,7 @@ int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi)
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
- if (peer->status == Established && peer->afc_nego[afi][safi]) {
+ if (peer_established(peer) && peer->afc_nego[afi][safi]) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
bgp_default_originate(peer, afi, safi, 1);
bgp_announce_route(peer, afi, safi);
@@ -5166,7 +5165,7 @@ int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi)
member->default_rmap[afi][safi].map = NULL;
/* Update peer route announcements. */
- if (member->status == Established && member->afc_nego[afi][safi]) {
+ if (peer_established(member) && member->afc_nego[afi][safi]) {
update_group_adjust_peer(peer_af_find(member, afi, safi));
bgp_default_originate(member, afi, safi, 1);
bgp_announce_route(member, afi, safi);
@@ -5216,10 +5215,10 @@ static void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi,
{
if (outbound) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route(peer, afi, safi);
} else {
- if (peer->status != Established)
+ if (!peer_established(peer))
return;
if (CHECK_FLAG(peer->af_flags[afi][safi],
@@ -5415,7 +5414,7 @@ int peer_timers_connect_set(struct peer *peer, uint32_t connect)
/* Skip peer-group mechanics for regular peers. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
if (peer_active(peer))
BGP_EVENT_ADD(peer, BGP_Stop);
BGP_EVENT_ADD(peer, BGP_Start);
@@ -5436,7 +5435,7 @@ int peer_timers_connect_set(struct peer *peer, uint32_t connect)
member->connect = connect;
member->v_connect = connect;
- if (member->status != Established) {
+ if (!peer_established(member)) {
if (peer_active(member))
BGP_EVENT_ADD(member, BGP_Stop);
BGP_EVENT_ADD(member, BGP_Start);
@@ -5469,7 +5468,7 @@ int peer_timers_connect_unset(struct peer *peer)
/* Skip peer-group mechanics for regular peers. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
if (peer_active(peer))
BGP_EVENT_ADD(peer, BGP_Stop);
BGP_EVENT_ADD(peer, BGP_Start);
@@ -5490,7 +5489,7 @@ int peer_timers_connect_unset(struct peer *peer)
member->connect = 0;
member->v_connect = peer->bgp->default_connect_retry;
- if (member->status != Established) {
+ if (!peer_established(member)) {
if (peer_active(member))
BGP_EVENT_ADD(member, BGP_Stop);
BGP_EVENT_ADD(member, BGP_Start);
@@ -5517,7 +5516,7 @@ int peer_advertise_interval_set(struct peer *peer, uint32_t routeadv)
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
update_group_adjust_peer_afs(peer);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
/* Skip peer-group mechanics for regular peers. */
@@ -5540,7 +5539,7 @@ int peer_advertise_interval_set(struct peer *peer, uint32_t routeadv)
/* Update peer route announcements. */
update_group_adjust_peer_afs(member);
- if (member->status == Established)
+ if (peer_established(member))
bgp_announce_route_all(member);
}
@@ -5574,7 +5573,7 @@ int peer_advertise_interval_unset(struct peer *peer)
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
update_group_adjust_peer_afs(peer);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
/* Skip peer-group mechanics for regular peers. */
@@ -5599,7 +5598,7 @@ int peer_advertise_interval_unset(struct peer *peer)
/* Update peer route announcements. */
update_group_adjust_peer_afs(member);
- if (member->status == Established)
+ if (peer_established(member))
bgp_announce_route_all(member);
}
@@ -7144,7 +7143,7 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Re-check if peer violates maximum-prefix. */
- if ((peer->status == Established) && (peer->afc[afi][safi]))
+ if ((peer_established(peer)) && (peer->afc[afi][safi]))
bgp_maximum_prefix_overflow(peer, afi, safi, 1);
/* Skip peer-group mechanics for regular peers. */
@@ -7181,7 +7180,7 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
PEER_FLAG_MAX_PREFIX_WARNING);
/* Re-check if peer violates maximum-prefix. */
- if ((member->status == Established) && (member->afc[afi][safi]))
+ if ((peer_established(member)) && (member->afc[afi][safi]))
bgp_maximum_prefix_overflow(member, afi, safi, 1);
}
@@ -7466,7 +7465,7 @@ int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi,
{
struct peer_af *paf;
- if (peer->status != Established)
+ if (!peer_established(peer))
return 0;
if (!peer->afc[afi][safi])
@@ -7825,8 +7824,7 @@ void bgp_terminate(void)
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp))
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
- if (peer->status == Established
- || peer->status == OpenSent
+ if (peer_established(peer) || peer->status == OpenSent
|| peer->status == OpenConfirm)
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_PEER_UNCONFIG);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index ffac20c218..b18cd4ca77 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -2328,11 +2328,9 @@ static inline char *timestamp_string(time_t ts)
return ctime(&tbuf);
}
-static inline int peer_established(struct peer *peer)
+static inline bool peer_established(struct peer *peer)
{
- if (peer->status == Established)
- return 1;
- return 0;
+ return peer->status == Established;
}
static inline int peer_dynamic_neighbor(struct peer *peer)
diff --git a/doc/developer/conf.py b/doc/developer/conf.py
index 20265f4aad..8f282c0790 100644
--- a/doc/developer/conf.py
+++ b/doc/developer/conf.py
@@ -395,8 +395,11 @@ def setup(app):
# printfrr extensions
app.add_object_type("frrfmt", "frrfmt", parse_node=parse_frrfmt)
- # css overrides for HTML theme
- app.add_stylesheet("overrides.css")
+ if "add_css_file" in dir(app):
+ app.add_css_file("overrides.css")
+ else:
+ app.add_stylesheet("overrides.css")
+
# load Pygments lexer for FRR config syntax
#
# NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we
diff --git a/doc/requirements.txt b/doc/requirements.txt
new file mode 100644
index 0000000000..debc7f1889
--- /dev/null
+++ b/doc/requirements.txt
@@ -0,0 +1 @@
+sphinx==4.0.2
diff --git a/doc/user/conf.py b/doc/user/conf.py
index e0aec40443..6db58b07c3 100644
--- a/doc/user/conf.py
+++ b/doc/user/conf.py
@@ -386,16 +386,17 @@ def setup(app):
# node later on
app.add_object_type("clicmd", "clicmd", indextemplate="pair: %s; configuration command")
- # css overrides for HTML theme
- # Note sphinx version differences
- sver = vparse(sphinx.__version__)
-
- if sver < vparse("1.8.0"):
- app.add_stylesheet("overrides.css")
- app.add_javascript("overrides.js")
+ # I dont care how stupid this is
+ if "add_js_file" in dir(app):
+ app.add_js_file("overrides.js")
else:
+ app.add_javascript("overrides.js")
+
+ if "add_css_file" in dir(app):
app.add_css_file("overrides.css")
- app.add_js_file("overrides.js")
+ else:
+ app.add_stylesheet("overrides.css")
+
# load Pygments lexer for FRR config syntax
#
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index 2b478d334e..59a3af7645 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -83,7 +83,25 @@ OSPF6 router
OSPF6 area
==========
-Area support for OSPFv3 is not yet implemented.
+.. index:: [no] area A.B.C.D nssa
+.. clicmd:: [no] area A.B.C.D nssa
+
+NSSA Support in OSPFv3
+=======================
+
+The configuration of NSSA areas in OSPFv3 is supported using the CLI command
+area A.B.C.D nssa in ospf6 router configuration mode.
+The following functionalities are implemented as per RFC 3101:
+
+1. Advertising Type-7 LSA into NSSA area when external route is redistributed
+ into OSPFv3
+2. Processing Type-7 LSA received from neighbor and installing route in the
+ route table
+3. Support for NSSA ABR functionality which is generating Type-5 LSA when
+ backbone area is configured. Currently translation od TYpe-7 LSA to Type-5 LSA
+ is enabled by default.
+4. Support for NSSA Translator functionality when there are multiple NSSA ABR
+ in an area
.. _ospf6-interface:
diff --git a/doc/user/ospf_fundamentals.rst b/doc/user/ospf_fundamentals.rst
index 38c18e5526..c566059121 100644
--- a/doc/user/ospf_fundamentals.rst
+++ b/doc/user/ospf_fundamentals.rst
@@ -285,7 +285,7 @@ called `intra-area routes`.
Stub links may also be used as a way to describe links on which OSPF is
*not* spoken, known as `passive interfaces`, see
- :clicmd:`passive-interface INTERFACE`.
+ :clicmd:`ip ospf passive [A.B.C.D]`.
- Network LSA
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 211d5bd9f3..80ab279596 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -147,17 +147,11 @@ To start OSPF process you have to specify the OSPF router.
detail argument, all changes in adjacency status are shown. Without detail,
only changes to full or regressions are shown.
-.. clicmd:: passive-interface INTERFACE
+.. clicmd:: passive-interface default
-
- Do not speak OSPF interface on the
- given interface, but do advertise the interface as a stub link in the
- router-:abbr:`LSA (Link State Advertisement)` for this router. This
- allows one to advertise addresses on such connected interfaces without
- having to originate AS-External/Type-5 LSAs (which have global flooding
- scope) - as would occur if connected addresses were redistributed into
- OSPF (:ref:`redistribute-routes-to-ospf`). This is the only way to
- advertise non-OSPF links into stub areas.
+ Make all interfaces that belong to this router passive by default. For the
+ description of passive interface look at :clicmd:`ip ospf passive [A.B.C.D]`.
+ Per-interface configuration takes precedence over the default value.
.. clicmd:: timers throttle spf (0-600000) (0-600000) (0-600000)
@@ -617,6 +611,16 @@ Interfaces
Set number of seconds for InfTransDelay value. LSAs' age should be
incremented by this value when transmitting. The default value is 1 second.
+.. clicmd:: ip ospf passive [A.B.C.D]
+
+ Do not speak OSPF on the interface, but do advertise the interface as a stub
+ link in the router-:abbr:`LSA (Link State Advertisement)` for this router.
+ This allows one to advertise addresses on such connected interfaces without
+ having to originate AS-External/Type-5 LSAs (which have global flooding
+ scope) - as would occur if connected addresses were redistributed into
+ OSPF (:ref:`redistribute-routes-to-ospf`). This is the only way to
+ advertise non-OSPF links into stub areas.
+
.. clicmd:: ip ospf area (A.B.C.D|(0-4294967295))
@@ -653,12 +657,8 @@ Redistribution
NSSA areas and are not redistributed at all into Stub areas, where external
routes are not permitted.
- Note that for connected routes, one may instead use the `passive-interface`
- configuration.
-
-.. seealso::
-
- clicmd:`passive-interface INTERFACE`.
+ Note that for connected routes, one may instead use the
+ :clicmd:`ip ospf passive [A.B.C.D]` configuration.
.. clicmd:: default-information originate
@@ -1118,6 +1118,7 @@ of networks between the areas:
ip ospf message-digest-key 1 md5 ABCDEFGHIJK
!
interface ppp0
+ ip ospf passive
!
interface br0
ip ospf authentication message-digest
@@ -1126,7 +1127,6 @@ of networks between the areas:
router ospf
ospf router-id 192.168.0.1
redistribute connected
- passive interface ppp0
network 192.168.0.0/24 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
network 192.168.1.0/24 area 0.0.0.1
diff --git a/docker/ubuntu20-ci/Dockerfile b/docker/ubuntu20-ci/Dockerfile
index 71fde305e6..8b7557db1d 100644
--- a/docker/ubuntu20-ci/Dockerfile
+++ b/docker/ubuntu20-ci/Dockerfile
@@ -11,6 +11,7 @@ RUN apt update && \
install-info build-essential libsystemd-dev libsnmp-dev perl \
libcap-dev python2 libelf-dev \
sudo gdb curl iputils-ping time \
+ libgrpc++-dev libgrpc-dev protobuf-compiler-grpc \
mininet iproute2 iperf && \
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /tmp/get-pip.py && \
python2 /tmp/get-pip.py && \
@@ -57,6 +58,7 @@ RUN cd ~/frr && \
--sbindir=/usr/lib/frr \
--sysconfdir=/etc/frr \
--enable-vtysh \
+ --enable-grpc \
--enable-pimd \
--enable-sharpd \
--enable-multipath=64 \
diff --git a/lib/command.c b/lib/command.c
index 008f98a34c..7be54907ed 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -1058,6 +1058,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
return saved_ret;
if (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
/* This assumes all nodes above CONFIG_NODE are childs of
* CONFIG_NODE */
@@ -1071,6 +1072,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
ret = cmd_execute_command_real(vline, FILTER_RELAXED,
vty, cmd, 0);
if (ret == CMD_SUCCESS || ret == CMD_WARNING
+ || ret == CMD_ERR_AMBIGUOUS || ret == CMD_ERR_INCOMPLETE
|| ret == CMD_NOT_MY_INSTANCE
|| ret == CMD_WARNING_CONFIG_FAILED)
return ret;
@@ -1269,6 +1271,7 @@ int command_config_read_one_line(struct vty *vty,
while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
&& !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
&& ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
&& ret != CMD_NO_LEVEL_UP)
ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd,
@@ -1496,7 +1499,7 @@ static void permute(struct graph_node *start, struct vty *vty)
static void print_cmd(struct vty *vty, const char *cmd)
{
int i, j, len = strlen(cmd);
- char buf[len];
+ char buf[len + 1];
bool skip = false;
j = 0;
diff --git a/lib/routing_nb.h b/lib/routing_nb.h
index bdd12b262b..c185091a4b 100644
--- a/lib/routing_nb.h
+++ b/lib/routing_nb.h
@@ -1,6 +1,10 @@
#ifndef _FRR_ROUTING_NB_H_
#define _FRR_ROUTING_NB_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern const struct frr_yang_module_info frr_routing_info;
/* Mandatory callbacks. */
@@ -28,4 +32,8 @@ DECLARE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args));
void routing_control_plane_protocols_register_vrf_dependency(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _FRR_ROUTING_NB_H_ */
diff --git a/lib/zclient.c b/lib/zclient.c
index 10dda5ba0e..4a70881b57 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -3880,9 +3880,8 @@ static int zclient_read(struct thread *thread)
length -= ZEBRA_HEADER_SIZE;
if (zclient_debug)
- zlog_debug("zclient 0x%p command %s VRF %u",
- (void *)zclient, zserv_command_string(command),
- vrf_id);
+ zlog_debug("zclient %p command %s VRF %u", zclient,
+ zserv_command_string(command), vrf_id);
switch (command) {
case ZEBRA_CAPABILITIES:
diff --git a/lib/zebra.h b/lib/zebra.h
index 3b624117de..6a02dcb922 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -217,6 +217,10 @@
#define static_cast(l, r) (r)
#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifndef HAVE_STRLCAT
size_t strlcat(char *__restrict dest,
const char *__restrict src, size_t destsize);
@@ -379,4 +383,8 @@ typedef uint32_t route_tag_t;
#define ROUTE_TAG_MAX UINT32_MAX
#define ROUTE_TAG_PRI PRIu32
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_H */
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index 1af8aed1a9..a43118cb21 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -49,6 +49,7 @@
#include "ospf6_asbr.h"
#include "ospf6_abr.h"
#include "ospf6d.h"
+#include "ospf6_nssa.h"
unsigned char conf_debug_ospf6_abr;
@@ -57,14 +58,29 @@ int ospf6_is_router_abr(struct ospf6 *o)
struct listnode *node;
struct ospf6_area *oa;
int area_count = 0;
+ bool is_backbone = false;
- for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa))
+ for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s, area_id %pI4", __func__, &oa->area_id);
if (IS_AREA_ENABLED(oa))
area_count++;
- if (area_count > 1)
+ if (o->backbone == oa)
+ is_backbone = true;
+ }
+
+ if ((area_count > 1) && (is_backbone)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : set flag OSPF6_FLAG_ABR", __func__);
+ SET_FLAG(o->flag, OSPF6_FLAG_ABR);
return 1;
- return 0;
+ } else {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : reset flag OSPF6_FLAG_ABR", __func__);
+ UNSET_FLAG(o->flag, OSPF6_FLAG_ABR);
+ return 0;
+ }
}
static int ospf6_abr_nexthops_belong_to_area(struct ospf6_route *route,
@@ -156,37 +172,72 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
uint16_t type;
int is_debug = 0;
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : start area %s, route %pFX", __func__,
+ area->name, &route->prefix);
+
+ if (route->type == OSPF6_DEST_TYPE_ROUTER)
+ summary_table = area->summary_router;
+ else
+ summary_table = area->summary_prefix;
+
+ summary = ospf6_route_lookup(&route->prefix, summary_table);
+ if (summary) {
+ old = ospf6_lsdb_lookup(summary->path.origin.type,
+ summary->path.origin.id,
+ area->ospf6->router_id, area->lsdb);
+ /* Reset the OSPF6_LSA_UNAPPROVED flag */
+ if (old)
+ UNSET_FLAG(old->flag, OSPF6_LSA_UNAPPROVED);
+ }
+
/* Only destination type network, range or ASBR are considered */
if (route->type != OSPF6_DEST_TYPE_NETWORK
&& route->type != OSPF6_DEST_TYPE_RANGE
&& ((route->type != OSPF6_DEST_TYPE_ROUTER)
|| !CHECK_FLAG(route->path.router_bits, OSPF6_ROUTER_BIT_E))) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: Route type %d flag 0x%x is none of network, range nor ASBR, ignore",
+ __func__, route->type, route->path.router_bits);
return 0;
}
/* AS External routes are never considered */
if (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
|| route->path.type == OSPF6_PATH_TYPE_EXTERNAL2) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Path type is external, skip",
+ __func__);
return 0;
}
/* do not generate if the path's area is the same as target area */
if (route->path.area_id == area->area_id) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: The route is in the area itself, ignore",
+ __func__);
return 0;
}
/* do not generate if the nexthops belongs to the target area */
if (ospf6_abr_nexthops_belong_to_area(route, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: The route's nexthop is in the same area, ignore",
+ __func__);
return 0;
}
if (route->type == OSPF6_DEST_TYPE_ROUTER) {
if (ADV_ROUTER_IN_PREFIX(&route->prefix)
== area->ospf6->router_id) {
- zlog_debug(
- "%s: Skipping ASBR announcement for ABR (%pI4)",
- __func__,
- &ADV_ROUTER_IN_PREFIX(&route->prefix));
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: Skipping ASBR announcement for ABR (%pI4)",
+ __func__,
+ &ADV_ROUTER_IN_PREFIX(&route->prefix));
return 0;
}
}
@@ -195,12 +246,12 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER)) {
is_debug++;
- zlog_debug(
- "Originating summary in area %s for ASBR %pI4",
- area->name,
- &ADV_ROUTER_IN_PREFIX(&route->prefix));
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "Originating summary in area %s for ASBR %pI4",
+ area->name,
+ &ADV_ROUTER_IN_PREFIX(&route->prefix));
}
- summary_table = area->summary_router;
} else {
if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_ORIGINATE(INTER_PREFIX))
@@ -235,15 +286,8 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
zlog_debug(
"Originating summary in area %s for %pFX cost %u",
area->name, &route->prefix, route->path.cost);
- summary_table = area->summary_prefix;
}
- summary = ospf6_route_lookup(&route->prefix, summary_table);
- if (summary)
- old = ospf6_lsdb_lookup(summary->path.origin.type,
- summary->path.origin.id,
- area->ospf6->router_id, area->lsdb);
-
/* if this route has just removed, remove corresponding LSA */
if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) {
if (is_debug)
@@ -496,9 +540,15 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
/* create LSA */
lsa = ospf6_lsa_create(lsa_header);
+ /* Reset the unapproved flag */
+ UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+
/* Originate */
ospf6_lsa_originate_area(lsa, area);
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : finish area %s", __func__, area->name);
+
return 1;
}
@@ -566,8 +616,7 @@ ospf6_abr_range_summary_needs_update(struct ospf6_route *range, uint32_t cost)
return (redo_summary);
}
-static void ospf6_abr_range_update(struct ospf6_route *range,
- struct ospf6 *ospf6)
+void ospf6_abr_range_update(struct ospf6_route *range, struct ospf6 *ospf6)
{
uint32_t cost = 0;
struct listnode *node, *nnode;
@@ -579,6 +628,10 @@ static void ospf6_abr_range_update(struct ospf6_route *range,
/* update range's cost and active flag */
cost = ospf6_abr_range_compute_cost(range, ospf6);
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s: range %pFX, cost %d", __func__, &range->prefix,
+ cost);
+
/* Non-zero cost is a proxy for active longer prefixes in this range.
* If there are active routes covered by this range AND either the
* configured
@@ -595,6 +648,9 @@ static void ospf6_abr_range_update(struct ospf6_route *range,
*/
if (ospf6_abr_range_summary_needs_update(range, cost)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s: range %pFX update", __func__,
+ &range->prefix);
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa))
summary_orig +=
ospf6_abr_originate_summary_to_area(range, oa);
@@ -628,6 +684,8 @@ void ospf6_abr_originate_summary(struct ospf6_route *route, struct ospf6 *ospf6)
struct ospf6_area *oa;
struct ospf6_route *range = NULL;
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s: route %pFX", __func__, &route->prefix);
if (route->type == OSPF6_DEST_TYPE_NETWORK) {
oa = ospf6_area_lookup(route->path.area_id, ospf6);
diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h
index 6a912ac630..7c1ff4d389 100644
--- a/ospf6d/ospf6_abr.h
+++ b/ospf6d/ospf6_abr.h
@@ -56,6 +56,7 @@ struct ospf6_inter_router_lsa {
}
#define OSPF6_ABR_RANGE_CLEAR_COST(range) (range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC)
+#define IS_OSPF6_ABR(o) ((o)->flag & OSPF6_FLAG_ABR)
extern int ospf6_is_router_abr(struct ospf6 *o);
@@ -88,5 +89,8 @@ extern void ospf6_abr_old_path_update(struct ospf6_route *old_route,
struct ospf6_route_table *table);
extern void ospf6_abr_init(void);
extern void ospf6_abr_reexport(struct ospf6_area *oa);
+extern void ospf6_abr_range_update(struct ospf6_route *range,
+ struct ospf6 *ospf6);
+extern void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6);
#endif /*OSPF6_ABR_H*/
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
index d65e40279d..92934d3764 100644
--- a/ospf6d/ospf6_area.c
+++ b/ospf6d/ospf6_area.c
@@ -45,10 +45,25 @@
#include "ospf6_asbr.h"
#include "ospf6d.h"
#include "lib/json.h"
+#include "ospf6_nssa.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name");
+/* Utility functions. */
+int str2area_id(const char *str, struct in_addr *area_id, int *area_id_fmt)
+{
+ char *ep;
+
+ area_id->s_addr = htonl(strtoul(str, &ep, 10));
+ if (*ep && !inet_aton(str, area_id))
+ return -1;
+
+ *area_id_fmt = *ep ? OSPF6_AREA_FMT_DECIMAL : OSPF6_AREA_FMT_DOTTEDQUAD;
+
+ return 0;
+}
+
int ospf6_area_cmp(void *va, void *vb)
{
struct ospf6_area *oa = (struct ospf6_area *)va;
@@ -60,6 +75,7 @@ int ospf6_area_cmp(void *va, void *vb)
static void ospf6_area_lsdb_hook_add(struct ospf6_lsa *lsa)
{
switch (ntohs(lsa->header->type)) {
+
case OSPF6_LSTYPE_ROUTER:
case OSPF6_LSTYPE_NETWORK:
if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) {
@@ -82,6 +98,10 @@ static void ospf6_area_lsdb_hook_add(struct ospf6_lsa *lsa)
(struct ospf6_area *)lsa->lsdb->data);
break;
+ case OSPF6_LSTYPE_TYPE_7:
+ ospf6_asbr_lsa_add(lsa);
+ break;
+
default:
break;
}
@@ -111,7 +131,9 @@ static void ospf6_area_lsdb_hook_remove(struct ospf6_lsa *lsa)
ospf6_abr_examin_summary(lsa,
(struct ospf6_area *)lsa->lsdb->data);
break;
-
+ case OSPF6_LSTYPE_TYPE_7:
+ ospf6_asbr_lsa_remove(lsa, NULL);
+ break;
default:
break;
}
@@ -611,6 +633,8 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6)
else
vty_out(vty, " area %s stub\n", oa->name);
}
+ if (IS_AREA_NSSA(oa))
+ vty_out(vty, " area %s nssa\n", oa->name);
if (PREFIX_NAME_IN(oa))
vty_out(vty, " area %s filter-list prefix %s in\n",
oa->name, PREFIX_NAME_IN(oa));
@@ -1216,6 +1240,48 @@ DEFUN (no_ospf6_area_stub_no_summary,
return CMD_SUCCESS;
}
+DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd,
+ "area <A.B.C.D|(0-4294967295)> nssa",
+ "OSPF6 area parameters\n"
+ "OSPF6 area ID in IP address format\n"
+ "OSPF6 area ID as a decimal value\n"
+ "Configure OSPF6 area as nssa\n")
+{
+ int idx_ipv4_number = 1;
+ struct ospf6_area *area;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+ OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6);
+
+ if (!ospf6_area_nssa_set(ospf6, area)) {
+ vty_out(vty,
+ "First deconfigure all virtual link through this area\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
+ "no area <A.B.C.D|(0-4294967295)> nssa",
+ NO_STR
+ "OSPF6 area parameters\n"
+ "OSPF6 area ID in IP address format\n"
+ "OSPF6 area ID as a decimal value\n"
+ "Configure OSPF6 area as nssa\n")
+{
+ int idx_ipv4_number = 2;
+ struct ospf6_area *area;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+ OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6);
+
+ ospf6_area_nssa_unset(ospf6, area);
+
+ return CMD_SUCCESS;
+}
+
+
void ospf6_area_init(void)
{
install_element(VIEW_NODE, &show_ipv6_ospf6_spf_tree_cmd);
@@ -1237,6 +1303,10 @@ void ospf6_area_init(void)
install_element(OSPF6_NODE, &area_filter_list_cmd);
install_element(OSPF6_NODE, &no_area_filter_list_cmd);
+
+ /* "area nssa" commands. */
+ install_element(OSPF6_NODE, &ospf6_area_nssa_cmd);
+ install_element(OSPF6_NODE, &no_ospf6_area_nssa_cmd);
}
void ospf6_area_interface_delete(struct ospf6_interface *oi)
diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h
index 761fe75f73..fa761d732d 100644
--- a/ospf6d/ospf6_area.h
+++ b/ospf6d/ospf6_area.h
@@ -106,17 +106,27 @@ struct ospf6_area {
uint32_t full_nbrs; /* Fully adjacent neighbors. */
uint8_t intra_prefix_originate; /* Force intra_prefix lsa originate */
+ uint8_t NSSATranslatorRole; /* NSSA configured role */
+#define OSPF6_NSSA_ROLE_NEVER 0
+#define OSPF6_NSSA_ROLE_CANDIDATE 1
+#define OSPF6_NSSA_ROLE_ALWAYS 2
+ uint8_t NSSATranslatorState; /* NSSA operational role */
+#define OSPF6_NSSA_TRANSLATE_DISABLED 0
+#define OSPF6_NSSA_TRANSLATE_ENABLED 1
};
+#define OSPF6_AREA_DEFAULT 0x00
#define OSPF6_AREA_ENABLE 0x01
#define OSPF6_AREA_ACTIVE 0x02
#define OSPF6_AREA_TRANSIT 0x04 /* TransitCapability */
#define OSPF6_AREA_STUB 0x08
+#define OSPF6_AREA_NSSA 0x10
#define IS_AREA_ENABLED(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ENABLE))
#define IS_AREA_ACTIVE(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ACTIVE))
#define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT))
#define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB))
+#define IS_AREA_NSSA(oa) (CHECK_FLAG((oa)->flag, OSPF6_AREA_NSSA))
#define OSPF6_CMD_AREA_GET(str, oa, ospf6) \
{ \
@@ -153,5 +163,6 @@ extern void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6);
extern void ospf6_area_init(void);
struct ospf6_interface;
extern void ospf6_area_interface_delete(struct ospf6_interface *oi);
+int str2area_id(const char *str, struct in_addr *area_id, int *area_id_fmt);
#endif /* OSPF_AREA_H */
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 35f50898a6..e22e6560d0 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -50,6 +50,8 @@
#include "ospf6_intra.h"
#include "ospf6_flood.h"
#include "ospf6d.h"
+#include "ospf6_spf.h"
+#include "ospf6_nssa.h"
#include "lib/json.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info");
@@ -69,8 +71,8 @@ unsigned char conf_debug_ospf6_asbr = 0;
#define ZROUTE_NAME(x) zebra_route_string(x)
/* AS External LSA origination */
-static void ospf6_as_external_lsa_originate(struct ospf6_route *route,
- struct ospf6 *ospf6)
+void ospf6_as_external_lsa_originate(struct ospf6_route *route,
+ struct ospf6 *ospf6)
{
char buffer[OSPF6_MAX_LSASIZE];
struct ospf6_lsa_header *lsa_header;
@@ -175,6 +177,8 @@ int ospf6_orig_as_external_lsa(struct thread *thread)
if (oi->state == OSPF6_INTERFACE_DOWN)
return 0;
+ if (IS_AREA_NSSA(oi->area))
+ return 0;
type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
adv_router = oi->area->ospf6->router_id;
@@ -458,13 +462,48 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
}
}
+/* Check if the forwarding address is local address */
+static int ospf6_ase_forward_address_check(struct ospf6 *ospf6,
+ struct in6_addr *fwd_addr)
+{
+ struct listnode *anode, *node, *cnode;
+ struct ospf6_interface *oi;
+ struct ospf6_area *oa;
+ struct interface *ifp;
+ struct connected *c;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, oa)) {
+ for (ALL_LIST_ELEMENTS_RO(oa->if_list, node, oi)) {
+ if (!if_is_operative(oi->interface)
+ || oi->type == OSPF_IFTYPE_VIRTUALLINK)
+ continue;
+
+ ifp = oi->interface;
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+ if (IPV6_ADDR_SAME(&c->address->u.prefix6,
+ fwd_addr))
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
struct prefix asbr_id;
- struct ospf6_route *asbr_entry, *route, *old;
+ struct ospf6_route *asbr_entry, *route, *old = NULL;
struct ospf6_path *path;
struct ospf6 *ospf6;
+ int type;
+ struct ospf6_area *oa = NULL;
+ struct prefix fwd_addr;
+ ptrdiff_t offset;
+
+ type = ntohs(lsa->header->type);
+ oa = lsa->lsdb->data;
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
lsa->header);
@@ -495,11 +534,53 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
ospf6_linkstate_prefix(lsa->header->adv_router, htonl(0), &asbr_id);
asbr_entry = ospf6_route_lookup(&asbr_id, ospf6->brouter_table);
- if (asbr_entry == NULL
- || !CHECK_FLAG(asbr_entry->path.router_bits, OSPF6_ROUTER_BIT_E)) {
+ if (asbr_entry == NULL) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
zlog_debug("ASBR entry not found: %pFX", &asbr_id);
return;
+ } else {
+ /* The router advertising external LSA can be ASBR or ABR */
+ if (!CHECK_FLAG(asbr_entry->path.router_bits,
+ OSPF6_ROUTER_BIT_E)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "External bit reset ASBR route entry : %pFX",
+ &asbr_id);
+ return;
+ }
+ }
+
+ /* Check the forwarding address */
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) {
+ offset = sizeof(*external)
+ + OSPF6_PREFIX_SPACE(external->prefix.prefix_length);
+ memset(&fwd_addr, 0, sizeof(struct prefix));
+ fwd_addr.family = AF_INET6;
+ fwd_addr.prefixlen = IPV6_MAX_PREFIXLEN;
+ memcpy(&fwd_addr.u.prefix6, (caddr_t)external + offset,
+ sizeof(struct in6_addr));
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&fwd_addr.u.prefix6)) {
+ if (!ospf6_ase_forward_address_check(
+ ospf6, &fwd_addr.u.prefix6)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "Fwd address %pFX is local address",
+ &fwd_addr);
+ return;
+ }
+
+ /* Find the forwarding entry */
+ asbr_entry = ospf6_route_lookup_bestmatch(
+ &fwd_addr, ospf6->route_table);
+ if (asbr_entry == NULL) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "Fwd address not found: %pFX",
+ &fwd_addr);
+ return;
+ }
+ }
}
route = ospf6_route_create();
@@ -540,22 +621,38 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
zlog_debug(
- "%s: AS-External %u route add %pFX cost %u(%u) nh %u",
- __func__,
+ "%s: %s %u route add %pFX cost %u(%u) nh %u", __func__,
+ (type == OSPF6_LSTYPE_AS_EXTERNAL) ? "AS-External"
+ : "NSSA",
(route->path.type == OSPF6_PATH_TYPE_EXTERNAL1) ? 1 : 2,
&route->prefix, route->path.cost, route->path.u.cost_e2,
listcount(route->nh_list));
- old = ospf6_route_lookup(&route->prefix, ospf6->route_table);
+ if (type == OSPF6_LSTYPE_AS_EXTERNAL)
+ old = ospf6_route_lookup(&route->prefix, ospf6->route_table);
+ else if (type == OSPF6_LSTYPE_TYPE_7)
+ old = ospf6_route_lookup(&route->prefix, oa->route_table);
if (!old) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("%s: Adding new route", __func__);
/* Add the new route to ospf6 instance route table. */
- ospf6_route_add(route, ospf6->route_table);
+ if (type == OSPF6_LSTYPE_AS_EXTERNAL)
+ ospf6_route_add(route, ospf6->route_table);
+ /* Add the route to the area route table */
+ else if (type == OSPF6_LSTYPE_TYPE_7) {
+ ospf6_route_add(route, oa->route_table);
+ }
} else {
/* RFC 2328 16.4 (6)
* ECMP: Keep new equal preference path in current
* route's path list, update zebra with new effective
* list along with addition of ECMP path.
*/
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("%s : old route %pFX cost %u(%u) nh %u",
+ __func__, &route->prefix, route->path.cost,
+ route->path.u.cost_e2,
+ listcount(route->nh_list));
ospf6_asbr_update_route_ecmp_path(old, route, ospf6);
}
}
@@ -568,24 +665,42 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_route *route, *nroute, *route_to_del;
struct ospf6_area *oa = NULL;
struct ospf6 *ospf6;
+ int type;
+ bool debug = false;
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
lsa->header);
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
- zlog_debug("Withdraw AS-External route for %s", lsa->name);
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || (IS_OSPF6_DEBUG_NSSA))
+ debug = true;
ospf6 = ospf6_get_by_lsdb(lsa);
- if (ospf6_is_router_abr(ospf6))
- oa = ospf6->backbone;
- else
- oa = listnode_head(ospf6->area_list);
+ type = ntohs(lsa->header->type);
- if (oa == NULL)
+ if (type == OSPF6_LSTYPE_TYPE_7) {
+ if (debug)
+ zlog_debug("%s: Withdraw Type 7 route for %s",
+ __func__, lsa->name);
+ oa = lsa->lsdb->data;
+ } else {
+ if (debug)
+ zlog_debug("%s: Withdraw AS-External route for %s",
+ __func__, lsa->name);
+
+ if (ospf6_is_router_abr(ospf6))
+ oa = ospf6->backbone;
+ else
+ oa = listgetdata(listhead(ospf6->area_list));
+ }
+
+ if (oa == NULL) {
+ if (debug)
+ zlog_debug("%s: Invalid area", __func__);
return;
+ }
if (lsa->header->adv_router == oa->ospf6->router_id) {
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ if (debug)
zlog_debug("Ignore self-originated AS-External-LSA");
return;
}
@@ -623,22 +738,23 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
prefix.prefixlen = external->prefix.prefix_length;
ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix);
- route = ospf6_route_lookup(&prefix, oa->ospf6->route_table);
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ route = ospf6_route_lookup(&prefix, oa->route_table);
+ else
+ route = ospf6_route_lookup(&prefix, oa->ospf6->route_table);
+
if (route == NULL) {
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug)
zlog_debug("AS-External route %pFX not found", &prefix);
- }
-
ospf6_route_delete(route_to_del);
return;
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug)
zlog_debug(
"%s: Current route %pFX cost %u e2 %u, route to del cost %u e2 %u",
__func__, &prefix, route->path.cost, route->path.u.cost_e2,
route_to_del->path.cost, route_to_del->path.u.cost_e2);
- }
for (ospf6_route_lock(route);
route && ospf6_route_is_prefix(&prefix, route); route = nroute) {
@@ -690,7 +806,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
continue;
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: route %pFX path found with cost %u nh %u to remove.",
__func__, &prefix, route->path.cost,
@@ -731,7 +847,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
o_path->nh_list);
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: AS-External %u route %pFX update paths %u nh %u",
__func__,
@@ -774,8 +890,13 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
h_path->origin.adv_router;
}
} else {
- ospf6_route_remove(
- route, oa->ospf6->route_table);
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ ospf6_route_remove(
+ route, oa->route_table);
+ else
+ ospf6_route_remove(
+ route,
+ oa->ospf6->route_table);
}
}
continue;
@@ -790,7 +911,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
|| (route->path.cost != route_to_del->path.cost)
|| (route->path.u.cost_e2
!= route_to_del->path.u.cost_e2))) {
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: route %pFX to delete is not same, cost %u del cost %u. skip",
__func__, &prefix, route->path.cost,
@@ -805,7 +926,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
|| (route->path.origin.id != lsa->header->id))
continue;
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: AS-External %u route remove %pFX cost %u(%u) nh %u",
__func__,
@@ -815,7 +936,10 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
&route->prefix, route->path.cost, route->path.u.cost_e2,
listcount(route->nh_list));
}
- ospf6_route_remove(route, oa->ospf6->route_table);
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ ospf6_route_remove(route, oa->route_table);
+ else
+ ospf6_route_remove(route, oa->ospf6->route_table);
}
if (route != NULL)
ospf6_route_unlock(route);
@@ -937,7 +1061,7 @@ void ospf6_asbr_distribute_list_update(int type, struct ospf6 *ospf6)
&ospf6->t_distribute_update);
}
-static void ospf6_asbr_routemap_update(const char *mapname)
+void ospf6_asbr_routemap_update(const char *mapname)
{
int type;
struct listnode *node, *nnode;
@@ -1015,7 +1139,7 @@ static void ospf6_asbr_routemap_event(const char *name)
int ospf6_asbr_is_asbr(struct ospf6 *o)
{
- return o->external_table->count;
+ return (o->external_table->count || IS_OSPF6_ASBR(o));
}
struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type,
@@ -1072,9 +1196,50 @@ static void ospf6_redist_del(struct ospf6 *ospf6, struct ospf6_redist *red,
}
}
+/*Set the status of the ospf instance to ASBR based on the status parameter,
+ * rechedule SPF calculation, originate router LSA*/
+void ospf6_asbr_status_update(struct ospf6 *ospf6, int status)
+{
+ struct listnode *lnode, *lnnode;
+ struct ospf6_area *oa;
+
+ zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status);
+
+ if (status) {
+ if (IS_OSPF6_ASBR(ospf6)) {
+ zlog_info("ASBR[%s:Status:%d]: Already ASBR",
+ ospf6->name, status);
+ return;
+ }
+ SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
+ } else {
+ if (!IS_OSPF6_ASBR(ospf6)) {
+ zlog_info("ASBR[%s:Status:%d]: Already non ASBR",
+ ospf6->name, status);
+ return;
+ }
+ UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
+ }
+
+ /* Transition from/to status ASBR, schedule timer. */
+ ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE);
+
+ /* Reoriginate router LSA for all areas */
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa))
+ OSPF6_ROUTER_LSA_SCHEDULE(oa);
+}
+
static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id)
{
+ struct ospf6 *ospf6 = NULL;
ospf6_zebra_redistribute(type, vrf_id);
+
+ ospf6 = ospf6_lookup_by_vrf_id(vrf_id);
+
+ if (!ospf6)
+ return;
+
+ ospf6_asbr_status_update(ospf6, ++ospf6->redist_count);
}
static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
@@ -1096,6 +1261,8 @@ static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
}
ospf6_asbr_routemap_unset(red);
+ zlog_debug("%s: redist_count %d", __func__, ospf6->redist_count);
+ ospf6_asbr_status_update(ospf6, --ospf6->redist_count);
}
/* When an area is unstubified, flood all the external LSAs in the area */
@@ -1139,35 +1306,6 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa)
}
}
-/* Update ASBR status. */
-static void ospf6_asbr_status_update(struct ospf6 *ospf6, uint8_t status)
-{
- struct listnode *lnode, *lnnode;
- struct ospf6_area *oa;
-
- zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status);
-
- if (status) {
- if (IS_OSPF6_ASBR(ospf6)) {
- zlog_info("ASBR[%s:Status:%d]: Already ASBR",
- ospf6->name, status);
- return;
- }
- SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
- } else {
- if (!IS_OSPF6_ASBR(ospf6)) {
- zlog_info("ASBR[%s:Status:%d]: Already non ASBR",
- ospf6->name, status);
- return;
- }
- UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
- }
-
- ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE);
- for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa))
- OSPF6_ROUTER_LSA_SCHEDULE(oa);
-}
-
void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
struct prefix *prefix,
unsigned int nexthop_num,
@@ -1175,6 +1313,8 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
struct ospf6 *ospf6)
{
route_map_result_t ret;
+ struct listnode *lnode;
+ struct ospf6_area *oa;
struct ospf6_route troute;
struct ospf6_external_info tinfo;
struct ospf6_route *route, *match;
@@ -1276,6 +1416,11 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
match->path.origin.id = htonl(info->id);
ospf6_as_external_lsa_originate(match, ospf6);
ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ if (IS_AREA_NSSA(oa))
+ ospf6_nssa_lsa_originate(match, oa);
+ }
+
return;
}
@@ -1334,13 +1479,19 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
route->path.origin.id = htonl(info->id);
ospf6_as_external_lsa_originate(route, ospf6);
ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ if (IS_AREA_NSSA(oa))
+ ospf6_nssa_lsa_originate(route, oa);
+ }
}
void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex,
struct prefix *prefix, struct ospf6 *ospf6)
{
+ struct ospf6_area *oa;
struct ospf6_route *match;
struct ospf6_external_info *info = NULL;
+ struct listnode *lnode;
struct route_node *node;
struct ospf6_lsa *lsa;
struct prefix prefix_id;
@@ -1369,8 +1520,27 @@ void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex,
lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
htonl(info->id), ospf6->router_id, ospf6->lsdb);
- if (lsa)
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_ASBR) {
+ zlog_debug("withdraw type 5 LSA for route %pFX",
+ prefix);
+ }
ospf6_lsa_purge(lsa);
+ }
+
+ /* Delete the NSSA LSA */
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7),
+ htonl(info->id), ospf6->router_id,
+ oa->lsdb);
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_ASBR) {
+ zlog_debug("withdraw type 7 LSA for route %pFX",
+ prefix);
+ }
+ ospf6_lsa_purge(lsa);
+ }
+ }
/* remove binding in external_id_table */
prefix_id.family = AF_INET;
@@ -2278,11 +2448,20 @@ static struct ospf6_lsa_handler as_external_handler = {
.lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str,
.lh_debug = 0};
+static struct ospf6_lsa_handler nssa_external_handler = {
+ .lh_type = OSPF6_LSTYPE_TYPE_7,
+ .lh_name = "NSSA",
+ .lh_short_name = "Type7",
+ .lh_show = ospf6_as_external_lsa_show,
+ .lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str,
+ .lh_debug = 0};
+
void ospf6_asbr_init(void)
{
ospf6_routemap_init();
ospf6_install_lsa_handler(&as_external_handler);
+ ospf6_install_lsa_handler(&nssa_external_handler);
install_element(VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd);
diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h
index 4774ac435a..418c157f36 100644
--- a/ospf6d/ospf6_asbr.h
+++ b/ospf6d/ospf6_asbr.h
@@ -71,6 +71,7 @@ struct ospf6_as_external_lsa {
}
extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa);
+
extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_route *asbr_entry);
extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry,
@@ -106,4 +107,9 @@ extern void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
extern void ospf6_asbr_distribute_list_update(int type, struct ospf6 *ospf6);
struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type,
unsigned short instance);
+extern void ospf6_asbr_routemap_update(const char *mapname);
+extern void ospf6_as_external_lsa_originate(struct ospf6_route *route,
+ struct ospf6 *ospf6);
+extern void ospf6_asbr_status_update(struct ospf6 *ospf6, int status);
+
#endif /* OSPF6_ASBR_H */
diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c
index 76e81aab7b..ac16e53d63 100644
--- a/ospf6d/ospf6_flood.c
+++ b/ospf6d/ospf6_flood.c
@@ -40,6 +40,7 @@
#include "ospf6_neighbor.h"
#include "ospf6_flood.h"
+#include "ospf6_nssa.h"
unsigned char conf_debug_ospf6_flooding;
@@ -213,12 +214,19 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa)
{
struct timeval now;
struct ospf6_lsa *old;
+ struct ospf6_area *area = NULL;
/* Remove the old instance from all neighbors' Link state
retransmission list (RFC2328 13.2 last paragraph) */
old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id,
lsa->header->adv_router, lsa->lsdb);
if (old) {
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : old LSA %s", __func__,
+ lsa->name);
+ lsa->external_lsa_id = old->external_lsa_id;
+ }
THREAD_OFF(old->expire);
THREAD_OFF(old->refresh);
ospf6_flood_clear(old);
@@ -265,6 +273,22 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa)
lsa->installed = now;
ospf6_lsdb_add(lsa, lsa->lsdb);
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) {
+ area = OSPF6_AREA(lsa->lsdb->data);
+ ospf6_translated_nssa_refresh(area, lsa, NULL);
+ ospf6_schedule_abr_task(area->ospf6);
+ }
+
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_ROUTER) {
+ area = OSPF6_AREA(lsa->lsdb->data);
+ if (old == NULL) {
+ if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)
+ || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type))
+ zlog_debug("%s: New router LSA %s", __func__,
+ lsa->name);
+ ospf6_abr_nssa_check_status(area->ospf6);
+ }
+ }
return;
}
@@ -370,7 +394,8 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa,
continue;
}
- if (oi->area->ospf6->inst_shutdown) {
+ if ((oi->area->ospf6->inst_shutdown)
+ || CHECK_FLAG(lsa->flag, OSPF6_LSA_FLUSH)) {
if (is_debug)
zlog_debug(
"%s: Send LSA %s (age %d) update now",
@@ -486,7 +511,12 @@ static void ospf6_flood_process(struct ospf6_neighbor *from,
continue;
if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
- && IS_AREA_STUB(oa))
+ && (IS_AREA_STUB(oa) || IS_AREA_NSSA(oa)))
+ continue;
+
+ /* Check for NSSA LSA */
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7
+ && !IS_AREA_NSSA(oa) && !OSPF6_LSA_IS_MAXAGE(lsa))
continue;
ospf6_flood_area(from, lsa, oa);
@@ -526,7 +556,7 @@ static void ospf6_flood_clear_interface(struct ospf6_lsa *lsa,
}
}
-static void ospf6_flood_clear_area(struct ospf6_lsa *lsa, struct ospf6_area *oa)
+void ospf6_flood_clear_area(struct ospf6_lsa *lsa, struct ospf6_area *oa)
{
struct listnode *node, *nnode;
struct ospf6_interface *oi;
@@ -555,7 +585,11 @@ static void ospf6_flood_clear_process(struct ospf6_lsa *lsa,
continue;
if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
- && IS_AREA_STUB(oa))
+ && (IS_AREA_STUB(oa) || (IS_AREA_NSSA(oa))))
+ continue;
+ /* Check for NSSA LSA */
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7
+ && !IS_AREA_NSSA(oa))
continue;
ospf6_flood_clear_area(lsa, oa);
@@ -567,6 +601,8 @@ void ospf6_flood_clear(struct ospf6_lsa *lsa)
struct ospf6 *ospf6;
ospf6 = ospf6_get_by_lsdb(lsa);
+ if (ospf6 == NULL)
+ return;
ospf6_flood_clear_process(lsa, ospf6);
}
diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h
index 6931024fff..5515a1c3fe 100644
--- a/ospf6d/ospf6_flood.h
+++ b/ospf6d/ospf6_flood.h
@@ -67,4 +67,6 @@ extern void ospf6_flood_interface(struct ospf6_neighbor *from,
extern int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
struct ospf6_lsa *lsa);
+extern void ospf6_flood_clear_area(struct ospf6_lsa *lsa,
+ struct ospf6_area *oa);
#endif /* OSPF6_FLOOD_H */
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index f037ea1f4d..b71d884fdc 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -1197,6 +1197,26 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
return 0;
}
+/* Find the global address to be used as a forwarding address in NSSA LSA.*/
+struct in6_addr *ospf6_interface_get_global_address(struct interface *ifp)
+{
+ struct listnode *n;
+ struct connected *c;
+ struct in6_addr *l = (struct in6_addr *)NULL;
+
+ /* for each connected address */
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) {
+ /* if family not AF_INET6, ignore */
+ if (c->address->family != AF_INET6)
+ continue;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6))
+ l = &c->address->u.prefix6;
+ }
+ return l;
+}
+
+
static int show_ospf6_interface_common(struct vty *vty, vrf_id_t vrf_id,
int argc, struct cmd_token **argv,
int idx_ifname, int intf_idx,
diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h
index 48b2cbff74..fb1b947cf8 100644
--- a/ospf6d/ospf6_interface.h
+++ b/ospf6d/ospf6_interface.h
@@ -189,6 +189,8 @@ extern void ospf6_interface_if_add(struct interface *);
extern void ospf6_interface_state_update(struct interface *);
extern void ospf6_interface_connected_route_update(struct interface *);
extern void ospf6_interface_connected_route_add(struct connected *);
+extern struct in6_addr *
+ospf6_interface_get_global_address(struct interface *ifp);
/* interface event */
extern int interface_up(struct thread *);
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index 12d11d45c1..524edd3fae 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -193,6 +193,17 @@ static void ospf6_router_lsa_options_set(struct ospf6_area *oa,
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_E);
}
+ /* If the router is ASBR and the area-type is NSSA set the
+ * translate bit in router LSA.
+ */
+ if (IS_AREA_NSSA(oa)
+ && (ospf6_asbr_is_asbr(oa->ospf6) || IS_OSPF6_ABR(oa->ospf6))) {
+ if (oa->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS)
+ SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT);
+ } else {
+ UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT);
+ }
+
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_V);
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_W);
}
@@ -2055,6 +2066,7 @@ void ospf6_intra_route_calculation(struct ospf6_area *oa)
struct ospf6_lsa *lsa;
void (*hook_add)(struct ospf6_route *) = NULL;
void (*hook_remove)(struct ospf6_route *) = NULL;
+ char buf[PREFIX2STR_BUFFER];
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
zlog_debug("Re-examin intra-routes for area %s", oa->name);
@@ -2076,6 +2088,12 @@ void ospf6_intra_route_calculation(struct ospf6_area *oa)
oa->route_table->hook_remove = hook_remove;
for (route = ospf6_route_head(oa->route_table); route; route = nroute) {
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug("%s: route %s, flag 0x%x", __func__, buf,
+ route->flag);
+ }
+
nroute = ospf6_route_next(route);
if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)
&& CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) {
@@ -2092,6 +2110,9 @@ void ospf6_intra_route_calculation(struct ospf6_area *oa)
route->flag = 0;
} else {
/* Redo the summaries as things might have changed */
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
+ zlog_debug("%s: Originate summary for route %s",
+ __func__, buf);
ospf6_abr_originate_summary(route, oa->ospf6);
route->flag = 0;
}
@@ -2167,8 +2188,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_info("%s: border-router calculation for area %s", __func__,
- oa->name);
+ zlog_debug("%s: border-router calculation for area %s",
+ __func__, oa->name);
hook_add = oa->ospf6->brouter_table->hook_add;
hook_remove = oa->ospf6->brouter_table->hook_remove;
@@ -2189,8 +2210,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
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);
+ zlog_debug("%p: mark as removing: area %s brouter %s",
+ (void *)brouter, oa->name, brouter_name);
ospf6_brouter_debug_print(brouter);
}
}
@@ -2223,8 +2244,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
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);
+ zlog_debug("%p: transfer: area %s brouter %s",
+ (void *)brouter, oa->name, brouter_name);
ospf6_brouter_debug_print(brouter);
}
}
@@ -2297,7 +2318,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
brouter_id)
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
oa->area_id))
- zlog_info(
+ zlog_debug(
"%s: brouter %s disappears via area %s",
__func__, brouter_name, oa->name);
/* This is used to protect nbrouter from removed from
@@ -2325,8 +2346,9 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
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);
+ zlog_debug(
+ "brouter %s still exists via area %s",
+ brouter_name, oa->name);
/* But re-originate summaries */
ospf6_abr_originate_summary(brouter, oa->ospf6);
}
@@ -2341,8 +2363,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_info("%s: border-router calculation for area %s: done",
- __func__, oa->name);
+ zlog_debug("%s: border-router calculation for area %s: done",
+ __func__, oa->name);
}
static struct ospf6_lsa_handler router_handler = {
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c
index f5f429b041..a8f523295b 100644
--- a/ospf6d/ospf6_lsa.c
+++ b/ospf6d/ospf6_lsa.c
@@ -458,6 +458,7 @@ void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa,
case OSPF6_LSTYPE_INTER_PREFIX:
case OSPF6_LSTYPE_INTER_ROUTER:
case OSPF6_LSTYPE_AS_EXTERNAL:
+ case OSPF6_LSTYPE_TYPE_7:
if (use_json) {
json_object_string_add(
json_obj, "type",
@@ -486,7 +487,6 @@ void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa,
case OSPF6_LSTYPE_ROUTER:
case OSPF6_LSTYPE_NETWORK:
case OSPF6_LSTYPE_GROUP_MEMBERSHIP:
- case OSPF6_LSTYPE_TYPE_7:
case OSPF6_LSTYPE_LINK:
case OSPF6_LSTYPE_INTRA_PREFIX:
while (handler->lh_get_prefix_str(lsa, buf, sizeof(buf), cnt)
@@ -623,8 +623,8 @@ void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa,
vty_out(vty, "Flag: %x \n", lsa->flag);
vty_out(vty, "Lock: %d \n", lsa->lock);
vty_out(vty, "ReTx Count: %d\n", lsa->retrans_count);
- vty_out(vty, "Threads: Expire: 0x%p, Refresh: 0x%p \n",
- (void *)lsa->expire, (void *)lsa->refresh);
+ vty_out(vty, "Threads: Expire: %p, Refresh: %p\n", lsa->expire,
+ lsa->refresh);
vty_out(vty, "\n");
}
return;
diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h
index 84c3b85631..15b0d4ebbc 100644
--- a/ospf6d/ospf6_lsa.h
+++ b/ospf6d/ospf6_lsa.h
@@ -80,6 +80,11 @@
#define OSPF6_SCOPE_AS 0x4000
#define OSPF6_SCOPE_RESERVED 0x6000
+/* AS-external-LSA refresh method. */
+#define LSA_REFRESH_IF_CHANGED 0
+#define LSA_REFRESH_FORCE 1
+
+
/* XXX U-bit handling should be treated here */
#define OSPF6_LSA_SCOPE(type) (ntohs(type) & OSPF6_LSTYPE_SCOPE_MASK)
@@ -113,6 +118,7 @@ struct ospf6_lsa_header {
#define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2)
#define OSPF6_LSA_IS_SEQWRAP(L) ((L)->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER + 1))
+
struct ospf6_lsa {
char name[64]; /* dump string */
@@ -133,6 +139,8 @@ struct ospf6_lsa {
struct ospf6_lsdb *lsdb;
+ in_addr_t external_lsa_id;
+
/* lsa instance */
struct ospf6_lsa_header *header;
};
@@ -143,6 +151,7 @@ struct ospf6_lsa {
#define OSPF6_LSA_IMPLIEDACK 0x08
#define OSPF6_LSA_UNAPPROVED 0x10
#define OSPF6_LSA_SEQWRAPPED 0x20
+#define OSPF6_LSA_FLUSH 0x40
struct ospf6_lsa_handler {
uint16_t lh_type; /* host byte order */
diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c
index 18f121e3a2..304f03fde8 100644
--- a/ospf6d/ospf6_lsdb.c
+++ b/ospf6d/ospf6_lsdb.c
@@ -320,9 +320,17 @@ int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb)
struct ospf6_lsa *lsa, *lsanext;
for (ALL_LSDB(lsdb, lsa, lsanext)) {
- if (!OSPF6_LSA_IS_MAXAGE(lsa))
+ if (!OSPF6_LSA_IS_MAXAGE(lsa)) {
+ if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type))
+ zlog_debug("Not MaxAge %s", lsa->name);
continue;
+ }
+
if (lsa->retrans_count != 0) {
+ if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type))
+ zlog_debug("Remove MaxAge %s retrans_count %d",
+ lsa->name, lsa->retrans_count);
+
reschedule = 1;
continue;
}
@@ -341,6 +349,7 @@ int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb)
THREAD_OFF(lsa->refresh);
thread_execute(master, ospf6_lsa_refresh, lsa, 0);
} else {
+ zlog_debug("calling ospf6_lsdb_remove %s", lsa->name);
ospf6_lsdb_remove(lsa, lsdb);
}
}
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index f83be7b49c..9a1d8b79bc 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -530,7 +530,8 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh,
}
if (ntohs(his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
- && IS_AREA_STUB(on->ospf6_if->area)) {
+ && (IS_AREA_STUB(on->ospf6_if->area)
+ || IS_AREA_NSSA(on->ospf6_if->area))) {
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
zlog_debug(
"SeqNumMismatch (E-bit mismatch), discard");
@@ -750,7 +751,8 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh,
}
if (OSPF6_LSA_SCOPE(his->header->type) == OSPF6_SCOPE_AS
- && IS_AREA_STUB(on->ospf6_if->area)) {
+ && (IS_AREA_STUB(on->ospf6_if->area)
+ || IS_AREA_NSSA(on->ospf6_if->area))) {
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
zlog_debug("E-bit mismatch with LSA Headers");
ospf6_lsa_delete(his);
@@ -1927,7 +1929,8 @@ int ospf6_dbdesc_send_newone(struct thread *thread)
size = sizeof(struct ospf6_lsa_header) + sizeof(struct ospf6_dbdesc);
for (ALL_LSDB(on->summary_list, lsa, lsanext)) {
/* if stub area then don't advertise AS-External LSAs */
- if (IS_AREA_STUB(on->ospf6_if->area)
+ if ((IS_AREA_STUB(on->ospf6_if->area)
+ || IS_AREA_NSSA(on->ospf6_if->area))
&& ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) {
ospf6_lsdb_remove(lsa, on->summary_list);
continue;
diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c
new file mode 100644
index 0000000000..4d9b0a1978
--- /dev/null
+++ b/ospf6d/ospf6_nssa.c
@@ -0,0 +1,1396 @@
+/*
+ * OSPFv3 Not So Stubby Area implementation.
+ *
+ * Copyright (C) 2021 Kaushik Nath
+ * Copyright (C) 2021 Soman K.S
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <zebra.h>
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "linklist.h"
+#include "command.h"
+#include "thread.h"
+#include "plist.h"
+#include "filter.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_route.h"
+#include "ospf6_lsa.h"
+#include "ospf6_route.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+#include "ospf6_zebra.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+
+#include "ospf6_flood.h"
+#include "ospf6_intra.h"
+#include "ospf6_abr.h"
+#include "ospf6_asbr.h"
+#include "ospf6d.h"
+#include "ospf6_nssa.h"
+
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
+unsigned char config_debug_ospf6_nssa = 0;
+/* Determine whether this router is elected translator or not for area */
+static int ospf6_abr_nssa_am_elected(struct ospf6_area *oa)
+{
+ struct ospf6_lsa *lsa;
+ struct ospf6_router_lsa *router_lsa;
+ in_addr_t *best = NULL;
+ uint16_t type;
+
+ type = htons(OSPF6_LSTYPE_ROUTER);
+
+ /* Verify all the router LSA to compare the router ID */
+ for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) {
+
+ router_lsa = (struct ospf6_router_lsa
+ *)((caddr_t)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* ignore non-ABR routers */
+ if (!CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B))
+ continue;
+
+ /* Router has Nt flag - always translate */
+ if (CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: router %pI4 asserts Nt",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+
+ if (best == NULL)
+ best = &lsa->header->adv_router;
+ else if (IPV4_ADDR_CMP(best, &lsa->header->adv_router) < 0)
+ best = &lsa->header->adv_router;
+ }
+
+ if (best == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: best electable ABR not found",
+ __func__);
+ return 0;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: best electable ABR is: %pI4", __func__, best);
+
+ if (IPV4_ADDR_CMP(best, &oa->ospf6->router_id) <= 0) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: elected ABR is: %pI4", __func__, best);
+ return 1;
+ } else {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: not elected best %pI4, router ID %pI4",
+ __func__, best, &oa->ospf6->router_id);
+ return 0;
+ }
+}
+
+/* Flush the translated LSA when translation is disabled */
+static void ospf6_flush_translated_lsa(struct ospf6_area *area)
+{
+ uint16_t type;
+ struct ospf6_lsa *type7;
+ struct ospf6_lsa *type5;
+ struct ospf6 *ospf6 = area->ospf6;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: start area %s", __func__, area->name);
+
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, type7)) {
+ type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ type7->external_lsa_id,
+ ospf6->router_id, ospf6->lsdb);
+ if (type5 && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT))
+ ospf6_lsa_premature_aging(type5);
+ }
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: finish area %s", __func__, area->name);
+}
+
+/* Check NSSA status for all nssa areas*/
+void ospf6_abr_nssa_check_status(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct listnode *lnode, *nnode;
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, nnode, area)) {
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: checking area %s flag %x", __func__,
+ area->name, area->flag);
+
+ if (!IS_AREA_NSSA(area))
+ continue;
+
+ if (!CHECK_FLAG(area->ospf6->flag, OSPF6_FLAG_ABR)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: not ABR", __func__);
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_DISABLED;
+ ospf6_flush_translated_lsa(area);
+ } else {
+ /* Router is ABR */
+ if (area->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS)
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_ENABLED;
+ else {
+ /* We are a candidate for Translation */
+ if (ospf6_abr_nssa_am_elected(area) > 0) {
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_ENABLED;
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s: elected translator",
+ __func__);
+ } else {
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_DISABLED;
+ ospf6_flush_translated_lsa(area);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: not elected",
+ __func__);
+ }
+ }
+ }
+ }
+ /* RFC3101, 3.1:
+ * All NSSA border routers must set the E-bit in the Type-1
+ * router-LSAs of their directly attached non-stub areas, even
+ * when they are not translating.
+ */
+ if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR) && (ospf6->anyNSSA))
+ ospf6_asbr_status_update(ospf6, ++ospf6->redist_count);
+ else
+ ospf6_asbr_status_update(ospf6, --ospf6->redist_count);
+}
+
+/* Mark the summary LSA's as unapproved, when ABR status changes.*/
+static void ospf6_abr_unapprove_summaries(struct ospf6 *ospf6)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : considering area %pI4", __func__,
+ &area->area_id);
+ /* Inter area router LSA */
+ type = htons(OSPF6_LSTYPE_INTER_ROUTER);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s : approved unset on summary link id %pI4",
+ __func__, &lsa->header->id);
+ SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+ }
+ /* Inter area prefix LSA */
+ type = htons(OSPF6_LSTYPE_INTER_PREFIX);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s : approved unset on asbr-summary link id %pI4",
+ __func__, &lsa->header->id);
+ SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/* Re-advertise inter-area router LSA's */
+void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6)
+{
+ struct ospf6_route *brouter;
+ struct listnode *node, *nnode;
+ struct ospf6_area *oa;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Re-examining Inter-Router prefixes");
+
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) {
+ for (brouter = ospf6_route_head(oa->ospf6->brouter_table);
+ brouter; brouter = ospf6_route_next(brouter))
+ ospf6_abr_originate_summary_to_area(brouter, oa);
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Finished re-examining Inter-Router prefixes");
+}
+
+/* Advertise prefixes configured using area <area-id> range command */
+static void ospf6_abr_announce_aggregates(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct ospf6_route *range;
+ struct listnode *node, *nnode;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("ospf6_abr_announce_aggregates(): Start");
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ for (range = ospf6_route_head(area->range_table); range;
+ range = ospf6_route_next(range))
+ ospf6_abr_range_update(range, ospf6);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "ospf_abr_announce_aggregates(): looking at area %pI4",
+ &area->area_id);
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("ospf6_abr_announce_aggregates(): Stop");
+}
+
+/* Flush the summary LSA's which are not approved.*/
+void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : looking at area %pI4", __func__,
+ &area->area_id);
+
+ /* Inter area router LSA */
+ type = htons(OSPF6_LSTYPE_INTER_ROUTER);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) {
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ THREAD_OFF(lsa->refresh);
+ thread_execute(master, ospf6_lsa_expire, lsa,
+ 0);
+ }
+ }
+
+ /* Inter area prefix LSA */
+ type = htons(OSPF6_LSTYPE_INTER_PREFIX);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) {
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ THREAD_OFF(lsa->refresh);
+ thread_execute(master, ospf6_lsa_expire, lsa,
+ 0);
+ }
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/*
+ * This is the function taking care about ABR stuff, i.e.
+ * summary-LSA origination and flooding.
+ */
+static void ospf6_abr_task(struct ospf6 *ospf6)
+{
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Start", __func__);
+
+ if (ospf6->route_table == NULL || ospf6->brouter_table == NULL) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Routing tables are not yet ready",
+ __func__);
+ return;
+ }
+
+ ospf6_abr_unapprove_summaries(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : prepare aggregates", __func__);
+
+ ospf6_abr_range_reset_cost(ospf6);
+
+ if (IS_OSPF6_ABR(ospf6)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : process network RT", __func__);
+ ospf6_abr_prefix_resummarize(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : process router RT", __func__);
+ ospf6_asbr_prefix_readvertise(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : announce aggregates", __func__);
+ ospf6_abr_announce_aggregates(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : announce stub defaults", __func__);
+ ospf6_abr_defaults_to_stub(ospf6);
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : remove unapproved summaries", __func__);
+ ospf6_abr_remove_unapproved_summaries(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/* For NSSA Translations
+ * Mark the translated LSA's as unapproved. */
+static void ospf6_abr_unapprove_translates(struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+ struct ospf6_area *oa;
+ struct listnode *node;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_unapprove_translates(): Start");
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+ for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) {
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) {
+ SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : approved unset on link id %pI4",
+ __func__, &lsa->header->id);
+ }
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_unapprove_translates(): Stop");
+}
+
+/* Generate the translated external lsa from NSSA lsa */
+static struct ospf6_lsa *ospf6_lsa_translated_nssa_new(struct ospf6_area *area,
+ struct ospf6_lsa *type7)
+{
+ char *buffer;
+ struct ospf6_lsa *lsa;
+ struct ospf6_as_external_lsa *ext, *extnew;
+ struct ospf6_lsa_header *lsa_header;
+ caddr_t old_ptr, new_ptr;
+ struct ospf6_as_external_lsa *nssa;
+ struct prefix prefix;
+ struct ospf6_route *match;
+ struct ospf6 *ospf6 = area->ospf6;
+ ptrdiff_t tag_offset = 0;
+ route_tag_t network_order;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Start", __func__);
+
+ if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: Translation disabled for area %s",
+ __func__, area->name);
+ return NULL;
+ }
+
+ buffer = XCALLOC(MTYPE_OSPF6_LSA, OSPF6_MAX_LSASIZE);
+ lsa_header = (struct ospf6_lsa_header *)buffer;
+ extnew = (struct ospf6_as_external_lsa
+ *)((caddr_t)lsa_header
+ + sizeof(struct ospf6_lsa_header));
+ ext = (struct ospf6_as_external_lsa
+ *)((caddr_t)(type7->header)
+ + sizeof(struct ospf6_lsa_header));
+ old_ptr =
+ (caddr_t)((caddr_t)ext + sizeof(struct ospf6_as_external_lsa));
+ new_ptr = (caddr_t)((caddr_t)extnew
+ + sizeof(struct ospf6_as_external_lsa));
+
+ memcpy(extnew, ext, sizeof(struct ospf6_as_external_lsa));
+
+ /* find the translated Type-5 for this Type-7 */
+ nssa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ type7->header);
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = nssa->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa, &nssa->prefix);
+
+ /* Find the LSA from the external route */
+ match = ospf6_route_lookup(&prefix, area->route_table);
+ if (match == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : no matching route %pFX", __func__,
+ &prefix);
+ return NULL;
+ }
+
+ /* set Prefix */
+ memcpy(new_ptr, old_ptr, OSPF6_PREFIX_SPACE(ext->prefix.prefix_length));
+ ospf6_prefix_apply_mask(&extnew->prefix);
+ new_ptr += OSPF6_PREFIX_SPACE(extnew->prefix.prefix_length);
+
+ tag_offset =
+ sizeof(*ext) + OSPF6_PREFIX_SPACE(ext->prefix.prefix_length);
+
+ /* Forwarding address */
+ if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_F)) {
+ memcpy(new_ptr, (caddr_t)ext + tag_offset,
+ sizeof(struct in6_addr));
+ new_ptr += sizeof(struct in6_addr);
+ tag_offset += sizeof(struct in6_addr);
+ }
+ /* External Route Tag */
+ if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_T)) {
+ memcpy(&network_order, (caddr_t)ext + tag_offset,
+ sizeof(network_order));
+ network_order = htonl(network_order);
+ memcpy(new_ptr, &network_order, sizeof(network_order));
+ new_ptr += sizeof(network_order);
+ }
+
+ /* Fill LSA Header */
+ lsa_header->age = 0;
+ lsa_header->type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ lsa_header->id = htonl(ospf6->external_id);
+ ospf6->external_id++;
+ lsa_header->adv_router = ospf6->router_id;
+ lsa_header->seqnum =
+ ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
+ lsa_header->adv_router, ospf6->lsdb);
+ lsa_header->length = htons((caddr_t)new_ptr - (caddr_t)lsa_header);
+ type7->external_lsa_id = lsa_header->id;
+
+ /* LSA checksum */
+ ospf6_lsa_checksum(lsa_header);
+
+ /* create LSA */
+ lsa = ospf6_lsa_create(lsa_header);
+
+ SET_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT);
+ UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+
+ /* Originate */
+ ospf6_lsa_originate_process(lsa, ospf6);
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: Originated type5 LSA id %pI4", __func__,
+ &lsa_header->id);
+ return lsa;
+}
+
+/* Delete LSA from retransmission list */
+static void ospf6_ls_retransmit_delete_nbr_as(struct ospf6 *ospf6,
+ struct ospf6_lsa *lsa)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : start lsa %s", __func__, lsa->name);
+
+ /*The function ospf6_flood_clear_area removes LSA from
+ * retransmit list.
+ */
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area))
+ ospf6_flood_clear_area(lsa, area);
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : finish lsa %s", __func__, lsa->name);
+}
+
+/* Refresh translated AS-external-LSA. */
+struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *area,
+ struct ospf6_lsa *type7,
+ struct ospf6_lsa *type5)
+{
+ struct ospf6_lsa *new = NULL;
+ struct ospf6_as_external_lsa *ext_lsa;
+ struct prefix prefix;
+ struct ospf6 *ospf6 = area->ospf6;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : start area %s", __func__, area->name);
+
+ /* Sanity checks. */
+ assert(type7);
+
+ /* Find the AS external LSA */
+ if (type5 == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s: No translated Type-5 found for Type-7 with Id %pI4",
+ __func__, &type7->header->id);
+
+ /* find the translated Type-5 for this Type-7 */
+ ext_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ type7->header);
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = ext_lsa->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, ext_lsa,
+ &ext_lsa->prefix);
+
+ /* Find the AS external LSA from Type-7 LSA */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: try to find external LSA id %d",
+ __func__, type7->external_lsa_id);
+ type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ type7->external_lsa_id,
+ ospf6->router_id, ospf6->lsdb);
+ }
+
+ if (type5) {
+ if (CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) {
+ /* Delete LSA from neighbor retransmit-list. */
+ ospf6_ls_retransmit_delete_nbr_as(ospf6, type5);
+
+ /* Flush the LSA */
+ ospf6_lsa_premature_aging(type5);
+ } else {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: Invalid translated LSA %s",
+ __func__, type5->name);
+ return NULL;
+ }
+ }
+
+ /* create new translated LSA */
+ if (ospf6_lsa_age_current(type7) != OSPF_LSA_MAXAGE) {
+ if ((new = ospf6_lsa_translated_nssa_new(area, type7))
+ == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s: Could not translate Type-7 for %pI4",
+ __func__, &type7->header->id);
+ return NULL;
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: finish", __func__);
+
+ return new;
+}
+
+/* Originate Translated Type-5 for supplied Type-7 NSSA LSA */
+struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *oa,
+ struct ospf6_lsa *type7)
+{
+ struct ospf6_lsa *new;
+
+ if (ntohs(type7->header->type) != OSPF6_LSTYPE_TYPE_7)
+ return NULL;
+
+ if ((new = ospf6_lsa_translated_nssa_new(oa, type7)) == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : Could not translate Type-7, Id %pI4, to Type-5",
+ __func__, &type7->header->id);
+ return NULL;
+ }
+
+ return new;
+}
+
+int ospf6_abr_translate_nssa(struct ospf6_area *area, struct ospf6_lsa *lsa)
+{
+ /* Incoming Type-7 or later aggregated Type-7
+ *
+ * LSA is skipped if P-bit is off.
+ * LSA is aggregated if within range.
+ *
+ * The Type-7 is translated, Installed/Approved as a Type-5 into
+ * global LSDB, then Flooded through AS
+ *
+ * Later, any Unapproved Translated Type-5's are flushed/discarded
+ */
+
+ struct ospf6_lsa *old = NULL, *new = NULL;
+ struct ospf6_as_external_lsa *nssa_lsa;
+ struct prefix prefix;
+ struct ospf6_route *match;
+ struct ospf6 *ospf6;
+
+ ospf6 = area->ospf6;
+ nssa_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+
+ if (!CHECK_FLAG(nssa_lsa->prefix.prefix_options,
+ OSPF6_PREFIX_OPTION_P)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4, P-bit off, NO Translation",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4 external ID %pI4, Translating type 7 to 5",
+ __func__, &lsa->header->id, &lsa->external_lsa_id);
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = nssa_lsa->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa_lsa, &nssa_lsa->prefix);
+
+ if (!CHECK_FLAG(nssa_lsa->bits_metric, OSPF6_ASBR_BIT_F)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4, Forward address is 0, NO Translation",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+
+ /* Find the existing AS-External LSA for this prefix */
+ match = ospf6_route_lookup(&prefix, ospf6->external_table);
+ if (match) {
+ old = ospf6_lsdb_lookup(OSPF6_LSTYPE_AS_EXTERNAL,
+ match->path.origin.id, ospf6->router_id,
+ ospf6->lsdb);
+ }
+
+ /* Check Type 5 LSA using the matching external ID */
+ if (old == NULL) {
+ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ lsa->external_lsa_id, ospf6->router_id,
+ ospf6->lsdb);
+ }
+
+ if (old) {
+ /* Do not continue if type 5 LSA not approved */
+ if (CHECK_FLAG(old->flag, OSPF6_LSA_UNAPPROVED)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4 type 5 is not approved",
+ __func__, &old->header->id);
+ return 1;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : found old translated LSA Id %pI4, refreshing",
+ __func__, &old->header->id);
+
+ /* refresh */
+ new = ospf6_translated_nssa_refresh(area, lsa, old);
+ if (!new) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : could not refresh translated LSA Id %pI4",
+ __func__, &old->header->id);
+ }
+ } else {
+ /* no existing external route for this LSA Id
+ * originate translated LSA
+ */
+
+ if (ospf6_translated_nssa_originate(area, lsa) == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : Could not translate Type-7 for %pI4 to Type-5",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void ospf6_abr_process_nssa_translates(struct ospf6 *ospf6)
+{
+ /* Scan through all NSSA_LSDB records for all areas;
+ * If P-bit is on, translate all Type-7's to 5's and aggregate or\
+ * flood install as approved in Type-5 LSDB with XLATE Flag on
+ * later, do same for all aggregates... At end, DISCARD all
+ * remaining UNAPPROVED Type-5's (Aggregate is for future ) */
+
+ struct listnode *node;
+ struct ospf6_area *oa;
+ struct ospf6_lsa *lsa;
+ int type;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+
+ /* skip if not translator */
+ if (oa->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) {
+ zlog_debug("%s area %pI4 NSSATranslatorState %d",
+ __func__, &oa->area_id,
+ oa->NSSATranslatorState);
+ continue;
+ }
+
+ /* skip if not Nssa Area */
+ if (!IS_AREA_NSSA(oa)) {
+ zlog_debug("%s area %pI4 Flag %x", __func__,
+ &oa->area_id, oa->flag);
+ continue;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : looking at area %pI4", __func__,
+ &oa->area_id);
+
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) {
+ zlog_debug("%s : lsa %s , id %pI4 , adv router %pI4",
+ lsa->name, __func__, &lsa->header->id,
+ &lsa->header->adv_router);
+ ospf6_abr_translate_nssa(oa, lsa);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/* Generate translated type-5 LSA from the configured area ranges*/
+static void ospf6_abr_translate_nssa_range(struct ospf6 *ospf6)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *oa;
+ struct ospf6_route *range;
+ struct ospf6_lsa *lsa;
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) {
+ for (range = ospf6_route_head(oa->range_table); range;
+ range = ospf6_route_next(range)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "Translating range %pFX of area %pI4",
+ &range->prefix, &oa->area_id);
+ if (CHECK_FLAG(range->flag,
+ OSPF6_ROUTE_DO_NOT_ADVERTISE))
+ continue;
+
+ /* Find the NSSA LSA from the route */
+ /* Generate and flood external LSA */
+ lsa = ospf6_lsdb_lookup(OSPF6_LSTYPE_TYPE_7,
+ range->path.origin.id,
+ ospf6->router_id, oa->lsdb);
+ if (lsa)
+ ospf6_abr_translate_nssa(oa, lsa);
+ }
+ }
+}
+
+static void ospf6_abr_send_nssa_aggregates(struct ospf6 *ospf6)
+{
+ struct listnode *node;
+ struct ospf6_area *area;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
+ if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED)
+ continue;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : looking at area %pI4", __func__,
+ &area->area_id);
+
+ ospf6_abr_translate_nssa_range(ospf6);
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/*Flood max age LSA's for the unapproved LSA's */
+static int ospf6_abr_remove_unapproved_translates_apply(struct ospf6_lsa *lsa)
+{
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)
+ && CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) {
+ zlog_debug("%s : removing unapproved translates, lsa : %s",
+ __func__, lsa->name);
+
+ /* FLUSH THROUGHOUT AS */
+ ospf6_lsa_premature_aging(lsa);
+ }
+ return 0;
+}
+
+static void ospf6_abr_remove_unapproved_translates(struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+
+ /* All AREA PROCESS should have APPROVED necessary LSAs */
+ /* Remove any left over and not APPROVED */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_remove_unapproved_translates(): Start");
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa))
+ ospf6_abr_remove_unapproved_translates_apply(lsa);
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf_abr_remove_unapproved_translates(): Stop");
+}
+
+static void ospf6_abr_nssa_task(struct ospf6 *ospf6)
+{
+ /* called only if any_nssa */
+ struct ospf6_route *range;
+ struct ospf6_area *area;
+ struct listnode *node, *nnode;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Check for NSSA-ABR Tasks():");
+
+ if (!IS_OSPF6_ABR(ospf6)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s Not ABR", __func__);
+ return;
+ }
+
+ if (!ospf6->anyNSSA) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s Not NSSA", __func__);
+ return;
+ }
+
+ /* Each area must confirm TranslatorRole */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): Start");
+
+ /* For all Global Entries flagged "local-translate", unset APPROVED */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): unapprove translates");
+
+ ospf6_abr_unapprove_translates(ospf6);
+
+ /* RESET all Ranges in every Area, same as summaries */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): NSSA initialize aggregates");
+ ospf6_abr_range_reset_cost(ospf6);
+
+ /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or
+ * Aggregate as Type-7
+ * Install or Approve in Type-5 Global LSDB
+ */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): process translates");
+ ospf6_abr_process_nssa_translates(ospf6);
+
+ /* Translate/Send any "ranged" aggregates, and also 5-Install and
+ * Approve
+ * Scan Type-7's for aggregates, translate to Type-5's,
+ * Install/Flood/Approve
+ */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): send NSSA aggregates");
+ ospf6_abr_send_nssa_aggregates(ospf6); /*TURNED OFF FOR NOW */
+
+ /* Flush any unapproved previous translates from Global Data Base */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "ospf6_abr_nssa_task(): remove unapproved translates");
+ ospf6_abr_remove_unapproved_translates(ospf6);
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ for (range = ospf6_route_head(area->range_table); range;
+ range = ospf6_route_next(range)) {
+ if (CHECK_FLAG(range->flag,
+ OSPF6_ROUTE_DO_NOT_ADVERTISE))
+ ospf6_zebra_delete_discard(range, ospf6);
+ else
+ ospf6_zebra_add_discard(range, ospf6);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): Stop");
+}
+
+int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route,
+ int type)
+{
+ route_map_result_t ret;
+ struct prefix *prefix;
+ struct ospf6_redist *red;
+
+ if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id))
+ return 0;
+
+ prefix = &route->prefix;
+
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red)
+ return 0;
+
+ /* Change to new redist structure */
+ if (ROUTEMAP_NAME(red)) {
+ if (ROUTEMAP(red) == NULL)
+ ospf6_asbr_routemap_update(NULL);
+ if (ROUTEMAP(red) == NULL) {
+ zlog_warn(
+ "route-map \"%s\" not found, suppress redistributing",
+ ROUTEMAP_NAME(red));
+ return 0;
+ }
+ }
+
+ /* Change to new redist structure */
+ if (ROUTEMAP(red)) {
+ ret = route_map_apply(ROUTEMAP(red), prefix, route);
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("Denied by route-map \"%s\"",
+ ROUTEMAP_NAME(red));
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void ospf6_external_lsa_refresh_type(struct ospf6 *ospf6, uint8_t type,
+ unsigned short instance, int force)
+{
+ struct ospf6_route *route;
+ struct ospf6_external_info *info;
+ struct ospf6_lsa *lsa;
+
+ if (type == ZEBRA_ROUTE_MAX)
+ return;
+
+ for (route = ospf6_route_head(ospf6->external_table); route;
+ route = ospf6_route_next(route)) {
+ info = route->route_option;
+
+ /* Find the external LSA in the database */
+ if (!is_default_prefix(&route->prefix)) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id),
+ ospf6->router_id, ospf6->lsdb);
+
+ if (lsa) {
+ THREAD_OFF(lsa->refresh);
+
+ /* LSA is maxage, immediate refresh */
+ if (OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_flood(NULL, lsa);
+ else
+ thread_add_timer(master,
+ ospf6_lsa_refresh, lsa,
+ OSPF_LS_REFRESH_TIME,
+ &lsa->refresh);
+ } else {
+ /* LSA not found in the database
+ * Verify and originate external LSA
+ */
+ if (ospf6_redistribute_check(ospf6, route,
+ type))
+ ospf6_as_external_lsa_originate(route,
+ ospf6);
+ }
+ }
+ }
+}
+
+/* Refresh default route */
+static void ospf6_external_lsa_refresh_default(struct ospf6 *ospf6)
+{
+ struct ospf6_route *route;
+ struct ospf6_external_info *info;
+ struct ospf6_lsa *lsa;
+
+ for (route = ospf6_route_head(ospf6->external_table); route;
+ route = ospf6_route_next(route)) {
+ if (is_default_prefix(&route->prefix)) {
+ info = route->route_option;
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id),
+ ospf6->router_id, ospf6->lsdb);
+
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p",
+ (void *)lsa);
+ if (OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_flood(NULL, lsa);
+ else
+ thread_add_timer(master,
+ ospf6_lsa_refresh, lsa,
+ OSPF_LS_REFRESH_TIME,
+ &lsa->refresh);
+ } else if (!lsa) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "LSA[Type5:0.0.0.0]: Originate AS-external-LSA");
+ ospf6_as_external_lsa_originate(route, ospf6);
+ }
+ }
+ }
+}
+
+/* If there's redistribution configured, we need to refresh external
+ * LSAs in order to install Type-7 and flood to all NSSA Areas
+ */
+void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6)
+{
+ int type;
+ struct ospf6_redist *red;
+
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red)
+ return;
+
+ ospf6_external_lsa_refresh_type(ospf6, type, red->instance,
+ LSA_REFRESH_IF_CHANGED);
+ }
+ ospf6_external_lsa_refresh_default(ospf6);
+}
+
+/* This function performs ABR related processing */
+static int ospf6_abr_task_timer(struct thread *thread)
+{
+ struct ospf6 *ospf6 = THREAD_ARG(thread);
+
+ ospf6->t_abr_task = NULL;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Running ABR task on timer");
+
+ ospf6_is_router_abr(ospf6);
+ ospf6_abr_nssa_check_status(ospf6);
+ ospf6_abr_task(ospf6);
+ /* if nssa-abr, then scan Type-7 LSDB */
+ ospf6_abr_nssa_task(ospf6);
+ ospf6_asbr_nssa_redist_task(ospf6);
+
+ return 0;
+}
+
+void ospf6_schedule_abr_task(struct ospf6 *ospf6)
+{
+ if (ospf6->t_abr_task) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("ABR task already scheduled");
+ return;
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Scheduling ABR task");
+
+ thread_add_timer(master, ospf6_abr_task_timer, ospf6,
+ OSPF6_ABR_TASK_DELAY, &ospf6->t_abr_task);
+}
+
+/* Flush the NSSA LSAs from the area */
+static void ospf6_nssa_flush_area(struct ospf6_area *area)
+{
+ uint16_t type;
+ struct ospf6_lsa *lsa = NULL, *type5 = NULL;
+ struct ospf6 *ospf6 = area->ospf6;
+ const struct route_node *rt = NULL;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: area %s", __func__, area->name);
+
+ /* Flush the NSSA LSA */
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ rt = ospf6_lsdb_head(area->lsdb_self, 0, type, ospf6->router_id, &lsa);
+ while (lsa) {
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ SET_FLAG(lsa->flag, OSPF6_LSA_FLUSH);
+ ospf6_flood(NULL, lsa);
+ /* Flush the translated LSA */
+ if (ospf6_is_router_abr(ospf6)) {
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ type5 = ospf6_lsdb_lookup(
+ htons(type), lsa->external_lsa_id,
+ ospf6->router_id, ospf6->lsdb);
+ if (type5
+ && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) {
+ type5->header->age = htons(OSPF_LSA_MAXAGE);
+ SET_FLAG(type5->flag, OSPF6_LSA_FLUSH);
+ ospf6_flood(NULL, type5);
+ }
+ }
+ lsa = ospf6_lsdb_next(rt, lsa);
+ }
+}
+
+static void ospf6_area_nssa_update(struct ospf6_area *area)
+{
+ struct ospf6_route *route;
+
+ if (IS_AREA_NSSA(area)) {
+ if (!ospf6_is_router_abr(area->ospf6))
+ OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E);
+ area->ospf6->anyNSSA++;
+ OSPF6_OPT_SET(area->options, OSPF6_OPT_N);
+ area->NSSATranslatorRole = OSPF6_NSSA_ROLE_CANDIDATE;
+ } else if (IS_AREA_ENABLED(area)) {
+ if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
+ zlog_debug("Normal area for if %s", area->name);
+ OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_N);
+ if (ospf6_is_router_abr(area->ospf6))
+ OSPF6_OPT_SET(area->options, OSPF6_OPT_E);
+ area->ospf6->anyNSSA--;
+ area->NSSATranslatorState = OSPF6_NSSA_TRANSLATE_DISABLED;
+ }
+
+ /* Refresh router LSA */
+ if (IS_AREA_NSSA(area)) {
+ OSPF6_ROUTER_LSA_SCHEDULE(area);
+
+ /* Check if router is ABR */
+ if (ospf6_is_router_abr(area->ospf6)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Router is ABR area %s", area->name);
+ ospf6_schedule_abr_task(area->ospf6);
+ } else {
+ /* Router is not ABR */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("NSSA area %s", area->name);
+
+ /* Originate NSSA LSA */
+ for (route = ospf6_route_head(
+ area->ospf6->external_table);
+ route; route = ospf6_route_next(route))
+ ospf6_nssa_lsa_originate(route, area);
+ }
+ } else {
+ /* Disable NSSA */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Normal area %s", area->name);
+ ospf6_nssa_flush_area(area);
+ ospf6_area_disable(area);
+ ospf6_area_delete(area);
+ }
+}
+
+int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area)
+{
+
+ if (!IS_AREA_NSSA(area)) {
+ SET_FLAG(area->flag, OSPF6_AREA_NSSA);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("area %s nssa set", area->name);
+ ospf6_area_nssa_update(area);
+ }
+
+ return 1;
+}
+
+int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area)
+{
+ if (IS_AREA_NSSA(area)) {
+ UNSET_FLAG(area->flag, OSPF6_AREA_NSSA);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("area %s nssa reset", area->name);
+ ospf6_area_nssa_update(area);
+ }
+
+ return 1;
+}
+
+/* Find the NSSA forwarding address */
+static struct in6_addr *ospf6_get_nssa_fwd_addr(struct ospf6_area *oa)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_interface *oi;
+
+ for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) {
+ if (if_is_operative(oi->interface))
+ if (oi->area && IS_AREA_NSSA(oi->area))
+ return ospf6_interface_get_global_address(
+ oi->interface);
+ }
+ return NULL;
+}
+
+void ospf6_nssa_lsa_originate(struct ospf6_route *route,
+ struct ospf6_area *area)
+{
+ char buffer[OSPF6_MAX_LSASIZE];
+ struct ospf6_lsa_header *lsa_header;
+ struct ospf6_lsa *lsa;
+ struct ospf6_external_info *info = route->route_option;
+ struct in6_addr *fwd_addr;
+
+ struct ospf6_as_external_lsa *as_external_lsa;
+ char buf[PREFIX2STR_BUFFER];
+ caddr_t p;
+
+ if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) {
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug("Originate AS-External-LSA for %s", buf);
+ }
+
+ /* prepare buffer */
+ memset(buffer, 0, sizeof(buffer));
+ lsa_header = (struct ospf6_lsa_header *)buffer;
+ as_external_lsa = (struct ospf6_as_external_lsa
+ *)((caddr_t)lsa_header
+ + sizeof(struct ospf6_lsa_header));
+ p = (caddr_t)((caddr_t)as_external_lsa
+ + sizeof(struct ospf6_as_external_lsa));
+
+ /* Fill AS-External-LSA */
+ /* Metric type */
+ if (route->path.metric_type == OSPF6_PATH_TYPE_EXTERNAL2)
+ SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+ else
+ UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+
+ /* external route tag */
+ if (info->tag)
+ SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+ else
+ UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+
+ /* Set metric */
+ OSPF6_ASBR_METRIC_SET(as_external_lsa, route->path.cost);
+
+ /* prefixlen */
+ as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
+
+ /* PrefixOptions */
+ as_external_lsa->prefix.prefix_options = route->path.prefix_options;
+
+ /* Set the P bit */
+ as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P;
+
+ /* don't use refer LS-type */
+ as_external_lsa->prefix.prefix_refer_lstype = htons(0);
+
+ /* set Prefix */
+ memcpy(p, &route->prefix.u.prefix6,
+ OSPF6_PREFIX_SPACE(route->prefix.prefixlen));
+ ospf6_prefix_apply_mask(&as_external_lsa->prefix);
+ p += OSPF6_PREFIX_SPACE(route->prefix.prefixlen);
+
+ /* Forwarding address */
+ fwd_addr = ospf6_get_nssa_fwd_addr(area);
+ if (fwd_addr) {
+ memcpy(p, fwd_addr, sizeof(struct in6_addr));
+ p += sizeof(struct in6_addr);
+ SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
+ } else
+ UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
+
+ /* External Route Tag */
+ if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) {
+ route_tag_t network_order = htonl(info->tag);
+
+ memcpy(p, &network_order, sizeof(network_order));
+ p += sizeof(network_order);
+ }
+
+ /* Fill LSA Header */
+ lsa_header->age = 0;
+ lsa_header->type = htons(OSPF6_LSTYPE_TYPE_7);
+ lsa_header->id = route->path.origin.id;
+ lsa_header->adv_router = area->ospf6->router_id;
+ lsa_header->seqnum =
+ ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
+ lsa_header->adv_router, area->ospf6->lsdb);
+ lsa_header->length = htons((caddr_t)p - (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, area);
+}
+
+void ospf6_abr_check_translate_nssa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa)
+{
+ struct ospf6_lsa *type5 = NULL;
+ struct ospf6 *ospf6 = area->ospf6;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : start", __func__);
+
+ type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ lsa->external_lsa_id, ospf6->router_id,
+ ospf6->lsdb);
+
+ if (ospf6_is_router_abr(ospf6) && (type5 == NULL)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Originating type5 LSA", __func__);
+ ospf6_lsa_translated_nssa_new(area, lsa);
+ }
+}
+
+DEFUN(debug_ospf6_nssa, debug_ospf6_nssa_cmd,
+ "debug ospf6 nssa",
+ DEBUG_STR
+ OSPF6_STR
+ "Debug OSPFv3 NSSA function\n")
+{
+ OSPF6_DEBUG_NSSA_ON();
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_debug_ospf6_nssa, no_debug_ospf6_nssa_cmd,
+ "no debug ospf6 nssa",
+ NO_STR
+ DEBUG_STR
+ OSPF6_STR
+ "Debug OSPFv3 NSSA function\n")
+{
+ OSPF6_DEBUG_NSSA_OFF();
+ return CMD_SUCCESS;
+}
+
+void config_write_ospf6_debug_nssa(struct vty *vty)
+{
+ if (IS_OSPF6_DEBUG_NSSA)
+ vty_out(vty, "debug ospf6 nssa\n");
+}
+
+void install_element_ospf6_debug_nssa(void)
+{
+ install_element(ENABLE_NODE, &debug_ospf6_nssa_cmd);
+ install_element(ENABLE_NODE, &no_debug_ospf6_nssa_cmd);
+ install_element(CONFIG_NODE, &debug_ospf6_nssa_cmd);
+ install_element(CONFIG_NODE, &no_debug_ospf6_nssa_cmd);
+}
diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h
new file mode 100644
index 0000000000..a171d76d44
--- /dev/null
+++ b/ospf6d/ospf6_nssa.h
@@ -0,0 +1,76 @@
+/*
+ * OSPFv3 Not So Stubby Area implementation.
+ *
+ * Copyright (C) 2021 Kaushik Nath
+ * Copyright (C) 2021 Soman K.S
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef OSPF6_NSSA_H
+#define OSPF6_NSSA_H
+
+#define OSPF6_OPTION_NP 0x08
+#define OSPF6_LS_INFINITY 0xffffff
+
+#define OSPF6_OPT_N (1 << 3) /* Handling Type-7 LSA Capability */
+
+#define OSPF6_DEBUG_NSSA 0x09
+/* Debug option */
+extern unsigned char config_debug_ospf6_nssa;
+
+#define OSPF6_DEBUG_NSSA_ON() (config_debug_ospf6_nssa = 1)
+#define OSPF6_DEBUG_NSSA_OFF() (config_debug_ospf6_nssa = 0)
+#define IS_OSPF6_DEBUG_NSSA config_debug_ospf6_nssa
+
+#define CHECK_LSA_TYPE_1_TO_5_OR_7(type) \
+ ((type == OSPF6_ROUTER_LSA_MIN_SIZE) \
+ || (type == OSPF6_NETWORK_LSA_MIN_SIZE) \
+ || (type == OSPF6_LINK_LSA_MIN_SIZE) \
+ || (type == OSPF6_INTRA_PREFIX_LSA_MIN_SIZE) \
+ || (type == OSPF6_AS_NSSA_LSA))
+
+#define OSPF6_LSA_APPROVED 0x08
+#define OSPF6_LSA_LOCAL_XLT 0x40
+
+#define OSPF6_ABR_TASK_DELAY 7
+
+int ospf6_area_nssa_no_summary_set(struct ospf6 *ospf6, struct in_addr area_id);
+int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area);
+int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area);
+
+extern void ospf6_nssa_lsa_flush(struct ospf6 *ospf6, struct prefix_ipv6 *p);
+extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *,
+ struct ospf6_lsa *,
+ struct ospf6_lsa *);
+extern struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *,
+ struct ospf6_lsa *);
+
+extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6);
+
+extern void ospf6_schedule_abr_task(struct ospf6 *ospf6);
+void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6);
+extern void ospf6_nssa_lsa_originate(struct ospf6_route *route,
+ struct ospf6_area *area);
+extern void install_element_ospf6_debug_nssa(void);
+int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route,
+ int type);
+extern int ospf6_abr_translate_nssa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa);
+extern void ospf6_abr_check_translate_nssa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa);
+extern void ospf6_abr_nssa_check_status(struct ospf6 *ospf6);
+extern void config_write_ospf6_debug_nssa(struct vty *vty);
+#endif /* OSPF6_NSSA_H */
diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h
index da6b270e01..b98dc38b72 100644
--- a/ospf6d/ospf6_proto.h
+++ b/ospf6d/ospf6_proto.h
@@ -35,6 +35,8 @@
#define OSPF6_ROUTER_BIT_V (1 << 2)
#define OSPF6_ROUTER_BIT_E (1 << 1)
#define OSPF6_ROUTER_BIT_B (1 << 0)
+#define OSPF6_ROUTER_BIT_NT (1 << 4)
+
/* OSPF options */
/* present in HELLO, DD, LSA */
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 908011c946..0a026785f4 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -688,6 +688,9 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
if (node->info == old) {
node->info = route;
SET_FLAG(route->flag, OSPF6_ROUTE_BEST);
+ if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
+ zlog_debug("%s: replace old route %s",
+ __func__, buf);
}
if (old->prev)
@@ -745,12 +748,12 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
UNSET_FLAG(next->flag, OSPF6_ROUTE_BEST);
SET_FLAG(route->flag, OSPF6_ROUTE_BEST);
if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_info(
+ zlog_debug(
"%s %p: route add %p cost %u: replacing previous best: %p cost %u",
ospf6_route_table_name(table),
(void *)table, (void *)route,
- route->path.cost,
- (void *)next, next->path.cost);
+ route->path.cost, (void *)next,
+ next->path.cost);
}
route->installed = now;
@@ -876,6 +879,9 @@ void ospf6_route_remove(struct ospf6_route *route,
if (route->next && route->next->rnode == node) {
node->info = route->next;
SET_FLAG(route->next->flag, OSPF6_ROUTE_BEST);
+ if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
+ zlog_debug("%s: remove route %s", __func__,
+ buf);
} else {
node->info = NULL;
route->rnode = NULL;
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 7652d71c59..b0a8f01bcc 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -37,11 +37,13 @@
#include "ospf6_area.h"
#include "ospf6_proto.h"
#include "ospf6_abr.h"
+#include "ospf6_asbr.h"
#include "ospf6_spf.h"
#include "ospf6_intra.h"
#include "ospf6_interface.h"
#include "ospf6d.h"
#include "ospf6_abr.h"
+#include "ospf6_nssa.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex");
@@ -640,6 +642,9 @@ static int ospf6_spf_calculation_thread(struct thread *t)
areas_processed++;
}
+ /* External LSA calculation */
+ ospf6_ase_calculate_timer_add(ospf6);
+
if (ospf6_is_router_abr(ospf6))
ospf6_abr_defaults_to_stub(ospf6);
@@ -1105,3 +1110,162 @@ void ospf6_remove_temp_router_lsa(struct ospf6_area *area)
ospf6_lsdb_remove(lsa, area->temp_router_lsa_lsdb);
}
}
+
+int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
+ struct ospf6_area *area)
+{
+ struct ospf6_route *route;
+ struct ospf6_as_external_lsa *external;
+ struct prefix prefix;
+ void (*hook_add)(struct ospf6_route *) = NULL;
+ void (*hook_remove)(struct ospf6_route *) = NULL;
+
+ assert(lsa);
+
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s : start", __func__);
+
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7)
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: Processing Type-7", __func__);
+
+ /* Stay away from any Local Translated Type-7 LSAs */
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: Rejecting Local translated LSA",
+ __func__);
+ return 0;
+ }
+
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+ prefix.family = AF_INET6;
+ prefix.prefixlen = external->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix);
+
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) {
+ hook_add = ospf6->route_table->hook_add;
+ hook_remove = ospf6->route_table->hook_remove;
+ ospf6->route_table->hook_add = NULL;
+ ospf6->route_table->hook_remove = NULL;
+
+ if (!OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_asbr_lsa_add(lsa);
+
+ ospf6->route_table->hook_add = hook_add;
+ ospf6->route_table->hook_remove = hook_remove;
+
+ route = ospf6_route_lookup(&prefix, ospf6->route_table);
+ if (route == NULL) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: no external route %pFX",
+ __func__, &prefix);
+ return 0;
+ }
+
+ 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, ospf6->route_table);
+ else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)
+ || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) {
+ if (hook_add) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug(
+ "%s: add external route %pFX",
+ __func__, &prefix);
+ (*hook_add)(route);
+ }
+ }
+ } else if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) {
+ hook_add = area->route_table->hook_add;
+ hook_remove = area->route_table->hook_remove;
+ area->route_table->hook_add = NULL;
+ area->route_table->hook_remove = NULL;
+
+ if (!OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_asbr_lsa_add(lsa);
+
+ area->route_table->hook_add = hook_add;
+ area->route_table->hook_remove = hook_remove;
+
+ route = ospf6_route_lookup(&prefix, area->route_table);
+ if (route == NULL) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: no route %pFX, area %s",
+ __func__, &prefix, area->name);
+ return 0;
+ }
+
+ 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)) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s : remove route %pFX, area %s",
+ __func__, &prefix, area->name);
+ ospf6_route_remove(route, area->route_table);
+ } else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)
+ || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) {
+ if (hook_add) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug(
+ "%s: add nssa route %pFX, area %s",
+ __func__, &prefix, area->name);
+ (*hook_add)(route);
+ }
+ ospf6_abr_check_translate_nssa(area, lsa);
+ }
+ }
+ return 0;
+}
+
+static int ospf6_ase_calculate_timer(struct thread *t)
+{
+ struct ospf6 *ospf6;
+ struct ospf6_lsa *lsa;
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+ uint16_t type;
+
+ ospf6 = THREAD_ARG(t);
+ ospf6->t_ase_calc = NULL;
+
+ /* Calculate external route for each AS-external-LSA */
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa))
+ ospf6_ase_calculate_route(ospf6, lsa, NULL);
+
+ /* This version simple adds to the table all NSSA areas */
+ if (ospf6->anyNSSA) {
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s : looking at area %s", __func__,
+ area->name);
+
+ if (IS_OSPF6_DEBUG_SPF(PROCESS)) {
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ for (ALL_LSDB_TYPED(area->lsdb, type, lsa))
+ ospf6_ase_calculate_route(ospf6, lsa,
+ area);
+ }
+ }
+ }
+ return 0;
+}
+
+void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6)
+{
+ if (ospf6 == NULL)
+ return;
+
+ thread_add_timer(master, ospf6_ase_calculate_timer, ospf6,
+ OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc);
+}
diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h
index 4660bfd05d..d6fbc5c13b 100644
--- a/ospf6d/ospf6_spf.h
+++ b/ospf6d/ospf6_spf.h
@@ -35,6 +35,8 @@ extern unsigned char conf_debug_ospf6_spf;
#define IS_OSPF6_DEBUG_SPF(level) \
(conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_##level)
+#define OSPF6_ASE_CALC_INTERVAL 1
+
PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue);
/* Transit Vertex */
struct ospf6_vertex {
@@ -161,5 +163,7 @@ extern struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area,
struct ospf6_lsdb *lsdb,
uint32_t adv_router);
extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area);
-
+extern void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6);
+extern int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
+ struct ospf6_area *area);
#endif /* OSPF6_SPF_H */
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index 42405ca35e..33b5dd1960 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -52,6 +52,7 @@
#include "ospf6_spf.h"
#include "ospf6d.h"
#include "lib/json.h"
+#include "ospf6_nssa.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_TOP, "OSPF6 top");
@@ -259,7 +260,15 @@ static void ospf6_top_lsdb_hook_remove(struct ospf6_lsa *lsa)
static void ospf6_top_route_hook_add(struct ospf6_route *route)
{
- struct ospf6 *ospf6 = route->table->scope;
+ struct ospf6 *ospf6 = NULL;
+ struct ospf6_area *oa = NULL;
+
+ if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL)
+ ospf6 = route->table->scope;
+ else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) {
+ oa = (struct ospf6_area *)route->table->scope;
+ ospf6 = oa->ospf6;
+ }
ospf6_abr_originate_summary(route, ospf6);
ospf6_zebra_route_update_add(route, ospf6);
@@ -267,7 +276,15 @@ static void ospf6_top_route_hook_add(struct ospf6_route *route)
static void ospf6_top_route_hook_remove(struct ospf6_route *route)
{
- struct ospf6 *ospf6 = route->table->scope;
+ struct ospf6 *ospf6 = NULL;
+ struct ospf6_area *oa = NULL;
+
+ if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL)
+ ospf6 = route->table->scope;
+ else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) {
+ oa = (struct ospf6_area *)route->table->scope;
+ ospf6 = oa->ospf6;
+ }
route->flag |= OSPF6_ROUTE_REMOVE;
ospf6_abr_originate_summary(route, ospf6);
@@ -917,8 +934,10 @@ DEFUN (ospf6_interface_area,
ospf6_interface_enable(oi);
/* If the router is ABR, originate summary routes */
- if (ospf6_is_router_abr(ospf6))
+ if (ospf6_is_router_abr(ospf6)) {
ospf6_abr_enable_area(oa);
+ ospf6_schedule_abr_task(oa->ospf6);
+ }
return CMD_SUCCESS;
}
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 238d6a40ce..e4dfebe1de 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -95,6 +95,8 @@ struct ospf6 {
struct list *redist[ZEBRA_ROUTE_MAX + 1];
uint8_t flag;
+#define OSPF6_FLAG_ABR 0x04
+#define OSPF6_FLAG_ASBR 0x08
int redistribute; /* Num of redistributed protocols. */
@@ -145,14 +147,17 @@ struct ospf6 {
* to support ECMP.
*/
uint16_t max_multipath;
+ /* Count of NSSA areas */
+ uint8_t anyNSSA;
+ struct thread *t_abr_task; /* ABR task timer. */
+ uint32_t redist_count;
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(ospf6);
#define OSPF6_DISABLED 0x01
#define OSPF6_STUB_ROUTER 0x02
-#define OSPF6_FLAG_ASBR 0x04
#define OSPF6_MAX_IF_ADDRS 100
#define OSPF6_MAX_IF_ADDRS_JUMBO 200
#define OSPF6_DEFAULT_MTU 1500
diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c
index da8c695f65..65f0aa664e 100644
--- a/ospf6d/ospf6d.c
+++ b/ospf6d/ospf6d.c
@@ -46,6 +46,7 @@
#include "ospf6d.h"
#include "ospf6_bfd.h"
#include "lib/json.h"
+#include "ospf6_nssa.h"
DEFINE_MGROUP(OSPF6D, "ospf6d");
@@ -94,6 +95,7 @@ static int config_write_ospf6_debug(struct vty *vty)
config_write_ospf6_debug_asbr(vty);
config_write_ospf6_debug_abr(vty);
config_write_ospf6_debug_flood(vty);
+ config_write_ospf6_debug_nssa(vty);
return 0;
}
@@ -153,6 +155,8 @@ static uint16_t parse_type_spec(int idx_lsa, int argc, struct cmd_token **argv)
type = htons(OSPF6_LSTYPE_INTER_PREFIX);
else if (strmatch(argv[idx_lsa]->text, "link"))
type = htons(OSPF6_LSTYPE_LINK);
+ else if (strmatch(argv[idx_lsa]->text, "type-7"))
+ type = htons(OSPF6_LSTYPE_TYPE_7);
}
return type;
@@ -1419,6 +1423,7 @@ void ospf6_init(struct thread_master *master)
install_element_ospf6_debug_asbr();
install_element_ospf6_debug_abr();
install_element_ospf6_debug_flood();
+ install_element_ospf6_debug_nssa();
install_element_ospf6_clear_interface();
diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am
index 00388afd38..2e1891be71 100644
--- a/ospf6d/subdir.am
+++ b/ospf6d/subdir.am
@@ -6,6 +6,7 @@ if OSPF6D
noinst_LIBRARIES += ospf6d/libospf6.a
sbin_PROGRAMS += ospf6d/ospf6d
vtysh_scan += \
+ ospf6d/ospf6_nssa.c \
ospf6d/ospf6_abr.c \
ospf6d/ospf6_asbr.c \
ospf6d/ospf6_area.c \
@@ -30,6 +31,7 @@ man8 += $(MANBUILD)/frr-ospf6d.8
endif
ospf6d_libospf6_a_SOURCES = \
+ ospf6d/ospf6_nssa.c \
ospf6d/ospf6_abr.c \
ospf6d/ospf6_area.c \
ospf6d/ospf6_asbr.c \
@@ -53,6 +55,7 @@ ospf6d_libospf6_a_SOURCES = \
# end
noinst_HEADERS += \
+ ospf6d/ospf6_nssa.h \
ospf6d/ospf6_abr.h \
ospf6d/ospf6_area.h \
ospf6d/ospf6_asbr.h \
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 7eb587899b..6c1ac6761a 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -597,7 +597,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi,
Designated Router, chances are that all the neighbors have
received the LSA already. */
if (NBR_IS_DR(inbr) || NBR_IS_BDR(inbr)) {
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug("%s: DR/BDR NOT SEND to int %s (%s)",
__func__, IF_NAME(oi),
ospf_get_name(oi->ospf));
@@ -611,7 +611,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi,
end up retransmitting the updates. */
if (oi->state == ISM_Backup) {
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
"%s: ISM_Backup NOT SEND to int %s (%s)",
__func__, IF_NAME(oi),
@@ -626,8 +626,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi,
(which must be > 0) when it is copied into the outgoing Link
State Update packet (until the LS age field reaches the maximum
value of MaxAge). */
- /* XXX HASSO: Is this IS_DEBUG_OSPF_NSSA really correct? */
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug("%s: DR/BDR sending upd to int %s (%s)", __func__,
IF_NAME(oi), ospf_get_name(oi->ospf));
diff --git a/ospfd/ospf_gr_helper.c b/ospfd/ospf_gr_helper.c
index a25057a27f..b32318b832 100644
--- a/ospfd/ospf_gr_helper.c
+++ b/ospfd/ospf_gr_helper.c
@@ -52,7 +52,7 @@
static const char * const ospf_exit_reason_desc[] = {
"Unknown reason",
- "Helper inprogress",
+ "Helper in progress",
"Topology Change",
"Grace timer expiry",
"Successful graceful restart",
@@ -159,10 +159,8 @@ const char *ospf_rejected_reason2str(unsigned int reason)
* Returns:
* Nothing
*/
-void ospf_gr_helper_init(struct ospf *ospf)
+void ospf_gr_helper_instance_init(struct ospf *ospf)
{
- int rc;
-
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug("%s, GR Helper init.", __func__);
@@ -176,6 +174,37 @@ void ospf_gr_helper_init(struct ospf *ospf)
ospf->enable_rtr_list =
hash_create(ospf_enable_rtr_hash_key, ospf_enable_rtr_hash_cmp,
"OSPF enable router hash");
+}
+
+/*
+ * De-Initialize GR helper config data structures.
+ *
+ * OSPF
+ * OSPF pointer
+ *
+ * Returns:
+ * Nothing
+ */
+void ospf_gr_helper_instance_stop(struct ospf *ospf)
+{
+ if (IS_DEBUG_OSPF_GR_HELPER)
+ zlog_debug("%s, GR helper deinit.", __func__);
+
+ ospf_enable_rtr_hash_destroy(ospf);
+}
+
+/*
+ * Initialize GR helper config data structures.
+ *
+ * Returns:
+ * Nothing
+ */
+void ospf_gr_helper_init(void)
+{
+ int rc;
+
+ if (IS_DEBUG_OSPF_GR_HELPER)
+ zlog_debug("%s, GR Helper init.", __func__);
rc = ospf_register_opaque_functab(
OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA, NULL, NULL, NULL,
@@ -191,20 +220,15 @@ void ospf_gr_helper_init(struct ospf *ospf)
/*
* De-Initialize GR helper config data structures.
*
- * OSPF
- * OSPF pointer
- *
* Returns:
* Nothing
*/
-void ospf_gr_helper_stop(struct ospf *ospf)
+void ospf_gr_helper_stop(void)
{
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug("%s, GR helper deinit.", __func__);
- ospf_enable_rtr_hash_destroy(ospf);
-
ospf_delete_opaque_functab(OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA);
}
@@ -342,7 +366,7 @@ static int ospf_handle_grace_timer_expiry(struct thread *thread)
* Grace LSA received from RESTARTER.
*
* nbr
- * ospf neighbour which requets the router to act as
+ * OSPF neighbour which requests the router to act as
* HELPER.
*
* Returns:
@@ -374,11 +398,11 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug(
- "%s, Grace LSA received from %pI4, grace interval:%u, restartreason :%s",
+ "%s, Grace LSA received from %pI4, grace interval:%u, restart reason:%s",
__func__, &restart_addr, grace_interval,
ospf_restart_reason2str(restart_reason));
- /* Incase of broadcast links, if RESTARTER is DR_OTHER,
+ /* In case of broadcast links, if RESTARTER is DR_OTHER,
* grace LSA might be received from DR, so need to get
* actual neighbour info , here RESTARTER.
*/
@@ -441,7 +465,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
return OSPF_GR_NOT_HELPER;
}
- /* Check the retranmission list of this
+ /* Check the retransmission list of this
* neighbour, check any change in lsas.
*/
if (ospf->strict_lsa_check && !ospf_ls_retransmit_isempty(restarter)
@@ -459,7 +483,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
if (ntohs(lsa->data->ls_age) >= grace_interval) {
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug(
- "%s, Grace LSA age(%d) is more than the graceinterval(%d)",
+ "%s, Grace LSA age(%d) is more than the grace interval(%d)",
__func__, lsa->data->ls_age, grace_interval);
restarter->gr_helper_info.rejected_reason =
OSPF_HELPER_LSA_AGE_MORE;
@@ -508,7 +532,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
restarter->gr_helper_info.gr_restart_reason = restart_reason;
restarter->gr_helper_info.rejected_reason = OSPF_HELPER_REJECTED_NONE;
- /* Incremnet the active restarer count */
+ /* Increment the active restarter count */
ospf->active_restarter_cnt++;
if (IS_DEBUG_OSPF_GR_HELPER)
@@ -528,7 +552,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
* retransmission list.
*
* nbr
- * ospf neighbor
+ * OSPF neighbor
*
* Returns:
* TRUE - if any change in the lsa.
@@ -579,7 +603,7 @@ static bool ospf_check_change_in_rxmt_list(struct ospf_neighbor *nbr)
* ospf
* ospf pointer
* lsa
- * topo change occured due to this lsa type (1 to 5 and 7)
+ * topo change occurred due to this lsa type (1 to 5 and 7)
*
* Returns:
* Nothing
@@ -593,7 +617,7 @@ void ospf_helper_handle_topo_chg(struct ospf *ospf, struct ospf_lsa *lsa)
return;
/* Topo change not required to be handled if strict
- * LSA check is disbaled for this router.
+ * LSA check is disabled for this router.
*/
if (!ospf->strict_lsa_check)
return;
@@ -683,25 +707,26 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
ospf->active_restarter_cnt--;
/* If the exit not triggered due to grace timer
- * expairy , stop the grace timer.
+ * expiry, stop the grace timer.
*/
if (reason != OSPF_GR_HELPER_GRACE_TIMEOUT)
THREAD_OFF(nbr->gr_helper_info.t_grace_timer);
/* check exit triggered due to successful completion
* of graceful restart.
- * If no, bringdown the neighbour.
+ * If no, bring down the neighbour.
*/
if (reason != OSPF_GR_HELPER_COMPLETED) {
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug(
"%s, Failed GR exit, so bringing down the neighbour",
__func__);
- OSPF_NSM_EVENT_EXECUTE(nbr, NSM_KillNbr);
+ OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_KillNbr);
}
/*Recalculate the DR for the network segment */
- ospf_dr_election(oi);
+ if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_NBMA)
+ ospf_dr_election(oi);
/* Originate a router LSA */
ospf_router_lsa_update_area(oi->area);
@@ -712,7 +737,7 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
}
/*
- * Process Maxage Grace LSA.
+ * Process MaxAge Grace LSA.
* It is a indication for successful completion of GR.
* If router acting as HELPER, It exits from helper role.
*
@@ -723,7 +748,7 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
* Grace LSA received from RESTARTER.
*
* nbr
- * ospf neighbour which requets the router to act as
+ * OSPF neighbour which requests the router to act as
* HELPER.
*
* Returns:
@@ -778,7 +803,7 @@ void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
* Disable/Enable HELPER support on router level.
*
* ospf
- * OSPFpointer.
+ * OSPF pointer.
*
* status
* TRUE/FALSE
@@ -819,7 +844,7 @@ void ospf_gr_helper_support_set(struct ospf *ospf, bool support)
lookup.advRtrAddr.s_addr =
nbr->router_id.s_addr;
/* check if helper support enabled for the
- * correspodning routerid.If enabled, dont
+ * corresponding routerid.If enabled, dont
* dont exit from helper role.
*/
if (hash_lookup(ospf->enable_rtr_list, &lookup))
@@ -995,72 +1020,115 @@ static void show_ospf_grace_lsa_info(struct vty *vty, struct ospf_lsa *lsa)
lsah = (struct lsa_header *)lsa->data;
if (lsa->size <= OSPF_LSA_HEADER_SIZE) {
- vty_out(vty, "%% Invalid LSA length: %d\n", length);
+ if (vty)
+ vty_out(vty, "%% Invalid LSA length: %d\n", length);
+ else
+ zlog_debug("%% Invalid LSA length: %d", length);
return;
}
length = lsa->size - OSPF_LSA_HEADER_SIZE;
- vty_out(vty, " TLV info:\n");
+ if (vty)
+ vty_out(vty, " TLV info:\n");
+ else
+ zlog_debug(" TLV info:");
for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh;
tlvh = TLV_HDR_NEXT(tlvh)) {
/* Check TLV len */
if (sum + TLV_SIZE(tlvh) > length) {
- vty_out(vty, "%% Invalid TLV length: %u\n",
- TLV_SIZE(tlvh));
+ if (vty)
+ vty_out(vty, "%% Invalid TLV length: %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug("%% Invalid TLV length: %u",
+ TLV_SIZE(tlvh));
return;
}
switch (ntohs(tlvh->type)) {
case GRACE_PERIOD_TYPE:
- if (TLV_SIZE(tlvh) <
- sizeof(struct grace_tlv_graceperiod)) {
- vty_out(vty,
- "%% Invalid grace TLV length %u\n",
- TLV_SIZE(tlvh));
+ if (TLV_SIZE(tlvh)
+ < sizeof(struct grace_tlv_graceperiod)) {
+ if (vty)
+ vty_out(vty,
+ "%% Invalid grace TLV length %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug(
+ "%% Invalid grace TLV length %u",
+ TLV_SIZE(tlvh));
return;
}
gracePeriod = (struct grace_tlv_graceperiod *)tlvh;
sum += TLV_SIZE(tlvh);
- vty_out(vty, " Grace period:%d\n",
- ntohl(gracePeriod->interval));
+ if (vty)
+ vty_out(vty, " Grace period:%d\n",
+ ntohl(gracePeriod->interval));
+ else
+ zlog_debug(" Grace period:%d",
+ ntohl(gracePeriod->interval));
break;
case RESTART_REASON_TYPE:
- if (TLV_SIZE(tlvh) <
- sizeof(struct grace_tlv_restart_reason)) {
- vty_out(vty,
- "%% Invalid reason TLV length %u\n",
- TLV_SIZE(tlvh));
+ if (TLV_SIZE(tlvh)
+ < sizeof(struct grace_tlv_restart_reason)) {
+ if (vty)
+ vty_out(vty,
+ "%% Invalid reason TLV length %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug(
+ "%% Invalid reason TLV length %u",
+ TLV_SIZE(tlvh));
return;
}
grReason = (struct grace_tlv_restart_reason *)tlvh;
sum += TLV_SIZE(tlvh);
- vty_out(vty, " Restart reason:%s\n",
- ospf_restart_reason2str(grReason->reason));
+ if (vty)
+ vty_out(vty, " Restart reason:%s\n",
+ ospf_restart_reason2str(
+ grReason->reason));
+ else
+ zlog_debug(" Restart reason:%s",
+ ospf_restart_reason2str(
+ grReason->reason));
break;
case RESTARTER_IP_ADDR_TYPE:
- if (TLV_SIZE(tlvh) <
- sizeof(struct grace_tlv_restart_addr)) {
- vty_out(vty,
- "%% Invalid addr TLV length %u\n",
- TLV_SIZE(tlvh));
+ if (TLV_SIZE(tlvh)
+ < sizeof(struct grace_tlv_restart_addr)) {
+ if (vty)
+ vty_out(vty,
+ "%% Invalid addr TLV length %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug(
+ "%% Invalid addr TLV length %u",
+ TLV_SIZE(tlvh));
return;
}
restartAddr = (struct grace_tlv_restart_addr *)tlvh;
sum += TLV_SIZE(tlvh);
- vty_out(vty, " Restarter address:%pI4\n",
- &restartAddr->addr);
+ if (vty)
+ vty_out(vty, " Restarter address:%pI4\n",
+ &restartAddr->addr);
+ else
+ zlog_debug(" Restarter address:%pI4",
+ &restartAddr->addr);
break;
default:
- vty_out(vty, " Unknown TLV type %d\n",
- ntohs(tlvh->type));
+ if (vty)
+ vty_out(vty, " Unknown TLV type %d\n",
+ ntohs(tlvh->type));
+ else
+ zlog_debug(" Unknown TLV type %d",
+ ntohs(tlvh->type));
break;
}
diff --git a/ospfd/ospf_gr_helper.h b/ospfd/ospf_gr_helper.h
index c355bb4f3d..bd6d1d7462 100644
--- a/ospfd/ospf_gr_helper.h
+++ b/ospfd/ospf_gr_helper.h
@@ -156,8 +156,10 @@ const char *ospf_exit_reason2str(unsigned int reason);
const char *ospf_restart_reason2str(unsigned int reason);
const char *ospf_rejected_reason2str(unsigned int reason);
-extern void ospf_gr_helper_init(struct ospf *ospf);
-extern void ospf_gr_helper_stop(struct ospf *ospf);
+extern void ospf_gr_helper_instance_init(struct ospf *ospf);
+extern void ospf_gr_helper_instance_stop(struct ospf *ospf);
+extern void ospf_gr_helper_init(void);
+extern void ospf_gr_helper_stop(void);
extern int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
struct ospf_neighbor *nbr);
extern void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index 72dc699bd9..49829d86f1 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -2670,15 +2670,16 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi,
if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) {
zlog_debug(
- "ospf_lsa_install() Premature Aging lsa 0x%p, seqnum 0x%x",
- (void *)lsa,
+ "%s() Premature Aging lsa %p, seqnum 0x%x",
+ __func__, lsa,
ntohl(lsa->data->ls_seqnum));
ospf_lsa_header_dump(lsa->data);
}
} else {
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
zlog_debug(
- "ospf_lsa_install() got an lsa with seq 0x80000000 that was not self originated. Ignoring");
+ "%s() got an lsa with seq 0x80000000 that was not self originated. Ignoring",
+ __func__);
ospf_lsa_header_dump(lsa->data);
}
return old;
@@ -2764,9 +2765,8 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi,
*/
if (IS_LSA_MAXAGE(new)) {
if (IS_DEBUG_OSPF(lsa, LSA_INSTALL))
- zlog_debug("LSA[Type%d:%pI4]: Install LSA 0x%p, MaxAge",
- new->data->type, &new->data->id,
- (void *)lsa);
+ zlog_debug("LSA[Type%d:%pI4]: Install LSA %p, MaxAge",
+ new->data->type, &new->data->id, lsa);
ospf_lsa_maxage(ospf, lsa);
}
@@ -2855,8 +2855,8 @@ static int ospf_maxage_lsa_remover(struct thread *thread)
if (CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "originating new lsa for lsa 0x%p",
- (void *)lsa);
+ "originating new lsa for lsa %p",
+ lsa);
ospf_lsa_refresh(ospf, lsa);
}
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
index 91ba3044fe..d94de12994 100644
--- a/ospfd/ospf_main.c
+++ b/ospfd/ospf_main.c
@@ -225,6 +225,7 @@ int main(int argc, char **argv)
ospf_route_map_init();
ospf_opaque_init();
+ ospf_gr_helper_init();
/* OSPF errors init */
ospf_error_init();
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index 42bf914f67..fac2f97141 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -2119,15 +2119,12 @@ void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0)
goto out;
}
+ /* This lsa will be flushed and removed eventually. */
+ ospf_lsa_flush(top, lsa);
+
/* Dequeue listnode entry from the list. */
listnode_delete(oipt->id_list, oipi);
- /* Disassociate internal control information with the given lsa. */
- free_opaque_info_per_id((void *)oipi);
-
- /* Force given lsa's age to MaxAge. */
- lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
-
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"Schedule Type-%u Opaque-LSA to FLUSH: [opaque-type=%u, opaque-id=%x]",
@@ -2135,8 +2132,8 @@ void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0)
GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)),
GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)));
- /* This lsa will be flushed and removed eventually. */
- ospf_lsa_flush(top, lsa);
+ /* Disassociate internal control information with the given lsa. */
+ free_opaque_info_per_id((void *)oipi);
out:
return;
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 2de6731758..580eee13cf 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -1862,9 +1862,9 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
return;
}
- /* Get list of LSAs from Link State Update packet. - Also perorms Stages
- * 1 (validate LSA checksum) and 2 (check for LSA consistent type)
- * of section 13.
+ /* Get list of LSAs from Link State Update packet. - Also performs
+ * Stages 1 (validate LSA checksum) and 2 (check for LSA consistent
+ * type) of section 13.
*/
lsas = ospf_ls_upd_list_lsa(nbr, s, oi, size);
@@ -1890,7 +1890,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
struct ospf_lsa *ls_ret, *current;
int ret = 1;
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA))
zlog_debug("LSA Type-%d from %pI4, ID: %pI4, ADV: %pI4",
lsa->data->type, &ospfh->router_id,
&lsa->data->id, &lsa->data->adv_router);
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index 3849d4b7ea..43d6ff44ba 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -1889,19 +1889,19 @@ static int ospf_spf_calculate_schedule_worker(struct thread *thread)
rbuf[0] = '\0';
if (spf_reason_flags) {
- if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_ROUTER_LSA_INSTALL))
strlcat(rbuf, "R, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_NETWORK_LSA_INSTALL))
strlcat(rbuf, "N, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_SUMMARY_LSA_INSTALL))
strlcat(rbuf, "S, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL))
strlcat(rbuf, "AS, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE)
+ if (spf_reason_flags & (1 << SPF_FLAG_ABR_STATUS_CHANGE))
strlcat(rbuf, "ABR, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE)
+ if (spf_reason_flags & (1 << SPF_FLAG_ASBR_STATUS_CHANGE))
strlcat(rbuf, "ASBR, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_MAXAGE)
+ if (spf_reason_flags & (1 << SPF_FLAG_MAXAGE))
strlcat(rbuf, "M, ", sizeof(rbuf));
size_t rbuflen = strlen(rbuf);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index fb2d790532..cb64187d72 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -362,88 +362,79 @@ DEFPY (no_ospf_router_id,
}
-static void ospf_passive_interface_default(struct ospf *ospf, uint8_t newval)
+static void ospf_passive_interface_default_update(struct ospf *ospf,
+ uint8_t newval)
{
- struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
struct listnode *ln;
- struct interface *ifp;
struct ospf_interface *oi;
ospf->passive_interface_default = newval;
- FOR_ALL_INTERFACES (vrf, ifp) {
- if (ifp && OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp),
- passive_interface))
- UNSET_IF_PARAM(IF_DEF_PARAMS(ifp), passive_interface);
- }
- for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, ln, oi)) {
- if (OSPF_IF_PARAM_CONFIGURED(oi->params, passive_interface))
- UNSET_IF_PARAM(oi->params, passive_interface);
- /* update multicast memberships */
+ /* update multicast memberships */
+ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, ln, oi))
ospf_if_set_multicast(oi);
- }
}
-static void ospf_passive_interface_update_addr(struct ospf *ospf,
- struct interface *ifp,
- struct ospf_if_params *params,
- uint8_t value,
- struct in_addr addr)
+static void ospf_passive_interface_update(struct interface *ifp)
{
- uint8_t dflt;
+ struct route_node *rn;
- params->passive_interface = value;
- if (params != IF_DEF_PARAMS(ifp)) {
- if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp),
- passive_interface))
- dflt = IF_DEF_PARAMS(ifp)->passive_interface;
- else
- dflt = ospf->passive_interface_default;
+ /*
+ * XXX We should call ospf_if_set_multicast on exactly those
+ * interfaces for which the passive property changed. It is too much
+ * work to determine this set, so we do this for every interface.
+ * This is safe and reasonable because ospf_if_set_multicast uses a
+ * record of joined groups to avoid systems calls if the desired
+ * memberships match the current memership.
+ */
- if (value != dflt)
- SET_IF_PARAM(params, passive_interface);
- else
- UNSET_IF_PARAM(params, passive_interface);
+ for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
+ struct ospf_interface *oi = rn->info;
- ospf_free_if_params(ifp, addr);
- ospf_if_update_params(ifp, addr);
+ if (oi)
+ ospf_if_set_multicast(oi);
}
+
+ /*
+ * XXX It is not clear what state transitions the interface needs to
+ * undergo when going from active to passive and vice versa. Fixing
+ * this will require precise identification of interfaces having such a
+ * transition.
+ */
}
-static void ospf_passive_interface_update(struct ospf *ospf,
- struct interface *ifp,
- struct ospf_if_params *params,
- uint8_t value)
+DEFUN (ospf_passive_interface_default,
+ ospf_passive_interface_default_cmd,
+ "passive-interface default",
+ "Suppress routing updates on an interface\n"
+ "Suppress routing updates on interfaces by default\n")
{
- params->passive_interface = value;
- if (params == IF_DEF_PARAMS(ifp)) {
- if (value != ospf->passive_interface_default)
- SET_IF_PARAM(params, passive_interface);
- else
- UNSET_IF_PARAM(params, passive_interface);
- }
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ ospf_passive_interface_default_update(ospf, OSPF_IF_PASSIVE);
+
+ return CMD_SUCCESS;
}
-DEFUN (ospf_passive_interface,
+DEFUN_HIDDEN (ospf_passive_interface_addr,
ospf_passive_interface_addr_cmd,
- "passive-interface <IFNAME [A.B.C.D]|default>",
+ "passive-interface IFNAME [A.B.C.D]",
"Suppress routing updates on an interface\n"
"Interface's name\n"
- "IPv4 address\n"
- "Suppress routing updates on interfaces by default\n")
+ "IPv4 address\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 2;
struct interface *ifp = NULL;
struct in_addr addr = {.s_addr = INADDR_ANY};
- int ret;
struct ospf_if_params *params;
- struct route_node *rn;
+ int ret;
+
+ vty_out(vty,
+ "This command is deprecated, because it is not VRF-aware.\n");
+ vty_out(vty,
+ "Please, use \"ip ospf passive\" on an interface instead.\n");
- if (strmatch(argv[1]->text, "default")) {
- ospf_passive_interface_default(ospf, OSPF_IF_PASSIVE);
- return CMD_SUCCESS;
- }
if (ospf->vrf_id != VRF_UNKNOWN)
ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id);
@@ -452,8 +443,6 @@ DEFUN (ospf_passive_interface,
return CMD_WARNING_CONFIG_FAILED;
}
- params = IF_DEF_PARAMS(ifp);
-
if (argc == 3) {
ret = inet_aton(argv[idx_ipv4]->arg, &addr);
if (!ret) {
@@ -464,45 +453,39 @@ DEFUN (ospf_passive_interface,
params = ospf_get_if_params(ifp, addr);
ospf_if_update_params(ifp, addr);
- ospf_passive_interface_update_addr(ospf, ifp, params,
- OSPF_IF_PASSIVE, addr);
+ } else {
+ params = IF_DEF_PARAMS(ifp);
}
- ospf_passive_interface_update(ospf, ifp, params, OSPF_IF_PASSIVE);
+ params->passive_interface = OSPF_IF_PASSIVE;
+ SET_IF_PARAM(params, passive_interface);
- /* XXX We should call ospf_if_set_multicast on exactly those
- * interfaces for which the passive property changed. It is too much
- * work to determine this set, so we do this for every interface.
- * This is safe and reasonable because ospf_if_set_multicast uses a
- * record of joined groups to avoid systems calls if the desired
- * memberships match the current memership.
- */
+ ospf_passive_interface_update(ifp);
- for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
- struct ospf_interface *oi = rn->info;
+ return CMD_SUCCESS;
+}
- if (oi && (OSPF_IF_PARAM(oi, passive_interface)
- == OSPF_IF_PASSIVE))
- ospf_if_set_multicast(oi);
- }
- /*
- * XXX It is not clear what state transitions the interface needs to
- * undergo when going from active to passive. Fixing this will
- * require precise identification of interfaces having such a
- * transition.
- */
+DEFUN (no_ospf_passive_interface_default,
+ no_ospf_passive_interface_default_cmd,
+ "no passive-interface default",
+ NO_STR
+ "Allow routing updates on an interface\n"
+ "Allow routing updates on interfaces by default\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ ospf_passive_interface_default_update(ospf, OSPF_IF_ACTIVE);
return CMD_SUCCESS;
}
-DEFUN (no_ospf_passive_interface,
+DEFUN_HIDDEN (no_ospf_passive_interface,
no_ospf_passive_interface_addr_cmd,
- "no passive-interface <IFNAME [A.B.C.D]|default>",
+ "no passive-interface IFNAME [A.B.C.D]",
NO_STR
"Allow routing updates on an interface\n"
"Interface's name\n"
- "IPv4 address\n"
- "Allow routing updates on interfaces by default\n")
+ "IPv4 address\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 3;
@@ -510,12 +493,11 @@ DEFUN (no_ospf_passive_interface,
struct in_addr addr = {.s_addr = INADDR_ANY};
struct ospf_if_params *params;
int ret;
- struct route_node *rn;
- if (strmatch(argv[2]->text, "default")) {
- ospf_passive_interface_default(ospf, OSPF_IF_ACTIVE);
- return CMD_SUCCESS;
- }
+ vty_out(vty,
+ "This command is deprecated, because it is not VRF-aware.\n");
+ vty_out(vty,
+ "Please, use \"no ip ospf passive\" on an interface instead.\n");
if (ospf->vrf_id != VRF_UNKNOWN)
ifp = if_get_by_name(argv[2]->arg, ospf->vrf_id);
@@ -525,8 +507,6 @@ DEFUN (no_ospf_passive_interface,
return CMD_WARNING_CONFIG_FAILED;
}
- params = IF_DEF_PARAMS(ifp);
-
if (argc == 4) {
ret = inet_aton(argv[idx_ipv4]->arg, &addr);
if (!ret) {
@@ -534,30 +514,22 @@ DEFUN (no_ospf_passive_interface,
"Please specify interface address by A.B.C.D\n");
return CMD_WARNING_CONFIG_FAILED;
}
-
params = ospf_lookup_if_params(ifp, addr);
if (params == NULL)
return CMD_SUCCESS;
- ospf_passive_interface_update_addr(ospf, ifp, params,
- OSPF_IF_ACTIVE, addr);
+ } else {
+ params = IF_DEF_PARAMS(ifp);
}
- ospf_passive_interface_update(ospf, ifp, params, OSPF_IF_ACTIVE);
- /* XXX We should call ospf_if_set_multicast on exactly those
- * interfaces for which the passive property changed. It is too much
- * work to determine this set, so we do this for every interface.
- * This is safe and reasonable because ospf_if_set_multicast uses a
- * record of joined groups to avoid systems calls if the desired
- * memberships match the current memership.
- */
- for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
- struct ospf_interface *oi = rn->info;
-
- if (oi
- && (OSPF_IF_PARAM(oi, passive_interface) == OSPF_IF_ACTIVE))
- ospf_if_set_multicast(oi);
+ params->passive_interface = OSPF_IF_ACTIVE;
+ UNSET_IF_PARAM(params, passive_interface);
+ if (params != IF_DEF_PARAMS(ifp)) {
+ ospf_free_if_params(ifp, addr);
+ ospf_if_update_params(ifp, addr);
}
+ ospf_passive_interface_update(ifp);
+
return CMD_SUCCESS;
}
@@ -4435,21 +4407,27 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty,
ospf_nbr_state_message(nbr, msgbuf, 16);
- long time_store;
-
- time_store =
- monotime_until(
- &nbr->t_inactivity->u.sands,
- NULL)
- / 1000LL;
-
json_object_int_add(json_neighbor, "priority",
nbr->priority);
json_object_string_add(json_neighbor, "state",
msgbuf);
- json_object_int_add(json_neighbor,
- "deadTimeMsecs",
- time_store);
+
+ if (nbr->t_inactivity) {
+ long time_store;
+
+ time_store = monotime_until(
+ &nbr->t_inactivity
+ ->u.sands,
+ NULL)
+ / 1000LL;
+ json_object_int_add(json_neighbor,
+ "deadTimeMsecs",
+ time_store);
+ } else {
+ json_object_string_add(json_neighbor,
+ "deadTimeMsecs",
+ "inactive");
+ }
json_object_string_add(
json_neighbor, "address",
inet_ntop(AF_INET, &nbr->src,
@@ -9094,6 +9072,82 @@ DEFUN (no_ip_ospf_area,
return CMD_SUCCESS;
}
+DEFUN (ip_ospf_passive,
+ ip_ospf_passive_cmd,
+ "ip ospf passive [A.B.C.D]",
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Suppress routing updates on an interface\n"
+ "Address of interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int idx_ipv4 = 3;
+ struct in_addr addr;
+ struct ospf_if_params *params;
+ int ret;
+
+ if (argc == 4) {
+ ret = inet_aton(argv[idx_ipv4]->arg, &addr);
+ if (!ret) {
+ vty_out(vty,
+ "Please specify interface address by A.B.C.D\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ params = ospf_get_if_params(ifp, addr);
+ ospf_if_update_params(ifp, addr);
+ } else {
+ params = IF_DEF_PARAMS(ifp);
+ }
+
+ params->passive_interface = OSPF_IF_PASSIVE;
+ SET_IF_PARAM(params, passive_interface);
+
+ ospf_passive_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ospf_passive,
+ no_ip_ospf_passive_cmd,
+ "no ip ospf passive [A.B.C.D]",
+ NO_STR
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Enable routing updates on an interface\n"
+ "Address of interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int idx_ipv4 = 4;
+ struct in_addr addr;
+ struct ospf_if_params *params;
+ int ret;
+
+ if (argc == 5) {
+ ret = inet_aton(argv[idx_ipv4]->arg, &addr);
+ if (!ret) {
+ vty_out(vty,
+ "Please specify interface address by A.B.C.D\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ params = ospf_lookup_if_params(ifp, addr);
+ if (params == NULL)
+ return CMD_SUCCESS;
+ } else {
+ params = IF_DEF_PARAMS(ifp);
+ }
+
+ params->passive_interface = OSPF_IF_ACTIVE;
+ UNSET_IF_PARAM(params, passive_interface);
+ if (params != IF_DEF_PARAMS(ifp)) {
+ ospf_free_if_params(ifp, addr);
+ ospf_if_update_params(ifp, addr);
+ }
+
+ ospf_passive_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (ospf_redistribute_source,
ospf_redistribute_source_cmd,
"redistribute " FRR_REDIST_STR_OSPFD " [{metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
@@ -11865,6 +11919,14 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
vty_out(vty, "\n");
}
+ if (OSPF_IF_PARAM_CONFIGURED(params,
+ passive_interface)) {
+ vty_out(vty, " ip ospf passive");
+ if (params != IF_DEF_PARAMS(ifp) && rn)
+ vty_out(vty, " %pI4", &rn->p.u.prefix4);
+ vty_out(vty, "\n");
+ }
+
/* LDP-Sync print */
if (params && params->ldp_sync_info)
ospf_ldp_sync_if_write_config(vty, params);
@@ -12312,10 +12374,6 @@ static int config_write_ospf_distance(struct vty *vty, struct ospf *ospf)
static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
{
- struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
- struct interface *ifp;
- struct ospf_interface *oi;
- struct listnode *node = NULL;
int write = 0;
/* `router ospf' print. */
@@ -12418,33 +12476,6 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
vty_out(vty, " no proactive-arp\n");
}
- FOR_ALL_INTERFACES (vrf, ifp)
- if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp),
- passive_interface)
- && IF_DEF_PARAMS(ifp)->passive_interface
- != ospf->passive_interface_default) {
- vty_out(vty, " %spassive-interface %s\n",
- IF_DEF_PARAMS(ifp)->passive_interface ? ""
- : "no ",
- ifp->name);
- }
- for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
- if (!OSPF_IF_PARAM_CONFIGURED(oi->params, passive_interface))
- continue;
- if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(oi->ifp),
- passive_interface)) {
- if (oi->params->passive_interface
- == IF_DEF_PARAMS(oi->ifp)->passive_interface)
- continue;
- } else if (oi->params->passive_interface
- == ospf->passive_interface_default)
- continue;
-
- vty_out(vty, " %spassive-interface %s %pI4\n",
- oi->params->passive_interface ? "" : "no ",
- oi->ifp->name, &oi->address->u.prefix4);
- }
-
/* TI-LFA print. */
if (ospf->ti_lfa_enabled) {
if (ospf->ti_lfa_protection_type == OSPF_TI_LFA_NODE_PROTECTION)
@@ -12639,6 +12670,10 @@ static void ospf_vty_if_init(void)
install_element(INTERFACE_NODE, &ip_ospf_area_cmd);
install_element(INTERFACE_NODE, &no_ip_ospf_area_cmd);
+ /* "ip ospf passive" commands. */
+ install_element(INTERFACE_NODE, &ip_ospf_passive_cmd);
+ install_element(INTERFACE_NODE, &no_ip_ospf_passive_cmd);
+
/* These commands are compatibitliy for previous version. */
install_element(INTERFACE_NODE, &ospf_authentication_key_cmd);
install_element(INTERFACE_NODE, &ospf_message_digest_key_cmd);
@@ -12798,7 +12833,9 @@ void ospf_vty_init(void)
install_element(OSPF_NODE, &no_ospf_router_id_cmd);
/* "passive-interface" commands. */
+ install_element(OSPF_NODE, &ospf_passive_interface_default_cmd);
install_element(OSPF_NODE, &ospf_passive_interface_addr_cmd);
+ install_element(OSPF_NODE, &no_ospf_passive_interface_default_cmd);
install_element(OSPF_NODE, &no_ospf_passive_interface_addr_cmd);
/* "ospf abr-type" commands. */
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 38c0ca2b67..e95ee55e68 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -386,7 +386,7 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name)
new->proactive_arp = OSPF_PROACTIVE_ARP_DEFAULT;
- ospf_gr_helper_init(new);
+ ospf_gr_helper_instance_init(new);
ospf_asbr_external_aggregator_init(new);
@@ -651,6 +651,9 @@ void ospf_terminate(void)
for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf))
ospf_finish(ospf);
+ /* Cleanup GR */
+ ospf_gr_helper_stop();
+
/* Cleanup route maps */
route_map_finish();
@@ -692,7 +695,6 @@ static void ospf_finish_final(struct ospf *ospf)
struct route_node *rn;
struct ospf_nbr_nbma *nbr_nbma;
struct ospf_lsa *lsa;
- struct interface *ifp;
struct ospf_interface *oi;
struct ospf_area *area;
struct ospf_vl_data *vl_data;
@@ -740,15 +742,6 @@ static void ospf_finish_final(struct ospf *ospf)
if (ospf->vrf_id == VRF_DEFAULT)
ospf_ldp_sync_gbl_exit(ospf, true);
- /* Remove ospf interface config params: only passive-interface */
- FOR_ALL_INTERFACES (vrf, ifp) {
- struct ospf_if_params *params;
-
- params = IF_DEF_PARAMS(ifp);
- if (OSPF_IF_PARAM_CONFIGURED(params, passive_interface))
- UNSET_IF_PARAM(params, passive_interface);
- }
-
/* Reset interface. */
for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi))
ospf_if_free(oi);
@@ -825,8 +818,8 @@ static void ospf_finish_final(struct ospf *ospf)
if ((lsa = rn->info) != NULL) {
ospf_lsa_unlock(&lsa);
rn->info = NULL;
+ route_unlock_node(rn);
}
- route_unlock_node(rn);
}
route_table_finish(ospf->maxage_lsa);
@@ -900,7 +893,7 @@ static void ospf_finish_final(struct ospf *ospf)
list_delete(&ospf->oi_write_q);
/* Reset GR helper data structers */
- ospf_gr_helper_stop(ospf);
+ ospf_gr_helper_instance_stop(ospf);
close(ospf->fd);
stream_free(ospf->ibuf);
diff --git a/staticd/static_debug.h b/staticd/static_debug.h
index 3a96339f47..ee9f7b0537 100644
--- a/staticd/static_debug.h
+++ b/staticd/static_debug.h
@@ -23,11 +23,14 @@
#ifndef _STATIC_DEBUG_H
#define _STATIC_DEBUG_H
-
#include <zebra.h>
#include "lib/debug.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* staticd debugging records */
extern struct debug static_dbg_events;
extern struct debug static_dbg_route;
@@ -70,5 +73,8 @@ int static_debug_status_write(struct vty *vty);
*/
void static_debug_set(int vtynode, bool onoff, bool events, bool route);
+#ifdef __cplusplus
+}
+#endif
#endif /* _STATIC_DEBUG_H */
diff --git a/staticd/static_nb.h b/staticd/static_nb.h
index 0313ae1c3a..5c3030fcfa 100644
--- a/staticd/static_nb.h
+++ b/staticd/static_nb.h
@@ -18,6 +18,10 @@
#ifndef _FRR_STATIC_NB_H_
#define _FRR_STATIC_NB_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern const struct frr_yang_module_info frr_staticd_info;
/* Mandatory callbacks. */
@@ -181,4 +185,8 @@ int routing_control_plane_protocols_name_validate(
FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \
FRR_STATIC_ROUTE_NH_KEY_XPATH
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/staticd/static_nht.h b/staticd/static_nht.h
index 9139c367d1..08dba2ebb5 100644
--- a/staticd/static_nht.h
+++ b/staticd/static_nht.h
@@ -20,6 +20,10 @@
#ifndef __STATIC_NHT_H__
#define __STATIC_NHT_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* When we get notification that nexthop tracking has an answer for
* us call this function to find the nexthop we are tracking so it
@@ -53,4 +57,9 @@ extern void static_nht_mark_state(struct prefix *sp, vrf_id_t vrf_id,
*/
extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/staticd/static_routes.h b/staticd/static_routes.h
index f64a40329d..1269621cc9 100644
--- a/staticd/static_routes.h
+++ b/staticd/static_routes.h
@@ -24,6 +24,10 @@
#include "table.h"
#include "memory.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
DECLARE_MGROUP(STATIC);
/* Static route label information */
@@ -216,4 +220,9 @@ extern void zebra_stable_node_cleanup(struct route_table *table,
*/
extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/staticd/static_vrf.h b/staticd/static_vrf.h
index 3977899054..be311af8c4 100644
--- a/staticd/static_vrf.h
+++ b/staticd/static_vrf.h
@@ -20,6 +20,10 @@
#ifndef __STATIC_VRF_H__
#define __STATIC_VRF_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct static_vrf {
struct vrf *vrf;
@@ -43,4 +47,8 @@ struct route_table *static_vrf_static_table(afi_t afi, safi_t safi,
struct static_vrf *svrf);
extern void static_vrf_terminate(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/staticd/static_vty.h b/staticd/static_vty.h
index 7ffc8d9c98..01577685e5 100644
--- a/staticd/static_vty.h
+++ b/staticd/static_vty.h
@@ -19,8 +19,17 @@
#ifndef __STATIC_VTY_H__
#define __STATIC_VTY_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int static_config(struct vty *vty, struct static_vrf *svrf,
afi_t afi, safi_t safi, const char *cmd);
void static_vty_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c
index 19c578c60e..40275908f7 100644
--- a/staticd/static_zebra.c
+++ b/staticd/static_zebra.c
@@ -529,6 +529,16 @@ void static_zebra_init(void)
"Static Nexthop Tracking hash");
}
+/* static_zebra_stop used by tests/lib/test_grpc.cpp */
+void static_zebra_stop(void)
+{
+ if (!zclient)
+ return;
+ zclient_stop(zclient);
+ zclient_free(zclient);
+ zclient = NULL;
+}
+
void static_zebra_vrf_register(struct vrf *vrf)
{
if (vrf->vrf_id == VRF_DEFAULT)
diff --git a/staticd/static_zebra.h b/staticd/static_zebra.h
index 9f93f3ee63..ca6308559e 100644
--- a/staticd/static_zebra.h
+++ b/staticd/static_zebra.h
@@ -19,6 +19,10 @@
#ifndef __STATIC_ZEBRA_H__
#define __STATIC_ZEBRA_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct thread_master *master;
extern void static_zebra_nht_register(struct route_node *rn,
@@ -28,9 +32,15 @@ extern void static_zebra_route_add(struct route_node *rn,
struct static_path *pn, safi_t safi,
bool install);
extern void static_zebra_init(void);
+/* static_zebra_stop used by tests/lib/test_grpc.cpp */
+extern void static_zebra_stop(void);
extern void static_zebra_vrf_register(struct vrf *vrf);
extern void static_zebra_vrf_unregister(struct vrf *vrf);
extern int static_zebra_nh_update(struct route_node *rn,
struct static_nexthop *nh);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/tests/lib/test_grpc.cpp b/tests/lib/test_grpc.cpp
new file mode 100644
index 0000000000..491796802a
--- /dev/null
+++ b/tests/lib/test_grpc.cpp
@@ -0,0 +1,979 @@
+/*
+ * May 16 2021, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ *
+ * This program 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 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <time.h>
+#include <unistd.h>
+#include <zebra.h>
+
+#include "filter.h"
+#include "frr_pthread.h"
+#include "libfrr.h"
+#include "routing_nb.h"
+#include "northbound_cli.h"
+#include "thread.h"
+#include "vrf.h"
+#include "vty.h"
+
+#include "staticd/static_debug.h"
+#include "staticd/static_nb.h"
+#include "staticd/static_vrf.h"
+#include "staticd/static_vty.h"
+#include "staticd/static_zebra.h"
+
+// GRPC C++ includes
+#include <string>
+#include <sstream>
+#include <grpc/grpc.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/security/credentials.h>
+#include "grpc/frr-northbound.grpc.pb.h"
+
+DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm));
+DEFINE_KOOH(frr_fini, (), ());
+
+struct vty *vty;
+
+bool mpls_enabled;
+struct thread_master *master;
+struct zebra_privs_t static_privs = {0};
+struct frrmod_runtime *grpc_module;
+char binpath[2 * MAXPATHLEN + 1];
+
+extern const char *json_expect1;
+extern const char *json_expect2;
+extern const char *json_expect3;
+extern const char *json_loadconf1;
+
+int test_dbg = 1;
+
+void inline test_debug(const std::string &s)
+{
+ if (test_dbg)
+ std::cout << s << std::endl;
+}
+
+// static struct option_chain modules[] = {{ .arg = "grpc:50051" }]
+// static struct option_chain **modnext = modules->next;
+
+static const struct frr_yang_module_info *const staticd_yang_modules[] = {
+ &frr_interface_info, &frr_filter_info, &frr_routing_info,
+ &frr_staticd_info, &frr_vrf_info,
+};
+
+static int grpc_thread_stop(struct thread *thread);
+
+static void static_startup(void)
+{
+ // struct frrmod_runtime module;
+ // static struct option_chain *oc;
+ char moderr[256] = {};
+ cmd_init(1);
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ zprivs_preinit(&static_privs);
+ zprivs_init(&static_privs);
+
+ /* Load the server side module -- check libtool path first */
+ std::string modpath = std::string(binpath) + std::string("../../../lib/.libs");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr, sizeof(moderr));
+ if (!grpc_module) {
+ modpath = std::string(binpath) + std::string("../../lib");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr,
+ sizeof(moderr));
+ }
+ if (!grpc_module) {
+ std::cout << "Failed to load grpc module:" << moderr
+ << std::endl;
+ exit(1);
+ }
+
+ static_debug_init();
+
+ master = thread_master_create(NULL);
+ nb_init(master, staticd_yang_modules, array_size(staticd_yang_modules),
+ false);
+
+ static_zebra_init();
+ vty_init(master, true);
+ static_vrf_init();
+ static_vty_init();
+
+ hook_register(routing_conf_event,
+ routing_control_plane_protocols_name_validate);
+
+ routing_control_plane_protocols_register_vrf_dependency();
+
+ // Add a route
+ vty = vty_new();
+ vty->type = vty::VTY_TERM;
+ vty_config_enter(vty, true, false);
+
+ auto ret = cmd_execute(vty, "ip route 11.0.0.0/8 Null0", NULL, 0);
+ assert(!ret);
+
+ ret = cmd_execute(vty, "end", NULL, 0);
+ assert(!ret);
+
+ nb_cli_pending_commit_check(vty);
+
+ frr_pthread_init();
+
+ // frr_config_fork();
+ hook_call(frr_late_init, master);
+}
+
+static void static_shutdown(void)
+{
+ hook_call(frr_fini);
+ vty_close(vty);
+ vrf_terminate();
+ vty_terminate();
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+ thread_master_free(master);
+ master = NULL;
+}
+
+using frr::Northbound;
+using grpc::Channel;
+using grpc::ClientAsyncResponseReader;
+using grpc::ClientContext;
+using grpc::CompletionQueue;
+using grpc::Status;
+
+class NorthboundClient
+{
+ public:
+ NorthboundClient(std::shared_ptr<Channel> channel)
+ : stub_(frr::Northbound::NewStub(channel))
+ {
+ }
+
+ void Commit(uint32_t candidate_id)
+ {
+ frr::CommitRequest request;
+ frr::CommitResponse reply;
+ ClientContext context;
+ Status status;
+
+ request.set_candidate_id(candidate_id);
+
+ request.set_phase(frr::CommitRequest::ALL);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+#if 0
+ request.set_phase(frr::CommitRequest::VALIDATE);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ request.set_phase(frr::CommitRequest::PREPARE);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ request.set_phase(frr::CommitRequest::APPLY);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+#endif
+ }
+
+ uint32_t CreateCandidate()
+ {
+ frr::CreateCandidateRequest request;
+ frr::CreateCandidateResponse reply;
+ ClientContext context;
+ Status status;
+
+ status = stub_->CreateCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ return reply.candidate_id();
+ }
+
+ void DeleteCandidate(uint32_t candidate_id)
+ {
+ frr::DeleteCandidateRequest request;
+ frr::DeleteCandidateResponse reply;
+ ClientContext context;
+ Status status;
+
+ request.set_candidate_id(candidate_id);
+ status = stub_->DeleteCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ void EditCandidate(uint32_t candidate_id, const std::string &path,
+ const std::string &value)
+ {
+ frr::EditCandidateRequest request;
+ frr::EditCandidateResponse reply;
+ ClientContext context;
+
+ request.set_candidate_id(candidate_id);
+ frr::PathValue *pv = request.add_update();
+ pv->set_path(path);
+ pv->set_value(value);
+
+ Status status = stub_->EditCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ std::string Get(const std::string &path,
+ frr::GetRequest::DataType dtype, frr::Encoding enc,
+ bool with_defaults)
+ {
+ frr::GetRequest request;
+ frr::GetResponse reply;
+ ClientContext context;
+ std::ostringstream ss;
+
+ request.set_type(dtype);
+ request.set_encoding(enc);
+ request.set_with_defaults(with_defaults);
+ request.add_path(path);
+
+ auto stream = stub_->Get(&context, request);
+ while (stream->Read(&reply)) {
+ ss << reply.data().data() << std::endl;
+ }
+ auto status = stream->Finish();
+ _throw_if_not_ok(status);
+ return ss.str();
+ }
+
+ std::string GetCapabilities()
+ {
+ frr::GetCapabilitiesRequest request;
+ frr::GetCapabilitiesResponse reply;
+ ClientContext context;
+
+ Status status =
+ stub_->GetCapabilities(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ std::ostringstream ss;
+ ss << "Capabilities:" << std::endl
+ << "\tVersion: " << reply.frr_version() << std::endl
+ << "\tRollback Support: " << reply.rollback_support()
+ << std::endl
+ << "\tSupported Modules:";
+
+ for (int i = 0; i < reply.supported_modules_size(); i++) {
+ auto sm = reply.supported_modules(i);
+ ss << std::endl
+ << "\t\tName: \"" << sm.name()
+ << "\" Revision: " << sm.revision() << " Org: \""
+ << sm.organization() << "\"";
+ }
+
+ ss << std::endl << "\tSupported Encodings:";
+
+ for (int i = 0; i < reply.supported_encodings_size(); i++) {
+ auto se = reply.supported_encodings(i);
+ auto desc =
+ google::protobuf::GetEnumDescriptor<decltype(
+ se)>();
+ ss << std::endl
+ << "\t\t" << desc->FindValueByNumber(se)->name();
+ }
+
+ ss << std::endl;
+
+ return ss.str();
+ }
+
+ void LoadToCandidate(uint32_t candidate_id, bool is_replace,
+ bool is_json, const std::string &data)
+ {
+ frr::LoadToCandidateRequest request;
+ frr::LoadToCandidateResponse reply;
+ frr::DataTree *dt = new frr::DataTree;
+ ClientContext context;
+
+ request.set_candidate_id(candidate_id);
+ request.set_type(is_replace
+ ? frr::LoadToCandidateRequest::REPLACE
+ : frr::LoadToCandidateRequest::MERGE);
+ dt->set_encoding(is_json ? frr::JSON : frr::XML);
+ dt->set_data(data);
+ request.set_allocated_config(dt);
+
+ Status status =
+ stub_->LoadToCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ std::string ListTransactions()
+ {
+ frr::ListTransactionsRequest request;
+ frr::ListTransactionsResponse reply;
+ ClientContext context;
+ std::ostringstream ss;
+
+ auto stream = stub_->ListTransactions(&context, request);
+
+ while (stream->Read(&reply)) {
+ ss << "Tx ID: " << reply.id()
+ << " client: " << reply.client()
+ << " date: " << reply.date()
+ << " comment: " << reply.comment() << std::endl;
+ }
+
+ auto status = stream->Finish();
+ _throw_if_not_ok(status);
+ return ss.str();
+ }
+
+ private:
+ std::unique_ptr<frr::Northbound::Stub> stub_;
+
+ void _throw_if_not_ok(Status &status)
+ {
+ if (!status.ok())
+ throw std::runtime_error(
+ std::to_string(status.error_code()) + ": "
+ + status.error_message());
+ }
+};
+
+
+bool stop = false;
+
+int grpc_client_test_stop(struct frr_pthread *fpt, void **result)
+{
+ test_debug("client: STOP pthread");
+
+ assert(fpt->running);
+ atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
+
+ test_debug("client: joining pthread");
+ pthread_join(fpt->thread, result);
+
+ test_debug("client: joined pthread");
+ return 0;
+}
+
+int find_first_diff(const std::string &s1, const std::string &s2)
+{
+ int s1len = s1.length();
+ int s2len = s2.length();
+ int mlen = std::min(s1len, s2len);
+
+ for (int i = 0; i < mlen; i++)
+ if (s1[i] != s2[i])
+ return i;
+ return s1len == s2len ? -1 : mlen;
+}
+
+void assert_no_diff(const std::string &s1, const std::string &s2)
+{
+ int pos = find_first_diff(s1, s2);
+ if (pos == -1)
+ return;
+ std::cout << "not ok" << std::endl;
+ std::cout << "Same: " << s1.substr(0, pos) << std::endl;
+ std::cout << "Diff s1: " << s1.substr(pos) << std::endl;
+ std::cout << "Diff s2: " << s2.substr(pos) << std::endl;
+ assert(false);
+}
+
+void assert_config_same(NorthboundClient &client, const std::string &compare)
+{
+ std::string confs = client.Get("/frr-routing:routing",
+ frr::GetRequest::ALL, frr::JSON, true);
+ assert_no_diff(confs, compare);
+ std::cout << "ok" << std::endl;
+}
+
+void grpc_client_run_test(void)
+{
+ NorthboundClient client(grpc::CreateChannel(
+ "localhost:50051", grpc::InsecureChannelCredentials()));
+
+ std::string reply = client.GetCapabilities();
+
+ uint32_t cid;
+ cid = client.CreateCandidate();
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+ assert(cid == 1);
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+ cid = client.CreateCandidate();
+ assert(cid == 2);
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+
+ /*
+ * Get initial configuration
+ */
+ std::cout << "Comparing initial config...";
+ assert_config_same(client, json_expect1);
+
+ /*
+ * Add config using EditCandidate
+ */
+
+ char xpath_buf[1024];
+ strlcpy(xpath_buf,
+ "/frr-routing:routing/control-plane-protocols/"
+ "control-plane-protocol[type='frr-staticd:staticd']"
+ "[name='staticd'][vrf='default']/frr-staticd:staticd/route-list",
+ sizeof(xpath_buf));
+ int slen = strlen(xpath_buf);
+ for (int i = 0; i < 4; i++) {
+ snprintf(xpath_buf + slen, sizeof(xpath_buf) - slen,
+ "[prefix='13.0.%d.0/24']"
+ "[afi-safi='frr-routing:ipv4-unicast']/"
+ "path-list[table-id='0'][distance='1']/"
+ "frr-nexthops/nexthop[nh-type='blackhole']"
+ "[vrf='default'][gateway=''][interface='(null)']",
+ i);
+ client.EditCandidate(cid, xpath_buf, "");
+ }
+ client.Commit(cid);
+ std::cout << "Comparing EditCandidate config...";
+ assert_config_same(client, json_expect2);
+
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+
+ /*
+ * Add config using LoadToCandidate
+ */
+
+ cid = client.CreateCandidate();
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+
+ client.LoadToCandidate(cid, false, true, json_loadconf1);
+ client.Commit(cid);
+
+ std::cout << "Comparing LoadToCandidate config...";
+ assert_config_same(client, json_expect3);
+
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+
+ std::string ltxreply = client.ListTransactions();
+ // std::cout << "client: pthread received: " << ltxreply << std::endl;
+}
+
+void *grpc_client_test_start(void *arg)
+{
+ struct frr_pthread *fpt = (struct frr_pthread *)arg;
+ fpt->master->owner = pthread_self();
+ frr_pthread_set_name(fpt);
+ frr_pthread_notify_running(fpt);
+
+ try {
+ grpc_client_run_test();
+ std::cout << "TEST PASSED" << std::endl;
+ } catch (std::exception &e) {
+ std::cout << "Exception in test: " << e.what() << std::endl;
+ }
+
+ // Signal FRR event loop to stop
+ test_debug("client: pthread: adding event to stop us");
+ thread_add_event(master, grpc_thread_stop, NULL, 0, NULL);
+
+ test_debug("client: pthread: DONE (returning)");
+
+ return NULL;
+}
+
+static int grpc_thread_start(struct thread *thread)
+{
+ struct frr_pthread_attr client = {
+ .start = grpc_client_test_start,
+ .stop = grpc_client_test_stop,
+ };
+
+ auto pth = frr_pthread_new(&client, "GRPC Client thread", "grpc");
+ frr_pthread_run(pth, NULL);
+ frr_pthread_wait_running(pth);
+
+ return 0;
+}
+
+static int grpc_thread_stop(struct thread *thread)
+{
+ std::cout << __func__ << ": frr_pthread_stop_all" << std::endl;
+ frr_pthread_stop_all();
+ std::cout << __func__ << ": static_shutdown" << std::endl;
+ static_shutdown();
+ std::cout << __func__ << ": exit cleanly" << std::endl;
+ exit(0);
+}
+
+/*
+ * return abs path to this binary with trailing `/`. Does not parse path
+ * environment to find in path, which should not matter for unit testing.
+ */
+static int get_binpath(const char *argv0, char cwd[2 * MAXPATHLEN + 1])
+{
+ const char *rch;
+ if (argv0[0] == '/') {
+ *cwd = 0;
+ rch = strrchr(argv0, '/');
+ strlcpy(cwd, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1));
+ return 0;
+ }
+ if (!(rch = strrchr(argv0, '/'))) {
+ /* Does not handle using PATH, shouldn't matter for test */
+ errno = EINVAL;
+ return -1;
+ }
+ if (!getcwd(cwd, MAXPATHLEN))
+ return -1;
+ int len = strlen(cwd);
+ cwd[len++] = '/';
+ strlcpy(cwd + len, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1));
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ assert(argc >= 1);
+ if (get_binpath(argv[0], binpath) < 0)
+ exit(1);
+
+ static_startup();
+
+ thread_add_event(master, grpc_thread_start, NULL, 0, NULL);
+
+ /* Event Loop */
+ struct thread thread;
+ while (thread_fetch(master, &thread))
+ thread_call(&thread);
+ return 0;
+}
+
+// clang-format off
+
+const char *json_expect1 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
+
+const char *json_loadconf1 = R"NONCE(
+{
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "10.0.0.0/13",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default"
+ }
+ ]
+ }
+})NONCE";
+
+const char *json_expect2 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.0.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.1.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.2.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.3.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
+
+const char *json_expect3 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.0.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.1.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.2.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.3.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "10.0.0.0/13",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
diff --git a/tests/lib/test_grpc.py b/tests/lib/test_grpc.py
new file mode 100644
index 0000000000..06ae6c05dd
--- /dev/null
+++ b/tests/lib/test_grpc.py
@@ -0,0 +1,23 @@
+import inspect
+import os
+import subprocess
+import pytest
+import frrtest
+
+class TestGRPC(object):
+ program = "./test_grpc"
+
+ @pytest.mark.skipif(
+ 'S["GRPC_TRUE"]=""\n' not in open("../config.status").readlines(),
+ reason="GRPC not enabled",
+ )
+ def test_exits_cleanly(self):
+ basedir = os.path.dirname(inspect.getsourcefile(type(self)))
+ program = os.path.join(basedir, self.program)
+ proc = subprocess.Popen(
+ [frrtest.binpath(program)], stdin=subprocess.PIPE, stdout=subprocess.PIPE
+ )
+ output, _ = proc.communicate()
+ self.exitcode = proc.wait()
+ if self.exitcode != 0:
+ raise frrtest.TestExitNonzero(self)
diff --git a/tests/subdir.am b/tests/subdir.am
index 3996699774..ca477851e3 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -106,6 +106,12 @@ check_PROGRAMS = \
$(TESTS_ZEBRA) \
# end
+if GRPC
+check_PROGRAMS += \
+ tests/lib/test_grpc \
+ #end
+endif
+
if ZEROMQ
check_PROGRAMS += \
tests/lib/test_zmq \
@@ -156,9 +162,19 @@ TESTS_CFLAGS = \
# end
# note no -Werror
+TESTS_CXXFLAGS = \
+ $(AC_CXXFLAGS) \
+ $(LIBYANG_CFLAGS) \
+ $(SAN_FLAGS) \
+ # end
+# note no -Werror
+
ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP)
BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD)
+if GRPC
+GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
+endif
OSPFD_TEST_LDADD = ospfd/libfrrospf.a $(ALL_TESTS_LDADD)
OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD)
ZEBRA_TEST_LDADD = zebra/label_manager.o $(ALL_TESTS_LDADD)
@@ -251,6 +267,12 @@ tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD)
tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c
nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c
+if GRPC
+tests_lib_test_grpc_CXXFLAGS = $(WERROR) $(TESTS_CXXFLAGS)
+tests_lib_test_grpc_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_grpc_LDADD = $(GRPC_TESTS_LDADD)
+tests_lib_test_grpc_SOURCES = tests/lib/test_grpc.cpp
+endif
tests_lib_test_assert_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_assert_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_assert_LDADD = $(ALL_TESTS_LDADD)
diff --git a/tests/topotests/config_timing/r1/staticd.conf b/tests/topotests/config_timing/r1/staticd.conf
new file mode 100644
index 0000000000..0f9f97ca1a
--- /dev/null
+++ b/tests/topotests/config_timing/r1/staticd.conf
@@ -0,0 +1 @@
+log timestamp precision 3
diff --git a/tests/topotests/config_timing/r1/zebra.conf b/tests/topotests/config_timing/r1/zebra.conf
new file mode 100644
index 0000000000..46fd965034
--- /dev/null
+++ b/tests/topotests/config_timing/r1/zebra.conf
@@ -0,0 +1,18 @@
+log timestamp precision 3
+
+ip prefix-list ANY permit 0.0.0.0/0 le 32
+ipv6 prefix-list ANY seq 10 permit any
+
+route-map RM-NONE4 deny 10
+exit-route-map
+
+route-map RM-NONE6 deny 10
+exit-route-map
+
+interface r1-eth0
+ ip address 100.0.0.1/24
+ ipv6 address 2102::1/64
+exit
+
+ip protocol static route-map RM-NONE4
+ipv6 protocol static route-map RM-NONE6
diff --git a/tests/topotests/config_timing/test_config_timing.py b/tests/topotests/config_timing/test_config_timing.py
new file mode 100644
index 0000000000..db8baa860d
--- /dev/null
+++ b/tests/topotests/config_timing/test_config_timing.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+#
+# June 2 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test the timing of config operations.
+
+The initial add of 10k routes is used as a baseline for timing and all future
+operations are expected to complete in under 2 times that baseline. This is a
+lot of slop; however, the pre-batching code some of these operations (e.g.,
+adding the same set of 10k routes) would take 100 times longer, so the intention
+is to catch those types of regressions.
+"""
+
+import datetime
+import ipaddress
+import math
+import os
+import sys
+import pytest
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.staticd]
+
+class TimingTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+ tgen.add_router("r1")
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+
+def setup_module(mod):
+ tgen = Topogen(TimingTopo, mod.__name__)
+ tgen.start_topology()
+
+ 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_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def get_ip_networks(super_prefix, count):
+ count_log2 = math.log(count, 2)
+ if count_log2 != int(count_log2):
+ count_log2 = int(count_log2) + 1
+ else:
+ count_log2 = int(count_log2)
+ network = ipaddress.ip_network(super_prefix)
+ return tuple(network.subnets(count_log2))[0:count]
+
+def test_static_timing():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def do_config(
+ count, bad_indices, base_delta, d_multiplier, add=True, do_ipv6=False, super_prefix=None, en_dbg=False
+ ):
+ router_list = tgen.routers()
+ tot_delta = float(0)
+
+ optype = "adding" if add else "removing"
+ iptype = "IPv6" if do_ipv6 else "IPv4"
+ if super_prefix is None:
+ super_prefix = u"2001::/48" if do_ipv6 else u"10.0.0.0/8"
+ via = u"lo"
+ optyped = "added" if add else "removed"
+
+ for rname, router in router_list.items():
+ router.logger.info("{} {} static {} routes".format(
+ optype, count, iptype)
+ )
+
+ # Generate config file.
+ config_file = os.path.join(
+ router.logdir, rname, "{}-routes-{}.conf".format(
+ iptype.lower(), optype
+ )
+ )
+ with open(config_file, "w") as f:
+ for i, net in enumerate(get_ip_networks(super_prefix, count)):
+ if i in bad_indices:
+ if add:
+ f.write("ip route {} {} bad_input\n".format(net, via))
+ else:
+ f.write("no ip route {} {} bad_input\n".format(net, via))
+ elif add:
+ f.write("ip route {} {}\n".format(net, via))
+ else:
+ f.write("no ip route {} {}\n".format(net, via))
+
+ # Enable debug
+ if en_dbg:
+ router.vtysh_cmd("debug northbound callbacks configuration")
+
+ # Load config file.
+ load_command = 'vtysh -f "{}"'.format(config_file)
+ tstamp = datetime.datetime.now()
+ output = router.run(load_command)
+ delta = (datetime.datetime.now() - tstamp).total_seconds()
+ tot_delta += delta
+
+ router.logger.info(
+ "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format(
+ load_command, output, delta
+ )
+ )
+
+ limit_delta = base_delta * d_multiplier
+ logger.info(
+ "{} {} {} static routes under {} in {}s (limit: {}s)".format(
+ optyped, count, iptype.lower(), super_prefix, tot_delta, limit_delta
+ )
+ )
+ if limit_delta:
+ assert tot_delta <= limit_delta
+
+ return tot_delta
+
+ # Number of static routes
+ prefix_count = 10000
+ prefix_base = [[u"10.0.0.0/8", u"11.0.0.0/8"],
+ [u"2100:1111:2220::/44", u"2100:3333:4440::/44"]]
+
+ bad_indices = []
+ for ipv6 in [False, True]:
+ base_delta = do_config(prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0])
+
+ # Another set of same number of prefixes
+ do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][1])
+
+ # Duplicate config
+ do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][0])
+
+ # Remove 1/2 of duplicate
+ do_config(prefix_count / 2, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][0])
+
+ # Add all back in so 1/2 replicate 1/2 new
+ do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][0])
+
+ # remove all
+ delta = do_config(prefix_count, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][0])
+ delta += do_config(prefix_count, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][1])
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf6_topo2/r2/ospf6d.conf b/tests/topotests/ospf6_topo2/r2/ospf6d.conf
index d4bb0e2a41..e88e965c78 100644
--- a/tests/topotests/ospf6_topo2/r2/ospf6d.conf
+++ b/tests/topotests/ospf6_topo2/r2/ospf6d.conf
@@ -6,12 +6,18 @@ interface r2-eth1
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
+interface r2-eth2
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
router ospf6
ospf6 router-id 10.254.254.2
redistribute connected
redistribute static
default-information originate always metric 123
area 0.0.0.1 stub
+ area 0.0.0.2 nssa
interface r2-eth0 area 0.0.0.1
interface r2-eth1 area 0.0.0.0
+ interface r2-eth2 area 0.0.0.2
!
diff --git a/tests/topotests/ospf6_topo2/r2/zebra.conf b/tests/topotests/ospf6_topo2/r2/zebra.conf
index 891945a4e7..559f502b0c 100644
--- a/tests/topotests/ospf6_topo2/r2/zebra.conf
+++ b/tests/topotests/ospf6_topo2/r2/zebra.conf
@@ -6,3 +6,6 @@ interface r2-eth0
interface r2-eth1
ipv6 address 2001:db8:2::2/64
!
+interface r2-eth2
+ ipv6 address 2001:db8:3::1/64
+!
diff --git a/tests/topotests/ospf6_topo2/r4/ospf6d.conf b/tests/topotests/ospf6_topo2/r4/ospf6d.conf
new file mode 100644
index 0000000000..813c0abff2
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r4/ospf6d.conf
@@ -0,0 +1,9 @@
+interface r4-eth0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.4
+ area 0.0.0.2 nssa
+ interface r4-eth0 area 0.0.0.2
+!
diff --git a/tests/topotests/ospf6_topo2/r4/zebra.conf b/tests/topotests/ospf6_topo2/r4/zebra.conf
new file mode 100644
index 0000000000..86cb972a9f
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r4/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface r4-eth0
+ ipv6 address 2001:db8:3::2/64
+!
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.dot b/tests/topotests/ospf6_topo2/test_ospf6_topo2.dot
index ba7a36f2b5..238ec7a5a0 100644
--- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.dot
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.dot
@@ -34,6 +34,12 @@ graph template {
fillcolor="#f08080",
style=filled,
];
+ r4 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
# Switches
sw1 [
@@ -62,10 +68,16 @@ graph template {
}
subgraph cluster1 {
+ label="area 0.0.0.2";
+ r4 -- sw3 [label="eth0\n.2"];
+ }
+
+ subgraph cluster2 {
label="area 0.0.0.0";
r2 -- sw1 [label="eth0\n.1"];
r2 -- sw2 [label="eth1\n.2"];
+ r2 -- sw3 [label="eth2\n.1"];
+
r3 -- sw2 [label="eth0\n.1"];
- r3 -- sw3 [label="eth1\n.2"];
}
}
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.png b/tests/topotests/ospf6_topo2/test_ospf6_topo2.png
index ee1de60736..4e79559a60 100644
--- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.png
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.png
Binary files differ
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
index efc8565bb3..0fe5228ce6 100644
--- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
@@ -47,6 +47,49 @@ from mininet.topo import Topo
pytestmark = [pytest.mark.ospf6d]
+def expect_lsas(router, area, lsas, wait=5, extra_params=""):
+ """
+ Run the OSPFv3 show LSA database command and expect the supplied LSAs.
+
+ Optional parameters:
+ * `wait`: amount of seconds to wait.
+ * `extra_params`: extra LSA database parameters.
+ * `inverse`: assert the inverse of the expected.
+ """
+ tgen = get_topogen()
+
+ command = "show ipv6 ospf6 database {} json".format(extra_params)
+
+ logger.info("waiting OSPFv3 router '{}' LSA".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ command,
+ {"areaScopedLinkStateDb": [{"areaId": area, "lsa": lsas}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+
+ assert result is None, assertmsg
+
+
+def expect_ospfv3_routes(router, routes, wait=5):
+ "Run command `ipv6 ospf6 route` and expect route with type."
+ tgen = get_topogen()
+
+ logger.info("waiting OSPFv3 router '{}' route".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 route json",
+ {"routes": routes}
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+
+ assert result is None, assertmsg
+
+
class OSPFv3Topo2(Topo):
"Test topology builder"
@@ -54,8 +97,8 @@ class OSPFv3Topo2(Topo):
"Build function"
tgen = get_topogen(self)
- # Create 3 routers
- for routern in range(1, 4):
+ # Create 4 routers
+ for routern in range(1, 5):
tgen.add_router("r{}".format(routern))
switch = tgen.add_switch("s1")
@@ -66,6 +109,10 @@ class OSPFv3Topo2(Topo):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r4"])
+
def setup_module(mod):
"Sets up the pytest environment"
@@ -110,7 +157,52 @@ def test_wait_protocol_convergence():
expect_neighbor_full("r1", "10.254.254.2")
expect_neighbor_full("r2", "10.254.254.1")
expect_neighbor_full("r2", "10.254.254.3")
+ expect_neighbor_full("r2", "10.254.254.4")
expect_neighbor_full("r3", "10.254.254.2")
+ expect_neighbor_full("r4", "10.254.254.2")
+
+
+def test_ospfv3_expected_route_types():
+ "Test routers route type to determine if NSSA/Stub is working as expected."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_ospf6_route_types(router, expected_summary):
+ "Expect the correct route types."
+ logger.info("waiting OSPFv3 router '{}'".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 route summary json",
+ expected_summary,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Stub router: no external routes.
+ expect_ospf6_route_types(
+ "r1",
+ {
+ "numberOfIntraAreaRoutes": 1,
+ "numberOfInterAreaRoutes": 3,
+ "numberOfExternal1Routes": 0,
+ "numberOfExternal2Routes": 0,
+ },
+ )
+ # NSSA router: no external routes.
+ expect_ospf6_route_types(
+ "r4",
+ {
+ "numberOfIntraAreaRoutes": 1,
+ "numberOfInterAreaRoutes": 2,
+ "numberOfExternal1Routes": 0,
+ "numberOfExternal2Routes": 0,
+ },
+ )
def test_ospf6_default_route():
@@ -134,36 +226,96 @@ def test_ospf6_default_route():
assertmsg = '"{}" convergence failure'.format(router)
assert result is None, assertmsg
- def expect_lsa(router, area, prefix, metric):
- "Test OSPF6 LSA existence."
- logger.info("waiting OSPFv3 router '{}' LSA".format(router))
- test_func = partial(
- topotest.router_json_cmp,
- tgen.gears[router],
- "show ipv6 ospf6 database inter-prefix detail json",
- {
- "areaScopedLinkStateDb": [
- {
- "areaId": area,
- "lsa": [
- {
- "prefix": prefix,
- "metric": metric,
- }
- ],
- }
- ]
- },
- )
- _, result = topotest.run_and_expect(test_func, None, count=4, wait=1)
- assertmsg = '"{}" convergence failure'.format(router)
- assert result is None, assertmsg
-
metric = 123
- expect_lsa("r1", "0.0.0.1", "::/0", metric)
+ expect_lsas(
+ "r1",
+ "0.0.0.1",
+ [{"prefix": "::/0", "metric": metric}],
+ extra_params="inter-prefix detail",
+ )
expect_route("r1", "::/0", metric + 10)
+def test_nssa_lsa_type7():
+ """
+ Test that static route gets announced as external route when redistributed
+ and gets removed when redistribution stops.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Add new static route and check if it gets announced as LSA Type-7.
+ #
+ config = """
+ configure terminal
+ ipv6 route 2001:db8:100::/64 Null0
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ lsas = [
+ {
+ "type": "NSSA",
+ "advertisingRouter": "10.254.254.2",
+ "prefix": "2001:db8:100::/64",
+ "forwardingAddress": "2001:db8:3::1",
+ }
+ ]
+ route = {
+ "2001:db8:100::/64": {
+ "pathType": "E1",
+ "nextHops": [
+ {"nextHop": "::", "interfaceName": "r4-eth0"}
+ ]
+ }
+ }
+
+ logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to show up")
+ expect_lsas("r4", "0.0.0.2", lsas, wait=30, extra_params="type-7 detail")
+ expect_ospfv3_routes("r4", route, wait=30)
+
+ #
+ # Remove static route and check for LSA Type-7 removal.
+ #
+ config = """
+ configure terminal
+ no ipv6 route 2001:db8:100::/64 Null0
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ def dont_expect_lsa(unexpected_lsa):
+ "Specialized test function to expect LSA go missing"
+ output = tgen.gears["r4"].vtysh_cmd("show ipv6 ospf6 database type-7 detail json", isjson=True)
+ for lsa in output['areaScopedLinkStateDb'][0]['lsa']:
+ if lsa["prefix"] == unexpected_lsa["prefix"]:
+ if lsa["forwardingAddress"] == unexpected_lsa["forwardingAddress"]:
+ return lsa
+ return None
+
+ def dont_expect_route(unexpected_route):
+ "Specialized test function to expect route go missing"
+ output = tgen.gears["r4"].vtysh_cmd("show ipv6 ospf6 route json", isjson=True)
+ if output["routes"].has_key(unexpected_route):
+ return output["routes"][unexpected_route]
+ return None
+
+
+ logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to go away")
+
+ # Test that LSA doesn't exist.
+ test_func = partial(dont_expect_lsa, lsas[0])
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" LSA still exists'.format("r4")
+ assert result is None, assertmsg
+
+ # Test that route doesn't exist.
+ test_func = partial(dont_expect_route, "2001:db8:100::/64")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" route still exists'.format("r4")
+ assert result is None, assertmsg
+
+
def teardown_module(_mod):
"Teardown the pytest environment"
tgen = get_topogen()
diff --git a/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json
new file mode 100644
index 0000000000..1ddccb81b4
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json
@@ -0,0 +1,195 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r0-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR0"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
new file mode 100644
index 0000000000..88b549732c
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
@@ -0,0 +1,779 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""OSPF Summarisation Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+from time import sleep
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ kill_router_daemons,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ verify_rib,
+ create_static_routes,
+ step,
+ start_router_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ topo_daemons,
+ create_prefix_lists,
+ create_route_maps,
+ create_interfaces_cfg,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_rib,
+ create_router_ospf,
+ verify_ospf_summary,
+)
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospf_asbr_summary_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+NETWORK_11 = {"ipv4": ["11.0.20.6/32", "11.0.20.7/32"]}
+
+NETWORK2 = {
+ "ipv4": [
+ "12.0.20.1/32",
+ "12.0.20.2/32",
+ "12.0.20.3/32",
+ "12.0.20.4/32",
+ "12.0.20.5/32",
+ ]
+}
+SUMMARY = {"ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"]}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A0 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A0
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A0 +---+
+
+TESTCASES =
+1. OSPF summarisation functionality.
+2. OSPF summarisation with metric type 2.
+3. OSPF summarisation with Tag option
+4. OSPF summarisation with advertise and no advertise option
+5. OSPF summarisation Chaos.
+6. OSPF summarisation with route map filtering.
+7. OSPF summarisation with route map modification of metric type.
+8. OSPF CLI Show verify ospf ASBR summary config and show commands behaviours.
+"""
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def red_static(dut, config=True):
+ """
+ Local 'def' for Redstribute static routes inside ospf.
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """
+ Local 'def' for Redstribute connected routes inside ospf
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf": {"redistribute": [{"redist_type": "connected", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_type5_summary_tc43_p0(request):
+ """OSPF summarisation with metric type 2."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change the summary address mask to lower match (ex - 16 to 8)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "16"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "11.0.0.0/16": {
+ "Summary address": "11.0.0.0/16",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Change the summary address mask to higher match (ex - 8 to 24)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "24"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "11.0.0.0/16": {
+ "Summary address": "11.0.0.0/24",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+ step("Configure 2 summary address with different mask of same network.")
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(" Un configure one of the summary address.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "24"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc48_p0(request):
+ """OSPF summarisation with route map modification of metric type."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Configure route map and & rule to permit configured summary address,"
+ " redistribute static & connected routes with the route map."
+ )
+ step("Configure prefixlist to permit the static routes, add to route map.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure metric type as 1 in route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [{"action": "permit", "set": {"metric-type": "type-1"}}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Un configure metric type from route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "set": {"metric-type": "type-1"},
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change rule from permit to deny in prefix list.")
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index a50dccbda6..72835e7526 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -493,6 +493,7 @@ static int vtysh_execute_func(const char *line, int pager)
*/
while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON
&& ret != CMD_WARNING && ret != CMD_WARNING_CONFIG_FAILED
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& vty->node > CONFIG_NODE) {
vty->node = node_parent(vty->node);
ret = cmd_execute(vty, line, &cmd, 1);
@@ -794,6 +795,7 @@ int vtysh_mark_file(const char *filename)
*/
while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON
&& ret != CMD_WARNING && ret != CMD_WARNING_CONFIG_FAILED
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& vty->node > CONFIG_NODE) {
vty->node = node_parent(vty->node);
ret = cmd_execute_command_strict(vline, vty, &cmd);
diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c
index ca6a33cd52..5373f27882 100644
--- a/zebra/zebra_gr.c
+++ b/zebra/zebra_gr.c
@@ -118,7 +118,7 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client)
}
/*
- * A helper function to delte and destory client info.
+ * A helper function to delete and destroy client info.
*/
static void zebra_gr_client_info_delte(struct zserv *client,
struct client_gr_info *info)
@@ -162,7 +162,7 @@ int32_t zebra_gr_client_disconnect(struct zserv *client)
client->restart_time = monotime(&tv);
- /* For all the GR instance start the starle removal timer. */
+ /* For all the GR instance start the stale removal timer. */
TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) {
if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities)
&& (info->t_stale_removal == NULL)) {
@@ -298,7 +298,7 @@ void zebra_gr_client_reconnect(struct zserv *client)
/*
* Update the graceful restart information
* for the client instance.
- * This function handles all the capabilties that are received.
+ * This function handles all the capabilities that are received.
*/
static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api)
{
@@ -334,7 +334,7 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api)
if (!info)
info = zebra_gr_client_info_create(client);
- /* Udpate other parameters */
+ /* Update other parameters */
if (!info->gr_enable) {
client->gr_instance_count++;
@@ -460,7 +460,7 @@ static int32_t zebra_gr_route_stale_delete_timer_expiry(struct thread *thread)
cnt = zebra_gr_delete_stale_routes(info);
- /* Retsart the timer */
+ /* Restart the timer */
if (cnt > 0) {
LOG_GR("%s: Client %s processed %d routes. Start timer again",
__func__, zebra_route_string(client->proto), cnt);
@@ -471,7 +471,7 @@ static int32_t zebra_gr_route_stale_delete_timer_expiry(struct thread *thread)
&info->t_stale_removal);
} else {
/* No routes to delete for the VRF */
- LOG_GR("%s: Client %s all starle routes processed", __func__,
+ LOG_GR("%s: Client %s all stale routes processed", __func__,
zebra_route_string(client->proto));
XFREE(MTYPE_TMP, info->current_prefix);
@@ -668,7 +668,7 @@ static void zebra_gr_process_client_stale_routes(struct zserv *client,
* Cancel the stale timer and process the routes
*/
if (info->t_stale_removal) {
- LOG_GR("%s: Client %s cancled stale delete timer vrf %d",
+ LOG_GR("%s: Client %s canceled stale delete timer vrf %d",
__func__, zebra_route_string(client->proto),
info->vrf_id);
THREAD_OFF(info->t_stale_removal);
diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c
index 3b0c75151b..40a2c94e2a 100644
--- a/zebra/zebra_mlag.c
+++ b/zebra/zebra_mlag.c
@@ -111,7 +111,7 @@ void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len)
struct stream *s = NULL;
int msg_type = 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_MLAG_BUF_LIMIT);
/*
* Place holder we need the message type first
*/
@@ -1081,7 +1081,7 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data,
case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD_BULK: {
ZebraMlagMrouteAddBulk *Bulk_msg = NULL;
ZebraMlagMrouteAdd *msg = NULL;
- size_t i;
+ size_t i, length_spot;
Bulk_msg = zebra_mlag_mroute_add_bulk__unpack(
NULL, hdr->data.len, hdr->data.data);
@@ -1093,10 +1093,17 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data,
stream_putw(s, (Bulk_msg->n_mroute_add
* sizeof(struct mlag_mroute_add)));
/* No. of msgs in Batch */
- stream_putw(s, Bulk_msg->n_mroute_add);
+ length_spot = stream_putw(s, Bulk_msg->n_mroute_add);
/* Actual Data */
for (i = 0; i < Bulk_msg->n_mroute_add; i++) {
+ if (STREAM_SIZE(s)
+ < VRF_NAMSIZ + 22 + INTERFACE_NAMSIZ) {
+ zlog_warn(
+ "We have received more messages than we can parse at this point in time: %zu",
+ Bulk_msg->n_mroute_add);
+ break;
+ }
msg = Bulk_msg->mroute_add[i];
@@ -1116,13 +1123,16 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data,
else
stream_put(s, NULL, INTERFACE_NAMSIZ);
}
+
+ stream_putw_at(s, length_spot, i + 1);
+
zebra_mlag_mroute_add_bulk__free_unpacked(Bulk_msg,
NULL);
} break;
case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL_BULK: {
ZebraMlagMrouteDelBulk *Bulk_msg = NULL;
ZebraMlagMrouteDel *msg = NULL;
- size_t i;
+ size_t i, length_spot;
Bulk_msg = zebra_mlag_mroute_del_bulk__unpack(
NULL, hdr->data.len, hdr->data.data);
@@ -1134,10 +1144,16 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data,
stream_putw(s, (Bulk_msg->n_mroute_del
* sizeof(struct mlag_mroute_del)));
/* No. of msgs in Batch */
- stream_putw(s, Bulk_msg->n_mroute_del);
+ length_spot = stream_putw(s, Bulk_msg->n_mroute_del);
/* Actual Data */
for (i = 0; i < Bulk_msg->n_mroute_del; i++) {
+ if (STREAM_SIZE(s)
+ < VRF_NAMSIZ + 16 + INTERFACE_NAMSIZ) {
+ zlog_warn(
+ "We have received more messages than we can parse at this time");
+ break;
+ }
msg = Bulk_msg->mroute_del[i];
@@ -1154,6 +1170,9 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data,
else
stream_put(s, NULL, INTERFACE_NAMSIZ);
}
+
+ stream_putw_at(s, length_spot, i + 1);
+
zebra_mlag_mroute_del_bulk__free_unpacked(Bulk_msg,
NULL);
} break;