diff options
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 Binary files differindex ee1de60736..4e79559a60 100644 --- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.png +++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.png 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; |
